别只盯着gopacket了,看看这个强大的网络库
本机暂存
<blockquote><p>Go 网络编程,大家第一反应就是 gopacket。但如果你用过 Scapy,你会发现 gopacket 的 API 繁琐得让人抓狂。goscapy 把 Scapy 的优雅搬到了 Go 里——流式构建、自动校验和、协议自动推断、一行代码搞定数据包,还能嗅探、发送、收响应。</p></blockquote><hr><p>最早接触到python生态圈的<a href="https://scapy.net/">scapy</a>是两年前,在和交换机的同学搞交换机探针的时候,他写了几行python代码就实现了一个发包探测程序,我立马就被scapy吸引了,居然写网络测试程序可以这么简单?<br>![image-20260608231845123.png<img src="/2026/06/11/beyond-gopacket-powerful-network-lib/image-20260608231845123.png" class=""><br>之后就粗略的了解一下scapy,并想把它移植的Go生态圈。Go生态圈有gopacket、golang.org/x/net等一些库了,在我没了解scapy之前,我也一直使用gopacket、golang.org/x/net做我们的百度网络黑盒探测程序,我以为gopacket已经足够好了,直到我认识到scapy。</p><span id="more"></span><p>当然scapy的简洁性有一部分是来自 python语法,比如操作符重载、参数默认值等等,可以把写法简化,同时也不可否认scapy的设计为可用性做了很好的设计,它设计的出发点决定了这个库的简洁性。</p><p>但是蹉跎了 2 年,我一直没有行动起来把scapy移植到goscapy。就在前一段时间,我们的网络工具开源前,我终于决定动手了,借助AI的力量,使用goal-worlflow流程, 快速将scapy迁移到了Go生态圈,这就是创建 goscapy (<a href="https://goscapy.rpcx.io/%EF%BC%89">https://goscapy.rpcx.io/)</a> 的初衷,我们开源的网络工具也使用goscapy进行了改写。</p><p>先看一段代码。用 gopacket 构造一个 ICMP Echo Request,你要写这么多:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// gopacket 方式</span></span><br><span class="line">buf := gopacket.NewSerializeBuffer()</span><br><span class="line">opts := gopacket.SerializeOptions{ComputeChecksums: <span class="literal">true</span>, FixLengths: <span class="literal">true</span>}</span><br><span class="line">eth := layers.Ethernet{SrcMAC: srcMAC, DstMAC: dstMAC, EthernetType: layers.EthernetTypeIPv4}</span><br><span class="line">ip4 := layers.IPv4{SrcIP: srcIP, DstIP: dstIP, Protocol: layers.IPProtocolICMP, Version: <span class="number">4</span>, TTL: <span class="number">64</span>}</span><br><span class="line">icmp := layers.ICMPv4{TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoRequest, <span class="number">0</span>)}</span><br><span class="line">gopacket.SerializeLayers(buf, opts, &eth, &ip4, &icmp)</span><br><span class="line">packetData := buf.Bytes()</span><br></pre></td></tr></table></figure><p>再看 goscapy:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// goscapy 方式</span></span><br><span class="line">pkt, _ := goscapy.EtherIPICMP(<span class="string">"ff:ff:ff:ff:ff:ff"</span>, <span class="string">"8.8.8.8"</span>, <span class="number">8</span>, <span class="number">0</span>)</span><br></pre></td></tr></table></figure><p>一行。完事。<br>![image-20260608232737202.png<img src="/2026/06/11/beyond-gopacket-powerful-network-lib/image-20260608232737202.png" class=""></p><p>再看一个实现ping功能的代码:<br>![image-20260608230503539.png<img src="/2026/06/11/beyond-gopacket-powerful-network-lib/image-20260608230503539.png" class=""><br>你可以明显感觉到 scapy/goscapy 的简洁和强大。</p><p>不是 gopacket 不好——它是 C 库 libpcap 的 Go 封装,功能强大,但 API 设计带着浓浓的 C 风格。构造一个包要手动创建 SerializeBuffer、设选项、逐层填充结构体字段、最后调用 SerializeLayers。</p><p>goscapy 走的是另一条路:<strong>Scapy 式的流式 API + Go 的类型安全</strong>。今天来聊聊这个库。而且还不依赖libpcap库,不需要CGO。</p><h2 id="01-核心设计:流式-Builder-快捷函数"><a href="#01-核心设计:流式-Builder-快捷函数" class="headerlink" title="01 核心设计:流式 Builder + 快捷函数"></a>01 核心设计:流式 Builder + 快捷函数</h2><p>goscapy 提供两种构造数据包的方式:Builder API 和 Shortcut 函数。</p><p><strong>Builder API</strong> —— 像搭积木一样逐层叠加协议:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">pkt, err := goscapy.NewEthernet().</span><br><span class="line"> SrcMAC(<span class="string">"aa:bb:cc:dd:ee:ff"</span>).</span><br><span class="line"> DstMAC(<span class="string">"ff:ff:ff:ff:ff:ff"</span>).</span><br><span class="line"> Over(goscapy.NewIP().SrcIP(<span class="string">"192.168.1.1"</span>).DstIP(<span class="string">"8.8.8.8"</span>)).</span><br><span class="line"> Over(goscapy.NewICMP().Type(<span class="number">8</span>).Code(<span class="number">0</span>)).</span><br><span class="line"> Build()</span><br></pre></td></tr></table></figure><p>每层协议对应一个类型安全的 Builder——<code>EthernetBuilder</code>、<code>IPBuilder</code>、<code>TCPBuilder</code>……方法链式调用,<code>Over()</code> 把上层协议叠上去,<code>Build()</code> 序列化成字节。全程编译期检查,拼错字段名直接报错。</p><p>![image-20260608232843411.png<img src="/2026/06/11/beyond-gopacket-powerful-network-lib/image-20260608232843411.png" class=""></p><p><strong>Shortcut 函数</strong> —— 常用组合一行搞定:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Ethernet + IP + TCP SYN</span></span><br><span class="line">pkt, _ := goscapy.EtherIPTCP(<span class="string">"aa:bb:cc:dd:ee:ff"</span>, <span class="string">"ff:ff:ff:ff:ff:ff"</span>,</span><br><span class="line"> <span class="string">"192.168.1.1"</span>, <span class="string">"10.0.0.1"</span>, <span class="number">12345</span>, <span class="number">80</span>, layers.TCPSyn)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Ethernet + IP + UDP</span></span><br><span class="line">pkt, _ := goscapy.EtherIPUDP(<span class="string">"aa:bb:cc:dd:ee:ff"</span>, <span class="string">"ff:ff:ff:ff:ff:ff"</span>,</span><br><span class="line"> <span class="string">"192.168.1.1"</span>, <span class="string">"8.8.8.8"</span>, <span class="number">54321</span>, <span class="number">53</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Ethernet + ARP</span></span><br><span class="line">pkt, _ := goscapy.EtherARP(<span class="string">"aa:bb:cc:dd:ee:ff"</span>, <span class="string">"ff:ff:ff:ff:ff:ff"</span>,</span><br><span class="line"> <span class="string">"192.168.1.1"</span>, <span class="string">"192.168.1.2"</span>, layers.ARPWhoHas)</span><br></pre></td></tr></table></figure><p>内置了十几个快捷函数,覆盖 Ethernet/IPv4/IPv6 + TCP/UDP/ICMP/ARP/DNS/DHCP/VXLAN/GRE/OSPF/BGP/QUIC 等常见组合。</p><h2 id="02-两个杀手锏:自动校验和-层间绑定"><a href="#02-两个杀手锏:自动校验和-层间绑定" class="headerlink" title="02 两个杀手锏:自动校验和 + 层间绑定"></a>02 两个杀手锏:自动校验和 + 层间绑定</h2><p>手写过网络协议的人都知道,最烦的不是拼接字节,是算校验和。IP header checksum、TCP checksum(要算 pseudo-header)、UDP checksum、ICMP checksum……每层都有自己的计算规则,TCP 和 UDP 的校验和还要跨越两层协议(伪头部 + 传输层数据)。</p><p>goscapy 把这事彻底自动化了。</p><h3 id="自动校验和"><a href="#自动校验和" class="headerlink" title="自动校验和"></a>自动校验和</h3><p><code>Build()</code> 的时候,不需要你手动算任何校验和。每层协议注册了 BuildHook——IP 层自动算 total length 和 header checksum,TCP 层自动算 data offset 和 TCP checksum(包括 IPv4/IPv6 pseudo-header),UDP 和 ICMP 同理。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 你只管设你关心的字段,校验和全自动</span></span><br><span class="line">pkt, _ := goscapy.NewEthernet().</span><br><span class="line"> SrcMAC(<span class="string">"aa:bb:cc:dd:ee:ff"</span>).DstMAC(<span class="string">"ff:ff:ff:ff:ff:ff"</span>).</span><br><span class="line"> Over(goscapy.NewIP().SrcIP(<span class="string">"192.168.1.100"</span>).DstIP(<span class="string">"93.184.216.34"</span>)).</span><br><span class="line"> Over(goscapy.NewTCP().SrcPort(<span class="number">12345</span>).DstPort(<span class="number">80</span>).Flags(layers.TCPSyn)).</span><br><span class="line"> Build()</span><br><span class="line"><span class="comment">// IP checksum ✅ TCP checksum ✅ 全部自动完成</span></span><br></pre></td></tr></table></figure><p>核心机制是 <code>BuildHook</code>:每个协议在 init 阶段注册自己的钩子函数,<code>Build()</code> 时分两趟执行——第一趟序列化非 hook 层,第二趟让 hook 层拿到完整的上下文(上层字节、伪头部地址)直接写 buf。零拷贝,无额外分配。<br>![image-20260608233007808.png<img src="/2026/06/11/beyond-gopacket-powerful-network-lib/image-20260608233007808.png" class=""></p><h3 id="层间绑定"><a href="#层间绑定" class="headerlink" title="层间绑定"></a>层间绑定</h3><p>另一个让 gopacket 用户头疼的事:手动设置协议类型字段。IP 层叠了 TCP?你得记得设 <code>IP.Proto = 6</code>。以太网上跑 IPv6?得设 <code>EtherType = 0x86DD</code>。忘了就发出一个畸形包,还不好排查。</p><p>goscapy 用绑定规则(BindingRule)自动处理这事。<code>Over()</code> 调用时,框架查找注册表:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 内部注册表(init 时加载)</span></span><br><span class="line">packet.RegisterBinding(<span class="string">"IP"</span>, <span class="string">"Ethernet"</span>, <span class="string">"type"</span>, <span class="type">uint16</span>(<span class="number">0x0800</span>)) <span class="comment">// IP over Ethernet</span></span><br><span class="line">packet.RegisterBinding(<span class="string">"IPv6"</span>, <span class="string">"Ethernet"</span>, <span class="string">"type"</span>, <span class="type">uint16</span>(<span class="number">0x86DD</span>)) <span class="comment">// IPv6 over Ethernet</span></span><br><span class="line">packet.RegisterBinding(<span class="string">"ARP"</span>, <span class="string">"Ethernet"</span>, <span class="string">"type"</span>, <span class="type">uint16</span>(<span class="number">0x0806</span>)) <span class="comment">// ARP over Ethernet</span></span><br><span class="line">packet.RegisterBinding(<span class="string">"TCP"</span>, <span class="string">"IP"</span>, <span class="string">"proto"</span>, <span class="type">uint8</span>(<span class="number">6</span>)) <span class="comment">// TCP over IP</span></span><br><span class="line">packet.RegisterBinding(<span class="string">"UDP"</span>, <span class="string">"IP"</span>, <span class="string">"proto"</span>, <span class="type">uint8</span>(<span class="number">17</span>)) <span class="comment">// UDP over IP</span></span><br><span class="line">packet.RegisterBinding(<span class="string">"ICMP"</span>, <span class="string">"IP"</span>, <span class="string">"proto"</span>, <span class="type">uint8</span>(<span class="number">1</span>)) <span class="comment">// ICMP over IP</span></span><br><span class="line">packet.RegisterBinding(<span class="string">"Dot1Q"</span>, <span class="string">"Ethernet"</span>, <span class="string">"type"</span>, <span class="type">uint16</span>(<span class="number">0x8100</span>)) <span class="comment">// VLAN over Ethernet</span></span><br><span class="line">packet.RegisterBinding(<span class="string">"GRE"</span>, <span class="string">"IP"</span>, <span class="string">"proto"</span>, <span class="type">uint8</span>(<span class="number">47</span>)) <span class="comment">// GRE over IP</span></span><br><span class="line">packet.RegisterBinding(<span class="string">"OSPF"</span>, <span class="string">"IP"</span>, <span class="string">"proto"</span>, <span class="type">uint8</span>(<span class="number">89</span>)) <span class="comment">// OSPF over IP</span></span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>当你写 <code>NewEthernet().Over(NewIP())</code> 时,Ethernet 的 <code>type</code> 字段自动设成 <code>0x0800</code>。你不需要知道这些魔数,协议栈帮你搞定。<br>![image-20260608233117805.png<img src="/2026/06/11/beyond-gopacket-powerful-network-lib/image-20260608233117805.png" class=""></p><h2 id="03-数据包解析:自动协议推断"><a href="#03-数据包解析:自动协议推断" class="headerlink" title="03 数据包解析:自动协议推断"></a>03 数据包解析:自动协议推断</h2><p>构造包只是故事的一半,解析包是另一半。</p><p>goscapy 的 <code>Dissect</code> 能从原始字节自动推断出完整的协议栈:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">raw := []<span class="type">byte</span>{<span class="number">0xff</span>, <span class="number">0xff</span>, <span class="number">0xff</span>, <span class="number">0xff</span>, <span class="number">0xff</span>, <span class="number">0xff</span>, <span class="number">0xaa</span>, <span class="number">0xbb</span>, ...}</span><br><span class="line">pkt, _ := packet.DissectByProto(raw, <span class="string">"Ethernet"</span>)</span><br><span class="line">fmt.Println(pkt.String()) <span class="comment">// "Ethernet / IP / ICMP"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 访问具体层</span></span><br><span class="line">ipLayer := pkt.GetLayer(<span class="string">"IP"</span>)</span><br><span class="line">srcIP, _ := ipLayer.Get(<span class="string">"src"</span>) <span class="comment">// net.IP{192, 168, 1, 1}</span></span><br></pre></td></tr></table></figure><p>解析引擎靠注册表驱动。每一层解析完后,查 <code>keyField</code> 找到下一层——Ethernet 看看 <code>type</code> 字段(0x0800 → IP),IP 看看 <code>proto</code> 字段(6 → TCP),TCP 看看 <code>dport</code>(80 → HTTP)。VXLAN 这种隧道协议还能递归解析内层包,最多支持 8 层嵌套。</p><p>启发式规则也注册了一大堆:UDP 53 端口 → DNS,TCP 80 → HTTP,UDP 4789 → VXLAN,IP proto 47 → GRE……抓到的包基本都能自动识别到应用层。<br>![image-20260608233529079.png<img src="/2026/06/11/beyond-gopacket-powerful-network-lib/image-20260608233529079.png" class=""></p><h2 id="04-嗅探和收发包:Scapy-的-sr-srp-到-Go"><a href="#04-嗅探和收发包:Scapy-的-sr-srp-到-Go" class="headerlink" title="04 嗅探和收发包:Scapy 的 sr/srp 到 Go"></a>04 嗅探和收发包:Scapy 的 sr/srp 到 Go</h2><p>写过 Scapy 的人一定对 <code>sr()</code> 和 <code>sr1()</code> 不陌生——发一个包,自动等响应,还能做协议级匹配。goscapy 把这套逻辑完整搬过来了。</p><h3 id="发包"><a href="#发包" class="headerlink" title="发包"></a>发包</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 构造结构化的 Packet 对象(不是 Build 出 []byte)</span></span><br><span class="line">pkt := goscapy.NewEthernet().</span><br><span class="line"> DstMAC(<span class="string">"ff:ff:ff:ff:ff:ff"</span>).</span><br><span class="line"> Over(goscapy.NewIP().SrcIP(<span class="string">"192.168.1.1"</span>).DstIP(<span class="string">"8.8.8.8"</span>)).</span><br><span class="line"> Over(goscapy.NewICMP().Type(<span class="number">8</span>).Code(<span class="number">0</span>)).</span><br><span class="line"> Packet()</span><br><span class="line"></span><br><span class="line">sendrecv.Send(pkt, <span class="string">"eth0"</span>) <span class="comment">// L3 发送(IP 层,OS 补 Ethernet)</span></span><br><span class="line">sendrecv.Sendp(pkt, <span class="string">"eth0"</span>) <span class="comment">// L2 发送(完整以太网帧)</span></span><br></pre></td></tr></table></figure><h3 id="发包收响应"><a href="#发包收响应" class="headerlink" title="发包收响应"></a>发包收响应</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 类似 Scapy 的 sr1():发 ICMP Echo,等第一个响应</span></span><br><span class="line">sent, reply, err := sendrecv.Sr1(pkt, <span class="string">"eth0"</span>, <span class="number">3</span>*time.Second, <span class="literal">nil</span>)</span><br><span class="line"><span class="keyword">if</span> reply != <span class="literal">nil</span> {</span><br><span class="line"> ipLayer := reply.GetLayer(<span class="string">"IP"</span>)</span><br><span class="line"> srcIP, _ := ipLayer.Get(<span class="string">"src"</span>) <span class="comment">// 8.8.8.8 的回复</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>DefaultMatch</code> 自动匹配响应包——ICMP Echo Request 配 Echo Reply(ID 匹配),TCP SYN 配 SYN-ACK(端口翻转 + ack = seq+1),UDP 配端口翻转,DNS 匹 transaction ID,ARP 配 IP 交换。不用写一行匹配逻辑。<br>![image-20260608233328575.png<img src="/2026/06/11/beyond-gopacket-powerful-network-lib/image-20260608233328575.png" class=""></p><h3 id="嗅探"><a href="#嗅探" class="headerlink" title="嗅探"></a>嗅探</h3><p>两种 API 风格,按场景选:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 回调式:适合流式处理</span></span><br><span class="line">sniff.Sniff(sniff.SniffConfig{</span><br><span class="line"> Iface: <span class="string">"eth0"</span>,</span><br><span class="line"> Filter: <span class="string">"tcp port 80"</span>, <span class="comment">// BPF 过滤器</span></span><br><span class="line"> Timeout: <span class="number">10</span> * time.Second,</span><br><span class="line">}, <span class="function"><span class="keyword">func</span><span class="params">(pkt *packet.Packet)</span></span> <span class="type">bool</span> {</span><br><span class="line"> fmt.Println(pkt.String())</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span> <span class="comment">// false 停止嗅探</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通道式:适合批量收集</span></span><br><span class="line">ch, stop := sniff.SniffChan(sniff.SniffConfig{Iface: <span class="string">"eth0"</span>, Count: <span class="number">5</span>})</span><br><span class="line"><span class="keyword">defer</span> stop()</span><br><span class="line"><span class="keyword">for</span> pkt := <span class="keyword">range</span> ch {</span><br><span class="line"> fmt.Println(pkt)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>BPF 过滤器在内核层面生效,不是用户态过滤,性能有保证。</p><h2 id="05-协议覆盖:从链路层到应用层"><a href="#05-协议覆盖:从链路层到应用层" class="headerlink" title="05 协议覆盖:从链路层到应用层"></a>05 协议覆盖:从链路层到应用层</h2><p>goscapy 不只是 TCP/UDP/ICMP 三板斧。看一下完整的协议支持:</p><table><thead><tr><th>层级</th><th>协议</th></tr></thead><tbody><tr><td><strong>链路层</strong></td><td>Ethernet、ARP、802.1Q (VLAN)、802.11 (WiFi)、RadioTap、Bluetooth HCI/L2CAP/ATT</td></tr><tr><td><strong>网络层</strong></td><td>IPv4、IPv6、IPv6 扩展头(Hop-by-Hop/Routing/Fragment/DestOpts)、ICMPv6、NDP</td></tr><tr><td><strong>传输层</strong></td><td>TCP(含 Options)、UDP、ICMP、GRE、VXLAN</td></tr><tr><td><strong>路由协议</strong></td><td>OSPF、BGP、LLDP</td></tr><tr><td><strong>应用层</strong></td><td>DNS、DHCP、HTTP、NTP、TLS、QUIC、SNMP、RADIUS、LDAP、Kerberos</td></tr><tr><td><strong>IoT/无线</strong></td><td>Zigbee (NWK/APS/Cluster)、LoRaWAN、Bluetooth</td></tr><tr><td><strong>监控/流</strong></td><td>Netflow V5/V9、IPFIX、RTP、RTCP、SIP、ERSPAN</td></tr><tr><td><strong>隧道</strong></td><td>VXLAN、GRE、ERSPAN v3、Dot1Q (QinQ)</td></tr><tr><td>![image-20260608233708377.png<img src="/2026/06/11/beyond-gopacket-powerful-network-lib/image-20260608233708377.png" class=""></td><td></td></tr><tr><td>每个协议都有 Builder + 解析注册 + 绑定规则,开箱即用。</td><td></td></tr></tbody></table><h2 id="06-实战:ARP-扫描器"><a href="#06-实战:ARP-扫描器" class="headerlink" title="06 实战:ARP 扫描器"></a>06 实战:ARP 扫描器</h2><p>![image-20260608233850514.png<img src="/2026/06/11/beyond-gopacket-powerful-network-lib/image-20260608233850514.png" class=""></p><p>goscapy 自带了一个 <code>arping</code> 包,用大概 200 行代码实现了完整的 ARP 子网扫描器。这段代码值得细看,因为它展示了 goscapy 的实际用法:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 构造 ARP who-has 包</span></span><br><span class="line">pkt := goscapy.NewEthernet().</span><br><span class="line"> DstMAC(<span class="string">"ff:ff:ff:ff:ff:ff"</span>).</span><br><span class="line"> SrcMAC(srcMAC).</span><br><span class="line"> Type(layers.EtherTypeARP).</span><br><span class="line"> Over(goscapy.NewARP().</span><br><span class="line"> Op(layers.ARPWhoHas).</span><br><span class="line"> SrcMAC(srcMAC).</span><br><span class="line"> SrcIP(srcIP).</span><br><span class="line"> DstIP(targetIP)).</span><br><span class="line"> Packet()</span><br><span class="line"></span><br><span class="line"><span class="comment">// L2 发送,等第一个 ARP 响应</span></span><br><span class="line">_, reply, err := sendrecv.Srp1(pkt, iface, timeout, <span class="literal">nil</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 从回复中提取 MAC 地址</span></span><br><span class="line">arpLayer := reply.GetLayer(<span class="string">"ARP"</span>)</span><br><span class="line">hwSrc, _ := arpLayer.Get(<span class="string">"hwsrc"</span>)</span><br></pre></td></tr></table></figure><p>几个值得注意的点:</p><ol><li><strong><code>Packet()</code> 而不是 <code>Build()</code></strong>——获取结构化的 Packet 对象,用于后续传给 <code>Srp1</code></li><li><strong><code>Srp1</code></strong>——Scapy 的 <code>srp1()</code> Go 版本,L2 发送 + 等匹配响应</li><li><strong><code>DefaultMatch</code> 自动匹配 ARP 响应</strong>——op=is-at + psrc 匹配请求的 pdst</li></ol><p>扫描器还做了并发控制——用 semaphore 限制 50 个并发探测,整个 /24 子网一两秒扫完:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">ARP scan: 192.168.1.0/24 on en0</span><br><span class="line">IP MAC RTT</span><br><span class="line">--------------------------------------------------</span><br><span class="line">192.168.1.1 aa:bb:cc:dd:ee:ff 1.2ms</span><br><span class="line">192.168.1.100 11:22:33:44:55:66 0.8ms</span><br><span class="line">192.168.1.254 ff:ee:dd:cc:bb:aa 1.5ms</span><br><span class="line">--------------------------------------------------</span><br><span class="line">Found 3 hosts in 0.52s</span><br></pre></td></tr></table></figure><h2 id="07-实战:TCP-SYN-扫描"><a href="#07-实战:TCP-SYN-扫描" class="headerlink" title="07 实战:TCP SYN 扫描"></a>07 实战:TCP SYN 扫描</h2><p>再看一个端口扫描的例子。构造 TCP SYN 包,发出去,等 SYN-ACK 回来就是 open,等 RST 或超时就是 closed/filtered:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">pkt := goscapy.NewIP().SrcIP(srcIP).DstIP(targetIP).</span><br><span class="line"> Over(goscapy.NewTCP().SrcPort(<span class="number">12345</span>).DstPort(port).Flags(layers.TCPSyn)).</span><br><span class="line"> Packet()</span><br><span class="line"></span><br><span class="line">_, reply, err := sendrecv.Sr1(pkt, iface, <span class="number">2</span>*time.Second, <span class="literal">nil</span>)</span><br><span class="line"><span class="keyword">if</span> err == <span class="literal">nil</span> && reply != <span class="literal">nil</span> {</span><br><span class="line"> tcpLayer := reply.GetLayer(<span class="string">"TCP"</span>)</span><br><span class="line"> flags, _ := tcpLayer.Get(<span class="string">"flags"</span>)</span><br><span class="line"> <span class="keyword">if</span> flags.(<span class="type">uint8</span>)&layers.TCPSyn != <span class="number">0</span> && flags.(<span class="type">uint8</span>)&layers.TCPAck != <span class="number">0</span> {</span><br><span class="line"> fmt.Printf(<span class="string">"Port %d: OPEN\n"</span>, port)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>DefaultMatch</code> 帮你验证 SYN-ACK:端口翻转 + flags 必须同时包含 SYN 和 ACK + ack 必须等于 seq+1。你不用写任何匹配逻辑。</p><h2 id="08-内部架构:三层抽象"><a href="#08-内部架构:三层抽象" class="headerlink" title="08 内部架构:三层抽象"></a>08 内部架构:三层抽象</h2><p>goscapy 的代码组织很清晰,三个核心抽象:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">┌─────────────────────────────────────────┐</span><br><span class="line">│ goscapy (Builder + Shortcut) │ ← 用户 API 层</span><br><span class="line">│ 流式 Builder / 一行 Shortcut │</span><br><span class="line">├─────────────────────────────────────────┤</span><br><span class="line">│ packet (Packet / Layer / Dissect) │ ← 核心抽象层</span><br><span class="line">│ Build / Dissect / Binding / Hook │</span><br><span class="line">├─────────────────────────────────────────┤</span><br><span class="line">│ fields (Field 类型系统) │ ← 序列化基础设施</span><br><span class="line">│ Byte/Short/Int/Long/MAC/IP/Str/Bit... │</span><br><span class="line">└─────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><p><strong>fields 层</strong>:每个协议字段都是一个 <code>Field</code> 接口实现——<code>ByteField</code>、<code>ShortField</code>、<code>IntField</code>、<code>MACField</code>、<code>IPField</code>、<code>IPv6Field</code>、<code>StrField</code>、<code>ConditionalField</code>……负责 Pack(序列化)和 Unpack(反序列化)。还支持零拷贝的 <code>PackInto</code> 接口,序列化直接写入目标 buffer。</p><p><strong>packet 层</strong>:<code>Layer</code> 持有字段定义 + 运行时值,<code>Packet</code> 是 Layer 的有序栈。核心逻辑包括:</p><ul><li><code>Build()</code>:三趟序列化——算总长度 → 序列化非 hook 层 → 执行 BuildHook(校验和/长度计算)</li><li><code>Dissect()</code>:注册表驱动的递归解析,支持隧道嵌套</li><li><code>BindingRule</code>:层间字段自动绑定</li><li><code>BuildHook</code>:校验和、长度等派生字段自动计算</li></ul><p><strong>goscapy 层</strong>:面向用户的 Builder API 和 Shortcut 函数,每个协议一个 Builder 类型,方法链式调用,编译期类型安全。<br>![image-20260608234050612.png<img src="/2026/06/11/beyond-gopacket-powerful-network-lib/image-20260608234050612.png" class=""><br>这个分层的好处是:如果你要支持一个新协议,只需要定义 Layer 的字段列表、注册绑定规则和 BuildHook,Builder 层的代码机械性很强。协议扩展是注册式的,不是继承式的。</p><h2 id="09-和-gopacket-正面-PK"><a href="#09-和-gopacket-正面-PK" class="headerlink" title="09 和 gopacket 正面 PK"></a>09 和 gopacket 正面 PK</h2><p>说了这么多,来个直接对比:</p><table><thead><tr><th>维度</th><th>gopacket</th><th>goscapy</th></tr></thead><tbody><tr><td><strong>构造包</strong></td><td>手动填充结构体 + SerializeLayers</td><td>流式 Builder / 一行 Shortcut</td></tr><tr><td><strong>校验和</strong></td><td>需要 <code>SerializeOptions{ComputeChecksums: true}</code></td><td>完全自动,无需任何选项</td></tr><tr><td><strong>协议类型字段</strong></td><td>手动设 EtherType/IP Proto</td><td>层间绑定自动推断</td></tr><tr><td><strong>解析</strong></td><td><code>PacketSource.NextPacket()</code> + 类型断言</td><td><code>Dissect()</code> 自动协议推断</td></tr><tr><td><strong>发包</strong></td><td>需要 <code>pcap.OpenLive</code> + <code>Handle.WritePacketData</code></td><td><code>Send()</code> / <code>Sendp()</code> 直接发</td></tr><tr><td><strong>收响应</strong></td><td>手动读包 + 手动匹配</td><td><code>Sr1()</code> 自动协议级匹配</td></tr><tr><td><strong>嗅探</strong></td><td><code>PacketSource.Packets()</code> channel</td><td>回调式 + 通道式两种 API</td></tr><tr><td><strong>依赖</strong></td><td>依赖 libpcap / npcap C 库</td><td>纯 Go,零 C 依赖</td></tr><tr><td><strong>跨平台</strong></td><td>需要 CGO + 安装 libpcap</td><td>原生支持 macOS/Linux</td></tr><tr><td><strong>协议数量</strong></td><td>非常多(libpcap 生态)</td><td>30+ 协议,持续扩展中</td></tr><tr><td>![image-20260608234240098.png<img src="/2026/06/11/beyond-gopacket-powerful-network-lib/image-20260608234240098.png" class=""></td><td></td><td></td></tr><tr><td><strong>gopacket 更适合</strong>:需要解析冷门协议、对 pcap 文件读写有强需求、已经重度依赖 libpcap 生态的项目。</td><td></td><td></td></tr></tbody></table><p><strong>goscapy 更适合</strong>:网络工具开发、安全扫描、协议测试、网络监控探测——任何需要快速构造和收发数据包的场景。纯 Go 部署简单,API 用起来舒服。</p><h2 id="10-性能:零拷贝序列化"><a href="#10-性能:零拷贝序列化" class="headerlink" title="10 性能:零拷贝序列化"></a>10 性能:零拷贝序列化</h2><p>goscapy 在序列化上做了不少优化:</p><ul><li><strong><code>SerializeInto</code></strong>:直接写入目标 buffer,无额外堆分配</li><li><strong><code>BuildInto</code></strong>:用户提供 buffer,整个包一次序列化完成</li><li><strong><code>RecvInto</code></strong>:收包直接读入用户 buffer,减少一次拷贝</li><li><strong>校验和零拷贝</strong>:<code>checksumIPv4Pseudo</code> 直接折叠多个内存区域,不拼接</li><li><strong><code>WireSize</code></strong>:预计算序列化大小,一次分配精确大小的 buffer</li></ul><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 零拷贝发送</span></span><br><span class="line">buf := <span class="built_in">make</span>([]<span class="type">byte</span>, <span class="number">1500</span>) <span class="comment">// MTU 大小的 buffer</span></span><br><span class="line">result, err := pkt.BuildInto(buf) <span class="comment">// 直接写入,无额外分配</span></span><br></pre></td></tr></table></figure><p>还有 Linux 特有的高性能接收模式——<code>AF_PACKET</code> mmap、零拷贝 (<code>PACKET_QDISC_BYPASS</code>)、<code>io_uring</code> 原始套接字——适合高频探测场景。这些在 examples 目录里有完整示例(23-packet-mmap、21-zerocopy、22-uring-raw-socket)。</p><h2 id="11-丰富的示例库"><a href="#11-丰富的示例库" class="headerlink" title="11 丰富的示例库"></a>11 丰富的示例库</h2><p>goscapy 自带了 50+ 个示例,覆盖从基础到高级的几乎所有场景:</p><ul><li>基础:ping、traceroute、TCP SYN 扫描、ARP 扫描</li><li>协议:DNS 客户端、DHCP 客户端、NTP 客户端</li><li>隧道:VXLAN 封装、GRE 隧道、ERSPAN</li><li>高级:PCAP 读写、TCP 流重组、BPF 过滤</li><li>性能:零拷贝收包、io_uring、packet mmap、批量发送</li><li>无线:802.11 WiFi 帧、蓝牙 HCI/L2CAP、Zigbee、LoRaWAN</li><li>安全:p0f 指纹、端口扫描、ARP 扫描</li></ul><p>每个示例都是可编译运行的小程序,直接 <code>go run</code> 就能跑。</p><hr><p>项目地址:<a href="https://github.com/smallnest/goscapy">github.com/smallnest/goscapy</a></p><p>纯 Go,MIT 协议,零 C 依赖。<code>go get github.com/smallnest/goscapy</code> 就能用。</p><p>如果你在做网络工具、安全扫描、协议测试、监控探测——试试 goscapy,可能会让你重新爱上 Go 网络编程。</p>
同分类推荐文章
- Go 实验特性详解 (2026-06-21 10:05:27)
- amd64 微架构级别对 Go 程序性能提升多少? (2026-06-21 09:38:49)
- Loop Engineering 实践:我把 RDMA 开发库移植到 Go 语言,花费 239 块钱 (2026-06-17 04:00:24)
建议继续学习
- libcurl的使用总结(二) (累计阅读 15,054)
- Go Reflect 性能 (累计阅读 14,121)
- 面向“接口”编程和面向“实现”编程 (累计阅读 13,886)
- 一种基于长连接的社交游戏服务器程序构架 (累计阅读 7,471)
- 浅析linux kernel network之socket创建 (累计阅读 6,717)
- TCP链接主动关闭不发fin包奇怪行为分析 (累计阅读 6,688)
- TCP之close_wait (累计阅读 6,516)
- iPhone下的libcurl with SSL for iOS (累计阅读 6,326)
- libevent源码浅析: http库 (累计阅读 6,169)
- 从Go看,语言设计(一) (累计阅读 6,146)