阅读go语言工具源码系列之gopacket(谷歌出品)----第二集 layers-巧妙的抽象与无聊的协议包

上一集中我们讲到了wpcap.dll的go封装方法,对于linux系统下libpcap的go封装采用的是常用的cgo方式,想了解的可以看看pcap文件夹中的pcap_unix.go。

我们得到了wpcap.dll的go调用,就可以利用它来进行列举所有网络设备,例如以下代码

package main  
  
import (  
"fmt"  
"github.com/google/gopacket/pcap"  
"log"  
)
// 得到所有的(网络)设备  
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)  
}  
}

也可以抓取某个网络设备的数据包,例如以下代码

package main  
  
import (  
"fmt"  
"github.com/google/gopacket"  
"github.com/google/gopacket/layers"  
"github.com/google/gopacket/pcap"  
"log"  
_ "strings"  
"time"  
)  
  
var (  
device string = "你的网络设备名"  
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.Println("Protocol: ", ip.Protocol)  
fmt.Println()  
}  
// Let's see if the packet is TCP  
// 判断数据包是否为TCP数据包,可解析源端口、目的端口、seq序列号、tcp标志位等  
tcpLayer := packet.Layer(layers.LayerTypeTCP)  
if tcpLayer != nil {  
fmt.Println("TCP layer detected.")  
tcp, _ := tcpLayer.(*layers.TCP)  
// TCP layer variables:  
// SrcPort, DstPort, Seq, Ack, DataOffset, Window, Checksum, Urgent  
// Bool flags: FIN, SYN, RST, PSH, ACK, URG, ECE, CWR, NS  
fmt.Printf("From port %d to %d\n", tcp.SrcPort, tcp.DstPort)  
fmt.Println("Sequence number: ", tcp.Seq)  
fmt.Println()  
}  
// Iterate over all layers, printing out each layer type  
fmt.Println("All packet layers:")  
for _, layer := range packet.Layers() {  
fmt.Println("- ", layer.LayerType())  
}  
///.......................................................  
// Check for errors  
// 判断layer是否存在错误  
if err := packet.ErrorLayer(); err != nil {  
fmt.Println("Error decoding some part of the packet:", err)  
}  
}

我们可以看到上述代码中,需要对抓取到的网络数据包进行解包操作,每个数据包就像是洋葱一样由各层协议层层封装而成。如果你愿意一层一层一层地剥开它的心,哈哈哈
在这里插入图片描述

gopacket库对于每个协议的解包(解析)操作都对应一个go语言文件,放在了layers文件夹中。

本集我们讲解的内容:gopacket库中对于数据包packet的抽象以及对于layers中对于常见协议的封装操作。

packet

我们先来聚焦一下packet.go。gopacket库中使用Packet来表示一个一个的数据包。这里是gopacket库抽象的Packet接口,

type Packet interface {  
 Functions for outputting the packet as a human-readable string:  
 ------------------------------------------------------------------  
// String returns a human-readable string representation of the packet.// It uses LayerString on each layer to output the layer.String() string  
// Dump returns a verbose human-readable string representation of the packet,// including a hex dump of all layers. It uses LayerDump on each layer to// output the layer.  
Dump() string  
  
 Functions for accessing arbitrary packet layers:  
 ------------------------------------------------------------------  
// Layers returns all layers in this packet, computing them as necessaryLayers() []Layer  
// Layer returns the first layer in this packet of the given type, or nilLayer(LayerType) Layer  
// LayerClass returns the first layer in this packet of the given class,// or nil.  
LayerClass(LayerClass) Layer  
  
 Functions for accessing specific types of packet layers. These functions  
 return the first layer of each type found within the packet.  
 ------------------------------------------------------------------  
// 返回第一个链路层  
// LinkLayer returns the first link layer in the packet
LinkLayer() LinkLayer  
// 返回第一个网络层  
// NetworkLayer returns the first network layer in the packet
NetworkLayer() NetworkLayer  
// 返回第一个传输层  
// TransportLayer returns the first transport layer in the packet
TransportLayer() TransportLayer  
// 返回第一个应用层  
// ApplicationLayer returns the first application layer in the packet
ApplicationLayer() ApplicationLayer  
// ErrorLayer is particularly useful, since it returns nil if the packet// was fully decoded successfully, and non-nil if an error was encountered  
// in decoding and the packet was only partially decoded. Thus, its output  
// can be used to determine if the entire packet was able to be decoded.  
ErrorLayer() ErrorLayer  
  
 Functions for accessing data specific to the packet:  
 ------------------------------------------------------------------  
// Data returns the set of bytes that make up this entire packet.
Data() []byte  
// Metadata returns packet metadata associated with this packet.
Metadata() *PacketMetadata  
}

