//定義過濾器
func getFilter(port uint16) string {
// filter := fmt.Sprintf("udp and ((src port %v) or (dst port %v))", port, port)
filter := fmt.Sprintf("((src port %v) or (dst port %v))", port, port)
return filter
}
【酷Go推薦】網絡流量抓包庫 gopacket - 知乎 https://zhuanlan.zhihu.com/p/361737169
網絡流量抓包庫 gopacket · GoCN社區 https://gocn.vip/topics/11829
一、gopacket 簡介
1、gopacket 是什么?
gopacket 是 google 出品的 golang 三方庫,質量還是靠的住,項目地址為:github.com/google/gopacket
gopacket 到底是什么呢?是個抓取網絡數據包的庫,這么說可能還有點抽象,但是抓包工具大家可能都使用過。
Windows 平台下有 Wireshark 抓包工具,其底層抓包庫是 npcap(以前是 winpcap);
Linux 平台下有 Tcpdump,其抓包庫是 libpcap;
而 gopacket 庫可以說是 libpcap 和 npcap 的 go 封裝,提供了更方便的 go 語言操作接口。
對於抓包庫來說,常規功能就是抓包,而網絡抓包有以下幾個步驟:
1、枚舉主機上網絡設備的接口
2、針對某一網口進行抓包
3、解析數據包的 mac 層、ip 層、tcp/udp 層字段等
4、ip 分片重組,或 tcp 分段重組成上層協議如 http 協議的數據
5、對上層協議進行頭部解析和負載部分解析
2、應用場景有哪些?
場景 1:網絡流量分析
對網絡設備流量進行實時采集以及數據包分析。
場景 2:偽造數據包
不少網絡安全工具,需要偽造網絡數據包,填充上必要的協議字段后發送給對端設備,從而達到一些目的。
場景 3:離線 pcap 文件的讀取和寫入
二、安裝部署
2、1 安裝 libpcap 或 npcap 三方庫
在使用 gopacket 包時,首先要確保在 windows 平台下安裝了 npcap 或 winpcap,或者是在 linux 平台下安裝了 libpcap 庫。
npcap 下載地址:https://nmap.org/npcap/
libpcap 下載地址:https://www.tcpdump.org/
下載自己電腦對應的操作系統版本的庫
如果不想從官網下載 libpcap 庫的話,也可以采用 centos 的 yum 命令或 ubuntu 的 apt get 命令來進行安裝。
2、2 安裝 gopacket 庫
go get github.com/google/gopacket
三、使用方法
3、1 枚舉網絡設備
package main import ( "fmt" "log" "github.com/google/gopacket/pcap" ) func main() { // 得到所有的(網絡)設備 devices, err := pcap.FindAllDevs() if err != nil { log.Fatal(err) } // 打印設備信息 fmt.Println("Devices found:") for _, device := range devices { fmt.Println("\nName: ", device.Name) fmt.Println("Description: ", device.Description) fmt.Println("Devices addresses: ", device.Description) for _, address := range device.Addresses { fmt.Println("- IP address: ", address.IP) fmt.Println("- Subnet mask: ", address.Netmask) } } }
先調用 pcap.FindAllDevs() 獲取當前主機所有的網絡設備,網絡設備有哪些屬性呢?
// Interface describes a single network interface on a machine. type Interface struct { Name string //設備名稱 Description string //設備描述信息 Flags uint32 Addresses []InterfaceAddress //網口的地址信息列表 } // InterfaceAddress describes an address associated with an Interface. // Currently, it's IPv4/6 specific. type InterfaceAddress struct { IP net.IP Netmask net.IPMask // Netmask may be nil if we were unable to retrieve it. Broadaddr net.IP // Broadcast address for this IP may be nil P2P net.IP // P2P destination address for this IP may be nil }
3、2 打開一個設備進行抓包
package main import ( "fmt" "github.com/google/gopacket" "github.com/google/gopacket/pcap" "log" "time" ) var ( device string = "eth0" snapshot_len int32 = 1024 promiscuous bool = false err error timeout time.Duration = 30 * time.Second handle *pcap.Handle ) func main() { // 打開某一網絡設備 handle, err = pcap.OpenLive(device, snapshot_len, promiscuous, timeout) if err != nil {log.Fatal(err) } defer handle.Close() // Use the handle as a packet source to process all packets packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) for packet := range packetSource.Packets() { // Process packet here fmt.Println(packet) } }
1)實時捕獲
2、1 節中我們枚舉了當前主機的所有網絡設備,現在需要打開網絡設備並進行實時捕獲數據包,需調用 pcap.OpenLive 來打開網絡設備,其函數原型如下:
func OpenLive(device string, snaplen int32, promisc bool, timeout time.Duration) (handle *Handle, _ error)
device:網絡設備的名稱,如 eth0,也可以填充 pcap.FindAllDevs() 返回的設備的 Name
snaplen: 每個數據包讀取的最大長度 the maximum size to read for each packet
promisc:是否將網口設置為混雜模式,即是否接收目的地址不為本機的包
timeout:設置抓到包返回的超時。如果設置成 30s,那么每 30s 才會刷新一次數據包;設置成負數,會立刻刷新數據包,即不做等待
函數返回值:是一個 *Handle 類型的返回值,可能作為 gopacket 其他函數調用時作為函數參數來傳遞。
注意事項:
一定要記得釋放掉 handle,如文中的 defer handle.Close()。
2)創建數據包源
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
第一個參數為 OpenLive 的返回值,指向 Handle 類型的指針變量 handle。
第二個參數為 handle.LinkType() 此參數默認是以太網鏈路,一般我們抓包,也是從 2 層以太網鏈路上抓取。
3)讀取數據包
//packetSource.Packets()是個channel類型,此處是從channel類型的數據通道中持續的讀取網絡數據包 for packet := range packetSource.Packets() { // Process packet here fmt.Println(packet) }
3、3 解碼數據包的各層
我們可以獲取原始數據包,並嘗試將其強制轉換為已知格式。如 ethernet、IP 和 TCP 層。
Layers 包是 gopacket 的 Go 庫中的新功能,在底層 libpcap 庫中不存在。它是 gopacket 庫的非常有用的一部分。它允許我們輕松地識別數據包是否包含特定類型的層。這個代碼示例將演示如何使用 layers 包來查看包是否是 ethernet、IP 和 TCP,以及如何輕松訪問這些頭中的字段。
package main import ( "fmt" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcap" "log" "strings" "time" ) var ( device string = "eth0" snapshotLen int32 = 1024 promiscuous bool = false err error timeout time.Duration = 30 * time.Second handle *pcap.Handle ) func main() { // Open device handle, err = pcap.OpenLive(device, snapshotLen, promiscuous, timeout) if err != nil {log.Fatal(err) } defer handle.Close() packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) for packet := range packetSource.Packets() { printPacketInfo(packet) } } func printPacketInfo(packet gopacket.Packet) { // Let's see if the packet is an ethernet packet // 判斷數據包是否為以太網數據包,可解析出源mac地址、目的mac地址、以太網類型(如ip類型)等 ethernetLayer := packet.Layer(layers.LayerTypeEthernet) if ethernetLayer != nil { fmt.Println("Ethernet layer detected.") ethernetPacket, _ := ethernetLayer.(*layers.Ethernet) fmt.Println("Source MAC: ", ethernetPacket.SrcMAC) fmt.Println("Destination MAC: ", ethernetPacket.DstMAC) // Ethernet type is typically IPv4 but could be ARP or other fmt.Println("Ethernet type: ", ethernetPacket.EthernetType) fmt.Println() } // Let's see if the packet is IP (even though the ether type told us) // 判斷數據包是否為IP數據包,可解析出源ip、目的ip、協議號等 ipLayer := packet.Layer(layers.LayerTypeIPv4) if ipLayer != nil { fmt.Println("IPv4 layer detected.") ip, _ := ipLayer.(*layers.IPv4) // IP layer variables: // Version (Either 4 or 6) // IHL (IP Header Length in 32-bit words) // TOS, Length, Id, Flags, FragOffset, TTL, Protocol (TCP?), // Checksum, SrcIP, DstIP fmt.Printf("From %s to %s\n", ip.SrcIP, ip.DstIP) fmt.