请教:在 Linux 中使用 gopacket 给本机发送 icmp 包无响应

2023-08-02 18:48:22 +08:00
 fansfans

在 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")
}

987 次点击
所在节点    程序员
9 条回复
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 抓包硬编码地址也行
wdf1286
2023-08-03 10:34:11 +08:00
建议先用 tcpdump 抓 lo 看看真实的 ping 包是什么样的再用 gopacket 模仿
fansfans
2023-08-03 12:13:27 +08:00
@wdf1286 因为 windows 上无法使用 PacketConn 读取数据包,还是的用 gopacket ,因此在 linux 平台也沿用了同一套代码
192.168.2.12 是本机 Ip,所以 mac 地址是没问题的,这个在抓包对比中确认过了
fansfans
2023-08-03 12:14:14 +08:00
@zone10 因为 windows 上无法使用 PacketConn 读取数据包,还是的用 gopacket ,因此在 linux 平台也沿用了同一套代码
192.168.2.12 是本机 Ip,所以 mac 地址是没问题的,这个在抓包对比中确认过了
fansfans
2023-08-03 12:15:37 +08:00
@wdf1286 对比了两个数据包基本是一致的,并且使用 gopacket 发送有成功过的数据包依旧没有响应,不知道是不是有什么特殊的处理
fansfans
2023-08-03 12:43:56 +08:00
相同的数据包,使用 packconn 发送有响应,使用 gopacket 无响应
![]( https://p.sda1.dev/12/886e2307ff51b283194147cec39d8369/image.png)
zone10
2023-08-03 14:44:22 +08:00
@fansfans 好吧, 没看清楚题意, 有两个问题, 第一, 本地主机不走以太网而是用回环地址 Lookback, 不过应该不是包无响应的原因. 如果你把 ping 设置的久一点然后通过抓包就能发现, ping 程序的进程 id 跟 icmp 包头的 id 值是一样的, 所以响应这个 ping 请求的应该就是当前这个 ping 进程自己, 而你的程序没有处理响应 icmp 包的逻辑. 给其他 IP 发正常是因为响应你 icmp 请求的逻辑在其他主机
zone10
2023-08-03 14:59:43 +08:00
@zone10 补充一下, windows ping 本机默认走的 loopback 地址, linux 默认走的以太网, linux ping 设置 -A 选项后走的 loopback 地址速度瞬间快了十倍
fansfans
2023-08-03 18:46:53 +08:00
@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 进行相应的处理。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/961903

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX