Use of network traffic analysis gopacket library in go

1. Preface

Recently, the author wants to learn how to use go to construct tcp data packets. This involves the use of the gopacket library, so here are some learning records about the gopacket library.

2. Introduction

gopacket is a network packet processing library in the Go language. It provides a convenient API to read, analyze, modify and generate network packets. You can use this library to modify packet contents for specific network testing or security purposes.

The process of modifying the data package is roughly as follows:

  1. Use gopacket to read packets and decode them into protocol-specific data structures (e.g. IP, TCP, etc.).

  2. Modify the corresponding fields, such as source IP address, destination IP address, source port, destination port, etc.

  3. Encode the packet into a raw byte stream and write it back to the network.

Modification of data packets can be easily achieved through the gopacket library.

3. Use

Loading: go get -u github.com/google/gopacket

1. Enumerate all network devices

func GetAllDevs() {
//Get all device information
devices, err := pcap.FindAllDevs()
if err != nil {
log.Fatal(err)
}
//Print device information
fmt.Println("Devices information found:")
for _, device := range devices {
fmt.Println("\
Name:", device.Name)
fmt.Println("Description information:", device.Description)
fmt.Println("Devices address information:", device.Addresses) //The network port address information list includes IP, mask, broadcast address, P2P
for _, address := range device.Addresses {
fmt.Println("-The IP address is: ", address.IP)
fmt.Println("-The mask is:", address.Netmask)
fmt.Println("-The broadcast address is: ", address.Broadaddr)
fmt.Println("- P2P address is: ", address.P2P)
fmt.Println()
}

}
}


//Get the names of all network devices with IP
func GetAllDevsHaveAddress() (string, error) {
pcapDevices, err := pcap.FindAllDevs()
if err != nil {
return "", errors.New(fmt.Sprintf("list pcapDevices failed: %s", err.Error()))
}
var buf strings.Builder
for _, dev := range pcapDevices {
fmt.Sprint("Dev:", dev.Name, "\tDes:", dev.Description)
buf.WriteString(dev.Name)
if len(dev.Addresses) > 0 {
for _, ips := range dev.Addresses {
fmt.Sprint("\tAddr:", ips.IP.String())
//buf.WriteString(ips.IP.String())
}

}
buf.WriteString("\
")
}
return buf.String(), nil
}

//Get the device name by IP
func GetDevByIp(ip net.IP) (devName string, err error) {
devices, err := pcap.FindAllDevs()
if err != nil {
return
}
for _, d := range devices {
for _, address := range d.Addresses {
_ip := address.IP.To4()
if _ip != nil & amp; & amp; _ip.IsGlobalUnicast() & amp; & amp; _ip.Equal(ip) {
return d.Name, nil
}
}
}
return "", errors.New("can not find dev")
}

2. Obtain the active network card IP, network card MAC, gateway, gateway mac, and active network card device name

The main demonstration here is to obtain the IP of the active network card, network card mac, gateway, gateway mac based on the target IP, and then obtain the device name through the IP

package example

import (
"errors"
"fmt"
"net"

"github.com/google/gopacket/pcap"
"github.com/google/gopacket/routing"
"github.com/jackpal/gateway"
"github.com/libp2p/go-netroute"
)

// Find the local IP address and MAC address that match the specified IP address. In fact, it is to detect whether they are on the same network segment.
func GetIfaceMac(ifaceAddr net.IP) (src net.IP, mac net.HardwareAddr) {
interfaces, _ := net.Interfaces() //Get network interface information on the local computer
for _, iface := range interfaces { //Traverse all network interfaces
if addrs, err := iface.Addrs(); err == nil { //Get the IP address list of the interface
for _, addr := range addrs { //Traverse the IP address of the interface
if addr.(*net.IPNet).Contains(ifaceAddr) { //Check whether each IP address contains the given ifaceAddr (ie, passed parameters). The contains method used here checks whether the given IP and the interface IP are the same. subnet
return addr.(*net.IPNet).IP, iface.HardwareAddr //If matching, return the interface IP and mac address
}
}

}

}
return nil, nil

}

