Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/net/route: ParseRIB fails with errMessageTooShort on an AF_ROUTE message from Darwin #44740

Closed
bradfitz opened this issue Mar 2, 2021 · 1 comment
Labels
NeedsFix The path to resolution is known, but the work has not been done. OS-Darwin
Milestone

Comments

@bradfitz
Copy link
Contributor

bradfitz commented Mar 2, 2021

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

@dmitshur dmitshur added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Mar 2, 2021
@dmitshur dmitshur added this to the Backlog milestone Mar 2, 2021
hurricanehrndz added a commit to hurricanehrndz/go that referenced this issue Aug 29, 2024
sizeofSockaddrInet is 16, but first byte of sockaddr specifies the size
of the address. 16 works for most cases, except with Netmasks addresses,
on Darwin where only the significant bits are in the msg.

i.e.
06 02 00 00 ff ff

The above byte sequence is for a sockaddr that is 6 bytes long representing
an ipv4 for address that is 255.255.0.0.

Confirmed by using `route monitor`.

sources:
https://github.com/apple/darwin-xnu/blob/main/bsd/net/route.h
https://github.com/apple/darwin-xnu/blob/main/bsd/sys/socket.h#L603
hurricanehrndz added a commit to hurricanehrndz/go that referenced this issue Aug 29, 2024
sizeofSockaddrInet is 16, but first byte of sockaddr specifies the size
of the address. 16 works for most cases, except with Netmasks addresses,
on Darwin where only the significant bits are in the msg.

i.e.
06 02 00 00 ff ff

The above byte sequence is for a sockaddr that is 6 bytes long representing
an ipv4 for address that is 255.255.0.0.

Confirmed by using `route monitor`.

sources:
https://github.com/apple/darwin-xnu/blob/main/bsd/net/route.h
https://github.com/apple/darwin-xnu/blob/main/bsd/sys/socket.h#L603
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/609577 mentions this issue: On Darwin Fix address parsing of route msgs

hurricanehrndz added a commit to hurricanehrndz/golang-net that referenced this issue Sep 5, 2024
partial fix golang/go#44740

sizeofSockaddrInet is 16, but first byte of sockaddr specifies the size
of the address.

16 works for most cases, except Netmasks addresses, on Darwin where only
the significant bits are in the msg.

Take this route message as an example
```
88 00 05 01 00 00 00 00 41 08 00 00 07 00 00 00 92 7b 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
DST 10 02 00 00 64 71 00 00 00 00 00 00 00 00 00 00 // 100.113.0.0
GW 14 12 21 00 01 08 00 00 75 74 75 6e 34 33 31 39 00 00 00 00 // !utun4319
MASK 06 02 00 00 ff ff // 255.255.0.0
NULL 00 00
```

i.e. ipv4
```
06 02 00 00 ff ff
```

The above byte sequence is for a sockaddr that is 6 bytes long
representing an ipv4 for address that is 255.255.0.0.

i.e. ipv6 netmask
```
0e 1e 00 00 00 00 00 00 ff ff ff ff ff ff 00 00
```

The above is /48 netmask that should also be parsed using b[0] of the
sockaddr that contains the lenght.

Confirmed by using `route monitor`.

sources:
https://github.com/apple/darwin-xnu/blob/main/bsd/net/route.h
https://github.com/apple/darwin-xnu/blob/main/bsd/sys/socket.h#L603
hurricanehrndz added a commit to hurricanehrndz/golang-net that referenced this issue Sep 5, 2024
sizeofSockaddrInet is 16, but first byte of sockaddr specifies actual
size of sockaddr.

Although, 16 works for most cases, it fails for Netmasks addresses. On
Darwin only the significant bits of the netmask are in the msg.

Take this route message as an example

```
// rt_msg_hdr
88 00 05 01 00 00 00 00
41 08 00 00 07 00 00 00
92 7b 00 00 01 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00

// metrics
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00

// SOCKADDRS - DST  (100.113.0.0)
10 02 00 00 64 71 00 00
00 00 00 00 00 00 00 00

// GW  utun4319
14 12 21 00 01 08 00 00
75 74 75 6e 34 33 31 39
00 00 00 00

// NETMASK 255.255.0.0
06 02 00 00 ff ff

// NULL
00 00
```

i.e. ipv4
```
06 02 00 00 ff ff
```

The above byte sequence is for a sockaddr that is 6 bytes long
representing an ipv4 for address that is 255.255.0.0.

i.e. ipv6 netmask
```
0e 1e 00 00 00 00 00 00 ff ff ff ff ff ff 00 00
```

The above is `/48` netmask that should also be parsed using `b[0]` of the
sockaddr that contains the length.

Confirmed by using `route monitor`.

Fixes golang/go#44740

sources:
https://github.com/apple/darwin-xnu/blob/main/bsd/net/route.h
https://github.com/apple/darwin-xnu/blob/main/bsd/sys/socket.h#L603
@dmitshur dmitshur added NeedsFix The path to resolution is known, but the work has not been done. and removed NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Sep 6, 2024
@dmitshur dmitshur modified the milestones: Backlog, Unreleased Sep 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsFix The path to resolution is known, but the work has not been done. OS-Darwin
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants