You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用gopacket发送UDP包至127.0.0.1,Wireshark可见但netcat未接收

为什么用gopacket发送的环回UDP包能在Wireshark看到但无法到达netcat?

问题描述

我尝试用gopacket向127.0.0.1发送UDP包,代码如下:

package main
import (
 "fmt"
 "net"
 "github.com/google/gopacket"
 "github.com/google/gopacket/layers"
 "github.com/google/gopacket/pcap"
)
func main() {
 handle, err := pcap.OpenLive("lo", 1500, false, pcap.BlockForever)
 if err != nil {
 fmt.Printf("%s\n", err.Error())
 return
 }
 eth := layers.Ethernet{
 EthernetType: layers.EthernetTypeIPv4,
 SrcMAC: net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
 DstMAC: net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
 }
 ip := layers.IPv4{
 Version: 4,
 TTL: 64,
 SrcIP: net.IP{127, 0, 0, 1},
 DstIP: net.IP{127, 0, 0, 1},
 Protocol: layers.IPProtocolUDP,
 }
 udp := layers.UDP{
 SrcPort: 62003,
 DstPort: 8080,
 }
 udp.SetNetworkLayerForChecksum(&ip)
 payload := []byte{'a', 'b', 'c', '\n'}
 options := gopacket.SerializeOptions{
 ComputeChecksums: true,
 FixLengths: true,
 }
 buffer := gopacket.NewSerializeBuffer()
 err = gopacket.SerializeLayers(buffer, options, &eth, &ip, &udp, gopacket.Payload(payload), )
 if err != nil {
 fmt.Printf("[-] Serialize error: %s\n", err.Error())
 return
 }
 outgoingPacket := buffer.Bytes()
 err = handle.WritePacketData(outgoingPacket)
 if err != nil {
 fmt.Printf("[-] Error while sending: %s\n", err.Error())
 return
 }
}

我在终端用netcat监听:nc -ulp 8080 -s 127.0.0.1,运行代码后,Wireshark能在环回接口看到生成的数据包且校验和正确,但数据包从未到达netcat。请问这是为什么?

原因分析

其实问题出在环回接口的特殊处理逻辑上:

  • 当你用pcap直接往环回接口写入完整的以太网帧时,系统内核的网络栈根本不会处理这些帧。环回接口本来就是绕过物理链路层设计的,内核期望收到的是IP层及以上的数据包,而不是你手动构造的以太网帧。
  • Wireshark能抓到这些帧是因为它是在链路层做捕获的,但内核不会去解析你手动塞进去的以太网帧,自然也就不会把数据包往上传递给netcat这类应用程序。

解决方案

这里有两种可行的解决方式:

方案1:跳过以太网层,直接发送IP数据包

修改代码,去掉以太网层的构造,只序列化IP、UDP和Payload部分,这样内核就能正确处理数据包了:

package main
import (
 "fmt"
 "net"
 "github.com/google/gopacket"
 "github.com/google/gopacket/layers"
 "github.com/google/gopacket/pcap"
)
func main() {
 handle, err := pcap.OpenLive("lo", 1500, false, pcap.BlockForever)
 if err != nil {
 fmt.Printf("%s\n", err.Error())
 return
 }
 defer handle.Close()

 // 构造IP层
 ip := layers.IPv4{
 Version:  4,
 TTL:      64,
 SrcIP:    net.IP{127, 0, 0, 1},
 DstIP:    net.IP{127, 0, 0, 1},
 Protocol: layers.IPProtocolUDP,
 }

 // 构造UDP层并计算校验和
 udp := layers.UDP{
 SrcPort: 62003,
 DstPort: 8080,
 }
 udp.SetNetworkLayerForChecksum(&ip)

 payload := []byte{'a', 'b', 'c', '\n'}
 options := gopacket.SerializeOptions{
 ComputeChecksums: true,
 FixLengths:       true,
 }

 buffer := gopacket.NewSerializeBuffer()
 // 只序列化IP、UDP和Payload,跳过以太网层
 err = gopacket.SerializeLayers(buffer, options, &ip, &udp, gopacket.Payload(payload))
 if err != nil {
 fmt.Printf("[-] Serialize error: %s\n", err.Error())
 return
 }

 outgoingPacket := buffer.Bytes()
 err = handle.WritePacketData(outgoingPacket)
 if err != nil {
 fmt.Printf("[-] Error while sending: %s\n", err.Error())
 return
 }
 fmt.Println("[+] Packet sent successfully")
}

方案2:使用Go标准库直接发送(更简单可靠)

如果你不需要手动构造底层数据包的需求,直接用Go标准库的net包发送UDP包会更省心,因为它会通过系统网络栈正常传递数据包:

package main
import (
 "fmt"
 "net"
)
func main() {
 // 建立UDP连接
 conn, err := net.DialUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 62003}, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 8080})
 if err != nil {
 fmt.Printf("[-] Dial error: %s\n", err.Error())
 return
 }
 defer conn.Close()

 // 发送数据
 _, err = conn.Write([]byte{'a', 'b', 'c', '\n'})
 if err != nil {
 fmt.Printf("[-] Send error: %s\n", err.Error())
 return
 }
 fmt.Println("[+] Data sent successfully")
}

验证

运行修改后的任意一个方案代码,你就能看到netcat成功接收到abc\n的内容了。

内容的提问来源于stack exchange,提问作者paddlesteamer

火山引擎 最新活动