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