packet结构中可以看到有几个layer类型,LinkLayer,NetworkLayer ,TransportLayer,ApplicationLayer ,ErrorLayer。我们从这几个名称可以猜出(除去ErrorLayer这个表示错误的layer,其他刚好四个),其对应的应该是TCP/IP体系结构。
在这里插入图片描述

有了数据包对应的结构,接下来就是对于相应数据包每一层协议封装相应操作。

以太网协议

请看源码中layers->ethernet.go

ethernet.go是gopacket库根据ethernet协议对ethernet数据包包装的各种操作函数,里面不涉及执行顺序,所以我建议我们先看一下代码里面具体类型结构

type Ethernet struct {  
BaseLayer  
// 源物理地址 目的物理地址  
SrcMAC, DstMAC net.HardwareAddr  
// 以太网类型  
EthernetType EthernetType  
// Length is only set if a length field exists within this header. Ethernet  
// headers follow two different standards, one that uses an EthernetType, the  
// other which defines a length the follows with a LLC header (802.3). If the// former is the case, we set EthernetType and Length stays 0. In the latter// case, we set Length and EthernetType = EthernetTypeLLC.  
//数据包长度
Length uint16  
}

补充

这里补充一点以太网协议相关知识

以太网是一种产生较早,使用相当广泛的局域网技术。最初是由Xerox(施乐)公司创建(大概是1973年诞生)并由Xerox、 Intel和DEC公司联合开发的基带局域网规范,后来被电气与电子工程师协会( IEEE)所采纳作为802.3的标准。

目前以太网根据速度等级分类大概分为:标准以太网(10Mbit/s),快速以太网(100Mbit/s),千兆以太网(1000Mbit/s),以及更快的万兆以太网(10Gbit/s)

以太网协议(Ethernet Protocol)按照七层(OSI)网络模型来划分的话属于数据链路层的协议,

常用的以太网MAC帧格式有两种标准,一种是DIX Ethernet V2标准(即以太网V2标准),另一种是IEEE的802.3标准。这里只介绍使用得最多的以太网V2的MAC帧格式(如下图)。
![[Pasted image 20240126142219.png]]
图中假定网络层使用的是IP协议。实际上使用其他的协议也是可以的。

IEEE 802.3标准规定的MAC帧格式与上面所讲的以太网V2 MAC帧格式的区别就是两个地方。
第一,IEEE 802.3规定的MAC帧的第三个字段是“长度/类型”。当这个字段值大于0x0600时(相当于十进制的1536),就表示“类型”。这样的帧和以太网V2 MAC帧完全一样。只有当这个字段值小于0x0600时才表示“长度”,即MAC帧的数据部分长度。显然,在这种情况下,若数据字段的长度与长度字段的值不一致,则该帧为无效的MAC帧。实际上,前面我们已经讲过,由于以太网采用了曼彻斯特编码,长度字段并无实际意义。

第二,当“长度/类型”字段值小于0x0600时,数据字段必须装入上面的逻辑链路控制LLC子层的LLC帧。
由于现在广泛使用的局域网只有以太网,因此LLC帧已经失去了原来的意义(见本章3.3.1节第1小节“以太网的两个标准”)。现在市场上流行的都是以太网V2的MAC帧,但大家也常常把它称为IEEE 802.3标准的MAC帧。------《计算机网络第七版 谢希仁》