// Get the network device name through IP
func GetDevByIp(ip net.IP) (devName string, err error) {
devices, err := pcap.FindAllDevs() //Get all network devices
if err != nil {
return
}

for _, d := range devices {
for _, address := range d.Addresses {
_ip := address.IP.To4() //Check whether the IP is an ipv4 address
if _ip != nil & amp; & amp; _ip.IsGlobalUnicast() & amp; & amp; _ip.Equal(ip) { //The address is ipv4, the address is equal to the incoming IP, and the address is the global unicast address. The conditions are met
return d.Name, nil
}
}
}
return "", errors.New("The corresponding device could not be found")

}

// Obtain the network card information of the packet through the scanned target IP, and return the source IP, source mac, gateway IP, and device name
func GetIpFromRouter(dstIp net.IP) (srcIp net.IP, srcMac net.HardwareAddr, gw net.IP, devName string, err error) {
//First verify whether the scanned IP is on the same network segment
srcIp, srcMac = GetIfaceMac(dstIp)
if srcIp == nil {
//If they are not on the same network segment, query the route
var r routing.Router//Create a variable of type routing.Router for querying routing information
r, err = netroute.New() //Initialize routing.Router
if err == nil {
var iface *net.Interface //Create variable iface to save network interface information related to routing
iface, gw, srcIp, err = r.Route(dstIp) //Query routing information through routing, including routing information corresponding to the target IP address dstIp. iface saves the network interface associated with the routing information, gw saves the gateway IP address, and srcIp saves the local IP address associated with the routing information
if err == nil {
if iface != nil {
srcMac = iface.HardwareAddr //If routing information matching the target IP address is found, that is, iface is not nil, set srcMac to the MAC address of the network interface. Otherwise, continue to the next step
} else {
_, srcMac = GetIfaceMac(srcIp)
}
}
}
//If an error occurred in the previous step or srcMac is nil, it attempts to obtain the information of the first default gateway.
if err != nil || srcMac == nil {
//Take the first default route
gw, err = gateway.DiscoverGateway()//Get the IP address of the first default gateway
if err == nil {
srcIp, srcMac = GetIfaceMac(gw)
}
}
}
gw = gw.To4()
srcIp = srcIp.To4()
devName, err = GetDevByIp(srcIp)
if srcIp == nil || err != nil || srcMac == nil {
if err == nil {
err = fmt.Errorf("err")

}
return nil, nil, nil, "", fmt.Errorf("no router,%s", err)
}
return

}

ps: The author’s previous thinking went wrong. I was obsessed with first obtaining the IP of the active network card and then obtaining the network card device name through the IP. Later, I reflected on it and found that for scanning, I obtained the source IP through the target IP. Source mac, network card information

3. Capture network card data packets

var (
device string = "\Device\\
PF_{111223-123344}" //Network card name, you can also use the FindAllDevs function to obtain the network card name, such as \
PF_{6A5F41. . . }
snapshot_len int32 = 1024 //Maximum length of each packet read
promiscuous bool = false //Whether to set the network port to promiscuous mode, that is, whether to receive packets whose destination is not the local machine
err error
timeout time.Duration = 30 * time.Second //Set the timeout time for packet capture return. If it is set to 30s, the data packet will be refreshed every 30s. If it is set to a negative number, the data packet will be refreshed immediately.
handle *pcap.Handle //It is a return value of *Handle type, which may be passed as a function parameter when calling other functions of gopacket (be sure to release it)
)

func GetPacp() {
handle, err = pcap.OpenLive(device, snapshot_len, promiscuous, timeout) //Open the network device and capture data packets in real time
if err != nil {
log.Fatal(err)
}
defer handle.Close() //The handle must be closed at the end

packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) //The first parameter is the return value of OpenLive, pointing to the pointer variable handle of Handle type. The second parameter is handle.LinkType(). This parameter defaults to an Ethernet link. Generally, when we capture packets, we also capture them from the Layer 2 Ethernet link.

//Read data packets. packetSource.Packets() is a channel type. Here, network data packets are continuously read from the channel type data channel.
for packet := range packetSource.Packets() {
fmt.Println(packet)
}

}

