瀏覽代碼

route: add support for the manipulation of routing informaion

This change adds the Marshal method of RouteMessage to make it
possible to exchange route messages between userspace processes and
the kernel for the manipulation of routing information base inside the
kernel.

Change-Id: I0cf2c1a391820f41eb9c5eac1c172598cb2e1533
Reviewed-on: https://go-review.googlesource.com/36077
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Mikio Hara 9 年之前
父節點
當前提交
41bba8d80b

+ 148 - 4
route/address.go

@@ -24,6 +24,39 @@ type LinkAddr struct {
 // Family implements the Family method of Addr interface.
 // Family implements the Family method of Addr interface.
 func (a *LinkAddr) Family() int { return sysAF_LINK }
 func (a *LinkAddr) Family() int { return sysAF_LINK }
 
 
+func (a *LinkAddr) lenAndSpace() (int, int) {
+	l := 8 + len(a.Name) + len(a.Addr)
+	return l, roundup(l)
+}
+
+func (a *LinkAddr) marshal(b []byte) (int, error) {
+	l, ll := a.lenAndSpace()
+	if len(b) < ll {
+		return 0, errShortBuffer
+	}
+	nlen, alen := len(a.Name), len(a.Addr)
+	if nlen > 255 || alen > 255 {
+		return 0, errInvalidAddr
+	}
+	b[0] = byte(l)
+	b[1] = sysAF_LINK
+	if a.Index > 0 {
+		nativeEndian.PutUint16(b[2:4], uint16(a.Index))
+	}
+	data := b[8:]
+	if nlen > 0 {
+		b[5] = byte(nlen)
+		copy(data[:nlen], a.Addr)
+		data = data[nlen:]
+	}
+	if alen > 0 {
+		b[6] = byte(alen)
+		copy(data[:alen], a.Name)
+		data = data[alen:]
+	}
+	return ll, nil
+}
+
 func parseLinkAddr(b []byte) (Addr, error) {
 func parseLinkAddr(b []byte) (Addr, error) {
 	if len(b) < 8 {
 	if len(b) < 8 {
 		return nil, errInvalidAddr
 		return nil, errInvalidAddr
@@ -90,6 +123,21 @@ type Inet4Addr struct {
 // Family implements the Family method of Addr interface.
 // Family implements the Family method of Addr interface.
 func (a *Inet4Addr) Family() int { return sysAF_INET }
 func (a *Inet4Addr) Family() int { return sysAF_INET }
 
 
+func (a *Inet4Addr) lenAndSpace() (int, int) {
+	return sizeofSockaddrInet, roundup(sizeofSockaddrInet)
+}
+
+func (a *Inet4Addr) marshal(b []byte) (int, error) {
+	l, ll := a.lenAndSpace()
+	if len(b) < ll {
+		return 0, errShortBuffer
+	}
+	b[0] = byte(l)
+	b[1] = sysAF_INET
+	copy(b[4:8], a.IP[:])
+	return ll, nil
+}
+
 // An Inet6Addr represents an internet address for IPv6.
 // An Inet6Addr represents an internet address for IPv6.
 type Inet6Addr struct {
 type Inet6Addr struct {
 	IP     [16]byte // IP address
 	IP     [16]byte // IP address
@@ -99,18 +147,36 @@ type Inet6Addr struct {
 // Family implements the Family method of Addr interface.
 // Family implements the Family method of Addr interface.
 func (a *Inet6Addr) Family() int { return sysAF_INET6 }
 func (a *Inet6Addr) Family() int { return sysAF_INET6 }
 
 
+func (a *Inet6Addr) lenAndSpace() (int, int) {
+	return sizeofSockaddrInet6, roundup(sizeofSockaddrInet6)
+}
+
+func (a *Inet6Addr) marshal(b []byte) (int, error) {
+	l, ll := a.lenAndSpace()
+	if len(b) < ll {
+		return 0, errShortBuffer
+	}
+	b[0] = byte(l)
+	b[1] = sysAF_INET6
+	copy(b[8:24], a.IP[:])
+	if a.ZoneID > 0 {
+		nativeEndian.PutUint32(b[24:28], uint32(a.ZoneID))
+	}
+	return ll, nil
+}
+
 // parseInetAddr parses b as an internet address for IPv4 or IPv6.
 // parseInetAddr parses b as an internet address for IPv4 or IPv6.
 func parseInetAddr(af int, b []byte) (Addr, error) {
 func parseInetAddr(af int, b []byte) (Addr, error) {
 	switch af {
 	switch af {
 	case sysAF_INET:
 	case sysAF_INET:
-		if len(b) < 16 {
+		if len(b) < sizeofSockaddrInet {
 			return nil, errInvalidAddr
 			return nil, errInvalidAddr
 		}
 		}
 		a := &Inet4Addr{}
 		a := &Inet4Addr{}
 		copy(a.IP[:], b[4:8])
 		copy(a.IP[:], b[4:8])
 		return a, nil
 		return a, nil
 	case sysAF_INET6:
 	case sysAF_INET6:
-		if len(b) < 28 {
+		if len(b) < sizeofSockaddrInet6 {
 			return nil, errInvalidAddr
 			return nil, errInvalidAddr
 		}
 		}
 		a := &Inet6Addr{ZoneID: int(nativeEndian.Uint32(b[24:28]))}
 		a := &Inet6Addr{ZoneID: int(nativeEndian.Uint32(b[24:28]))}
@@ -174,7 +240,7 @@ func parseKernelInetAddr(af int, b []byte) (int, Addr, error) {
 		off6 = 8 // offset of in6_addr
 		off6 = 8 // offset of in6_addr
 	)
 	)
 	switch {
 	switch {
-	case b[0] == 28: // size of sockaddr_in6
+	case b[0] == sizeofSockaddrInet6:
 		a := &Inet6Addr{}
 		a := &Inet6Addr{}
 		copy(a.IP[:], b[off6:off6+16])
 		copy(a.IP[:], b[off6:off6+16])
 		return int(b[0]), a, nil
 		return int(b[0]), a, nil
@@ -186,7 +252,7 @@ func parseKernelInetAddr(af int, b []byte) (int, Addr, error) {
 			copy(a.IP[:], b[l-off6:l])
 			copy(a.IP[:], b[l-off6:l])
 		}
 		}
 		return int(b[0]), a, nil
 		return int(b[0]), a, nil
-	case b[0] == 16: // size of sockaddr_in
+	case b[0] == sizeofSockaddrInet:
 		a := &Inet4Addr{}
 		a := &Inet4Addr{}
 		copy(a.IP[:], b[off4:off4+4])
 		copy(a.IP[:], b[off4:off4+4])
 		return int(b[0]), a, nil
 		return int(b[0]), a, nil
@@ -211,6 +277,24 @@ type DefaultAddr struct {
 // Family implements the Family method of Addr interface.
 // Family implements the Family method of Addr interface.
 func (a *DefaultAddr) Family() int { return a.af }
 func (a *DefaultAddr) Family() int { return a.af }
 
 
+func (a *DefaultAddr) lenAndSpace() (int, int) {
+	l := len(a.Raw)
+	return l, roundup(l)
+}
+
+func (a *DefaultAddr) marshal(b []byte) (int, error) {
+	l, ll := a.lenAndSpace()
+	if len(b) < ll {
+		return 0, errShortBuffer
+	}
+	if l > 255 {
+		return 0, errInvalidAddr
+	}
+	b[1] = byte(l)
+	copy(b[:l], a.Raw)
+	return ll, nil
+}
+
 func parseDefaultAddr(b []byte) (Addr, error) {
 func parseDefaultAddr(b []byte) (Addr, error) {
 	if len(b) < 2 || len(b) < int(b[0]) {
 	if len(b) < 2 || len(b) < int(b[0]) {
 		return nil, errInvalidAddr
 		return nil, errInvalidAddr
@@ -219,6 +303,66 @@ func parseDefaultAddr(b []byte) (Addr, error) {
 	return a, nil
 	return a, nil
 }
 }
 
 
+func addrsSpace(as []Addr) int {
+	var l int
+	for _, a := range as {
+		switch a := a.(type) {
+		case *LinkAddr:
+			_, ll := a.lenAndSpace()
+			l += ll
+		case *Inet4Addr:
+			_, ll := a.lenAndSpace()
+			l += ll
+		case *Inet6Addr:
+			_, ll := a.lenAndSpace()
+			l += ll
+		case *DefaultAddr:
+			_, ll := a.lenAndSpace()
+			l += ll
+		}
+	}
+	return l
+}
+
+// marshalAddrs marshals as returns a bitmap indicating which address
+// is stored in b.
+func marshalAddrs(b []byte, as []Addr) (uint, error) {
+	var attrs uint
+	for i, a := range as {
+		switch a := a.(type) {
+		case *LinkAddr:
+			l, err := a.marshal(b)
+			if err != nil {
+				return 0, err
+			}
+			b = b[l:]
+			attrs |= 1 << uint(i)
+		case *Inet4Addr:
+			l, err := a.marshal(b)
+			if err != nil {
+				return 0, err
+			}
+			b = b[l:]
+			attrs |= 1 << uint(i)
+		case *Inet6Addr:
+			l, err := a.marshal(b)
+			if err != nil {
+				return 0, err
+			}
+			b = b[l:]
+			attrs |= 1 << uint(i)
+		case *DefaultAddr:
+			l, err := a.marshal(b)
+			if err != nil {
+				return 0, err
+			}
+			b = b[l:]
+			attrs |= 1 << uint(i)
+		}
+	}
+	return attrs, nil
+}
+
 func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) ([]Addr, error) {
 func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) ([]Addr, error) {
 	var as [sysRTAX_MAX]Addr
 	var as [sysRTAX_MAX]Addr
 	af := int(sysAF_UNSPEC)
 	af := int(sysAF_UNSPEC)

+ 8 - 0
route/defs_darwin.go

@@ -13,6 +13,8 @@ package route
 #include <net/if.h>
 #include <net/if.h>
 #include <net/if_dl.h>
 #include <net/if_dl.h>
 #include <net/route.h>
 #include <net/route.h>
+
+#include <netinet/in.h>
 */
 */
 import "C"
 import "C"
 
 
@@ -23,6 +25,8 @@ const (
 	sysAF_LINK   = C.AF_LINK
 	sysAF_LINK   = C.AF_LINK
 	sysAF_INET6  = C.AF_INET6
 	sysAF_INET6  = C.AF_INET6
 
 
+	sysSOCK_RAW = C.SOCK_RAW
+
 	sysNET_RT_DUMP    = C.NET_RT_DUMP
 	sysNET_RT_DUMP    = C.NET_RT_DUMP
 	sysNET_RT_FLAGS   = C.NET_RT_FLAGS
 	sysNET_RT_FLAGS   = C.NET_RT_FLAGS
 	sysNET_RT_IFLIST  = C.NET_RT_IFLIST
 	sysNET_RT_IFLIST  = C.NET_RT_IFLIST
@@ -103,4 +107,8 @@ const (
 	sizeofRtMsghdrDarwin15  = C.sizeof_struct_rt_msghdr
 	sizeofRtMsghdrDarwin15  = C.sizeof_struct_rt_msghdr
 	sizeofRtMsghdr2Darwin15 = C.sizeof_struct_rt_msghdr2
 	sizeofRtMsghdr2Darwin15 = C.sizeof_struct_rt_msghdr2
 	sizeofRtMetricsDarwin15 = C.sizeof_struct_rt_metrics
 	sizeofRtMetricsDarwin15 = C.sizeof_struct_rt_metrics
+
+	sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
+	sizeofSockaddrInet    = C.sizeof_struct_sockaddr_in
+	sizeofSockaddrInet6   = C.sizeof_struct_sockaddr_in6
 )
 )

+ 8 - 0
route/defs_dragonfly.go

@@ -13,6 +13,8 @@ package route
 #include <net/if.h>
 #include <net/if.h>
 #include <net/if_dl.h>
 #include <net/if_dl.h>
 #include <net/route.h>
 #include <net/route.h>
+
+#include <netinet/in.h>
 */
 */
 import "C"
 import "C"
 
 
@@ -23,6 +25,8 @@ const (
 	sysAF_LINK   = C.AF_LINK
 	sysAF_LINK   = C.AF_LINK
 	sysAF_INET6  = C.AF_INET6
 	sysAF_INET6  = C.AF_INET6
 
 
+	sysSOCK_RAW = C.SOCK_RAW
+
 	sysNET_RT_DUMP   = C.NET_RT_DUMP
 	sysNET_RT_DUMP   = C.NET_RT_DUMP
 	sysNET_RT_FLAGS  = C.NET_RT_FLAGS
 	sysNET_RT_FLAGS  = C.NET_RT_FLAGS
 	sysNET_RT_IFLIST = C.NET_RT_IFLIST
 	sysNET_RT_IFLIST = C.NET_RT_IFLIST
@@ -102,4 +106,8 @@ const (
 
 
 	sizeofRtMsghdrDragonFlyBSD4  = C.sizeof_struct_rt_msghdr
 	sizeofRtMsghdrDragonFlyBSD4  = C.sizeof_struct_rt_msghdr
 	sizeofRtMetricsDragonFlyBSD4 = C.sizeof_struct_rt_metrics
 	sizeofRtMetricsDragonFlyBSD4 = C.sizeof_struct_rt_metrics
+
+	sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
+	sizeofSockaddrInet    = C.sizeof_struct_sockaddr_in
+	sizeofSockaddrInet6   = C.sizeof_struct_sockaddr_in6
 )
 )

+ 8 - 0
route/defs_freebsd.go

@@ -14,6 +14,8 @@ package route
 #include <net/if_dl.h>
 #include <net/if_dl.h>
 #include <net/route.h>
 #include <net/route.h>
 
 
+#include <netinet/in.h>
+
 struct if_data_freebsd7 {
 struct if_data_freebsd7 {
 	u_char ifi_type;
 	u_char ifi_type;
 	u_char ifi_physical;
 	u_char ifi_physical;
@@ -222,6 +224,8 @@ const (
 	sysAF_LINK   = C.AF_LINK
 	sysAF_LINK   = C.AF_LINK
 	sysAF_INET6  = C.AF_INET6
 	sysAF_INET6  = C.AF_INET6
 
 
+	sysSOCK_RAW = C.SOCK_RAW
+
 	sysNET_RT_DUMP     = C.NET_RT_DUMP
 	sysNET_RT_DUMP     = C.NET_RT_DUMP
 	sysNET_RT_FLAGS    = C.NET_RT_FLAGS
 	sysNET_RT_FLAGS    = C.NET_RT_FLAGS
 	sysNET_RT_IFLIST   = C.NET_RT_IFLIST
 	sysNET_RT_IFLIST   = C.NET_RT_IFLIST
@@ -326,4 +330,8 @@ const (
 	sizeofIfDataFreeBSD9Emu  = C.sizeof_struct_if_data_freebsd9
 	sizeofIfDataFreeBSD9Emu  = C.sizeof_struct_if_data_freebsd9
 	sizeofIfDataFreeBSD10Emu = C.sizeof_struct_if_data_freebsd10
 	sizeofIfDataFreeBSD10Emu = C.sizeof_struct_if_data_freebsd10
 	sizeofIfDataFreeBSD11Emu = C.sizeof_struct_if_data_freebsd11
 	sizeofIfDataFreeBSD11Emu = C.sizeof_struct_if_data_freebsd11
+
+	sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
+	sizeofSockaddrInet    = C.sizeof_struct_sockaddr_in
+	sizeofSockaddrInet6   = C.sizeof_struct_sockaddr_in6
 )
 )

+ 8 - 0
route/defs_netbsd.go

@@ -13,6 +13,8 @@ package route
 #include <net/if.h>
 #include <net/if.h>
 #include <net/if_dl.h>
 #include <net/if_dl.h>
 #include <net/route.h>
 #include <net/route.h>
+
+#include <netinet/in.h>
 */
 */
 import "C"
 import "C"
 
 
@@ -23,6 +25,8 @@ const (
 	sysAF_LINK   = C.AF_LINK
 	sysAF_LINK   = C.AF_LINK
 	sysAF_INET6  = C.AF_INET6
 	sysAF_INET6  = C.AF_INET6
 
 
+	sysSOCK_RAW = C.SOCK_RAW
+
 	sysNET_RT_DUMP   = C.NET_RT_DUMP
 	sysNET_RT_DUMP   = C.NET_RT_DUMP
 	sysNET_RT_FLAGS  = C.NET_RT_FLAGS
 	sysNET_RT_FLAGS  = C.NET_RT_FLAGS
 	sysNET_RT_IFLIST = C.NET_RT_IFLIST
 	sysNET_RT_IFLIST = C.NET_RT_IFLIST
@@ -101,4 +105,8 @@ const (
 
 
 	sizeofRtMsghdrNetBSD7  = C.sizeof_struct_rt_msghdr
 	sizeofRtMsghdrNetBSD7  = C.sizeof_struct_rt_msghdr
 	sizeofRtMetricsNetBSD7 = C.sizeof_struct_rt_metrics
 	sizeofRtMetricsNetBSD7 = C.sizeof_struct_rt_metrics
+
+	sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
+	sizeofSockaddrInet    = C.sizeof_struct_sockaddr_in
+	sizeofSockaddrInet6   = C.sizeof_struct_sockaddr_in6
 )
 )

+ 12 - 0
route/defs_openbsd.go

@@ -13,6 +13,8 @@ package route
 #include <net/if.h>
 #include <net/if.h>
 #include <net/if_dl.h>
 #include <net/if_dl.h>
 #include <net/route.h>
 #include <net/route.h>
+
+#include <netinet/in.h>
 */
 */
 import "C"
 import "C"
 
 
@@ -23,6 +25,8 @@ const (
 	sysAF_LINK   = C.AF_LINK
 	sysAF_LINK   = C.AF_LINK
 	sysAF_INET6  = C.AF_INET6
 	sysAF_INET6  = C.AF_INET6
 
 
+	sysSOCK_RAW = C.SOCK_RAW
+
 	sysNET_RT_DUMP    = C.NET_RT_DUMP
 	sysNET_RT_DUMP    = C.NET_RT_DUMP
 	sysNET_RT_FLAGS   = C.NET_RT_FLAGS
 	sysNET_RT_FLAGS   = C.NET_RT_FLAGS
 	sysNET_RT_IFLIST  = C.NET_RT_IFLIST
 	sysNET_RT_IFLIST  = C.NET_RT_IFLIST
@@ -91,3 +95,11 @@ const (
 	sysRTAX_LABEL   = C.RTAX_LABEL
 	sysRTAX_LABEL   = C.RTAX_LABEL
 	sysRTAX_MAX     = C.RTAX_MAX
 	sysRTAX_MAX     = C.RTAX_MAX
 )
 )
+
+const (
+	sizeofRtMsghdr = C.sizeof_struct_rt_msghdr
+
+	sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
+	sizeofSockaddrInet    = C.sizeof_struct_sockaddr_in
+	sizeofSockaddrInet6   = C.sizeof_struct_sockaddr_in6
+)

+ 2 - 6
route/message.go

@@ -7,9 +7,6 @@
 package route
 package route
 
 
 // A Message represents a routing message.
 // A Message represents a routing message.
-//
-// Note: This interface will be changed to support Marshal method in
-// future version.
 type Message interface {
 type Message interface {
 	// Sys returns operating system-specific information.
 	// Sys returns operating system-specific information.
 	Sys() []Sys
 	Sys() []Sys
@@ -52,11 +49,10 @@ func ParseRIB(typ RIBType, b []byte) ([]Message, error) {
 			b = b[l:]
 			b = b[l:]
 			continue
 			continue
 		}
 		}
-		mtyp := int(b[3])
-		if fn, ok := parseFns[mtyp]; !ok {
+		if w, ok := wireFormats[int(b[3])]; !ok {
 			nskips++
 			nskips++
 		} else {
 		} else {
-			m, err := fn(typ, b)
+			m, err := w.parse(typ, b)
 			if err != nil {
 			if err != nil {
 				return nil, err
 				return nil, err
 			}
 			}

+ 122 - 7
route/message_test.go

@@ -33,11 +33,28 @@ func TestFetchAndParseRIB(t *testing.T) {
 	}
 	}
 }
 }
 
 
+var (
+	rtmonSock int
+	rtmonErr  error
+)
+
+func init() {
+	// We need to keep rtmonSock alive to avoid treading on
+	// recycled socket descriptors.
+	rtmonSock, rtmonErr = syscall.Socket(sysAF_ROUTE, sysSOCK_RAW, sysAF_UNSPEC)
+}
+
+// TestMonitorAndParseRIB leaks a worker goroutine and a socket
+// descriptor but that's intentional.
 func TestMonitorAndParseRIB(t *testing.T) {
 func TestMonitorAndParseRIB(t *testing.T) {
 	if testing.Short() || os.Getuid() != 0 {
 	if testing.Short() || os.Getuid() != 0 {
 		t.Skip("must be root")
 		t.Skip("must be root")
 	}
 	}
 
 
+	if rtmonErr != nil {
+		t.Fatal(rtmonErr)
+	}
+
 	// We suppose that using an IPv4 link-local address and the
 	// We suppose that using an IPv4 link-local address and the
 	// dot1Q ID for Token Ring and FDDI doesn't harm anyone.
 	// dot1Q ID for Token Ring and FDDI doesn't harm anyone.
 	pv := &propVirtual{addr: "169.254.0.1", mask: "255.255.255.0"}
 	pv := &propVirtual{addr: "169.254.0.1", mask: "255.255.255.0"}
@@ -49,16 +66,18 @@ func TestMonitorAndParseRIB(t *testing.T) {
 	}
 	}
 	pv.teardown()
 	pv.teardown()
 
 
-	s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer syscall.Close(s)
-
 	go func() {
 	go func() {
 		b := make([]byte, os.Getpagesize())
 		b := make([]byte, os.Getpagesize())
 		for {
 		for {
-			n, err := syscall.Read(s, b)
+			// There's no easy way to unblock this read
+			// call because the routing message exchange
+			// over routing socket is a connectionless
+			// message-oriented protocol, no control plane
+			// for signaling connectivity, and we cannot
+			// use the net package of standard library due
+			// to the lack of support for routing socket
+			// and circular dependency.
+			n, err := syscall.Read(rtmonSock, b)
 			if err != nil {
 			if err != nil {
 				return
 				return
 			}
 			}
@@ -116,3 +135,99 @@ func TestParseRIBWithFuzz(t *testing.T) {
 		}
 		}
 	}
 	}
 }
 }
+
+func TestRouteMessage(t *testing.T) {
+	s, err := syscall.Socket(sysAF_ROUTE, sysSOCK_RAW, sysAF_UNSPEC)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer syscall.Close(s)
+
+	var ms []RouteMessage
+	for _, af := range []int{sysAF_INET, sysAF_INET6} {
+		rs, err := fetchAndParseRIB(af, sysNET_RT_DUMP)
+		if err != nil || len(rs) == 0 {
+			continue
+		}
+		switch af {
+		case sysAF_INET:
+			ms = append(ms, []RouteMessage{
+				{
+					Type: sysRTM_GET,
+					Addrs: []Addr{
+						&Inet4Addr{IP: [4]byte{127, 0, 0, 1}},
+						nil,
+						nil,
+						nil,
+						&LinkAddr{},
+						&Inet4Addr{},
+						nil,
+						&Inet4Addr{},
+					},
+				},
+				{
+					Type: sysRTM_GET,
+					Addrs: []Addr{
+						&Inet4Addr{IP: [4]byte{127, 0, 0, 1}},
+					},
+				},
+			}...)
+		case sysAF_INET6:
+			ms = append(ms, []RouteMessage{
+				{
+					Type: sysRTM_GET,
+					Addrs: []Addr{
+						&Inet6Addr{IP: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}},
+						nil,
+						nil,
+						nil,
+						&LinkAddr{},
+						&Inet6Addr{},
+						nil,
+						&Inet6Addr{},
+					},
+				},
+				{
+					Type: sysRTM_GET,
+					Addrs: []Addr{
+						&Inet6Addr{IP: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}},
+					},
+				},
+			}...)
+		}
+	}
+	for i, m := range ms {
+		m.ID = uintptr(os.Getpid())
+		m.Seq = i + 1
+		wb, err := m.Marshal()
+		if err != nil {
+			t.Fatalf("%v: %v", m, err)
+		}
+		if _, err := syscall.Write(s, wb); err != nil {
+			t.Fatalf("%v: %v", m, err)
+		}
+		rb := make([]byte, os.Getpagesize())
+		n, err := syscall.Read(s, rb)
+		if err != nil {
+			t.Fatalf("%v: %v", m, err)
+		}
+		rms, err := ParseRIB(0, rb[:n])
+		if err != nil {
+			t.Fatalf("%v: %v", m, err)
+		}
+		for _, rm := range rms {
+			err := rm.(*RouteMessage).Err
+			if err != nil {
+				t.Errorf("%v: %v", m, err)
+			}
+		}
+		ss, err := msgs(rms).validate()
+		if err != nil {
+			t.Fatalf("%v: %v", m, err)
+		}
+		for _, s := range ss {
+			t.Log(s)
+		}
+
+	}
+}

+ 54 - 5
route/route.go

@@ -24,21 +24,70 @@ var (
 	errMessageTooShort    = errors.New("message too short")
 	errMessageTooShort    = errors.New("message too short")
 	errInvalidMessage     = errors.New("invalid message")
 	errInvalidMessage     = errors.New("invalid message")
 	errInvalidAddr        = errors.New("invalid address")
 	errInvalidAddr        = errors.New("invalid address")
+	errShortBuffer        = errors.New("short buffer")
 )
 )
 
 
 // A RouteMessage represents a message conveying an address prefix, a
 // A RouteMessage represents a message conveying an address prefix, a
 // nexthop address and an output interface.
 // nexthop address and an output interface.
+//
+// Unlike other messages, this message can be used to query adjacency
+// information for the given address prefix, to add a new route, and
+// to delete or modify the existing route from the routing information
+// base inside the kernel by writing and reading route messages on a
+// routing socket.
+//
+// For the manipulation of routing information, the route message must
+// contain appropriate fields that include:
+//
+//	Version       = <must be specified>
+//	Type          = <must be specified>
+//	Flags         = <must be specified>
+//	Index         = <must be specified if necessary>
+//	ID            = <must be specified>
+//	Seq           = <must be specified>
+//	Addrs         = <must be specified>
+//
+// The Type field specifies a type of manipulation, the Flags field
+// specifies a class of target information and the Addrs field
+// specifies target information like the following:
+//
+//	route.RouteMessage{
+//		Version: RTM_VERSION,
+//		Type: RTM_GET,
+//		Flags: RTF_UP | RTF_HOST,
+//		ID: uintptr(os.Getpid()),
+//		Seq: 1,
+//		Addrs: []route.Addrs{
+//			RTAX_DST: &route.Inet4Addr{ ... },
+//			RTAX_IFP: &route.LinkAddr{ ... },
+//			RTAX_BRD: &route.Inet4Addr{ ... },
+//		},
+//	}
+//
+// The values for the above fields depend on the implementation of
+// each operating system.
+//
+// The Err field on a response message contains an error value on the
+// requested operation. If non-nil, the requested operation is failed.
 type RouteMessage struct {
 type RouteMessage struct {
-	Version int    // message version
-	Type    int    // message type
-	Flags   int    // route flags
-	Index   int    // interface index when atatched
-	Addrs   []Addr // addresses
+	Version int     // message version
+	Type    int     // message type
+	Flags   int     // route flags
+	Index   int     // interface index when atatched
+	ID      uintptr // sender's identifier; usually process ID
+	Seq     int     // sequence number
+	Err     error   // error on requested operation
+	Addrs   []Addr  // addresses
 
 
 	extOff int    // offset of header extension
 	extOff int    // offset of header extension
 	raw    []byte // raw message
 	raw    []byte // raw message
 }
 }
 
 
+// Marshal returns the binary encoding of m.
+func (m *RouteMessage) Marshal() ([]byte, error) {
+	return m.marshal()
+}
+
 // A RIBType reprensents a type of routing information base.
 // A RIBType reprensents a type of routing information base.
 type RIBType int
 type RIBType int
 
 

+ 36 - 0
route/route_classic.go

@@ -6,6 +6,36 @@
 
 
 package route
 package route
 
 
+import "syscall"
+
+func (m *RouteMessage) marshal() ([]byte, error) {
+	w, ok := wireFormats[m.Type]
+	if !ok {
+		return nil, errUnsupportedMessage
+	}
+	l := w.bodyOff + addrsSpace(m.Addrs)
+	b := make([]byte, l)
+	nativeEndian.PutUint16(b[:2], uint16(l))
+	if m.Version == 0 {
+		b[2] = sysRTM_VERSION
+	} else {
+		b[2] = byte(m.Version)
+	}
+	b[3] = byte(m.Type)
+	nativeEndian.PutUint32(b[8:12], uint32(m.Flags))
+	nativeEndian.PutUint16(b[4:6], uint16(m.Index))
+	nativeEndian.PutUint32(b[16:20], uint32(m.ID))
+	nativeEndian.PutUint32(b[20:24], uint32(m.Seq))
+	attrs, err := marshalAddrs(b[w.bodyOff:], m.Addrs)
+	if err != nil {
+		return nil, err
+	}
+	if attrs > 0 {
+		nativeEndian.PutUint32(b[12:16], uint32(attrs))
+	}
+	return b, nil
+}
+
 func (w *wireFormat) parseRouteMessage(typ RIBType, b []byte) (Message, error) {
 func (w *wireFormat) parseRouteMessage(typ RIBType, b []byte) (Message, error) {
 	if len(b) < w.bodyOff {
 	if len(b) < w.bodyOff {
 		return nil, errMessageTooShort
 		return nil, errMessageTooShort
@@ -19,9 +49,15 @@ func (w *wireFormat) parseRouteMessage(typ RIBType, b []byte) (Message, error) {
 		Type:    int(b[3]),
 		Type:    int(b[3]),
 		Flags:   int(nativeEndian.Uint32(b[8:12])),
 		Flags:   int(nativeEndian.Uint32(b[8:12])),
 		Index:   int(nativeEndian.Uint16(b[4:6])),
 		Index:   int(nativeEndian.Uint16(b[4:6])),
+		ID:      uintptr(nativeEndian.Uint32(b[16:20])),
+		Seq:     int(nativeEndian.Uint32(b[20:24])),
 		extOff:  w.extOff,
 		extOff:  w.extOff,
 		raw:     b[:l],
 		raw:     b[:l],
 	}
 	}
+	errno := syscall.Errno(nativeEndian.Uint32(b[28:32]))
+	if errno != 0 {
+		m.Err = errno
+	}
 	var err error
 	var err error
 	m.Addrs, err = parseAddrs(uint(nativeEndian.Uint32(b[12:16])), parseKernelInetAddr, b[w.bodyOff:])
 	m.Addrs, err = parseAddrs(uint(nativeEndian.Uint32(b[12:16])), parseKernelInetAddr, b[w.bodyOff:])
 	if err != nil {
 	if err != nil {

+ 34 - 1
route/route_openbsd.go

@@ -4,8 +4,35 @@
 
 
 package route
 package route
 
 
+import "syscall"
+
+func (m *RouteMessage) marshal() ([]byte, error) {
+	l := sizeofRtMsghdr + addrsSpace(m.Addrs)
+	b := make([]byte, l)
+	nativeEndian.PutUint16(b[:2], uint16(l))
+	if m.Version == 0 {
+		b[2] = sysRTM_VERSION
+	} else {
+		b[2] = byte(m.Version)
+	}
+	b[3] = byte(m.Type)
+	nativeEndian.PutUint16(b[4:6], uint16(sizeofRtMsghdr))
+	nativeEndian.PutUint32(b[16:20], uint32(m.Flags))
+	nativeEndian.PutUint16(b[6:8], uint16(m.Index))
+	nativeEndian.PutUint32(b[24:28], uint32(m.ID))
+	nativeEndian.PutUint32(b[28:32], uint32(m.Seq))
+	attrs, err := marshalAddrs(b[sizeofRtMsghdr:], m.Addrs)
+	if err != nil {
+		return nil, err
+	}
+	if attrs > 0 {
+		nativeEndian.PutUint32(b[12:16], uint32(attrs))
+	}
+	return b, nil
+}
+
 func (*wireFormat) parseRouteMessage(_ RIBType, b []byte) (Message, error) {
 func (*wireFormat) parseRouteMessage(_ RIBType, b []byte) (Message, error) {
-	if len(b) < 40 {
+	if len(b) < sizeofRtMsghdr {
 		return nil, errMessageTooShort
 		return nil, errMessageTooShort
 	}
 	}
 	l := int(nativeEndian.Uint16(b[:2]))
 	l := int(nativeEndian.Uint16(b[:2]))
@@ -17,12 +44,18 @@ func (*wireFormat) parseRouteMessage(_ RIBType, b []byte) (Message, error) {
 		Type:    int(b[3]),
 		Type:    int(b[3]),
 		Flags:   int(nativeEndian.Uint32(b[16:20])),
 		Flags:   int(nativeEndian.Uint32(b[16:20])),
 		Index:   int(nativeEndian.Uint16(b[6:8])),
 		Index:   int(nativeEndian.Uint16(b[6:8])),
+		ID:      uintptr(nativeEndian.Uint32(b[24:28])),
+		Seq:     int(nativeEndian.Uint32(b[28:32])),
 		raw:     b[:l],
 		raw:     b[:l],
 	}
 	}
 	ll := int(nativeEndian.Uint16(b[4:6]))
 	ll := int(nativeEndian.Uint16(b[4:6]))
 	if len(b) < ll {
 	if len(b) < ll {
 		return nil, errInvalidMessage
 		return nil, errInvalidMessage
 	}
 	}
+	errno := syscall.Errno(nativeEndian.Uint32(b[32:36]))
+	if errno != 0 {
+		m.Err = errno
+	}
 	as, err := parseAddrs(uint(nativeEndian.Uint32(b[12:16])), parseKernelInetAddr, b[ll:])
 	as, err := parseAddrs(uint(nativeEndian.Uint32(b[12:16])), parseKernelInetAddr, b[ll:])
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err

+ 3 - 4
route/sys.go

@@ -11,7 +11,7 @@ import "unsafe"
 var (
 var (
 	nativeEndian binaryByteOrder
 	nativeEndian binaryByteOrder
 	kernelAlign  int
 	kernelAlign  int
-	parseFns     map[int]parseFn
+	wireFormats  map[int]*wireFormat
 )
 )
 
 
 func init() {
 func init() {
@@ -22,7 +22,7 @@ func init() {
 	} else {
 	} else {
 		nativeEndian = bigEndian
 		nativeEndian = bigEndian
 	}
 	}
-	kernelAlign, parseFns = probeRoutingStack()
+	kernelAlign, wireFormats = probeRoutingStack()
 }
 }
 
 
 func roundup(l int) int {
 func roundup(l int) int {
@@ -32,9 +32,8 @@ func roundup(l int) int {
 	return (l + kernelAlign - 1) & ^(kernelAlign - 1)
 	return (l + kernelAlign - 1) & ^(kernelAlign - 1)
 }
 }
 
 
-type parseFn func(RIBType, []byte) (Message, error)
-
 type wireFormat struct {
 type wireFormat struct {
 	extOff  int // offset of header extension
 	extOff  int // offset of header extension
 	bodyOff int // offset of message body
 	bodyOff int // offset of message body
+	parse   func(RIBType, []byte) (Message, error)
 }
 }

+ 26 - 19
route/sys_darwin.go

@@ -49,32 +49,39 @@ func (m *InterfaceMessage) Sys() []Sys {
 	}
 	}
 }
 }
 
 
-func probeRoutingStack() (int, map[int]parseFn) {
+func probeRoutingStack() (int, map[int]*wireFormat) {
 	rtm := &wireFormat{extOff: 36, bodyOff: sizeofRtMsghdrDarwin15}
 	rtm := &wireFormat{extOff: 36, bodyOff: sizeofRtMsghdrDarwin15}
+	rtm.parse = rtm.parseRouteMessage
 	rtm2 := &wireFormat{extOff: 36, bodyOff: sizeofRtMsghdr2Darwin15}
 	rtm2 := &wireFormat{extOff: 36, bodyOff: sizeofRtMsghdr2Darwin15}
+	rtm2.parse = rtm2.parseRouteMessage
 	ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrDarwin15}
 	ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrDarwin15}
+	ifm.parse = ifm.parseInterfaceMessage
 	ifm2 := &wireFormat{extOff: 32, bodyOff: sizeofIfMsghdr2Darwin15}
 	ifm2 := &wireFormat{extOff: 32, bodyOff: sizeofIfMsghdr2Darwin15}
+	ifm2.parse = ifm2.parseInterfaceMessage
 	ifam := &wireFormat{extOff: sizeofIfaMsghdrDarwin15, bodyOff: sizeofIfaMsghdrDarwin15}
 	ifam := &wireFormat{extOff: sizeofIfaMsghdrDarwin15, bodyOff: sizeofIfaMsghdrDarwin15}
+	ifam.parse = ifam.parseInterfaceAddrMessage
 	ifmam := &wireFormat{extOff: sizeofIfmaMsghdrDarwin15, bodyOff: sizeofIfmaMsghdrDarwin15}
 	ifmam := &wireFormat{extOff: sizeofIfmaMsghdrDarwin15, bodyOff: sizeofIfmaMsghdrDarwin15}
+	ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage
 	ifmam2 := &wireFormat{extOff: sizeofIfmaMsghdr2Darwin15, bodyOff: sizeofIfmaMsghdr2Darwin15}
 	ifmam2 := &wireFormat{extOff: sizeofIfmaMsghdr2Darwin15, bodyOff: sizeofIfmaMsghdr2Darwin15}
+	ifmam2.parse = ifmam2.parseInterfaceMulticastAddrMessage
 	// Darwin kernels require 32-bit aligned access to routing facilities.
 	// Darwin kernels require 32-bit aligned access to routing facilities.
-	return 4, map[int]parseFn{
-		sysRTM_ADD:       rtm.parseRouteMessage,
-		sysRTM_DELETE:    rtm.parseRouteMessage,
-		sysRTM_CHANGE:    rtm.parseRouteMessage,
-		sysRTM_GET:       rtm.parseRouteMessage,
-		sysRTM_LOSING:    rtm.parseRouteMessage,
-		sysRTM_REDIRECT:  rtm.parseRouteMessage,
-		sysRTM_MISS:      rtm.parseRouteMessage,
-		sysRTM_LOCK:      rtm.parseRouteMessage,
-		sysRTM_RESOLVE:   rtm.parseRouteMessage,
-		sysRTM_NEWADDR:   ifam.parseInterfaceAddrMessage,
-		sysRTM_DELADDR:   ifam.parseInterfaceAddrMessage,
-		sysRTM_IFINFO:    ifm.parseInterfaceMessage,
-		sysRTM_NEWMADDR:  ifmam.parseInterfaceMulticastAddrMessage,
-		sysRTM_DELMADDR:  ifmam.parseInterfaceMulticastAddrMessage,
-		sysRTM_IFINFO2:   ifm2.parseInterfaceMessage,
-		sysRTM_NEWMADDR2: ifmam2.parseInterfaceMulticastAddrMessage,
-		sysRTM_GET2:      rtm2.parseRouteMessage,
+	return 4, map[int]*wireFormat{
+		sysRTM_ADD:       rtm,
+		sysRTM_DELETE:    rtm,
+		sysRTM_CHANGE:    rtm,
+		sysRTM_GET:       rtm,
+		sysRTM_LOSING:    rtm,
+		sysRTM_REDIRECT:  rtm,
+		sysRTM_MISS:      rtm,
+		sysRTM_LOCK:      rtm,
+		sysRTM_RESOLVE:   rtm,
+		sysRTM_NEWADDR:   ifam,
+		sysRTM_DELADDR:   ifam,
+		sysRTM_IFINFO:    ifm,
+		sysRTM_NEWMADDR:  ifmam,
+		sysRTM_DELMADDR:  ifmam,
+		sysRTM_IFINFO2:   ifm2,
+		sysRTM_NEWMADDR2: ifmam2,
+		sysRTM_GET2:      rtm2,
 	}
 	}
 }
 }

+ 22 - 17
route/sys_dragonfly.go

@@ -44,28 +44,33 @@ func (m *InterfaceMessage) Sys() []Sys {
 	}
 	}
 }
 }
 
 
-func probeRoutingStack() (int, map[int]parseFn) {
+func probeRoutingStack() (int, map[int]*wireFormat) {
 	var p uintptr
 	var p uintptr
 	rtm := &wireFormat{extOff: 40, bodyOff: sizeofRtMsghdrDragonFlyBSD4}
 	rtm := &wireFormat{extOff: 40, bodyOff: sizeofRtMsghdrDragonFlyBSD4}
+	rtm.parse = rtm.parseRouteMessage
 	ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrDragonFlyBSD4}
 	ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrDragonFlyBSD4}
+	ifm.parse = ifm.parseInterfaceMessage
 	ifam := &wireFormat{extOff: sizeofIfaMsghdrDragonFlyBSD4, bodyOff: sizeofIfaMsghdrDragonFlyBSD4}
 	ifam := &wireFormat{extOff: sizeofIfaMsghdrDragonFlyBSD4, bodyOff: sizeofIfaMsghdrDragonFlyBSD4}
+	ifam.parse = ifam.parseInterfaceAddrMessage
 	ifmam := &wireFormat{extOff: sizeofIfmaMsghdrDragonFlyBSD4, bodyOff: sizeofIfmaMsghdrDragonFlyBSD4}
 	ifmam := &wireFormat{extOff: sizeofIfmaMsghdrDragonFlyBSD4, bodyOff: sizeofIfmaMsghdrDragonFlyBSD4}
+	ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage
 	ifanm := &wireFormat{extOff: sizeofIfAnnouncemsghdrDragonFlyBSD4, bodyOff: sizeofIfAnnouncemsghdrDragonFlyBSD4}
 	ifanm := &wireFormat{extOff: sizeofIfAnnouncemsghdrDragonFlyBSD4, bodyOff: sizeofIfAnnouncemsghdrDragonFlyBSD4}
-	return int(unsafe.Sizeof(p)), map[int]parseFn{
-		sysRTM_ADD:        rtm.parseRouteMessage,
-		sysRTM_DELETE:     rtm.parseRouteMessage,
-		sysRTM_CHANGE:     rtm.parseRouteMessage,
-		sysRTM_GET:        rtm.parseRouteMessage,
-		sysRTM_LOSING:     rtm.parseRouteMessage,
-		sysRTM_REDIRECT:   rtm.parseRouteMessage,
-		sysRTM_MISS:       rtm.parseRouteMessage,
-		sysRTM_LOCK:       rtm.parseRouteMessage,
-		sysRTM_RESOLVE:    rtm.parseRouteMessage,
-		sysRTM_NEWADDR:    ifam.parseInterfaceAddrMessage,
-		sysRTM_DELADDR:    ifam.parseInterfaceAddrMessage,
-		sysRTM_IFINFO:     ifm.parseInterfaceMessage,
-		sysRTM_NEWMADDR:   ifmam.parseInterfaceMulticastAddrMessage,
-		sysRTM_DELMADDR:   ifmam.parseInterfaceMulticastAddrMessage,
-		sysRTM_IFANNOUNCE: ifanm.parseInterfaceAnnounceMessage,
+	ifanm.parse = ifanm.parseInterfaceAnnounceMessage
+	return int(unsafe.Sizeof(p)), map[int]*wireFormat{
+		sysRTM_ADD:        rtm,
+		sysRTM_DELETE:     rtm,
+		sysRTM_CHANGE:     rtm,
+		sysRTM_GET:        rtm,
+		sysRTM_LOSING:     rtm,
+		sysRTM_REDIRECT:   rtm,
+		sysRTM_MISS:       rtm,
+		sysRTM_LOCK:       rtm,
+		sysRTM_RESOLVE:    rtm,
+		sysRTM_NEWADDR:    ifam,
+		sysRTM_DELADDR:    ifam,
+		sysRTM_IFINFO:     ifm,
+		sysRTM_NEWMADDR:   ifmam,
+		sysRTM_DELMADDR:   ifmam,
+		sysRTM_IFANNOUNCE: ifanm,
 	}
 	}
 }
 }

+ 22 - 17
route/sys_freebsd.go

@@ -54,7 +54,7 @@ func (m *InterfaceMessage) Sys() []Sys {
 	}
 	}
 }
 }
 
 
-func probeRoutingStack() (int, map[int]parseFn) {
+func probeRoutingStack() (int, map[int]*wireFormat) {
 	var p uintptr
 	var p uintptr
 	wordSize := int(unsafe.Sizeof(p))
 	wordSize := int(unsafe.Sizeof(p))
 	align := int(unsafe.Sizeof(p))
 	align := int(unsafe.Sizeof(p))
@@ -130,21 +130,26 @@ func probeRoutingStack() (int, map[int]parseFn) {
 			ifm.bodyOff = sizeofIfMsghdrFreeBSD11
 			ifm.bodyOff = sizeofIfMsghdrFreeBSD11
 		}
 		}
 	}
 	}
-	return align, map[int]parseFn{
-		sysRTM_ADD:        rtm.parseRouteMessage,
-		sysRTM_DELETE:     rtm.parseRouteMessage,
-		sysRTM_CHANGE:     rtm.parseRouteMessage,
-		sysRTM_GET:        rtm.parseRouteMessage,
-		sysRTM_LOSING:     rtm.parseRouteMessage,
-		sysRTM_REDIRECT:   rtm.parseRouteMessage,
-		sysRTM_MISS:       rtm.parseRouteMessage,
-		sysRTM_LOCK:       rtm.parseRouteMessage,
-		sysRTM_RESOLVE:    rtm.parseRouteMessage,
-		sysRTM_NEWADDR:    ifam.parseInterfaceAddrMessage,
-		sysRTM_DELADDR:    ifam.parseInterfaceAddrMessage,
-		sysRTM_IFINFO:     ifm.parseInterfaceMessage,
-		sysRTM_NEWMADDR:   ifmam.parseInterfaceMulticastAddrMessage,
-		sysRTM_DELMADDR:   ifmam.parseInterfaceMulticastAddrMessage,
-		sysRTM_IFANNOUNCE: ifanm.parseInterfaceAnnounceMessage,
+	rtm.parse = rtm.parseRouteMessage
+	ifm.parse = ifm.parseInterfaceMessage
+	ifam.parse = ifam.parseInterfaceAddrMessage
+	ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage
+	ifanm.parse = ifanm.parseInterfaceAnnounceMessage
+	return align, map[int]*wireFormat{
+		sysRTM_ADD:        rtm,
+		sysRTM_DELETE:     rtm,
+		sysRTM_CHANGE:     rtm,
+		sysRTM_GET:        rtm,
+		sysRTM_LOSING:     rtm,
+		sysRTM_REDIRECT:   rtm,
+		sysRTM_MISS:       rtm,
+		sysRTM_LOCK:       rtm,
+		sysRTM_RESOLVE:    rtm,
+		sysRTM_NEWADDR:    ifam,
+		sysRTM_DELADDR:    ifam,
+		sysRTM_IFINFO:     ifm,
+		sysRTM_NEWMADDR:   ifmam,
+		sysRTM_DELMADDR:   ifmam,
+		sysRTM_IFANNOUNCE: ifanm,
 	}
 	}
 }
 }

+ 19 - 15
route/sys_netbsd.go

@@ -42,26 +42,30 @@ func (m *InterfaceMessage) Sys() []Sys {
 	}
 	}
 }
 }
 
 
-func probeRoutingStack() (int, map[int]parseFn) {
+func probeRoutingStack() (int, map[int]*wireFormat) {
 	rtm := &wireFormat{extOff: 40, bodyOff: sizeofRtMsghdrNetBSD7}
 	rtm := &wireFormat{extOff: 40, bodyOff: sizeofRtMsghdrNetBSD7}
+	rtm.parse = rtm.parseRouteMessage
 	ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrNetBSD7}
 	ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrNetBSD7}
+	ifm.parse = ifm.parseInterfaceMessage
 	ifam := &wireFormat{extOff: sizeofIfaMsghdrNetBSD7, bodyOff: sizeofIfaMsghdrNetBSD7}
 	ifam := &wireFormat{extOff: sizeofIfaMsghdrNetBSD7, bodyOff: sizeofIfaMsghdrNetBSD7}
+	ifam.parse = ifam.parseInterfaceAddrMessage
 	ifanm := &wireFormat{extOff: sizeofIfAnnouncemsghdrNetBSD7, bodyOff: sizeofIfAnnouncemsghdrNetBSD7}
 	ifanm := &wireFormat{extOff: sizeofIfAnnouncemsghdrNetBSD7, bodyOff: sizeofIfAnnouncemsghdrNetBSD7}
+	ifanm.parse = ifanm.parseInterfaceAnnounceMessage
 	// NetBSD 6 and above kernels require 64-bit aligned access to
 	// NetBSD 6 and above kernels require 64-bit aligned access to
 	// routing facilities.
 	// routing facilities.
-	return 8, map[int]parseFn{
-		sysRTM_ADD:        rtm.parseRouteMessage,
-		sysRTM_DELETE:     rtm.parseRouteMessage,
-		sysRTM_CHANGE:     rtm.parseRouteMessage,
-		sysRTM_GET:        rtm.parseRouteMessage,
-		sysRTM_LOSING:     rtm.parseRouteMessage,
-		sysRTM_REDIRECT:   rtm.parseRouteMessage,
-		sysRTM_MISS:       rtm.parseRouteMessage,
-		sysRTM_LOCK:       rtm.parseRouteMessage,
-		sysRTM_RESOLVE:    rtm.parseRouteMessage,
-		sysRTM_NEWADDR:    ifam.parseInterfaceAddrMessage,
-		sysRTM_DELADDR:    ifam.parseInterfaceAddrMessage,
-		sysRTM_IFANNOUNCE: ifanm.parseInterfaceAnnounceMessage,
-		sysRTM_IFINFO:     ifm.parseInterfaceMessage,
+	return 8, map[int]*wireFormat{
+		sysRTM_ADD:        rtm,
+		sysRTM_DELETE:     rtm,
+		sysRTM_CHANGE:     rtm,
+		sysRTM_GET:        rtm,
+		sysRTM_LOSING:     rtm,
+		sysRTM_REDIRECT:   rtm,
+		sysRTM_MISS:       rtm,
+		sysRTM_LOCK:       rtm,
+		sysRTM_RESOLVE:    rtm,
+		sysRTM_NEWADDR:    ifam,
+		sysRTM_DELADDR:    ifam,
+		sysRTM_IFANNOUNCE: ifanm,
+		sysRTM_IFINFO:     ifm,
 	}
 	}
 }
 }

+ 23 - 16
route/sys_openbsd.go

@@ -51,22 +51,29 @@ func (m *InterfaceMessage) Sys() []Sys {
 	}
 	}
 }
 }
 
 
