Skip to content

Commit

Permalink
route: fix RTM_GET netmask parsing on Darwin
Browse files Browse the repository at this point in the history
On Darwin, the AF_FAMILY byte of a sockaddr for a netmask or genmask
can be ignored if unreasonable. In such cases, it is the family of the
DST address that should instead be used.

Additionally, fixing faulty test data. 192.168.86.0 is a Class C network
address, that should have a subnet mask of 255.255.255.0. What's more is
the data can also be flag as incorrect considering structure padding
rules alone.

Further more, you can validate that `route get` will never actually return a
netmask for a host query, even though it should be  255.255.255.255.

You can run the following to check:
route -n get -host 127.0.0.1

You will note the reply has no mention of netmask.

Depends on CL 646556 - https://go.dev/cl/646556
Fixes golang/go#71578.

Change-Id: Id95669b649a416a380d26c5cdba0e3d1c4bc1ffb
GitHub-Last-Rev: 20064b2
GitHub-Pull-Request: #232
Reviewed-on: https://go-review.googlesource.com/c/net/+/647176
LUCI-TryBot-Result: Go LUCI <[email protected]>
Reviewed-by: Ian Lance Taylor <[email protected]>
Auto-Submit: Ian Lance Taylor <[email protected]>
Commit-Queue: Ian Lance Taylor <[email protected]>
Reviewed-by: Damien Neil <[email protected]>
  • Loading branch information
hurricanehrndz authored and gopherbot committed Feb 10, 2025
1 parent df97a48 commit cd9d661
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 6 deletions.
16 changes: 12 additions & 4 deletions route/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,13 +396,19 @@ func marshalAddrs(b []byte, as []Addr) (uint, error) {
func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) ([]Addr, error) {
var as [syscall.RTAX_MAX]Addr
af := int(syscall.AF_UNSPEC)
isInet := func(fam int) bool {
return fam == syscall.AF_INET || fam == syscall.AF_INET6
}
isMask := func(addrType uint) bool {
return addrType == syscall.RTAX_NETMASK || addrType == syscall.RTAX_GENMASK
}
for i := uint(0); i < syscall.RTAX_MAX && len(b) >= roundup(0); i++ {
if attrs&(1<<i) == 0 {
continue
}
if i <= syscall.RTAX_BRD {
switch b[1] {
case syscall.AF_LINK:
switch {
case b[1] == syscall.AF_LINK:
a, err := parseLinkAddr(b)
if err != nil {
return nil, err
Expand All @@ -413,8 +419,10 @@ func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) (
return nil, errMessageTooShort
}
b = b[l:]
case syscall.AF_INET, syscall.AF_INET6:
af = int(b[1])
case isInet(int(b[1])) || (isMask(i) && isInet(af)):
if isInet(int(b[1])) {
af = int(b[1])
}
a, err := parseInetAddr(af, b)
if err != nil {
return nil, err
Expand Down
4 changes: 2 additions & 2 deletions route/address_darwin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,

0x7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0,
},
[]Addr{
&Inet4Addr{IP: [4]byte{192, 168, 86, 0}},
&LinkAddr{Index: 4},
&Inet4Addr{IP: [4]byte{255, 255, 255, 255}},
&Inet4Addr{IP: [4]byte{255, 255, 255, 0}},
nil,
nil,
nil,
Expand Down
70 changes: 70 additions & 0 deletions route/example_darwin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2025 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 route_test

import (
"fmt"
"net/netip"
"os"
"syscall"

"golang.org/x/net/route"
"golang.org/x/sys/unix"
)

// This example demonstrates how to parse a response to RTM_GET request.
func ExampleParseRIB() {
fd, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
if err != nil {
return
}
defer unix.Close(fd)

// Create a RouteMessage with RTM_GET type
rtm := &route.RouteMessage{
Version: syscall.RTM_VERSION,
Type: unix.RTM_GET,
ID: uintptr(os.Getpid()),
Seq: 0,
Addrs: []route.Addr{
&route.Inet4Addr{IP: [4]byte{127, 0, 0, 0}},
},
}

// Marshal the message into bytes
msgBytes, err := rtm.Marshal()
if err != nil {
return
}

// Send the message over the routing socket
_, err = unix.Write(fd, msgBytes)
if err != nil {
return
}

// Read the response from the routing socket
var buf [2 << 10]byte
n, err := unix.Read(fd, buf[:])
if err != nil {
return
}

// Parse the response messages
msgs, err := route.ParseRIB(route.RIBTypeRoute, buf[:n])
if err != nil {
return
}
routeMsg, ok := msgs[0].(*route.RouteMessage)
if !ok {
return
}
netmask, ok := routeMsg.Addrs[2].(*route.Inet4Addr)
if !ok {
return
}
fmt.Println(netip.AddrFrom4(netmask.IP))
// Output: 255.0.0.0
}

0 comments on commit cd9d661

Please sign in to comment.