4. Decode the contents of each layer of the data packet

import (
"fmt"
"log"

"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
)

func DecodeLayers() {
//Open device
handle, err = pcap.OpenLive(device, snapshot_len, promiscuous, timeout)
if err != nil {
log.Fatal(err)
}
defer handle.Close()
packetSource2 := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource2.Packets() {
printPacketInfo(packet)
}
}

func printPacketInfo(packet gopacket.Packet) {
// Determine whether the data packet is an Ethernet data packet, which can parse out the source mac address, destination mac address, Ethernet type (such as ip type), etc.
ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
if ethernetLayer != nil {
fmt.Println("Ethernet packet detected")
ethernetPacket, _ := ethernetLayer.(*layers.Ethernet)
fmt.Println("The source Mac address is: ", ethernetPacket.SrcMAC)
fmt.Println("The destination Mac address is:", ethernetPacket.DstMAC)
//The Ethernet type is usually IPv4, but can also be ARP or other
fmt.Println("The Ethernet type is: ", ethernetPacket.EthernetType)
fmt.Println(ethernetPacket.Payload)
fmt.Println()
}

// Determine whether the data packet is an IP data packet, and can parse out the source IP, destination IP, protocol number, etc.
ipLayer := packet.Layer(layers.LayerTypeIPv4) //Capture ipv4 data packets here
if ipLayer != nil {
fmt.Println("IP layer packet 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("The source ip is %d and the destination ip is %d\
", ip.SrcIP, ip.DstIP)
fmt.Println("The protocol version is: ", ip.Version)
fmt.Println("Header length is:", ip.IHL)
fmt.Println("The differentiated service is:", ip.TOS)
fmt.Println("The total length is:", ip.Length)
fmt.Println("The identification id is: ", ip.Id)
fmt.Println("The flag is: ", ip.Flags)
fmt.Println("Fragment offset", ip.FragOffset)
fmt.Println("TTL", ip.TTL)
fmt.Println("Protocol is: ", ip.Protocol)
fmt.Println("Checksum is:", ip.Checksum)
fmt.Println("Base Layer", ip.BaseLayer)
fmt.Println("contents:", ip.Contents)
fmt.Println("The optional fields are: ", ip.Options)
fmt.Println("Padding is:", ip.Padding)
fmt.Println()
}

// Determine whether the data packet is a TCP data packet, which can analyze the source port, destination port, seq sequence number, tcp flag, etc.
tcpLayer := packet.Layer(layers.LayerTypeTCP)
if tcpLayer != nil {
fmt.Println("tcp packet detected")
tcp, _ := tcpLayer.(*layers.TCP)
// SrcPort, DstPort, Seq, Ack, DataOffset, Window, Checksum, Urgent
// Bool flags: FIN, SYN, RST, PSH, ACK, URG, ECE, CWR, NS
fmt.Printf("The source port is %d and the destination port is %d\
", tcp.SrcPort, tcp.DstPort)
fmt.Println("The sequence number is:", tcp.Seq)
fmt.Println("Confirmation number is: ", tcp.Ack)
fmt.Println("Data offset:", tcp.DataOffset)
fmt.Println("Flag bit:", tcp.CWR, tcp.ECE, tcp.URG, tcp.ACK, tcp.PSH, tcp.RST, tcp.SYN, tcp.FIN)
fmt.Println("Window size:", tcp.Window)
fmt.Println("Check value:", tcp.Checksum)
fmt.Println("Urgent pointer:", tcp.Urgent)
fmt.Println("tcp options:", tcp.Options)
fmt.Println("Padding:", tcp.Padding)
fmt.Println()
}

//Detect all layers in the packet
fmt.Println("All packets layer has:")
for _, layer := range packet.Layers() {
fmt.Println("--", layer.LayerType())
}

//Detect and determine whether there is an error in the layer
if err := packet.ErrorLayer(); err != nil {
fmt.Println("Error decoding some parts of the packet:", err)
}

}

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Cloud native entry-level skills treeHomepageOverview 16,776 people are learning the system