-func probeRoutingStack() (int, map[int]parseFn) {
+func probeRoutingStack() (int, map[int]*wireFormat) {
 	var p uintptr
 	var p uintptr
-	nooff := &wireFormat{extOff: -1, bodyOff: -1}
-	return int(unsafe.Sizeof(p)), map[int]parseFn{
-		sysRTM_ADD:        nooff.parseRouteMessage,
-		sysRTM_DELETE:     nooff.parseRouteMessage,
-		sysRTM_CHANGE:     nooff.parseRouteMessage,
-		sysRTM_GET:        nooff.parseRouteMessage,
-		sysRTM_LOSING:     nooff.parseRouteMessage,
-		sysRTM_REDIRECT:   nooff.parseRouteMessage,
-		sysRTM_MISS:       nooff.parseRouteMessage,
-		sysRTM_LOCK:       nooff.parseRouteMessage,
-		sysRTM_RESOLVE:    nooff.parseRouteMessage,
-		sysRTM_NEWADDR:    nooff.parseInterfaceAddrMessage,
-		sysRTM_DELADDR:    nooff.parseInterfaceAddrMessage,
-		sysRTM_IFINFO:     nooff.parseInterfaceMessage,
-		sysRTM_IFANNOUNCE: nooff.parseInterfaceAnnounceMessage,
+	rtm := &wireFormat{extOff: -1, bodyOff: -1}
+	rtm.parse = rtm.parseRouteMessage
+	ifm := &wireFormat{extOff: -1, bodyOff: -1}
+	ifm.parse = ifm.parseInterfaceMessage
+	ifam := &wireFormat{extOff: -1, bodyOff: -1}
+	ifam.parse = ifam.parseInterfaceAddrMessage
+	ifanm := &wireFormat{extOff: -1, bodyOff: -1}
+	ifanm.parse = ifanm.parseInterfaceAnnounceMessage
+	return int(unsafe.Sizeof(p)), map[int]*wireFormat{
+		sysRTM_ADD:        rtm,
+		sysRTM_DELETE:     rtm,
+		sysRTM_CHANGE:     rtm,
+		sysRTM_GET:        rtm,
+		sysRTM_LOSING:     rtm,
+		sysRTM_REDIRECT:   rtm,
+		sysRTM_MISS:       rtm,
+		sysRTM_LOCK:       rtm,
+		sysRTM_RESOLVE:    rtm,
+		sysRTM_NEWADDR:    ifam,
+		sysRTM_DELADDR:    ifam,
+		sysRTM_IFINFO:     ifm,
+		sysRTM_IFANNOUNCE: ifanm,
 	}
 	}
 }
 }

