Skip to content

route: fix RTM_GET netmask parsing on Darwin #232

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

Closed
Closed
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
}