gopacket 抓包 过滤器
//定义过滤器
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