+ 6 - 0
route/zsys_darwin.go

@@ -10,6 +10,8 @@ const (
 	sysAF_LINK   = 0x12
 	sysAF_LINK   = 0x12
 	sysAF_INET6  = 0x1e
 	sysAF_INET6  = 0x1e
 
 
+	sysSOCK_RAW = 0x3
+
 	sysNET_RT_DUMP    = 0x1
 	sysNET_RT_DUMP    = 0x1
 	sysNET_RT_FLAGS   = 0x2
 	sysNET_RT_FLAGS   = 0x2
 	sysNET_RT_IFLIST  = 0x3
 	sysNET_RT_IFLIST  = 0x3
@@ -90,4 +92,8 @@ const (
 	sizeofRtMsghdrDarwin15  = 0x5c
 	sizeofRtMsghdrDarwin15  = 0x5c
 	sizeofRtMsghdr2Darwin15 = 0x5c
 	sizeofRtMsghdr2Darwin15 = 0x5c
 	sizeofRtMetricsDarwin15 = 0x38
 	sizeofRtMetricsDarwin15 = 0x38
+
+	sizeofSockaddrStorage = 0x80
+	sizeofSockaddrInet    = 0x10
+	sizeofSockaddrInet6   = 0x1c
 )
 )

+ 6 - 0
route/zsys_dragonfly.go

@@ -10,6 +10,8 @@ const (
 	sysAF_LINK   = 0x12
 	sysAF_LINK   = 0x12
 	sysAF_INET6  = 0x1c
 	sysAF_INET6  = 0x1c
 
 
+	sysSOCK_RAW = 0x3
+
 	sysNET_RT_DUMP   = 0x1
 	sysNET_RT_DUMP   = 0x1
 	sysNET_RT_FLAGS  = 0x2
 	sysNET_RT_FLAGS  = 0x2
 	sysNET_RT_IFLIST = 0x3
 	sysNET_RT_IFLIST = 0x3
@@ -89,4 +91,8 @@ const (
 
 
 	sizeofRtMsghdrDragonFlyBSD4  = 0x98
 	sizeofRtMsghdrDragonFlyBSD4  = 0x98
 	sizeofRtMetricsDragonFlyBSD4 = 0x70
 	sizeofRtMetricsDragonFlyBSD4 = 0x70
+
+	sizeofSockaddrStorage = 0x80
+	sizeofSockaddrInet    = 0x10
+	sizeofSockaddrInet6   = 0x1c
 )
 )

+ 6 - 0
route/zsys_freebsd_386.go

@@ -10,6 +10,8 @@ const (
 	sysAF_LINK   = 0x12
 	sysAF_LINK   = 0x12
 	sysAF_INET6  = 0x1c
 	sysAF_INET6  = 0x1c
 
 
+	sysSOCK_RAW = 0x3
+
 	sysNET_RT_DUMP     = 0x1
 	sysNET_RT_DUMP     = 0x1
 	sysNET_RT_FLAGS    = 0x2
 	sysNET_RT_FLAGS    = 0x2
 	sysNET_RT_IFLIST   = 0x3
 	sysNET_RT_IFLIST   = 0x3
@@ -117,4 +119,8 @@ const (
 	sizeofIfDataFreeBSD9Emu  = 0x98
 	sizeofIfDataFreeBSD9Emu  = 0x98
 	sizeofIfDataFreeBSD10Emu = 0x98
 	sizeofIfDataFreeBSD10Emu = 0x98
 	sizeofIfDataFreeBSD11Emu = 0x98
 	sizeofIfDataFreeBSD11Emu = 0x98
+
+	sizeofSockaddrStorage = 0x80
+	sizeofSockaddrInet    = 0x10
+	sizeofSockaddrInet6   = 0x1c
 )
 )

