| // 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 | |
| } |