Home | History | Annotate | Download | only in syscall
      1 // Copyright 2015 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 // +build darwin dragonfly freebsd netbsd openbsd
      6 
      7 package syscall_test
      8 
      9 import (
     10 	"fmt"
     11 	"net"
     12 	"os"
     13 	"syscall"
     14 	"testing"
     15 	"time"
     16 )
     17 
     18 func TestRouteRIB(t *testing.T) {
     19 	for _, facility := range []int{syscall.NET_RT_DUMP, syscall.NET_RT_IFLIST} {
     20 		for _, param := range []int{syscall.AF_UNSPEC, syscall.AF_INET, syscall.AF_INET6} {
     21 			var err error
     22 			var b []byte
     23 			// The VM allocator wrapper functions can
     24 			// return ENOMEM easily.
     25 			for i := 0; i < 3; i++ {
     26 				b, err = syscall.RouteRIB(facility, param)
     27 				if err != nil {
     28 					time.Sleep(5 * time.Millisecond)
     29 					continue
     30 				}
     31 				break
     32 			}
     33 			if err != nil {
     34 				t.Error(facility, param, err)
     35 				continue
     36 			}
     37 			msgs, err := syscall.ParseRoutingMessage(b)
     38 			if err != nil {
     39 				t.Error(facility, param, err)
     40 				continue
     41 			}
     42 			var ipv4loopback, ipv6loopback bool
     43 			for _, m := range msgs {
     44 				flags, err := parseRoutingMessageHeader(m)
     45 				if err != nil {
     46 					t.Error(err)
     47 					continue
     48 				}
     49 				sas, err := parseRoutingSockaddrs(m)
     50 				if err != nil {
     51 					t.Error(err)
     52 					continue
     53 				}
     54 				if flags&(syscall.RTA_DST|syscall.RTA_IFA) != 0 {
     55 					sa := sas[syscall.RTAX_DST]
     56 					if sa == nil {
     57 						sa = sas[syscall.RTAX_IFA]
     58 					}
     59 					switch sa := sa.(type) {
     60 					case *syscall.SockaddrInet4:
     61 						if net.IP(sa.Addr[:]).IsLoopback() {
     62 							ipv4loopback = true
     63 						}
     64 					case *syscall.SockaddrInet6:
     65 						if net.IP(sa.Addr[:]).IsLoopback() {
     66 							ipv6loopback = true
     67 						}
     68 					}
     69 				}
     70 				t.Log(facility, param, flags, sockaddrs(sas))
     71 			}
     72 			if param == syscall.AF_UNSPEC && len(msgs) > 0 && !ipv4loopback && !ipv6loopback {
     73 				t.Errorf("no loopback facility found: ipv4/ipv6=%v/%v, %v", ipv4loopback, ipv6loopback, len(msgs))
     74 				continue
     75 			}
     76 		}
     77 	}
     78 }
     79 
     80 func TestRouteMonitor(t *testing.T) {
     81 	if testing.Short() || os.Getuid() != 0 {
     82 		t.Skip("must be root")
     83 	}
     84 
     85 	s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
     86 	if err != nil {
     87 		t.Fatal(err)
     88 	}
     89 	defer syscall.Close(s)
     90 
     91 	tmo := time.After(30 * time.Second)
     92 	go func() {
     93 		b := make([]byte, os.Getpagesize())
     94 		for {
     95 			n, err := syscall.Read(s, b)
     96 			if err != nil {
     97 				return
     98 			}
     99 			msgs, err := syscall.ParseRoutingMessage(b[:n])
    100 			if err != nil {
    101 				t.Error(err)
    102 				return
    103 			}
    104 			for _, m := range msgs {
    105 				flags, err := parseRoutingMessageHeader(m)
    106 				if err != nil {
    107 					t.Error(err)
    108 					continue
    109 				}
    110 				sas, err := parseRoutingSockaddrs(m)
    111 				if err != nil {
    112 					t.Error(err)
    113 					continue
    114 				}
    115 				t.Log(flags, sockaddrs(sas))
    116 			}
    117 		}
    118 	}()
    119 	<-tmo
    120 }
    121 
    122 type addrFamily byte
    123 
    124 func (f addrFamily) String() string {
    125 	switch f {
    126 	case syscall.AF_UNSPEC:
    127 		return "unspec"
    128 	case syscall.AF_LINK:
    129 		return "link"
    130 	case syscall.AF_INET:
    131 		return "inet4"
    132 	case syscall.AF_INET6:
    133 		return "inet6"
    134 	default:
    135 		return fmt.Sprintf("unknown %d", f)
    136 	}
    137 }
    138 
    139 type addrFlags uint32
    140 
    141 var addrFlagNames = [...]string{
    142 	"dst",
    143 	"gateway",
    144 	"netmask",
    145 	"genmask",
    146 	"ifp",
    147 	"ifa",
    148 	"author",
    149 	"brd",
    150 	"mpls1,tag,src", // sockaddr_mpls=dragonfly,netbsd, sockaddr_in/in6=openbsd
    151 	"mpls2,srcmask", // sockaddr_mpls=dragonfly, sockaddr_in/in6=openbsd
    152 	"mpls3,label",   // sockaddr_mpls=dragonfly, sockaddr_rtlabel=openbsd
    153 }
    154 
    155 func (f addrFlags) String() string {
    156 	var s string
    157 	for i, name := range addrFlagNames {
    158 		if f&(1<<uint(i)) != 0 {
    159 			if s != "" {
    160 				s += "|"
    161 			}
    162 			s += name
    163 		}
    164 	}
    165 	if s == "" {
    166 		return "<nil>"
    167 	}
    168 	return s
    169 }
    170 
    171 type sockaddrs []syscall.Sockaddr
    172 
    173 func (sas sockaddrs) String() string {
    174 	var s string
    175 	for _, sa := range sas {
    176 		if sa == nil {
    177 			continue
    178 		}
    179 		if len(s) > 0 {
    180 			s += " "
    181 		}
    182 		switch sa := sa.(type) {
    183 		case *syscall.SockaddrDatalink:
    184 			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)
    185 		case *syscall.SockaddrInet4:
    186 			s += fmt.Sprintf("%v", net.IP(sa.Addr[:]).To4())
    187 		case *syscall.SockaddrInet6:
    188 			s += fmt.Sprintf("%v", net.IP(sa.Addr[:]).To16())
    189 		}
    190 	}
    191 	if s == "" {
    192 		return "<nil>"
    193 	}
    194 	return s
    195 }
    196 
    197 func (sas sockaddrs) match(flags addrFlags) error {
    198 	var f addrFlags
    199 	family := syscall.AF_UNSPEC
    200 	for i := range sas {
    201 		if sas[i] != nil {
    202 			f |= 1 << uint(i)
    203 		}
    204 		switch sas[i].(type) {
    205 		case *syscall.SockaddrInet4:
    206 			if family == syscall.AF_UNSPEC {
    207 				family = syscall.AF_INET
    208 			}
    209 			if family != syscall.AF_INET {
    210 				return fmt.Errorf("got %v; want %v", sockaddrs(sas), family)
    211 			}
    212 		case *syscall.SockaddrInet6:
    213 			if family == syscall.AF_UNSPEC {
    214 				family = syscall.AF_INET6
    215 			}
    216 			if family != syscall.AF_INET6 {
    217 				return fmt.Errorf("got %v; want %v", sockaddrs(sas), family)
    218 			}
    219 		}
    220 	}
    221 	if f != flags {
    222 		return fmt.Errorf("got %v; want %v", f, flags)
    223 	}
    224 	return nil
    225 }
    226