+ 6 - 0
route/zsys_freebsd_amd64.go

@@ -10,6 +10,8 @@ const (
 	sysAF_LINK   = 0x12
 	sysAF_LINK   = 0x12
 	sysAF_INET6  = 0x1c
 	sysAF_INET6  = 0x1c
 
 
+	sysSOCK_RAW = 0x3
+
 	sysNET_RT_DUMP     = 0x1
 	sysNET_RT_DUMP     = 0x1
 	sysNET_RT_FLAGS    = 0x2
 	sysNET_RT_FLAGS    = 0x2
 	sysNET_RT_IFLIST   = 0x3
 	sysNET_RT_IFLIST   = 0x3
@@ -114,4 +116,8 @@ const (
 	sizeofIfDataFreeBSD9Emu  = 0x98
 	sizeofIfDataFreeBSD9Emu  = 0x98
 	sizeofIfDataFreeBSD10Emu = 0x98
 	sizeofIfDataFreeBSD10Emu = 0x98
 	sizeofIfDataFreeBSD11Emu = 0x98
 	sizeofIfDataFreeBSD11Emu = 0x98
+
+	sizeofSockaddrStorage = 0x80
+	sizeofSockaddrInet    = 0x10
+	sizeofSockaddrInet6   = 0x1c
 )
 )

