在 linux 中使用 gogpacket 给本地发送 icmp 包无响应,windows 中给本机发正常,给其他 Ip 发正常、linux 中给其他 Ip 发包也正常,但是给自己发包五响应,下图前两个包是使用 ping 发送的,显示正常,第三个包是 gopacket 发送的,没有响应
func TestPingPacket(t *testing.T) {
dstIP := net.ParseIP("192.168.2.12") // 目标 IP 地址
// 创建 ICMPv4 层
icmpLayer := &layers.ICMPv4{
TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoRequest, 0),
Id: uint16(os.Getpid()),
Seq: 1,
}
// 创建 IPv4 层
ipLayer := &layers.IPv4{
SrcIP: net.ParseIP("192.168.2.12"), // 源 IP 地址
DstIP: dstIP,
Version: 4,
TTL: 64,
Id: uint16(os.Getpid()),
Protocol: layers.IPProtocolICMPv4,
Flags: layers.IPv4DontFragment,
}
// 创建以太网( Ethernet )层
ethernetLayer := &layers.Ethernet{
SrcMAC: net.HardwareAddr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, // 源 MAC 地址
DstMAC: net.HardwareAddr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, // 目标 MAC 地址
EthernetType: layers.EthernetTypeIPv4, // 使用 IPv4
}
h := "cd470e0000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637"
b, _ := hex.DecodeString(h)
// 构造数据包
buffer := gopacket.NewSerializeBuffer()
// time
currentTime := time.Now().Unix()
byteData := make([]byte, 8)
binary.LittleEndian.PutUint64(byteData, uint64(currentTime))
byteData = append(byteData, b...)
payload := gopacket.Payload(byteData)
serializerOptions := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
err := payload.SerializeTo(buffer, serializerOptions)
if err != nil {
println(err.Error())
return
}
err = icmpLayer.SerializeTo(buffer, serializerOptions)
if err != nil {
println(err.Error())
return
}
err = ipLayer.SerializeTo(buffer, serializerOptions)
if err != nil {
println(err.Error())
return
}
err = ethernetLayer.SerializeTo(buffer, serializerOptions)
if err != nil {
println(err.Error())
return
}
handle, err := pcap.OpenLive("lo", 65536, false, pcap.BlockForever)
if err != nil {
fmt.Println("Error opening interface:", err)
return
}
defer handle.Close()
err = handle.WritePacketData(buffer.Bytes())
if err != nil {
fmt.Println("Error sending packet:", err)
return
}
println("send icmp ok")
}
1
zone10 2023-08-03 10:15:49 +08:00
是想学习 gopacket 还是想实现 icmp 功能, 如果是后者用 go 的标准库几行代码就搞定了, 没必要上 gopacket,
```go package main import "net" func main() { domain := "www.baidu.com" raddr, err := net.ResolveIPAddr("ip", domain) if err != nil { panic(err) } conn, err := net.DialIP("ip4:icmp", nil, raddr) if err != nil { panic(err) } defer conn.Close() buf := []byte{0x08, 0x00, 0xf7, 0xfe, 0x00, 0x01, 0x00, 0x00} _, err = conn.Write(buf) if err != nil { panic(err) } } ``` 如果是前者, 链路层的以太网帧构建出错, 源 MAC 是本机 MAC, 目的 MAC 是网关 MAC, 需要通过 ARP 协议获得, 或者通过 wireshark 抓包硬编码地址也行 |
2
wdf1286 2023-08-03 10:34:11 +08:00
建议先用 tcpdump 抓 lo 看看真实的 ping 包是什么样的再用 gopacket 模仿
|
3
fansfans OP @wdf1286 因为 windows 上无法使用 PacketConn 读取数据包,还是的用 gopacket ,因此在 linux 平台也沿用了同一套代码
192.168.2.12 是本机 Ip,所以 mac 地址是没问题的,这个在抓包对比中确认过了 |
4
fansfans OP @zone10 因为 windows 上无法使用 PacketConn 读取数据包,还是的用 gopacket ,因此在 linux 平台也沿用了同一套代码
192.168.2.12 是本机 Ip,所以 mac 地址是没问题的,这个在抓包对比中确认过了 |
5
fansfans OP @wdf1286 对比了两个数据包基本是一致的,并且使用 gopacket 发送有成功过的数据包依旧没有响应,不知道是不是有什么特殊的处理
|
6
fansfans OP 相同的数据包,使用 packconn 发送有响应,使用 gopacket 无响应
![]( https://p.sda1.dev/12/886e2307ff51b283194147cec39d8369/image.png) |
7
zone10 2023-08-03 14:44:22 +08:00
@fansfans 好吧, 没看清楚题意, 有两个问题, 第一, 本地主机不走以太网而是用回环地址 Lookback, 不过应该不是包无响应的原因. 如果你把 ping 设置的久一点然后通过抓包就能发现, ping 程序的进程 id 跟 icmp 包头的 id 值是一样的, 所以响应这个 ping 请求的应该就是当前这个 ping 进程自己, 而你的程序没有处理响应 icmp 包的逻辑. 给其他 IP 发正常是因为响应你 icmp 请求的逻辑在其他主机
|
8
zone10 2023-08-03 14:59:43 +08:00
@zone10 补充一下, windows ping 本机默认走的 loopback 地址, linux 默认走的以太网, linux ping 设置 -A 选项后走的 loopback 地址速度瞬间快了十倍
|
9
fansfans OP @zone10 的确如你所说,以下是 chatgpt 的回答:
在 Linux 系统中,当你使用 `gopacket` 自己构造 ICMP Echo Request 并发送给自己(本地回环地址 127.0.0.1 ),操作系统的内核并不会自动处理这个 ICMP 请求,因为这个请求在内核的 ICMP 协议栈中是不会被处理的。 当其他主机向你的主机发送 ICMP Echo Request 时,这个请求会经过网络接口,到达内核的网络协议栈。内核会处理 ICMP Echo Request ,然后根据协议处理机制,产生 ICMP Echo Reply 并通过网络接口发送回去。 但当你自己使用 `gopacket` 构造 ICMP Echo Request 并发送给自己时,数据包并不会经过网络接口,而是直接从应用程序发送到内核中的数据包处理层。在这种情况下,内核的 ICMP 协议栈并不会处理这个 ICMP Echo Request ,因为这个请求并没有经过网络接口。 因此,当你使用 `gopacket` 构造 ICMP Echo Request 并发送给自己时,你自己的应用程序需要负责处理这个 ICMP Echo Request ,并且根据需要产生 ICMP Echo Reply 并回复给自己。如果你希望收到 ICMP Echo Reply ,你需要在你的程序中对 ICMP Echo Request 进行相应的处理。 |