Server 용 Swift 로 Packet Capture Program 작성하기
현재 스위프트 지원 서버는 우분투만이 가능하다.
센토스도 설정을 맞추면 가능하긴 한데 정신건강을 위해 우분투를 사용하자.
Ubuntu 16.04 에서 Swift 를 설치하자
$ sudo apt-get install clang libicu-dev
이제 swift.org 에서 스위프트 압축된 파일을 다운로드 받는다.
$ wget -q -0 -https://swift.org/keys/all-keys.asc
$ tar xzf swift-<VERSION>-<PLATFORM>.tar.gz
이 명령은 /Home/user 밑에서 실행하여 하위로 푼다.
$ export PATH=/Home/user/usr/swift-4.0.3-RELEASE-ubuntu16.04/usr/bin:"${PATH}"
패스를 추가했다.
$ swift
Welcome to Swift version 4.0.3 어쩌고 나오면 성공한 것이다.
이제 libpcap Library 를 설치해보자.
$ sudo apt-get install libpcap-dev
이제 Swift 로 Packet Capture 할 준비가 되었다.
Cpcap 이란 폴더를 생성하자.
$ mkdir Cpcap
$ cd Cpcap
여기서 파일을 2개를 만들것이다.
$ touch Package.swift
$ touch module.modulemap
Package.swift 내용을 이렇다.
import PackageDescription
let package = Package(name: "Cpcap")
module.modulemap 내용을 이렇게 작성한다.
module Cpcap [system] {
header "/usr/include/pcap.h"
link "pcap"
export *
}
이제 git 을 생성해야 한다.
/Pcap 폴더에 위치한 지 확인 후 생성한다.
$ git init
$ git add Package.swift module.modulemap
$ git commit -m "Initial commit"
$ git tag 0.0.1
이제 Swift 에서 Pcap 을 패키지로 불러 올 수 있게 되었다.
일단 폴더를 Pcap 상위 폴더로 간다.
$ cd ..
$ ls
결과가 Pcap 폴더가 보여야 한다.
여기서 이제 서버용 패킷 캡쳐 프로그램일 ServerSideSwiftTap 를 생성한다.
$ mkdir ServerSideSwiftTap
$ cd ServerSideSwiftTap
이제 파일 2개를 만들 것이다.
$ touch Package.swift
$ touch main.swift
Package.swift 내용은 이렇다
import PackageDescription
let package = Package(name: "ServerSideSwiftTap", dependencies: [.Package(url: "../Cpcap", version: Version(0.0.1)..<Version(1.0.0))])
main.swift 내용을 이렇다.
import Foundation
import MongoKitten
import Cpcap
import Glibc
let server = try Server("mongodb://localhost:27017")
let database = server["packet_db"]
if server.isConnected {
print("Connected successfully to server")
}
let MAX_RECV_SIZE:Int32 = 65535
let PROMISC:Int32 = 1
let TIMEOUT:Int32 = 100
var frameNo = 0
let ver = UnsafePointer<Int8>(pcap_lib_version())!
print("pcap version -> \(String(cString: ver))")
var errbuf = UnsafeMutablePointer<Int8>.allocate(capacity: Int(PCAP_ERRBUF_SIZE))
let dev = pcap_lookupdev(errbuf)
var devName = "ens2"
if let dev = dev {
devName = String(cString: dev)
print("found \(devName)")
} else {
print("Could not get dev")
}
let pcapSession = pcap_open_live(devName, MAX_RECV_SIZE, PROMISC, TIMEOUT, errbuf)
let status = pcap_loop(pcapSession, -1, { (_, packHeader, packetData) in
// 헤더 받은 순서 및 시간 및 크기 표시
var tv_sec = Double(packHeader!.pointee.ts.tv_sec) + 60*60*9 // +0900
let sec = Int(tv_sec.truncatingRemainder(dividingBy: 60))
tv_sec = tv_sec / 60
let min = Int(tv_sec.truncatingRemainder(dividingBy: 60))
tv_sec = tv_sec / 60
let hour = Int(tv_sec.truncatingRemainder(dividingBy: 24))
frameNo = frameNo + 1
let str_Frame = String(format: "Frame %d: %d byte / %d byte %02d:%02d:%02d.%06ld", frameNo, packHeader!.pointee.caplen, packHeader!.pointee.len, hour, min, sec, packHeader!.pointee.ts.tv_usec)
//print(str_Frame)
// 패킷분석
let data = Data(bytes: packetData!, count: Int(packHeader!.pointee.caplen))
let arrayPacketData = Array(data)
let pa = PacketAnalyser.sharedInstance
//pa.process(packetDataArray: arrayPacketData)
let packetData = pa.processPacket(packetDataArray: arrayPacketData)
if packetData.src_ip != "" {
let userDocument: Document = [
"hour": String(format: "%02d", hour),
"minute": String(format: "%02d", min),
"second": String(format: "%02d", sec),
"mili_second": String(format: "%06d", packHeader!.pointee.ts.tv_usec),
"src_ip": packetData.src_ip,
"src_port": packetData.src_port,
"dst_ip": packetData.dst_ip,
"dst_port": packetData.dst_port,
"data": packetData.data
]
do {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd"
let str_date = dateFormatter.string(from: Date())
let collection_name = "tcp_collection_\(str_date)"
let collection = database[collection_name]
try collection.insert(userDocument)
print("success insert!!")
} catch {
print("tcp_collection insert error -> \(error.localizedDescription)")
}
}
}, nil)
pcap_close(pcapSession)
패킷분석을 위한 packetAnalyser.swift 를 작성한다.
import Foundation
class PacketAnalyser {
static let sharedInstance = PacketAnalyser()
func process(packetDataArray: Array<UInt8>) {
// Ethernet Ether_Type = 08 00 이면 IP
let dst_str = packetDataArray[0...5].map { String(format: "%02X", $0) }.joined(separator: ":")
let src_str = packetDataArray[6...11].map { String(format: "%02X", $0) }.joined(separator: ":")
let ether_type = packetDataArray[12...13].map { String(format: "%02X", $0) }.joined(separator: " ")
print("Ethernet: \(src_str) -> \(dst_str), \(ether_type) ")
if ether_type == "08 00"
{
// IP
let total_packet_size = UInt16(bigEndian: Data(bytes: packetDataArray[16...17]).withUnsafeBytes {$0.pointee})
let protocol_id = UInt8(bigEndian: packetDataArray[23]) // 1 <- ICMP, 2 <- IGMP, 6 <- TCP, 17 <- UDP
let src_ip = packetDataArray[26...29].map { String(format: "%d", $0) }.joined(separator: ".")
let dst_ip = packetDataArray[30...33].map { String(format: "%d", $0) }.joined(separator: ".")
print("IP: \(src_ip) -> \(dst_ip), \(protocol_id), \(total_packet_size) bytes")
// TCP
if protocol_id == 6
{
let src_port = UInt16(bigEndian: Data(bytes: packetDataArray[34...35]).withUnsafeBytes {$0.pointee})
let dst_port = UInt16(bigEndian: Data(bytes: packetDataArray[36...37]).withUnsafeBytes {$0.pointee})
let seq_no = UInt32(bigEndian: Data(bytes: packetDataArray[38...41]).withUnsafeBytes {$0.pointee})
let ack_no = UInt32(bigEndian: Data(bytes: packetDataArray[42...45]).withUnsafeBytes {$0.pointee})
let windows_size = UInt16(bigEndian: Data(bytes: packetDataArray[48...49]).withUnsafeBytes {$0.pointee})
let check_sum = UInt16(bigEndian: Data(bytes: packetDataArray[50...51]).withUnsafeBytes {$0.pointee})
print("TCP -> PORT: \(src_port) -> \(dst_port), seq: \(seq_no), ack: \(ack_no), window_size: \(windows_size), checksum: \(check_sum)")
let real_data = packetDataArray[54...].map { String(format: "%C", $0) }.joined()
print("Real Data -> \(real_data) \n")
}
}
}
func processPacket(packetDataArray: Array<UInt8>) -> PacketData {
var packetData = PacketData()
// Ethernet Ether_Type = 08 00 이면 IP
let ether_type = packetDataArray[12...13].map { String(format: "%02X", $0) }.joined(separator: " ")
if ether_type == "08 00"
{
// IP
let protocol_id = UInt8(bigEndian: packetDataArray[23]) // 1 <- ICMP, 2 <- IGMP, 6 <- TCP, 17 <- UDP
let src_ip = packetDataArray[26...29].map { String(format: "%d", $0) }.joined(separator: ".")
let dst_ip = packetDataArray[30...33].map { String(format: "%d", $0) }.joined(separator: ".")
// TCP
if protocol_id == 6
{
let src_port = UInt16(bigEndian: Data(bytes: packetDataArray[34...35]).withUnsafeBytes {$0.pointee})
let dst_port = UInt16(bigEndian: Data(bytes: packetDataArray[36...37]).withUnsafeBytes {$0.pointee})
//let real_data = packetDataArray[54...].map { String(format: "%C", $0) }.joined()
let real_data = packetDataArray[54...].map { String(format: "%02X", $0) }.joined()
packetData.src_ip = src_ip
packetData.src_port = "\(src_port)"
packetData.dst_ip = dst_ip
packetData.dst_port = "\(dst_port)"
packetData.data = real_data
}
}
return packetData
}
}
struct PacketData: Codable {
var src_ip: String = ""
var src_port: String = ""
var dst_ip: String = ""
var dst_port: String = ""
var data: String = ""
}
이제 컴파일을 해본다
해당 폴더(ServerSideSwiftTap)에서 다음 명령을 수행한다.
$ swift build
이제 .build 폴더가 생성되었을 것이다.
$ sudo ./.build/debug/ServerSideSwiftTap
몽고디비에 자료가 저장이 되면 성공한 것이다.
센토스도 설정을 맞추면 가능하긴 한데 정신건강을 위해 우분투를 사용하자.
Ubuntu 16.04 에서 Swift 를 설치하자
$ sudo apt-get install clang libicu-dev
이제 swift.org 에서 스위프트 압축된 파일을 다운로드 받는다.
$ wget -q -0 -https://swift.org/keys/all-keys.asc
$ tar xzf swift-<VERSION>-<PLATFORM>.tar.gz
이 명령은 /Home/user 밑에서 실행하여 하위로 푼다.
$ export PATH=/Home/user/usr/swift-4.0.3-RELEASE-ubuntu16.04/usr/bin:"${PATH}"
패스를 추가했다.
$ swift
Welcome to Swift version 4.0.3 어쩌고 나오면 성공한 것이다.
이제 libpcap Library 를 설치해보자.
$ sudo apt-get install libpcap-dev
이제 Swift 로 Packet Capture 할 준비가 되었다.
Cpcap 이란 폴더를 생성하자.
$ mkdir Cpcap
$ cd Cpcap
여기서 파일을 2개를 만들것이다.
$ touch Package.swift
$ touch module.modulemap
Package.swift 내용을 이렇다.
import PackageDescription
let package = Package(name: "Cpcap")
module.modulemap 내용을 이렇게 작성한다.
module Cpcap [system] {
header "/usr/include/pcap.h"
link "pcap"
export *
}
이제 git 을 생성해야 한다.
/Pcap 폴더에 위치한 지 확인 후 생성한다.
$ git init
$ git add Package.swift module.modulemap
$ git commit -m "Initial commit"
$ git tag 0.0.1
이제 Swift 에서 Pcap 을 패키지로 불러 올 수 있게 되었다.
일단 폴더를 Pcap 상위 폴더로 간다.
$ cd ..
$ ls
결과가 Pcap 폴더가 보여야 한다.
여기서 이제 서버용 패킷 캡쳐 프로그램일 ServerSideSwiftTap 를 생성한다.
$ mkdir ServerSideSwiftTap
$ cd ServerSideSwiftTap
이제 파일 2개를 만들 것이다.
$ touch Package.swift
$ touch main.swift
Package.swift 내용은 이렇다
import PackageDescription
let package = Package(name: "ServerSideSwiftTap", dependencies: [.Package(url: "../Cpcap", version: Version(0.0.1)..<Version(1.0.0))])
main.swift 내용을 이렇다.
import Foundation
import MongoKitten
import Cpcap
import Glibc
let server = try Server("mongodb://localhost:27017")
let database = server["packet_db"]
if server.isConnected {
print("Connected successfully to server")
}
let MAX_RECV_SIZE:Int32 = 65535
let PROMISC:Int32 = 1
let TIMEOUT:Int32 = 100
var frameNo = 0
let ver = UnsafePointer<Int8>(pcap_lib_version())!
print("pcap version -> \(String(cString: ver))")
var errbuf = UnsafeMutablePointer<Int8>.allocate(capacity: Int(PCAP_ERRBUF_SIZE))
let dev = pcap_lookupdev(errbuf)
var devName = "ens2"
if let dev = dev {
devName = String(cString: dev)
print("found \(devName)")
} else {
print("Could not get dev")
}
let pcapSession = pcap_open_live(devName, MAX_RECV_SIZE, PROMISC, TIMEOUT, errbuf)
let status = pcap_loop(pcapSession, -1, { (_, packHeader, packetData) in
// 헤더 받은 순서 및 시간 및 크기 표시
var tv_sec = Double(packHeader!.pointee.ts.tv_sec) + 60*60*9 // +0900
let sec = Int(tv_sec.truncatingRemainder(dividingBy: 60))
tv_sec = tv_sec / 60
let min = Int(tv_sec.truncatingRemainder(dividingBy: 60))
tv_sec = tv_sec / 60
let hour = Int(tv_sec.truncatingRemainder(dividingBy: 24))
frameNo = frameNo + 1
let str_Frame = String(format: "Frame %d: %d byte / %d byte %02d:%02d:%02d.%06ld", frameNo, packHeader!.pointee.caplen, packHeader!.pointee.len, hour, min, sec, packHeader!.pointee.ts.tv_usec)
//print(str_Frame)
// 패킷분석
let data = Data(bytes: packetData!, count: Int(packHeader!.pointee.caplen))
let arrayPacketData = Array(data)
let pa = PacketAnalyser.sharedInstance
//pa.process(packetDataArray: arrayPacketData)
let packetData = pa.processPacket(packetDataArray: arrayPacketData)
if packetData.src_ip != "" {
let userDocument: Document = [
"hour": String(format: "%02d", hour),
"minute": String(format: "%02d", min),
"second": String(format: "%02d", sec),
"mili_second": String(format: "%06d", packHeader!.pointee.ts.tv_usec),
"src_ip": packetData.src_ip,
"src_port": packetData.src_port,
"dst_ip": packetData.dst_ip,
"dst_port": packetData.dst_port,
"data": packetData.data
]
do {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd"
let str_date = dateFormatter.string(from: Date())
let collection_name = "tcp_collection_\(str_date)"
let collection = database[collection_name]
try collection.insert(userDocument)
print("success insert!!")
} catch {
print("tcp_collection insert error -> \(error.localizedDescription)")
}
}
}, nil)
pcap_close(pcapSession)
패킷분석을 위한 packetAnalyser.swift 를 작성한다.
import Foundation
class PacketAnalyser {
static let sharedInstance = PacketAnalyser()
func process(packetDataArray: Array<UInt8>) {
// Ethernet Ether_Type = 08 00 이면 IP
let dst_str = packetDataArray[0...5].map { String(format: "%02X", $0) }.joined(separator: ":")
let src_str = packetDataArray[6...11].map { String(format: "%02X", $0) }.joined(separator: ":")
let ether_type = packetDataArray[12...13].map { String(format: "%02X", $0) }.joined(separator: " ")
print("Ethernet: \(src_str) -> \(dst_str), \(ether_type) ")
if ether_type == "08 00"
{
// IP
let total_packet_size = UInt16(bigEndian: Data(bytes: packetDataArray[16...17]).withUnsafeBytes {$0.pointee})
let protocol_id = UInt8(bigEndian: packetDataArray[23]) // 1 <- ICMP, 2 <- IGMP, 6 <- TCP, 17 <- UDP
let src_ip = packetDataArray[26...29].map { String(format: "%d", $0) }.joined(separator: ".")
let dst_ip = packetDataArray[30...33].map { String(format: "%d", $0) }.joined(separator: ".")
print("IP: \(src_ip) -> \(dst_ip), \(protocol_id), \(total_packet_size) bytes")
// TCP
if protocol_id == 6
{
let src_port = UInt16(bigEndian: Data(bytes: packetDataArray[34...35]).withUnsafeBytes {$0.pointee})
let dst_port = UInt16(bigEndian: Data(bytes: packetDataArray[36...37]).withUnsafeBytes {$0.pointee})
let seq_no = UInt32(bigEndian: Data(bytes: packetDataArray[38...41]).withUnsafeBytes {$0.pointee})
let ack_no = UInt32(bigEndian: Data(bytes: packetDataArray[42...45]).withUnsafeBytes {$0.pointee})
let windows_size = UInt16(bigEndian: Data(bytes: packetDataArray[48...49]).withUnsafeBytes {$0.pointee})
let check_sum = UInt16(bigEndian: Data(bytes: packetDataArray[50...51]).withUnsafeBytes {$0.pointee})
print("TCP -> PORT: \(src_port) -> \(dst_port), seq: \(seq_no), ack: \(ack_no), window_size: \(windows_size), checksum: \(check_sum)")
let real_data = packetDataArray[54...].map { String(format: "%C", $0) }.joined()
print("Real Data -> \(real_data) \n")
}
}
}
func processPacket(packetDataArray: Array<UInt8>) -> PacketData {
var packetData = PacketData()
// Ethernet Ether_Type = 08 00 이면 IP
let ether_type = packetDataArray[12...13].map { String(format: "%02X", $0) }.joined(separator: " ")
if ether_type == "08 00"
{
// IP
let protocol_id = UInt8(bigEndian: packetDataArray[23]) // 1 <- ICMP, 2 <- IGMP, 6 <- TCP, 17 <- UDP
let src_ip = packetDataArray[26...29].map { String(format: "%d", $0) }.joined(separator: ".")
let dst_ip = packetDataArray[30...33].map { String(format: "%d", $0) }.joined(separator: ".")
// TCP
if protocol_id == 6
{
let src_port = UInt16(bigEndian: Data(bytes: packetDataArray[34...35]).withUnsafeBytes {$0.pointee})
let dst_port = UInt16(bigEndian: Data(bytes: packetDataArray[36...37]).withUnsafeBytes {$0.pointee})
//let real_data = packetDataArray[54...].map { String(format: "%C", $0) }.joined()
let real_data = packetDataArray[54...].map { String(format: "%02X", $0) }.joined()
packetData.src_ip = src_ip
packetData.src_port = "\(src_port)"
packetData.dst_ip = dst_ip
packetData.dst_port = "\(dst_port)"
packetData.data = real_data
}
}
return packetData
}
}
struct PacketData: Codable {
var src_ip: String = ""
var src_port: String = ""
var dst_ip: String = ""
var dst_port: String = ""
var data: String = ""
}
이제 컴파일을 해본다
해당 폴더(ServerSideSwiftTap)에서 다음 명령을 수행한다.
$ swift build
이제 .build 폴더가 생성되었을 것이다.
$ sudo ./.build/debug/ServerSideSwiftTap
몽고디비에 자료가 저장이 되면 성공한 것이다.
댓글
댓글 쓰기