+ 6 - 0
route/zsys_freebsd_arm.go

@@ -10,6 +10,8 @@ const (
 	sysAF_LINK   = 0x12
 	sysAF_LINK   = 0x12
 	sysAF_INET6  = 0x1c
 	sysAF_INET6  = 0x1c
 
 
+	sysSOCK_RAW = 0x3
+
 	sysNET_RT_DUMP     = 0x1
 	sysNET_RT_DUMP     = 0x1
 	sysNET_RT_FLAGS    = 0x2
 	sysNET_RT_FLAGS    = 0x2
 	sysNET_RT_IFLIST   = 0x3
 	sysNET_RT_IFLIST   = 0x3
@@ -114,4 +116,8 @@ const (
 	sizeofIfDataFreeBSD9Emu  = 0x60
 	sizeofIfDataFreeBSD9Emu  = 0x60
 	sizeofIfDataFreeBSD10Emu = 0x60
 	sizeofIfDataFreeBSD10Emu = 0x60
 	sizeofIfDataFreeBSD11Emu = 0x98
 	sizeofIfDataFreeBSD11Emu = 0x98
+
+	sizeofSockaddrStorage = 0x80
+	sizeofSockaddrInet    = 0x10
+	sizeofSockaddrInet6   = 0x1c
 )
 )

