1// Copyright 2016 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//go:build darwin || dragonfly || freebsd || netbsd || openbsd
6// +build darwin dragonfly freebsd netbsd openbsd
7
8// Package route provides basic functions for the manipulation of
9// packet routing facilities on BSD variants.
10//
11// The package supports any version of Darwin, any version of
12// DragonFly BSD, FreeBSD 7 and above, NetBSD 6 and above, and OpenBSD
13// 5.6 and above.
14package route
15
16import (
17	"errors"
18	"os"
19	"syscall"
20)
21
22var (
23	errUnsupportedMessage = errors.New("unsupported message")
24	errMessageMismatch    = errors.New("message mismatch")
25	errMessageTooShort    = errors.New("message too short")
26	errInvalidMessage     = errors.New("invalid message")
27	errInvalidAddr        = errors.New("invalid address")
28	errShortBuffer        = errors.New("short buffer")
29)
30
31// A RouteMessage represents a message conveying an address prefix, a
32// nexthop address and an output interface.
33//
34// Unlike other messages, this message can be used to query adjacency
35// information for the given address prefix, to add a new route, and
36// to delete or modify the existing route from the routing information
37// base inside the kernel by writing and reading route messages on a
38// routing socket.
39//
40// For the manipulation of routing information, the route message must
41// contain appropriate fields that include:
42//
43//	Version       = <must be specified>
44//	Type          = <must be specified>
45//	Flags         = <must be specified>
46//	Index         = <must be specified if necessary>
47//	ID            = <must be specified>
48//	Seq           = <must be specified>
49//	Addrs         = <must be specified>
50//
51// The Type field specifies a type of manipulation, the Flags field
52// specifies a class of target information and the Addrs field
53// specifies target information like the following:
54//
55//	route.RouteMessage{
56//		Version: RTM_VERSION,
57//		Type: RTM_GET,
58//		Flags: RTF_UP | RTF_HOST,
59//		ID: uintptr(os.Getpid()),
60//		Seq: 1,
61//		Addrs: []route.Addrs{
62//			RTAX_DST: &route.Inet4Addr{ ... },
63//			RTAX_IFP: &route.LinkAddr{ ... },
64//			RTAX_BRD: &route.Inet4Addr{ ... },
65//		},
66//	}
67//
68// The values for the above fields depend on the implementation of
69// each operating system.
70//
71// The Err field on a response message contains an error value on the
72// requested operation. If non-nil, the requested operation is failed.
73type RouteMessage struct {
74	Version int     // message version
75	Type    int     // message type
76	Flags   int     // route flags
77	Index   int     // interface index when attached
78	ID      uintptr // sender's identifier; usually process ID
79	Seq     int     // sequence number
80	Err     error   // error on requested operation
81	Addrs   []Addr  // addresses
82
83	extOff int    // offset of header extension
84	raw    []byte // raw message
85}
86
87// Marshal returns the binary encoding of m.
88func (m *RouteMessage) Marshal() ([]byte, error) {
89	return m.marshal()
90}
91
92// A RIBType represents a type of routing information base.
93type RIBType int
94
95const (
96	RIBTypeRoute     RIBType = syscall.NET_RT_DUMP
97	RIBTypeInterface RIBType = syscall.NET_RT_IFLIST
98)
99
100// FetchRIB fetches a routing information base from the operating
101// system.
102//
103// The provided af must be an address family.
104//
105// The provided arg must be a RIBType-specific argument.
106// When RIBType is related to routes, arg might be a set of route
107// flags. When RIBType is related to network interfaces, arg might be
108// an interface index or a set of interface flags. In most cases, zero
109// means a wildcard.
110func FetchRIB(af int, typ RIBType, arg int) ([]byte, error) {
111	try := 0
112	for {
113		try++
114		mib := [6]int32{sysCTL_NET, sysAF_ROUTE, 0, int32(af), int32(typ), int32(arg)}
115		n := uintptr(0)
116		if err := sysctl(mib[:], nil, &n, nil, 0); err != nil {
117			return nil, os.NewSyscallError("sysctl", err)
118		}
119		if n == 0 {
120			return nil, nil
121		}
122		b := make([]byte, n)
123		if err := sysctl(mib[:], &b[0], &n, nil, 0); err != nil {
124			// If the sysctl failed because the data got larger
125			// between the two sysctl calls, try a few times
126			// before failing. (golang.org/issue/45736).
127			const maxTries = 3
128			if err == syscall.ENOMEM && try < maxTries {
129				continue
130			}
131			return nil, os.NewSyscallError("sysctl", err)
132		}
133		return b[:n], nil
134	}
135}
136