图中所示的目的地址和源地址实际上就是MAC地址分别占了6字节,而 类型 则是用来标识上一层所使用的协议类型,如IP协议(0x0800),ARP(0x0806)等。FCS字段是帧校验字段,即Frame Check Sequence,用来保存CRC(循环冗余校验)校验值。
以太网协议中规定最小的以太网数据包长度应该是64字节,最大长度1518字节,除去目的地址、源地址(各6字节),类型(2字节),FEC字段(4字节),数据字段长度应该在46~1500字节。当数据字段的长度小于46字节时,MAC子层就会在数据字段的后面加入一个整数字节的填充字段,以保证以太网的MAC帧长不小于64字节。

OK,有了上面的知识补充,我们可以继续阅读ethernet.go源码了。

DecodeFromBytes

首先是对于以太网协议的解码操作,so look at function DecodeFromBytes():

func (eth *Ethernet) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {  
if len(data) < 14 {  
return errors.New("Ethernet packet too small")  
}  
eth.DstMAC = net.HardwareAddr(data[0:6])  
eth.SrcMAC = net.HardwareAddr(data[6:12])  
eth.EthernetType = EthernetType(binary.BigEndian.Uint16(data[12:14]))  
eth.BaseLayer = BaseLayer{data[:14], data[14:]}  
eth.Length = 0  
if eth.EthernetType < 0x0600 {  
eth.Length = uint16(eth.EthernetType)  
eth.EthernetType = EthernetTypeLLC  
if cmp := len(eth.Payload) - int(eth.Length); cmp < 0 {  
df.SetTruncated()  
} else if cmp > 0 {  
// Strip off bytes at the end, since we have too many bytes  
eth.Payload = eth.Payload[:len(eth.Payload)-cmp]  
}  
// fmt.Println(eth)  
}  
return nil  
}

首先当 len(data)<14时 即目的地址、源地址、类型三个字段不完整,如果长度够继续解析,

eth.DstMAC = net.HardwareAddr(data[0:6])  
eth.SrcMAC = net.HardwareAddr(data[6:12])  
eth.EthernetType = EthernetType(binary.BigEndian.Uint16(data[12:14]))  
eth.BaseLayer = BaseLayer{data[:14], data[14:]}
eth.Length = 0

目的地址、源地址、类型三个字段解析到变量中,然后将前14个字节和14个字节之后的数据复制到baselayer中。

// BaseLayer is a convenience struct which implements the LayerData and// LayerPayload functions of the Layer interface.
type BaseLayer struct {  
// Contents is the set of bytes that make up this layer. IE: for an// Ethernet packet, this would be the set of bytes making up the// Ethernet frame.
// 可以理解为封装头
Contents []byte  
// Payload is the set of bytes contained by (but not part of) this// Layer. Again, to take Ethernet as an example, this would be the// set of bytes encapsulated by the Ethernet protocol.
// 数据内容
Payload []byte  
}

然后是对于EthernetType进行一个判断,这个if语句是用来判断该数据packet采用的协议是以太网v2还是IEEE 802.3,依据见 补充 IEEE 802.3

if eth.EthernetType < 0x0600 {  
// 从第三个字段获取以太网数据长度信息  
eth.Length = uint16(eth.EthernetType)  
// 设置以太网协议类型  
eth.EthernetType = EthernetTypeLLC  
// 比较获取的以太网数据长度信息是否和实际的数据长度一样,小则设置截断,大则按照获取的以太网数据长度信息重置Payload  
if cmp := len(eth.Payload) - int(eth.Length); cmp < 0 {  
df.SetTruncated()  
} else if cmp > 0 {  
// Strip off bytes at the end, since we have too many bytes  
eth.Payload = eth.Payload[:len(eth.Payload)-cmp]  
}  
// fmt.Println(eth)  
}

其他

本文件代码中DecodeFromBytes是对于数据包的解包操作,而 SerializeTo 则是对于数据封装成数据包的操作 ,其他的没什么可讲的,基本是为了实现其他接口而写的,自己看吧。
基本上layers文件夹中每一个文件名都对应着一个协议,基本上操作和ethernet.go一样。

本集总结:
获取了抓取到的数据包后,需要对包进行解析,gopacket库的layers中封装了一系列对于各种协议的操作。我们知道了对应协议的格式内容也可以自己来编写类似的操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/350225.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

