前陣子花了一些時間在找一種方式,可以透過寫程式的方式抓取進出於任何device interfaces on Linux主機上的封包,目前主要是有這兩大作法: Raw Socket v.s. libpcap
Raw Socket:
可用於多種用途,例如發送和接收原始 IPv4 數據包而不必擔心鏈路層(即,它們插入 IP 層而不是網絡設備驅動程序)。如果需要訪問原始鏈接層,大多數操作系統上的Raw Socket不支持(Linux 是明顯的例外)。
Golang 使用 Raw Socket開發,原則上不需安裝其他套件,是透過system call方式開啟 Raw Socket,它的domain 可以使用AF_INET (Layer 3) 與 AF_PACKET (Layer 2),但AF_INET對於 outgoing packets 會抓不到,所以需使用AF_PACKET回比較完整。
可以透過interface上的IP來綁定特定interface,部分範例如下:
// AF_INET can't capture outgoing packets, must change to use AF_PACKET
// https://github.com/golang/go/issues/7653
// http://www.binarytides.com/packet-sniffer-code-in-c-using-linux-sockets-bsd-part-2/
proto := (syscall.ETH_P_ALL<<8)&0xff00 | syscall.ETH_P_ALL>>8 // change to Big-Endian order
fmt.Println("[DEBUG] proto: ", proto)
fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, proto)
if err != nil {
log.Fatal("socket: ", err)
}
defer syscall.Close(fd)
if t.addr != "" && t.addr != "0.0.0.0" {
ifi, err := net.InterfaceByName(t.addr)
if err != nil {
log.Fatal(t.addr, " interfacebyname: ", err)
}
lla := syscall.SockaddrLinklayer{Protocol: uint16(proto), Ifindex: ifi.Index}
if err := syscall.Bind(fd, &lla); err != nil {
log.Fatal("bind: ", err)
}
}
libpcap:
libpcap 在不同的操作系統上使用不同的機制。在 Linux 上,它使用 PF_PACKET Raw或Cooked Socket,這取決於它是否知道接口的 Linux 鏈路層類型(ARPHRD_ 值)以及該鏈路層類型的接口是否產生有用的鏈路層標頭。
Golang使用libpcap開發,在需要先安裝 libpcap-dev 套件,在Golang下有一強大的package : gopacket,其使用libpcap函示庫擷取封包,對上提供大量的封包處理的功能,並且還可以類似於tcpdump一樣設定過濾條件,部分範例如下:
// Open device
handle, err = pcap.OpenLive(device, snapshot_len, promiscuous, timeout)
if err != nil {
log.Printf("[%v] Error : %s", device, err)
}
// Set BPF Filter
if err := handle.SetBPFFilter("not port 22"); err != nil {
panic(err)
}
defer handle.Close()
// Use the handle as a packet source to process all packets
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
packets := packetSource.Packets()
timer := time.NewTimer(time.Duration(timerclick) * time.Second)
for {
select {
case packet := <-packets:
...
P.S: Gopacket相關參考文件如下:
使用 Gopacket 進行數據包捕獲、注入和分析
網路流量抓包庫 gopacket
為什麼使用libpcap會比較好?
三個原因:
1)正確設置更容易。
2) 它是可移植的,甚至可以移植到 Windows,它使用非常相似但不同的套接字 API。
3)它快得多。
Reference:
Does libpcap use raw sockets underneath them?
Why libpcap is better than sniffing with raw?
No comments:
Post a Comment