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