0

Golang darwin/amd64 平台中解析 IP Packet Header 得到的报文长度错误

 2 years ago
source link: https://www.v2ex.com/t/787485
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

V2EX  ›  Go

Golang darwin/amd64 平台中解析 IP Packet Header 得到的报文长度错误

  zeyexe · 3 小时 32 分钟前 · 253 次点击

直接贴代码,这是从 198.18.255.1 Ping 8.8.8.8(原始 IP 不是这个)返回的 IP 报文:

package main

import (
	"encoding/binary"
	"fmt"
	"golang.org/x/net/ipv4"
)

func main() {

	// https://en.wikipedia.org/wiki/IPv4#Total_Length
	var hdrBytes = []byte{
		69, 0, // Version |	IHL	| DSCP	| ECN
		0, 84, // Total_Length
		125, 47, // Identification
		0, 0, // Flags | Fragment Offset
		63,      // Time To Live
		1,       // Protocol
		85, 169, // Header Checksum
		8, 8, 8, 8, // Source IP Address
		198, 18, 255, 1, //Destination IP Address
	}
	header, err := ipv4.ParseHeader(hdrBytes)
	if err != nil {
		fmt.Printf("Parse failed. err: %v\n", err)
	}
	fmt.Printf("header: %v\n", header)
	fmt.Printf("header.TotalLen: %d\n", header.TotalLen)

	realTotalLen := binary.BigEndian.Uint16(hdrBytes[2:4])
	fmt.Printf("realTotalLen: %d\n", realTotalLen)

	totalLenByBigEndian := binary.BigEndian.Uint16(hdrBytes[2:4])
	fmt.Printf("totalLenByBigEndian: %d\n", totalLenByBigEndian)

	totalLenByLittleEndian := binary.LittleEndian.Uint16(hdrBytes[2:4])
	fmt.Printf("totalLenByLittleEndian: %d\n", totalLenByLittleEndian)

}

这段是解析 IP 报文 Header 的代码,在 Linux 和 Windows 执行得到的 header.TotalLen 都是 84,但是 darwin/amd64 就是 21524 。

按照相关规范,IP 报文 Header 字段均以大端序包装。这个看起来在 darwin 用了小端序解析。但是就算用小端序,这个值也应该是 21504 。

看 Go 解析的代码,原来还在解析时加上了 hdrlen 自身的 20 长度,这个似乎有点问题。我不了解那么多关于平台相关的,但是网络 IP 报文应该和平台无关吧。


	switch runtime.GOOS {
	case "darwin", "ios", "dragonfly", "netbsd":
		h.TotalLen = int(socket.NativeEndian.Uint16(b[2:4])) + hdrlen # 这里加了 Header 长度
		h.FragOff = int(socket.NativeEndian.Uint16(b[6:8]))
	case "freebsd":
		if freebsdVersion < 1100000 {
			h.TotalLen = int(socket.NativeEndian.Uint16(b[2:4]))
			if freebsdVersion < 1000000 {
				h.TotalLen += hdrlen
			}
			h.FragOff = int(socket.NativeEndian.Uint16(b[6:8]))
		} else {
			h.TotalLen = int(binary.BigEndian.Uint16(b[2:4]))
			h.FragOff = int(binary.BigEndian.Uint16(b[6:8]))
		}
	default:
		h.TotalLen = int(binary.BigEndian.Uint16(b[2:4]))
		h.FragOff = int(binary.BigEndian.Uint16(b[6:8]))
	}

然后 2 年前就有人提出这个问题了,但是还是没人修复。难道没人用 Mac 做 TCP/IP 网络编程吗。

Ref: https://github.com/golang/go/issues/32118


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK