blob: aa1bc21d3f00ae432d4c33a865ae0b64890a4281 [file] [log] [blame] [edit]
// Copyright 2016 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.
//go:build darwin || dragonfly || freebsd || netbsd || openbsd
package routebsd
import (
"net/netip"
"runtime"
"syscall"
)
// An Addr represents an address associated with packet routing.
type Addr interface {
// Family returns an address family.
Family() int
}
// A LinkAddr represents a link-layer address.
type LinkAddr struct {
Index int // interface index when attached
Name string // interface name when attached
Addr []byte // link-layer address when attached
}
// Family implements the Family method of Addr interface.
func (a *LinkAddr) Family() int { return syscall.AF_LINK }
func parseLinkAddr(b []byte) (Addr, error) {
if len(b) < 8 {
return nil, errInvalidAddr
}
_, a, err := parseKernelLinkAddr(syscall.AF_LINK, b[4:])
if err != nil {
return nil, err
}
a.(*LinkAddr).Index = int(nativeEndian.Uint16(b[2:4]))
return a, nil
}
// parseKernelLinkAddr parses b as a link-layer address in
// conventional BSD kernel form.
func parseKernelLinkAddr(_ int, b []byte) (int, Addr, error) {
// The encoding looks like the following:
// +----------------------------+
// | Type (1 octet) |
// +----------------------------+
// | Name length (1 octet) |
// +----------------------------+
// | Address length (1 octet) |
// +----------------------------+
// | Selector length (1 octet) |
// +----------------------------+
// | Data (variable) |
// +----------------------------+
//
// On some platforms, all-bit-one of length field means "don't
// care".
nlen, alen, slen := int(b[1]), int(b[2]), int(b[3])
if nlen == 0xff {
nlen = 0
}
if alen == 0xff {
alen = 0
}
if slen == 0xff {
slen = 0
}
l := 4 + nlen + alen + slen
if len(b) < l {
return 0, nil, errInvalidAddr
}
data := b[4:]
var name string
var addr []byte
if nlen > 0 {
name = string(data[:nlen])
data = data[nlen:]
}
if alen > 0 {
addr = data[:alen]
data = data[alen:]
}
return l, &LinkAddr{Name: name, Addr: addr}, nil
}
// An InetAddr represent an internet address using IPv4 or IPv6.
type InetAddr struct {
IP netip.Addr
}
func (a *InetAddr) Family() int {
if a.IP.Is4() {
return syscall.AF_INET
} else {
return syscall.AF_INET6
}
}
// parseInetAddr parses b as an internet address for IPv4 or IPv6.
func parseInetAddr(af int, b []byte) (Addr, error) {
const (
off4 = 4 // offset of in_addr
off6 = 8 // offset of in6_addr
ipv4Len = 4 // length of IPv4 address in bytes
ipv6Len = 16 // length of IPv6 address in bytes
)
switch af {
case syscall.AF_INET:
if len(b) < (off4+1) || len(b) < int(b[0]) {
return nil, errInvalidAddr
}
sockAddrLen := int(b[0])
var ip [ipv4Len]byte
if sockAddrLen != 0 {
// Calculate how many bytes of the address to copy:
// either full IPv4 length or the available length.
n := off4 + ipv4Len
if sockAddrLen < n {
n = sockAddrLen
}
copy(ip[:], b[off4:n])
}
a := &InetAddr{
IP: netip.AddrFrom4(ip),
}
return a, nil
case syscall.AF_INET6:
if len(b) < (off6+1) || len(b) < int(b[0]) {
return nil, errInvalidAddr
}
var ip [ipv6Len]byte
sockAddrLen := int(b[0])
if sockAddrLen != 0 {
n := off6 + ipv6Len
if sockAddrLen < n {
n = sockAddrLen
}
copy(ip[:], b[off6:n])
if ip[0] == 0xfe && ip[1]&0xc0 == 0x80 || ip[0] == 0xff && (ip[1]&0x0f == 0x01 || ip[1]&0x0f == 0x02) {
// KAME based IPv6 protocol stack usually
// embeds the interface index in the
// interface-local or link-local address as
// the kernel-internal form.
id := int(bigEndian.Uint16(ip[2:4]))
if id != 0 {
ip[2], ip[3] = 0, 0
}
}
}
// The kernel can provide an integer zone ID.
// We ignore it.
a := &InetAddr{
IP: netip.AddrFrom16(ip),
}
return a, nil
default:
return nil, errInvalidAddr
}
}
// parseKernelInetAddr parses b as an internet address in conventional
// BSD kernel form.
func parseKernelInetAddr(af int, b []byte) (int, Addr, error) {
// The encoding looks similar to the NLRI encoding.
// +----------------------------+
// | Length (1 octet) |
// +----------------------------+
// | Address prefix (variable) |
// +----------------------------+
//
// The differences between the kernel form and the NLRI
// encoding are:
//
// - The length field of the kernel form indicates the prefix
// length in bytes, not in bits
//
// - In the kernel form, zero value of the length field
// doesn't mean 0.0.0.0/0 or ::/0
//
// - The kernel form appends leading bytes to the prefix field
// to make the <length, prefix> tuple to be conformed with
// the routing message boundary
l := int(b[0])
if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
// On Darwin, an address in the kernel form is also
// used as a message filler.
if l == 0 || len(b) > roundup(l) {
l = roundup(l)
}
} else {
l = roundup(l)
}
if len(b) < l {
return 0, nil, errInvalidAddr
}
// Don't reorder case expressions.
// The case expressions for IPv6 must come first.
const (
off4 = 4 // offset of in_addr
off6 = 8 // offset of in6_addr
)
switch {
case b[0] == syscall.SizeofSockaddrInet6:
a := &InetAddr{
IP: netip.AddrFrom16([16]byte(b[off6 : off6+16])),
}
return int(b[0]), a, nil
case af == syscall.AF_INET6:
var ab [16]byte
if l-1 < off6 {
copy(ab[:], b[1:l])
} else {
copy(ab[:], b[l-off6:l])
}
a := &InetAddr{
IP: netip.AddrFrom16(ab),
}
return int(b[0]), a, nil
case b[0] == syscall.SizeofSockaddrInet4:
a := &InetAddr{
IP: netip.AddrFrom4([4]byte(b[off4 : off4+4])),
}
return int(b[0]), a, nil
default: // an old fashion, AF_UNSPEC or unknown means AF_INET
var ab [4]byte
if l-1 < off4 {
copy(ab[:], b[1:l])
} else {
copy(ab[:], b[l-off4:l])
}
a := &InetAddr{
IP: netip.AddrFrom4(ab),
}
return int(b[0]), a, nil
}
}
func parseAddrs(attrs uint, b []byte) ([]Addr, error) {
var as [syscall.RTAX_MAX]Addr
af := int(syscall.AF_UNSPEC)
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:
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:]
case syscall.AF_INET, syscall.AF_INET6:
af = int(b[1])
a, err := parseInetAddr(af, 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:]
default:
l, a, err := parseKernelInetAddr(af, b)
if err != nil {
return nil, err
}
as[i] = a
ll := roundup(l)
if len(b) < ll {
b = b[l:]
} else {
b = b[ll:]
}
}
} else {
// Skip unknown addresses.
l := roundup(int(b[0]))
if len(b) < l {
return nil, errMessageTooShort
}
b = b[l:]
}
}
// The only remaining bytes in b should be alignment.
// However, under some circumstances DragonFly BSD appears to put
// more addresses in the message than are indicated in the address
// bitmask, so don't check for this.
return as[:], nil
}