Преглед на файлове

x/net/internal/icmp: add ParseIPv4Header

This CL adds ParseIPv4Header for parsing an IPv4 header in ICMP
error message. We cannot use ipv4.ParseHeader for it because they
are different things processed by either ip_input or icmp_input.

LGTM=iant
R=iant
CC=golang-codereviews
https://golang.org/cl/184850043
Mikio Hara преди 11 години
родител
ревизия
2a8857c36e
променени са 3 файла, в които са добавени 143 реда и са изтрити 0 реда
  1. 61 0
      internal/icmp/ipv4.go
  2. 71 0
      internal/icmp/ipv4_test.go
  3. 11 0
      internal/icmp/sys_freebsd.go

+ 61 - 0
internal/icmp/ipv4.go

@@ -0,0 +1,61 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package icmp
+
+import (
+	"net"
+	"runtime"
+	"unsafe"
+
+	"golang.org/x/net/ipv4"
+)
+
+// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
+var freebsdVersion uint32
+
+// ParseIPv4Header parses b as an IPv4 header of ICMP error message
+// invoking packet, which is contained in ICMP error message.
+func ParseIPv4Header(b []byte) (*ipv4.Header, error) {
+	if len(b) < ipv4.HeaderLen {
+		return nil, errHeaderTooShort
+	}
+	hdrlen := int(b[0]&0x0f) << 2
+	if hdrlen > len(b) {
+		return nil, errBufferTooShort
+	}
+	h := &ipv4.Header{
+		Version:  int(b[0] >> 4),
+		Len:      hdrlen,
+		TOS:      int(b[1]),
+		ID:       int(b[4])<<8 | int(b[5]),
+		FragOff:  int(b[6])<<8 | int(b[7]),
+		TTL:      int(b[8]),
+		Protocol: int(b[9]),
+		Checksum: int(b[10])<<8 | int(b[11]),
+		Src:      net.IPv4(b[12], b[13], b[14], b[15]),
+		Dst:      net.IPv4(b[16], b[17], b[18], b[19]),
+	}
+	switch runtime.GOOS {
+	case "darwin":
+		// TODO(mikio): fix potential misaligned memory access
+		h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[2:3][0])))
+	case "freebsd":
+		if freebsdVersion >= 1000000 {
+			h.TotalLen = int(b[2])<<8 | int(b[3])
+		} else {
+			// TODO(mikio): fix potential misaligned memory access
+			h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[2:3][0])))
+		}
+	default:
+		h.TotalLen = int(b[2])<<8 | int(b[3])
+	}
+	h.Flags = ipv4.HeaderFlags(h.FragOff&0xe000) >> 13
+	h.FragOff = h.FragOff & 0x1fff
+	if hdrlen-ipv4.HeaderLen > 0 {
+		h.Options = make([]byte, hdrlen-ipv4.HeaderLen)
+		copy(h.Options, b[ipv4.HeaderLen:])
+	}
+	return h, nil
+}

+ 71 - 0
internal/icmp/ipv4_test.go

@@ -0,0 +1,71 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package icmp
+
+import (
+	"net"
+	"reflect"
+	"runtime"
+	"testing"
+
+	"golang.org/x/net/ipv4"
+)
+
+var (
+	wireHeaderFromKernel = [ipv4.HeaderLen]byte{
+		0x45, 0x01, 0xbe, 0xef,
+		0xca, 0xfe, 0x45, 0xdc,
+		0xff, 0x01, 0xde, 0xad,
+		172, 16, 254, 254,
+		192, 168, 0, 1,
+	}
+	wireHeaderFromTradBSDKernel = [ipv4.HeaderLen]byte{
+		0x45, 0x01, 0xef, 0xbe,
+		0xca, 0xfe, 0x45, 0xdc,
+		0xff, 0x01, 0xde, 0xad,
+		172, 16, 254, 254,
+		192, 168, 0, 1,
+	}
+	// TODO(mikio): Add platform dependent wire header formats when
+	// we support new platforms.
+
+	testHeader = &ipv4.Header{
+		Version:  ipv4.Version,
+		Len:      ipv4.HeaderLen,
+		TOS:      1,
+		TotalLen: 0xbeef,
+		ID:       0xcafe,
+		Flags:    ipv4.DontFragment,
+		FragOff:  1500,
+		TTL:      255,
+		Protocol: 1,
+		Checksum: 0xdead,
+		Src:      net.IPv4(172, 16, 254, 254),
+		Dst:      net.IPv4(192, 168, 0, 1),
+	}
+)
+
+func TestParseIPv4Header(t *testing.T) {
+	var wh []byte
+	switch runtime.GOOS {
+	case "darwin":
+		wh = wireHeaderFromTradBSDKernel[:]
+	case "freebsd":
+		if freebsdVersion >= 1000000 {
+			wh = wireHeaderFromKernel[:]
+		} else {
+			wh = wireHeaderFromTradBSDKernel[:]
+		}
+	default:
+		wh = wireHeaderFromKernel[:]
+	}
+	h, err := ParseIPv4Header(wh)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !reflect.DeepEqual(h, testHeader) {
+		t.Fatalf("got %#v; want %#v", h, testHeader)
+	}
+}

+ 11 - 0
internal/icmp/sys_freebsd.go

@@ -0,0 +1,11 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package icmp
+
+import "syscall"
+
+func init() {
+	freebsdVersion, _ = syscall.SysctlUint32("kern.osreldate")
+}