阅读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中封装了一系列对于各种协议的操作。我们知道了对应协议的格式内容也可以自己来编写类似的操作。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

知识产权如何转为实缴资本,实操

网上已传疯了&#xff0c;相关部门要求企业注册资本认缴的必须在5年内完成实缴&#xff0c;这一下子引起企业老板们着急了。以前公司注册时&#xff0c;很多老板因为是认缴资本&#xff0c;完全凭脑袋一拍&#xff0c;写上注册资金5000万&#xff0c;有的甚至写上几个小目标。现…

11.Elasticsearch应用(十一)

Elasticsearch应用&#xff08;十一&#xff09; 1.什么是自动补全 现代的搜索引擎&#xff0c;一般都会提供Suggest as you type的功能 帮助用户在输入搜索的过程中&#xff0c;进行自动补全或者纠错。通过协助用户输入更加精准的关键词&#xff0c;提高后续搜索阶段文档的…

【GitHub项目推荐--开源翻译模型】【转载】

开源翻译模型 Seamless M4T 旨在提供高质量的翻译&#xff0c;使来自不同语言的人们能够轻松地通过语音和文本进行交流&#xff0c;支持 101 种语言用于语音输入、96 种文本语言输入/输出、 可以输出 35 种语音语言。 此统一模型支持多个任务&#xff0c;而不依赖于多个单独的…

2024.1.27 寒假训练记录(10)

下午训练赛的榜歪得吓人&#xff0c;来补一下题。 文章目录 CF 1646B Quality vs QuantityCF 1326C Permutation PartitionsCF 1355D Game With ArrayAT ARC99B Snuke NumbersAT exawizards2019C Snuke the WizardCF 1788F XOR, Tree, and Queries CF 1646B Quality vs Quantit…

漏洞原理反射型XSS漏洞

漏洞原理XSS漏洞 1 反射型XSS php基础链接 Web渗透编程语言基础-CSDN博客 正常思维 http://127.0.0.1/websec/day01/xss_reflect.php?name%E6%88%91%E6%98%AF%E8%B0%81 http://127.0.0.1/14_WEBSEC/DAY01/xss_reflect.php?name我是谁 黑客思维 http://127.0.0.1/websec…

centos 离线安装yum

1先找一套相同cpu的虚拟机下载rpm 2 如果新的虚拟机没有yum源就替换 修改yum源-CSDN博客 1.2使用wget下载yum源配置文件 (1)备份本地yum源配置文件 备份命令&#xff1a; mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak(2)下载yum源配置文…

Unity 开发过程中如何优化内存

在开发Unity游戏时&#xff0c;优化内存使用是非常重要的。这不仅可以提高游戏性能&#xff0c;还能保证游戏在各种设备上都能顺利运行。以下是一些关于如何在Unity中优化内存使用的建议&#xff1a; 1. 了解并监控您的内存使用情况&#xff1a;您可以使用Unity的Profiler工具…

Qt Bezier闭合曲线插值(2D)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 标准的Bezier曲线拟合并不能穿过所有节点,这是由于其随着节点数量的增加,其多项式阶数也在增加,最终其形状也会越发复杂且难以控制。因此就有学者提出,使用三阶贝塞尔曲线分段去插值一条整体光滑的曲线,这样不…

echarts option series smooth

echarts option series smooth 平滑处理 smooth&#xff1a;0.3 echarts_04_line.html <!DOCTYPE html> <html lang"en"><head> <meta charset"utf-8"> <title></title> </head><body><div id&quo…

【Git版本控制】以及搭建gitlab服务

目录 一、Git介绍二、Git安装与全局配置1、git的全局配置&#xff1a;2、为常用的指令配置别名&#xff1a;3、Git初始化本地代码仓库4、Git的基础命令 三、分支四、Git远程仓库1、操作远程仓库2、从远程仓库克隆3、从远程仓库中抓取和拉取 五、Gitlab sever部署期间出现遇到的…

python爬虫demo——爬取历史平均房价

简单爬取历史房价 需求 爬取的网站汇聚数据的城市房价 https://fangjia.gotohui.com/ 功能 选择城市 https://fangjia.gotohui.com/fjdata-3 需要爬取年份的数据&#xff0c;等等 https://fangjia.gotohui.com/years/3/2018/ 使用bs4模块 使用bs4模块快速定义需要爬取的…

python asyncio异步编程

一、协程 协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相互切换执行。 对于协程而言,并不是计算机所提供的,计算机所提供的只有进程和线程、而协程则是通过程序员人为所创造的。我们通常将协程称为微线…

javaScript的书写位置

javascript的书写位置有哪些呢&#xff1f;有什么注意事项吗&#xff1f;让我为大家介绍一下吧&#xff01; 1.内部 注意&#xff1a;书写位置尽量写到文档末尾</ body>的前面 在我们内部书写JS的时候 <!DOCTYPE html> <html lang"en"> <hea…

有趣的数学 了解TensorFlow的自动微分的实现

一、简述 这里主要介绍了TensorFlow的自动微分(autodiff)功能如何工作,以及与其他解决方案的比较。假设您定义了一个函数,并且需要计算它的偏导数和,通常用于执行梯度下降(或某些其他优化算法)。可用的主要选择是手动微分、有限差分近似、正向模式自动微分和反向模式自动…

ETCD高可用架构涉及常用功能整理

ETCD高可用架构涉及常用功能整理 1. etcd的高可用系统架构和相关组件1.1 Quorum机制1.2 Raft协议 2. etcd的核心参数2.1 常规配置2.2 特殊优化配置2.2.1 强行拉起新集群 --force-new-cluster2.2.2 兼容磁盘io性能差2.2.3 etcd存储quota 3. etcd常用命令3.1 常用基础命令3.1.1 列…

Java 数据结构篇-深入了解排序算法(动态图 + 实现七种基本排序算法)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 实现冒泡排序 2.0 实现选择排序 2.1 选择排序的改良升级 3.0 实现堆排序 4.0 实现插入排序 5.0 实现希尔排序 6.0 实现归并排序 6.1 递归实现归并排序 6.2 使用…

HarmonyOS鸿蒙学习基础篇 - 通用事件

一、引言 HarmonyOS鸿蒙是华为推出的分布式操作系统&#xff0c;旨在为各种智能设备提供统一的操作系统。鸿蒙系统的一大特色是其强大的分布式能力&#xff0c;而通用事件则是实现这一能力的关键技术之一&#xff0c;本篇博客将介绍HarmonyOS鸿蒙中的通用事件。 二、 点击事件…

STM32-LwESP 移植

LwESP 是一个专门解析 Espressif 公司旗下 ESP 系列芯片 AT 指令的开源库&#xff0c;具有以下特性&#xff1a; 支持 Espressif 公司 ESP32, ESP32-C2, ESP32-C3, ESP32-C6 和 ESP8266 芯片。独立平台&#xff0c;采用 C99 标准编写&#xff0c;易于移植。允许不同的配置来优…

通讯录项目(终)

Start And Stick 上一期我们将通讯录的项目的基本功能已经实现&#xff0c;这一篇文章我们将对通讯录进行完善。 目录 Start And Stick 上期回顾&#xff1a; 上期必要代码&#xff1a; 数据打印&#xff1a; 代码讲解&#xff1a; 头部插入数据&#xff1a; 代码讲解&…

【Linux】专栏文章索引

为了方便 快速定位 和 便于文章间的相互引用等 作为一个快速准确的导航工具 Linux 目录&#xff1a; &#xff08;一&#xff09;CentOS 7 安装&配置Python