blob: 74d11f9f0a578e405c3077aad84b6a9e54d33549 [file] [log] [blame]
// Copyright 2015 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.
// +build darwin dragonfly freebsd netbsd openbsd
package syscall_test
import (
"fmt"
"net"
"os"
"syscall"
"testing"
"time"
)
func TestRouteRIB(t *testing.T) {
for _, facility := range []int{syscall.NET_RT_DUMP, syscall.NET_RT_IFLIST} {
for _, param := range []int{syscall.AF_UNSPEC, syscall.AF_INET, syscall.AF_INET6} {
var err error
var b []byte
// The VM allocator wrapper functions can
// return ENOMEM easily.
for i := 0; i < 3; i++ {
b, err = syscall.RouteRIB(facility, param)
if err != nil {
time.Sleep(5 * time.Millisecond)
continue
}
break
}
if err != nil {
t.Error(facility, param, err)
continue
}
msgs, err := syscall.ParseRoutingMessage(b)
if err != nil {
t.Error(facility, param, err)
continue
}
var ipv4loopback, ipv6loopback bool
for _, m := range msgs {
flags, err := parseRoutingMessageHeader(m)
if err != nil {
t.Error(err)
continue
}
sas, err := parseRoutingSockaddrs(m)
if err != nil {
t.Error(err)
continue
}
if flags&(syscall.RTA_DST|syscall.RTA_IFA) != 0 {
sa := sas[syscall.RTAX_DST]
if sa == nil {
sa = sas[syscall.RTAX_IFA]
}
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
if net.IP(sa.Addr[:]).IsLoopback() {
ipv4loopback = true
}
case *syscall.SockaddrInet6:
if net.IP(sa.Addr[:]).IsLoopback() {
ipv6loopback = true
}
}
}
t.Log(facility, param, flags, sockaddrs(sas))
}
if param == syscall.AF_UNSPEC && len(msgs) > 0 && !ipv4loopback && !ipv6loopback {
t.Errorf("no loopback facility found: ipv4/ipv6=%v/%v, %v", ipv4loopback, ipv6loopback, len(msgs))
continue
}
}
}
}
func TestRouteMonitor(t *testing.T) {
if testing.Short() || os.Getuid() != 0 {
t.Skip("must be root")
}
s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
if err != nil {
t.Fatal(err)
}
defer syscall.Close(s)
tmo := time.After(30 * time.Second)
go func() {
b := make([]byte, os.Getpagesize())
for {
n, err := syscall.Read(s, b)
if err != nil {
return
}
msgs, err := syscall.ParseRoutingMessage(b[:n])
if err != nil {
t.Error(err)
return
}
for _, m := range msgs {
flags, err := parseRoutingMessageHeader(m)
if err != nil {
t.Error(err)
continue
}
sas, err := parseRoutingSockaddrs(m)
if err != nil {
t.Error(err)
continue
}
t.Log(flags, sockaddrs(sas))
}
}
}()
<-tmo
}
var parseInterfaceMessageTests = []*syscall.InterfaceMessage{
// with link-layer address
{
Header: syscall.IfMsghdr{Version: syscall.RTM_VERSION, Addrs: syscall.RTA_IFP},
Data: []uint8{
0x11, 0x12, 0x2, 0x0, 0x6, 0x3, 0x6, 0x0,
0x77, 0x6d, 0x31, 0x01, 0x23, 0x45, 0xab, 0xcd,
0xef, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
},
},
// without link-layer address
{
Header: syscall.IfMsghdr{Version: syscall.RTM_VERSION, Addrs: syscall.RTA_IFP},
Data: []uint8{
0xe, 0x12, 0x4, 0x0, 0xf5, 0x6, 0x0, 0x0,
0x70, 0x66, 0x6c, 0x6f, 0x67, 0x30, 0x0, 0x0,
},
},
// no data
{
Header: syscall.IfMsghdr{Version: syscall.RTM_VERSION, Addrs: syscall.RTA_IFP},
Data: []uint8{
0x8, 0xa, 0xb, 0xc, 0xd, 0x0, 0x0, 0x0,
},
},
}
func TestParseInterfaceMessage(t *testing.T) {
for i, tt := range parseInterfaceMessageTests {
if _, err := syscall.ParseRoutingSockaddr(tt); err != nil {
t.Errorf("#%d: %v", i, err)
}
}
}
type addrFamily byte
func (f addrFamily) String() string {
switch f {
case syscall.AF_UNSPEC:
return "unspec"
case syscall.AF_LINK:
return "link"
case syscall.AF_INET:
return "inet4"
case syscall.AF_INET6:
return "inet6"
default:
return fmt.Sprintf("unknown %d", f)
}
}
type addrFlags uint32
var addrFlagNames = [...]string{
"dst",
"gateway",
"netmask",
"genmask",
"ifp",
"ifa",
"author",
"brd",
"mpls1,tag,src", // sockaddr_mpls=dragonfly,netbsd, sockaddr_in/in6=openbsd
"mpls2,srcmask", // sockaddr_mpls=dragonfly, sockaddr_in/in6=openbsd
"mpls3,label", // sockaddr_mpls=dragonfly, sockaddr_rtlabel=openbsd
}
func (f addrFlags) String() string {
var s string
for i, name := range addrFlagNames {
if f&(1<<uint(i)) != 0 {
if s != "" {
s += "|"
}
s += name
}
}
if s == "" {
return "<nil>"
}
return s
}
type sockaddrs []syscall.Sockaddr
func (sas sockaddrs) String() string {
var s string
for _, sa := range sas {
if sa == nil {
continue
}
if len(s) > 0 {
s += " "
}
switch sa := sa.(type) {
case *syscall.SockaddrDatalink:
s += fmt.Sprintf("[%v/%v/%v t/n/a/s=%v/%v/%v/%v]", sa.Len, addrFamily(sa.Family), sa.Index, sa.Type, sa.Nlen, sa.Alen, sa.Slen)
case *syscall.SockaddrInet4:
s += fmt.Sprintf("%v", net.IP(sa.Addr[:]).To4())
case *syscall.SockaddrInet6:
s += fmt.Sprintf("%v", net.IP(sa.Addr[:]).To16())
}
}
if s == "" {
return "<nil>"
}
return s
}
func (sas sockaddrs) match(flags addrFlags) error {
var f addrFlags
family := syscall.AF_UNSPEC
for i := range sas {
if sas[i] != nil {
f |= 1 << uint(i)
}
switch sas[i].(type) {
case *syscall.SockaddrInet4:
if family == syscall.AF_UNSPEC {
family = syscall.AF_INET
}
if family != syscall.AF_INET {
return fmt.Errorf("got %v; want %v", sockaddrs(sas), family)
}
case *syscall.SockaddrInet6:
if family == syscall.AF_UNSPEC {
family = syscall.AF_INET6
}
if family != syscall.AF_INET6 {
return fmt.Errorf("got %v; want %v", sockaddrs(sas), family)
}
}
}
if f != flags {
return fmt.Errorf("got %v; want %v", f, flags)
}
return nil
}