+ 6 - 0
route/zsys_netbsd.go

@@ -10,6 +10,8 @@ const (
 	sysAF_LINK   = 0x12
 	sysAF_LINK   = 0x12
 	sysAF_INET6  = 0x18
 	sysAF_INET6  = 0x18
 
 
+	sysSOCK_RAW = 0x3
+
 	sysNET_RT_DUMP   = 0x1
 	sysNET_RT_DUMP   = 0x1
 	sysNET_RT_FLAGS  = 0x2
 	sysNET_RT_FLAGS  = 0x2
 	sysNET_RT_IFLIST = 0x5
 	sysNET_RT_IFLIST = 0x5
@@ -88,4 +90,8 @@ const (
 
 
 	sizeofRtMsghdrNetBSD7  = 0x78
 	sizeofRtMsghdrNetBSD7  = 0x78
 	sizeofRtMetricsNetBSD7 = 0x50
 	sizeofRtMetricsNetBSD7 = 0x50
+
+	sizeofSockaddrStorage = 0x80
+	sizeofSockaddrInet    = 0x10
+	sizeofSockaddrInet6   = 0x1c
 )
 )

+ 10 - 0
route/zsys_openbsd.go

@@ -10,6 +10,8 @@ const (
 	sysAF_LINK   = 0x12
 	sysAF_LINK   = 0x12
 	sysAF_INET6  = 0x18
 	sysAF_INET6  = 0x18
 
 
+	sysSOCK_RAW = 0x3
+
 	sysNET_RT_DUMP    = 0x1
 	sysNET_RT_DUMP    = 0x1
 	sysNET_RT_FLAGS   = 0x2
 	sysNET_RT_FLAGS   = 0x2
 	sysNET_RT_IFLIST  = 0x3
 	sysNET_RT_IFLIST  = 0x3
@@ -78,3 +80,11 @@ const (
 	sysRTAX_LABEL   = 0xa
 	sysRTAX_LABEL   = 0xa
 	sysRTAX_MAX     = 0xb
 	sysRTAX_MAX     = 0xb
 )
 )
+
+const (
+	sizeofRtMsghdr = 0x60
+
+	sizeofSockaddrStorage = 0x100
+	sizeofSockaddrInet    = 0x10
+	sizeofSockaddrInet6   = 0x1c
+)