ipv4: fix potential misaligned memory access

Also makes use of encoding/binary package.

Change-Id: Id41ea874487f88a3de9fa28b613a77eb66643cc8
Reviewed-on: https://go-review.googlesource.com/19534
Run-TryBot: Mikio Hara <mikioh.mikioh@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/ipv4/header.go b/ipv4/header.go
index d108176..363d9c2 100644
--- a/ipv4/header.go
+++ b/ipv4/header.go
@@ -5,11 +5,11 @@
 package ipv4
 
 import (
+	"encoding/binary"
 	"fmt"
 	"net"
 	"runtime"
 	"syscall"
-	"unsafe"
 )
 
 const (
@@ -64,17 +64,16 @@
 	flagsAndFragOff := (h.FragOff & 0x1fff) | int(h.Flags<<13)
 	switch runtime.GOOS {
 	case "darwin", "dragonfly", "freebsd", "netbsd":
-		// TODO(mikio): fix potential misaligned memory access
-		*(*uint16)(unsafe.Pointer(&b[2:3][0])) = uint16(h.TotalLen)
-		*(*uint16)(unsafe.Pointer(&b[6:7][0])) = uint16(flagsAndFragOff)
+		nativeEndian.PutUint16(b[2:4], uint16(h.TotalLen))
+		nativeEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
 	default:
-		b[2], b[3] = byte(h.TotalLen>>8), byte(h.TotalLen)
-		b[6], b[7] = byte(flagsAndFragOff>>8), byte(flagsAndFragOff)
+		binary.BigEndian.PutUint16(b[2:4], uint16(h.TotalLen))
+		binary.BigEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
 	}
-	b[4], b[5] = byte(h.ID>>8), byte(h.ID)
+	binary.BigEndian.PutUint16(b[4:6], uint16(h.ID))
 	b[8] = byte(h.TTL)
 	b[9] = byte(h.Protocol)
-	b[10], b[11] = byte(h.Checksum>>8), byte(h.Checksum)
+	binary.BigEndian.PutUint16(b[10:12], uint16(h.Checksum))
 	if ip := h.Src.To4(); ip != nil {
 		copy(b[12:16], ip[:net.IPv4len])
 	}
@@ -89,9 +88,6 @@
 	return b, nil
 }
 
-// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
-var freebsdVersion uint32
-
 // ParseHeader parses b as an IPv4 header.
 func ParseHeader(b []byte) (*Header, error) {
 	if len(b) < HeaderLen {
@@ -105,30 +101,26 @@
 		Version:  int(b[0] >> 4),
 		Len:      hdrlen,
 		TOS:      int(b[1]),
-		ID:       int(b[4])<<8 | int(b[5]),
+		ID:       int(binary.BigEndian.Uint16(b[4:6])),
 		TTL:      int(b[8]),
 		Protocol: int(b[9]),
-		Checksum: int(b[10])<<8 | int(b[11]),
+		Checksum: int(binary.BigEndian.Uint16(b[10:12])),
 		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", "dragonfly", "netbsd":
-		// TODO(mikio): fix potential misaligned memory access
-		h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[2:3][0]))) + hdrlen
-		// TODO(mikio): fix potential misaligned memory access
-		h.FragOff = int(*(*uint16)(unsafe.Pointer(&b[6:7][0])))
+		h.TotalLen = int(nativeEndian.Uint16(b[2:4])) + hdrlen
+		h.FragOff = int(nativeEndian.Uint16(b[6:8]))
 	case "freebsd":
-		// TODO(mikio): fix potential misaligned memory access
-		h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[2:3][0])))
+		h.TotalLen = int(nativeEndian.Uint16(b[2:4]))
 		if freebsdVersion < 1000000 {
 			h.TotalLen += hdrlen
 		}
-		// TODO(mikio): fix potential misaligned memory access
-		h.FragOff = int(*(*uint16)(unsafe.Pointer(&b[6:7][0])))
+		h.FragOff = int(nativeEndian.Uint16(b[6:8]))
 	default:
-		h.TotalLen = int(b[2])<<8 | int(b[3])
-		h.FragOff = int(b[6])<<8 | int(b[7])
+		h.TotalLen = int(binary.BigEndian.Uint16(b[2:4]))
+		h.FragOff = int(binary.BigEndian.Uint16(b[6:8]))
 	}
 	h.Flags = HeaderFlags(h.FragOff&0xe000) >> 13
 	h.FragOff = h.FragOff & 0x1fff
diff --git a/ipv4/helper.go b/ipv4/helper.go
index 8a7ee90..acecfd0 100644
--- a/ipv4/helper.go
+++ b/ipv4/helper.go
@@ -5,8 +5,10 @@
 package ipv4
 
 import (
+	"encoding/binary"
 	"errors"
 	"net"
+	"unsafe"
 )
 
 var (
@@ -18,8 +20,23 @@
 	errOpNoSupport              = errors.New("operation not supported")
 	errNoSuchInterface          = errors.New("no such interface")
 	errNoSuchMulticastInterface = errors.New("no such multicast interface")
+
+	// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
+	freebsdVersion uint32
+
+	nativeEndian binary.ByteOrder
 )
 
+func init() {
+	i := uint32(1)
+	b := (*[4]byte)(unsafe.Pointer(&i))
+	if b[0] == 1 {
+		nativeEndian = binary.LittleEndian
+	} else {
+		nativeEndian = binary.BigEndian
+	}
+}
+
 func boolint(b bool) int {
 	if b {
 		return 1