韦东山嵌入式Liunx入门笔记一

文章目录 一、嵌入式Linux二、Ubuntu系统2-1 安装软件2-2 Linux文件(1) 文件架构(2)文件属性(3)文件命令(4) 解压、压缩文件(5) 网络命令 2-3 vi编辑器2-4 Ubuntu下包管理 三、配置网卡四、安装后续学习使用的软件4-1 MobaXterm4-2 FileZilla4-3 Source Insight4.04-4 下载BSP4…

vivado 定义和配置I/O端口、

定义和配置I/O端口 您可以使用Vivado IDE导入、创建和配置I/O端口&#xff0c;如中所述以下部分。 导入I/O端口 根据项目类型&#xff0c;可以使用以下方法导入I/O端口&#xff1a; •I/O规划项目&#xff1a;您可以将XDC和CSV文件导入空的I/O规划项目当您使用文件导入功能…

Java Lock源码解读

一&#xff0c;概述 多线程问题本质是多个线程共同访问了同一块内存&#xff0c;导致该内存状态不确定而产生了一系列问题。concurrent包中提供的Lock类本质是对线程对象进行监督、排队&#xff0c;调度&#xff0c;确保lock只能有一个线程或共享线程成功返回&#xff0c;否则…

幻兽帕鲁游戏服务器搭建by阿里云服务器4核16G和32G配置价格表

如何自建幻兽帕鲁服务器&#xff1f;基于阿里云服务器搭建幻兽帕鲁palworld服务器教程来了&#xff0c;一看就懂系列。本文是利用OOS中幻兽帕鲁扩展程序来一键部署幻兽帕鲁服务器&#xff0c;阿里云百科aliyunbaike.com分享官方基于阿里云服务器快速创建幻兽帕鲁服务器教程&…

go 引用fork后的模块的两种方式(replace和工作区)

很久没更新了&#xff0c;一是工作琐碎&#xff0c;二是处在舒适区&#xff0c;但最近看着身边的同事一个个离开&#xff0c;危机感骤然而生&#xff0c;不得不重拾书本&#xff0c;毕竟生活还得继续&#xff0c;不卷是不可能的&#xff0c;谁让我们生在这个卷中卷的国度&#…

3d gaussian splatting介绍整理

3D 高斯分布是用于实时辐射场渲染的 3D 高斯分布中描述的一种光栅化技术&#xff0c;它允许实时渲染从小图像样本中学习到的逼真场景。 paper github 本文翻译整理自&#xff1a; blog: Introduction to 3D Gaussian Splatting DDPMs - Part 2 给出一些2D图片&#xff0c;用…

「阿里云」幻兽帕鲁个人服务器已上线,3分钟快速搭建

基于阿里云搭建幻兽帕鲁服务器方法&#xff0c;1到2分钟部署完成&#xff0c;稳定运行无卡顿&#xff0c;阿里云服务器网aliyunfuwuqi.com分享保姆级手把手教程&#xff0c;基于阿里云计算巢、云服务器或无影云桌面都可以&#xff1a; 基于阿里云幻兽帕鲁服务器创建教程 基于…

WLAN

前言 今天给大家讲一个不一样的实验,生活息息相关,特别有意思的,顺便让大家放松放松 实验 一.引入 实验拓扑图: 明眼人已经知道我没要干嘛了,WIFI无线路由器 所有的PC设备都换成WIMP300N模块无线接收 成功后你们的拓扑图就会和我的一样 二、配置Linksys WRT300N   配置pc3…

循环测试之旅——深度解析Pytest插件 pytest-repeat

在软件开发中,测试的重要性不言而喻。而为了提高测试的鲁棒性和可靠性,Pytest插件 pytest-repeat 应运而生。这个插件可以帮助你轻松实现测试用例的循环运行,以更全面地评估代码的稳定性。本文将深入介绍 pytest-repeat 插件的基本用法和实际案例,助你更好地利用循环测试,…

独占指针:unique_ptr 与 函数调用 笔记

