阅读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;而不依赖于多个单独的…

漏洞原理反射型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…

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

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

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模块快速定义需要爬取的…

有趣的数学 了解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

CSS 多色正方形上升

<template><view class="loop cubes"><view class="item cubes"></view> <!-- 方块1 --><view class="item cubes"></view> <!-- 方块2 --><view class="item cubes"></vie…

day23 其他事件(页面加载事件、页面滚动事件)

目录 页面加载事件页面/元素滚动事件页面滚动事件——获取位置 页面加载事件 加载外部资源&#xff08;如图片、外联CSS和JavaScript等&#xff09;加载完毕时触发的事件为什么使用&#xff1a; 有时候需要等页面资源全部处理完毕再做一些事老代码喜欢把script写在head中&…

20.2K Star,一个简洁美观、易用的云盘系统

Hi&#xff0c;骚年&#xff0c;我是大 G&#xff0c;公众号「GitHub指北」会推荐 GitHub 上有趣有用的项目&#xff0c;一分钟 get 一个优秀的开源项目&#xff0c;挖掘开源的价值&#xff0c;欢迎关注。 现在的网盘动不动就限速&#xff0c;涨价&#xff0c;非常不爽&#x…

如何“做好冲突管理”?

一、工作实际案例 产品经理在主导跨部门需求项目时&#xff0c;项目在验收阶段&#xff0c;产品经理与业务人员完成初步验收&#xff0c;接下来需要开发同学解决验收问题。 开发小组长反馈&#xff1a;经开发内部评估&#xff0c;按当前项目排期&#xff0c;因上线时间紧张&a…

数据监控-Prometheus/Grafana

一、数据监控Prometheus 1、什么是Prometheus Prometheus是由SoundCloud开源监控告警解决方案,从2012年开始编写代码,到2015年github上开源以来,吸引不少用户以及公司的使用。Prometheus作为新一代的开源解决方案,很多理念与Google SRE的运维之道不谋而合。 2、Promet…