Skip to content

Commit cd9d661

Browse files
hurricanehrndzgopherbot
authored andcommitted
route: fix RTM_GET netmask parsing on Darwin
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 <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Ian Lance Taylor <iant@google.com> Auto-Submit: Ian Lance Taylor <iant@google.com> Commit-Queue: Ian Lance Taylor <iant@google.com> Reviewed-by: Damien Neil <dneil@google.com>
1 parent df97a48 commit cd9d661

File tree

3 files changed

+84
-6
lines changed

3 files changed

+84
-6
lines changed

route/address.go

+12-4
Original file line numberDiff line numberDiff line change
@@ -396,13 +396,19 @@ func marshalAddrs(b []byte, as []Addr) (uint, error) {
396396
func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) ([]Addr, error) {
397397
var as [syscall.RTAX_MAX]Addr
398398
af := int(syscall.AF_UNSPEC)
399+
isInet := func(fam int) bool {
400+
return fam == syscall.AF_INET || fam == syscall.AF_INET6
401+
}
402+
isMask := func(addrType uint) bool {
403+
return addrType == syscall.RTAX_NETMASK || addrType == syscall.RTAX_GENMASK
404+
}
399405
for i := uint(0); i < syscall.RTAX_MAX && len(b) >= roundup(0); i++ {
400406
if attrs&(1<<i) == 0 {
401407
continue
402408
}
403409
if i <= syscall.RTAX_BRD {
404-
switch b[1] {
405-
case syscall.AF_LINK:
410+
switch {
411+
case b[1] == syscall.AF_LINK:
406412
a, err := parseLinkAddr(b)
407413
if err != nil {
408414
return nil, err
@@ -413,8 +419,10 @@ func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) (
413419
return nil, errMessageTooShort
414420
}
415421
b = b[l:]
416-
case syscall.AF_INET, syscall.AF_INET6:
417-
af = int(b[1])
422+
case isInet(int(b[1])) || (isMask(i) && isInet(af)):
423+
if isInet(int(b[1])) {
424+
af = int(b[1])
425+
}
418426
a, err := parseInetAddr(af, b)
419427
if err != nil {
420428
return nil, err

route/address_darwin_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ var parseAddrsOnDarwinLittleEndianTests = []parseAddrsOnDarwinTest{
2929
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
3030
0x0, 0x0, 0x0, 0x0,
3131

32-
0x7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
32+
0x7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0,
3333
},
3434
[]Addr{
3535
&Inet4Addr{IP: [4]byte{192, 168, 86, 0}},
3636
&LinkAddr{Index: 4},
37-
&Inet4Addr{IP: [4]byte{255, 255, 255, 255}},
37+
&Inet4Addr{IP: [4]byte{255, 255, 255, 0}},
3838
nil,
3939
nil,
4040
nil,

route/example_darwin_test.go

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package route_test
6+
7+
import (
8+
"fmt"
9+
"net/netip"
10+
"os"
11+
"syscall"
12+
13+
"golang.org/x/net/route"
14+
"golang.org/x/sys/unix"
15+
)
16+
17+
// This example demonstrates how to parse a response to RTM_GET request.
18+
func ExampleParseRIB() {
19+
fd, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
20+
if err != nil {
21+
return
22+
}
23+
defer unix.Close(fd)
24+
25+
// Create a RouteMessage with RTM_GET type
26+
rtm := &route.RouteMessage{
27+
Version: syscall.RTM_VERSION,
28+
Type: unix.RTM_GET,
29+
ID: uintptr(os.Getpid()),
30+
Seq: 0,
31+
Addrs: []route.Addr{
32+
&route.Inet4Addr{IP: [4]byte{127, 0, 0, 0}},
33+
},
34+
}
35+
36+
// Marshal the message into bytes
37+
msgBytes, err := rtm.Marshal()
38+
if err != nil {
39+
return
40+
}
41+
42+
// Send the message over the routing socket
43+
_, err = unix.Write(fd, msgBytes)
44+
if err != nil {
45+
return
46+
}
47+
48+
// Read the response from the routing socket
49+
var buf [2 << 10]byte
50+
n, err := unix.Read(fd, buf[:])
51+
if err != nil {
52+
return
53+
}
54+
55+
// Parse the response messages
56+
msgs, err := route.ParseRIB(route.RIBTypeRoute, buf[:n])
57+
if err != nil {
58+
return
59+
}
60+
routeMsg, ok := msgs[0].(*route.RouteMessage)
61+
if !ok {
62+
return
63+
}
64+
netmask, ok := routeMsg.Addrs[2].(*route.Inet4Addr)
65+
if !ok {
66+
return
67+
}
68+
fmt.Println(netip.AddrFrom4(netmask.IP))
69+
// Output: 255.0.0.0
70+
}

0 commit comments

Comments
 (0)