Skip to content

Commit b1ee7b3

Browse files
committedDec 24, 2014
ipv4: clarify each platform's behavior on marshaling, parsing ipv4 header
For now we see three types of behaviors: conventional BSD, FreeBSD and other that including Linux, OpenBSD, Solaris and Windows. Change-Id: Iff7efe50b40bd3537b6ed8f02245552d8216c4f3
1 parent 8c5df24 commit b1ee7b3

File tree

2 files changed

+57
-55
lines changed

2 files changed

+57
-55
lines changed
 

‎ipv4/header.go

+38-46
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,6 @@ const (
4040
maxHeaderLen = 60 // sensible default, revisit if later RFCs define new usage of version and header length fields
4141
)
4242

43-
const (
44-
posTOS = 1 // type-of-service
45-
posTotalLen = 2 // packet total length
46-
posID = 4 // identification
47-
posFragOff = 6 // fragment offset
48-
posTTL = 8 // time-to-live
49-
posProtocol = 9 // next protocol
50-
posChecksum = 10 // checksum
51-
posSrc = 12 // source address
52-
posDst = 16 // destination address
53-
)
54-
5543
type HeaderFlags int
5644

5745
const (
@@ -83,10 +71,6 @@ func (h *Header) String() string {
8371
return fmt.Sprintf("ver: %v, hdrlen: %v, tos: %#x, totallen: %v, id: %#x, flags: %#x, fragoff: %#x, ttl: %v, proto: %v, cksum: %#x, src: %v, dst: %v", h.Version, h.Len, h.TOS, h.TotalLen, h.ID, h.Flags, h.FragOff, h.TTL, h.Protocol, h.Checksum, h.Src, h.Dst)
8472
}
8573

86-
// Please refer to the online manual; IP(4) on Darwin, FreeBSD and
87-
// OpenBSD. IP(7) on Linux.
88-
const supportsNewIPInput = runtime.GOOS == "linux" || runtime.GOOS == "openbsd"
89-
9074
// Marshal returns the binary encoding of the IPv4 header h.
9175
func (h *Header) Marshal() ([]byte, error) {
9276
if h == nil {
@@ -98,25 +82,26 @@ func (h *Header) Marshal() ([]byte, error) {
9882
hdrlen := HeaderLen + len(h.Options)
9983
b := make([]byte, hdrlen)
10084
b[0] = byte(Version<<4 | (hdrlen >> 2 & 0x0f))
101-
b[posTOS] = byte(h.TOS)
85+
b[1] = byte(h.TOS)
10286
flagsAndFragOff := (h.FragOff & 0x1fff) | int(h.Flags<<13)
103-
if supportsNewIPInput {
104-
b[posTotalLen], b[posTotalLen+1] = byte(h.TotalLen>>8), byte(h.TotalLen)
105-
b[posFragOff], b[posFragOff+1] = byte(flagsAndFragOff>>8), byte(flagsAndFragOff)
106-
} else {
87+
switch runtime.GOOS {
88+
case "darwin", "dragonfly", "freebsd", "netbsd":
10789
// TODO(mikio): fix potential misaligned memory access
108-
*(*uint16)(unsafe.Pointer(&b[posTotalLen : posTotalLen+1][0])) = uint16(h.TotalLen)
109-
*(*uint16)(unsafe.Pointer(&b[posFragOff : posFragOff+1][0])) = uint16(flagsAndFragOff)
90+
*(*uint16)(unsafe.Pointer(&b[2:3][0])) = uint16(h.TotalLen)
91+
*(*uint16)(unsafe.Pointer(&b[6:7][0])) = uint16(flagsAndFragOff)
92+
default:
93+
b[2], b[3] = byte(h.TotalLen>>8), byte(h.TotalLen)
94+
b[6], b[7] = byte(flagsAndFragOff>>8), byte(flagsAndFragOff)
11095
}
111-
b[posID], b[posID+1] = byte(h.ID>>8), byte(h.ID)
112-
b[posTTL] = byte(h.TTL)
113-
b[posProtocol] = byte(h.Protocol)
114-
b[posChecksum], b[posChecksum+1] = byte(h.Checksum>>8), byte(h.Checksum)
96+
b[4], b[5] = byte(h.ID>>8), byte(h.ID)
97+
b[8] = byte(h.TTL)
98+
b[9] = byte(h.Protocol)
99+
b[10], b[11] = byte(h.Checksum>>8), byte(h.Checksum)
115100
if ip := h.Src.To4(); ip != nil {
116-
copy(b[posSrc:posSrc+net.IPv4len], ip[:net.IPv4len])
101+
copy(b[12:16], ip[:net.IPv4len])
117102
}
118103
if ip := h.Dst.To4(); ip != nil {
119-
copy(b[posDst:posDst+net.IPv4len], ip[:net.IPv4len])
104+
copy(b[16:20], ip[:net.IPv4len])
120105
} else {
121106
return nil, errMissingAddress
122107
}
@@ -138,30 +123,37 @@ func ParseHeader(b []byte) (*Header, error) {
138123
if hdrlen > len(b) {
139124
return nil, errBufferTooShort
140125
}
141-
h := &Header{}
142-
h.Version = int(b[0] >> 4)
143-
h.Len = hdrlen
144-
h.TOS = int(b[posTOS])
145-
if supportsNewIPInput {
146-
h.TotalLen = int(b[posTotalLen])<<8 | int(b[posTotalLen+1])
147-
h.FragOff = int(b[posFragOff])<<8 | int(b[posFragOff+1])
148-
} else {
126+
h := &Header{
127+
Version: int(b[0] >> 4),
128+
Len: hdrlen,
129+
TOS: int(b[1]),
130+
ID: int(b[4])<<8 | int(b[5]),
131+
TTL: int(b[8]),
132+
Protocol: int(b[9]),
133+
Checksum: int(b[10])<<8 | int(b[11]),
134+
Src: net.IPv4(b[12], b[13], b[14], b[15]),
135+
Dst: net.IPv4(b[16], b[17], b[18], b[19]),
136+
}
137+
switch runtime.GOOS {
138+
case "darwin", "dragonfly", "netbsd":
139+
// TODO(mikio): fix potential misaligned memory access
140+
h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[2:3][0]))) + hdrlen
141+
// TODO(mikio): fix potential misaligned memory access
142+
h.FragOff = int(*(*uint16)(unsafe.Pointer(&b[6:7][0])))
143+
case "freebsd":
149144
// TODO(mikio): fix potential misaligned memory access
150-
h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[posTotalLen : posTotalLen+1][0])))
151-
if runtime.GOOS != "freebsd" || freebsdVersion < 1000000 {
145+
h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[2:3][0])))
146+
if freebsdVersion < 1000000 {
152147
h.TotalLen += hdrlen
153148
}
154149
// TODO(mikio): fix potential misaligned memory access
155-
h.FragOff = int(*(*uint16)(unsafe.Pointer(&b[posFragOff : posFragOff+1][0])))
150+
h.FragOff = int(*(*uint16)(unsafe.Pointer(&b[6:7][0])))
151+
default:
152+
h.TotalLen = int(b[2])<<8 | int(b[3])
153+
h.FragOff = int(b[6])<<8 | int(b[7])
156154
}
157155
h.Flags = HeaderFlags(h.FragOff&0xe000) >> 13
158156
h.FragOff = h.FragOff & 0x1fff
159-
h.ID = int(b[posID])<<8 | int(b[posID+1])
160-
h.TTL = int(b[posTTL])
161-
h.Protocol = int(b[posProtocol])
162-
h.Checksum = int(b[posChecksum])<<8 | int(b[posChecksum+1])
163-
h.Src = net.IPv4(b[posSrc], b[posSrc+1], b[posSrc+2], b[posSrc+3])
164-
h.Dst = net.IPv4(b[posDst], b[posDst+1], b[posDst+2], b[posDst+3])
165157
if hdrlen-HeaderLen > 0 {
166158
h.Options = make([]byte, hdrlen-HeaderLen)
167159
copy(h.Options, b[HeaderLen:])

‎ipv4/header_test.go

+19-9
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,17 @@ func TestMarshalHeader(t *testing.T) {
7373
t.Fatal(err)
7474
}
7575
var wh []byte
76-
if supportsNewIPInput {
77-
wh = wireHeaderToKernel[:]
78-
} else {
76+
switch runtime.GOOS {
77+
case "darwin", "dragonfly", "netbsd":
7978
wh = wireHeaderToTradBSDKernel[:]
79+
case "freebsd":
80+
if freebsdVersion < 1000000 {
81+
wh = wireHeaderToTradBSDKernel[:]
82+
} else {
83+
wh = wireHeaderFromFreeBSD10Kernel[:]
84+
}
85+
default:
86+
wh = wireHeaderToKernel[:]
8087
}
8188
if !bytes.Equal(b, wh) {
8289
t.Fatalf("got %#v; want %#v", b, wh)
@@ -85,14 +92,17 @@ func TestMarshalHeader(t *testing.T) {
8592

8693
func TestParseHeader(t *testing.T) {
8794
var wh []byte
88-
if supportsNewIPInput {
89-
wh = wireHeaderFromKernel[:]
90-
} else {
91-
if runtime.GOOS == "freebsd" && freebsdVersion >= 1000000 {
92-
wh = wireHeaderFromFreeBSD10Kernel[:]
93-
} else {
95+
switch runtime.GOOS {
96+
case "darwin", "dragonfly", "netbsd":
97+
wh = wireHeaderFromTradBSDKernel[:]
98+
case "freebsd":
99+
if freebsdVersion < 1000000 {
94100
wh = wireHeaderFromTradBSDKernel[:]
101+
} else {
102+
wh = wireHeaderFromFreeBSD10Kernel[:]
95103
}
104+
default:
105+
wh = wireHeaderFromKernel[:]
96106
}
97107
h, err := ParseHeader(wh)
98108
if err != nil {

0 commit comments

Comments
 (0)
Please sign in to comment.