Closed
Description
I have a macOS program which listens to route changes from the kernel.
It opens an AF_ROUTE socket:
fd, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, 0)
Then reads messages in a loop:
n, err := unix.Read(m.fd, m.buf[:])
if err != nil {
return nil, err
}
msgs, err := route.ParseRIB(route.RIBTypeRoute, m.buf[:n])
It works in general, until it hit this 50 byte message from the kernel:
func TestIssue1416RIB(t *testing.T) {
const ribHex = `32 00 05 10 30 00 00 00 00 00 00 00 04 00 00 00 14 12 04 00 06 03 06 00 65 6e 30 ac 87 a3 19 7f 82 00 00 00 0e 12 00 00 00 00 06 00 91 e0 f0 01 00 00`
rtmMsg, err := hex.DecodeString(strings.ReplaceAll(ribHex, " ", ""))
if err != nil {
t.Fatal(err)
}
msgs, err := route.ParseRIB(route.RIBTypeRoute, rtmMsg)
if err != nil {
t.Fatal(err)
}
t.Logf("Got: %#v", msgs)
}
Adding some logging and a panic where it was returning the error:
=== RUN TestIssue1416RIB
2021/03/02 09:32:32 ifmam len=50
2021/03/02 09:32:32 Got: &{Version:5 Type:16 Flags:0 Index:4 Addrs:[] raw:[50 0 5 16 48 0 0 0 0 0 0 0 4 0 0 0 20 18 4 0 6 3 6 0 101 110 48 172 135 163 25 127 130 0 0 0 14 18 0 0 0 0 6 0 145 224 240 1 0 0]}
2021/03/02 09:32:32 numAttrs = 48, bodyOff = 16, len(b) = 50, len remain = 34
2021/03/02 09:32:32 link[4] = &route.LinkAddr{Index:4, Name:"en0", Addr:[]uint8{0xac, 0x87, 0xa3, 0x19, 0x7f, 0x82}}
2021/03/02 09:32:32 link[5] = &route.LinkAddr{Index:0, Name:"", Addr:[]uint8{0x91, 0xe0, 0xf0, 0x1, 0x0, 0x0}}
--- FAIL: TestIssue1416RIB (0.00s)
panic: foo: len(b) 14 < roundup(14) of 16 [recovered]
panic: foo: len(b) 14 < roundup(14) of 16
goroutine 6 [running]:
testing.tRunner.func1.2(0x41c2020, 0xc0000510b0)
/Users/bradfitz/go/src/testing/testing.go:1144 +0x332
testing.tRunner.func1(0xc000001e00)
/Users/bradfitz/go/src/testing/testing.go:1147 +0x4b6
panic(0x41c2020, 0xc0000510b0)
/Users/bradfitz/go/src/runtime/panic.go:965 +0x1b9
golang.org/x/net/route.parseAddrs(0x30, 0x42193b0, 0xc0000640f0, 0x22, 0x60, 0x4056c34, 0x430a400, 0x432cfe0, 0x40dcd44, 0x0)
/Users/bradfitz/src/golang.org/x/net/route/address.go:389 +0x725
golang.org/x/net/route.(*wireFormat).parseInterfaceMulticastAddrMessage(0xc00000e210, 0x1, 0xc0000640e0, 0x32, 0x70, 0x40142c2, 0xc00003c648, 0x5fce0ba0, 0xb4929af58f107e09)
/Users/bradfitz/src/golang.org/x/net/route/interface_multicast.go:36 +0x4d3
golang.org/x/net/route.ParseRIB(0x1, 0xc0000640e0, 0x32, 0x70, 0x64, 0x70, 0x32, 0x0, 0x0)
/Users/bradfitz/src/golang.org/x/net/route/message.go:59 +0x23b
tailscale.com/wgengine/monitor.TestIssue1416RIB(0xc000001e00)
/Users/bradfitz/src/tailscale.com/wgengine/monitor/monitor_darwin_test.go:21 +0x15f
testing.tRunner(0xc000001e00, 0x4219d90)
/Users/bradfitz/go/src/testing/testing.go:1194 +0xef
created by testing.(*T).Run
/Users/bradfitz/go/src/testing/testing.go:1239 +0x2b3
exit status 2
FAIL tailscale.com/wgengine/monitor 0.083s
Notably, that LinkAddr (0x91, 0xe0, 0xf0, 0x1, 0x0, 0x0
) in the final 6 bytes of the 50 byte message.
But parseAddrs
is expecting some padding at the end? The errMessageTooShort
case it's hitting:
func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) ([]Addr, error) {
var as [sysRTAX_MAX]Addr
af := int(sysAF_UNSPEC)
for i := uint(0); i < sysRTAX_MAX && len(b) >= roundup(0); i++ {
if attrs&(1<<i) == 0 {
continue
}
if i <= sysRTAX_BRD {
switch b[1] {
case sysAF_LINK:
a, err := parseLinkAddr(b)
if err != nil {
return nil, err
}
as[i] = a
l := roundup(int(b[0]))
if len(b) < l {
return nil, errMessageTooShort
}
b = b[l:]
Maybe this package was designed purely for getting the RIBs via sysctl (with https://pkg.go.dev/golang.org/x/net/route#FetchRIB) where they differ from the format sent over AF_ROUTE sockets? Maybe?
/cc @ianlancetaylor @mikioh @tklauser @danderson @DentonGentry