推荐B站视频&#xff1a; 2.unique_ptr_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV18B4y187uL?p2&vd_sourcea934d7fc6f47698a29dac90a922ba5a3 3.unique_ptr与函数调用_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV18B4y187uL?p3&vd_sourcea934d…

MIT_线性代数笔记:第 29 讲 奇异值分解

目录 如何实现用矩阵数学语言描述这一过程举例 本讲介绍奇异值分解&#xff08;Singular value decomposition&#xff09;&#xff0c;简称 SVD。这是矩阵最终也是最好的分解&#xff0c;任意矩阵可分解为 A U Σ V T AUΣV^T AUΣVT&#xff0c;分解结果为正交矩阵 U&#x…

OpenAI API 的最新动态:新一代的嵌入模型,更新 GPT-4 Turbo,更新 GPT-3.5 Turbo 以及降低 API 价格

文章目录 一、前言二、主要内容三、总结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 OpenAI 正在推出新一代嵌入模型、新的 GPT-4 Turbo 和审查模型、新的 API 使用管理工具&#xff0c;而且很快就会降低 GPT-3.5 Turbo 的价格。 OpenAI…

【MAC】Multi-Level Monte Carlo Actor-Critic阅读笔记

基本思想&#xff1a; 利用多层次蒙特卡洛方法&#xff08;Multi-Level Monte Carlo&#xff0c;MLMC&#xff09;和Actor-Critic算法&#xff0c;解决平均奖励强化学习中的快速混合问题。 快速混合&#xff1f; 在强化学习中&#xff0c;当我们说一个策略"混合得快"…

3D视觉技术快讯

SparseGS主要解决了3D GS(Gaussian Splatting)与NeRF类似的稀疏视角问题&#xff0c;即当训练输入视角很稀疏时&#xff0c;GS会在训练中过拟合&#xff0c;从而在新视角上的测试结果较差。本论文则是提出使用原有的深度先验以及显式的约束来提升GS在稀疏视角下的表现&#xff…

以太网与PON网络的巅峰对决

在这网络的江湖中&#xff0c;各路江湖豪侠都神色匆忙地往同一个地方赶&#xff0c;豪侠们脸上都充满期待和焦虑&#xff0c;生怕错过了什么。这个地方就是传说中的园区网&#xff0c;因为在那里万众期待已久的以太网与PON网络的巅峰对决“将在今天上演。 一方是以太网大侠&am…

Hive 行列转换

行列转换 列转行 使用 lateral view explode(array|map) 或 lateral view inline(array_struct) 可以将列转换为行。 单列转多行&#xff0c;降维&#xff08;单列数组或键值对&#xff09; 示例1&#xff1a;explode(array(…)) select ..., A from T lateral view exp…

Java-List接口常用方法和遍历方法

List的继承结构 其中&#xff0c;红色为接口&#xff0c;蓝色为实现类 List的四大方法 List的基本操作void add(int index,E e)boolean remove(Object o)E remove(int index)E set(int index,E e)E get(int index)其中注意删除方法有两种&#xff0c;执行的时候主要选择实参…

作者推荐 | 【深入浅出MySQL】「底层原理」探秘缓冲池的核心奥秘,揭示终极洞察

探秘缓冲池的核心奥秘&#xff0c;揭示终极洞察 缓存池BufferPool机制MySQL缓冲池缓冲池缓冲池的问题 缓冲池的原理数据预读程序的局部性原则&#xff08;集中读写原理&#xff09;时间局部性空间局部性 innodb的数据页查询InnoDB的数据页InnoDB缓冲池缓存数据页InnoDB缓存数据…

可解释性AI

方向一&#xff1a;可解释性AI的定义与重要性 1. 什么是可解释人工智能 可解释人工智能&#xff08;Explainable Artificial Intelligence, XAI&#xff09;是指智能体以一种可解释、可理解、人机互动的方式&#xff0c;与人工智能系统的使用者、受影响者、决策者、开发者等&…

牛客网---------[USACO 2016 Jan S]Angry Cows

题目描述 Bessie the cow has designed what she thinks will be the next big hit video game: "Angry Cows". The premise, which she believes is completely original, is that the player shoots cows with a slingshot into a one-dimensional scene consistin…