|  | // Copyright 2016 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. | 
|  |  | 
|  | //go:build darwin || dragonfly || freebsd || netbsd || openbsd | 
|  | // +build darwin dragonfly freebsd netbsd openbsd | 
|  |  | 
|  | package route | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "os/exec" | 
|  | "runtime" | 
|  | "syscall" | 
|  | ) | 
|  |  | 
|  | func (m *RouteMessage) String() string { | 
|  | return fmt.Sprintf("%s", addrAttrs(nativeEndian.Uint32(m.raw[12:16]))) | 
|  | } | 
|  |  | 
|  | func (m *InterfaceMessage) String() string { | 
|  | var attrs addrAttrs | 
|  | if runtime.GOOS == "openbsd" { | 
|  | attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16])) | 
|  | } else { | 
|  | attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8])) | 
|  | } | 
|  | return fmt.Sprintf("%s", attrs) | 
|  | } | 
|  |  | 
|  | func (m *InterfaceAddrMessage) String() string { | 
|  | var attrs addrAttrs | 
|  | if runtime.GOOS == "openbsd" { | 
|  | attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16])) | 
|  | } else { | 
|  | attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8])) | 
|  | } | 
|  | return fmt.Sprintf("%s", attrs) | 
|  | } | 
|  |  | 
|  | func (m *InterfaceMulticastAddrMessage) String() string { | 
|  | return fmt.Sprintf("%s", addrAttrs(nativeEndian.Uint32(m.raw[4:8]))) | 
|  | } | 
|  |  | 
|  | func (m *InterfaceAnnounceMessage) String() string { | 
|  | what := "<nil>" | 
|  | switch m.What { | 
|  | case 0: | 
|  | what = "arrival" | 
|  | case 1: | 
|  | what = "departure" | 
|  | } | 
|  | return fmt.Sprintf("(%d %s %s)", m.Index, m.Name, what) | 
|  | } | 
|  |  | 
|  | func (m *InterfaceMetrics) String() string { | 
|  | return fmt.Sprintf("(type=%d mtu=%d)", m.Type, m.MTU) | 
|  | } | 
|  |  | 
|  | func (m *RouteMetrics) String() string { | 
|  | return fmt.Sprintf("(pmtu=%d)", m.PathMTU) | 
|  | } | 
|  |  | 
|  | type addrAttrs uint | 
|  |  | 
|  | var addrAttrNames = [...]string{ | 
|  | "dst", | 
|  | "gateway", | 
|  | "netmask", | 
|  | "genmask", | 
|  | "ifp", | 
|  | "ifa", | 
|  | "author", | 
|  | "brd", | 
|  | "df:mpls1-n:tag-o:src", // mpls1 for dragonfly, tag for netbsd, src for openbsd | 
|  | "df:mpls2-o:srcmask",   // mpls2 for dragonfly, srcmask for openbsd | 
|  | "df:mpls3-o:label",     // mpls3 for dragonfly, label for openbsd | 
|  | "o:bfd",                // bfd for openbsd | 
|  | "o:dns",                // dns for openbsd | 
|  | "o:static",             // static for openbsd | 
|  | "o:search",             // search for openbsd | 
|  | } | 
|  |  | 
|  | func (attrs addrAttrs) String() string { | 
|  | var s string | 
|  | for i, name := range addrAttrNames { | 
|  | if attrs&(1<<uint(i)) != 0 { | 
|  | if s != "" { | 
|  | s += "|" | 
|  | } | 
|  | s += name | 
|  | } | 
|  | } | 
|  | if s == "" { | 
|  | return "<nil>" | 
|  | } | 
|  | return s | 
|  | } | 
|  |  | 
|  | type msgs []Message | 
|  |  | 
|  | func (ms msgs) validate() ([]string, error) { | 
|  | var ss []string | 
|  | for _, m := range ms { | 
|  | switch m := m.(type) { | 
|  | case *RouteMessage: | 
|  | if err := addrs(m.Addrs).match(addrAttrs(nativeEndian.Uint32(m.raw[12:16]))); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | sys := m.Sys() | 
|  | if sys == nil { | 
|  | return nil, fmt.Errorf("no sys for %s", m.String()) | 
|  | } | 
|  | ss = append(ss, m.String()+" "+syss(sys).String()+" "+addrs(m.Addrs).String()) | 
|  | case *InterfaceMessage: | 
|  | var attrs addrAttrs | 
|  | if runtime.GOOS == "openbsd" { | 
|  | attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16])) | 
|  | } else { | 
|  | attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8])) | 
|  | } | 
|  | if err := addrs(m.Addrs).match(attrs); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | sys := m.Sys() | 
|  | if sys == nil { | 
|  | return nil, fmt.Errorf("no sys for %s", m.String()) | 
|  | } | 
|  | ss = append(ss, m.String()+" "+syss(sys).String()+" "+addrs(m.Addrs).String()) | 
|  | case *InterfaceAddrMessage: | 
|  | var attrs addrAttrs | 
|  | if runtime.GOOS == "openbsd" { | 
|  | attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16])) | 
|  | } else { | 
|  | attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8])) | 
|  | } | 
|  | if err := addrs(m.Addrs).match(attrs); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | ss = append(ss, m.String()+" "+addrs(m.Addrs).String()) | 
|  | case *InterfaceMulticastAddrMessage: | 
|  | if err := addrs(m.Addrs).match(addrAttrs(nativeEndian.Uint32(m.raw[4:8]))); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | ss = append(ss, m.String()+" "+addrs(m.Addrs).String()) | 
|  | case *InterfaceAnnounceMessage: | 
|  | ss = append(ss, m.String()) | 
|  | default: | 
|  | ss = append(ss, fmt.Sprintf("%+v", m)) | 
|  | } | 
|  | } | 
|  | return ss, nil | 
|  | } | 
|  |  | 
|  | type syss []Sys | 
|  |  | 
|  | func (sys syss) String() string { | 
|  | var s string | 
|  | for _, sy := range sys { | 
|  | switch sy := sy.(type) { | 
|  | case *InterfaceMetrics: | 
|  | if len(s) > 0 { | 
|  | s += " " | 
|  | } | 
|  | s += sy.String() | 
|  | case *RouteMetrics: | 
|  | if len(s) > 0 { | 
|  | s += " " | 
|  | } | 
|  | s += sy.String() | 
|  | } | 
|  | } | 
|  | return s | 
|  | } | 
|  |  | 
|  | type addrFamily int | 
|  |  | 
|  | func (af addrFamily) String() string { | 
|  | switch af { | 
|  | 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("%d", af) | 
|  | } | 
|  | } | 
|  |  | 
|  | const hexDigit = "0123456789abcdef" | 
|  |  | 
|  | type llAddr []byte | 
|  |  | 
|  | func (a llAddr) String() string { | 
|  | if len(a) == 0 { | 
|  | return "" | 
|  | } | 
|  | buf := make([]byte, 0, len(a)*3-1) | 
|  | for i, b := range a { | 
|  | if i > 0 { | 
|  | buf = append(buf, ':') | 
|  | } | 
|  | buf = append(buf, hexDigit[b>>4]) | 
|  | buf = append(buf, hexDigit[b&0xF]) | 
|  | } | 
|  | return string(buf) | 
|  | } | 
|  |  | 
|  | type ipAddr []byte | 
|  |  | 
|  | func (a ipAddr) String() string { | 
|  | if len(a) == 0 { | 
|  | return "<nil>" | 
|  | } | 
|  | if len(a) == 4 { | 
|  | return fmt.Sprintf("%d.%d.%d.%d", a[0], a[1], a[2], a[3]) | 
|  | } | 
|  | if len(a) == 16 { | 
|  | return fmt.Sprintf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]) | 
|  | } | 
|  | s := make([]byte, len(a)*2) | 
|  | for i, tn := range a { | 
|  | s[i*2], s[i*2+1] = hexDigit[tn>>4], hexDigit[tn&0xf] | 
|  | } | 
|  | return string(s) | 
|  | } | 
|  |  | 
|  | func (a *LinkAddr) String() string { | 
|  | name := a.Name | 
|  | if name == "" { | 
|  | name = "<nil>" | 
|  | } | 
|  | lla := llAddr(a.Addr).String() | 
|  | if lla == "" { | 
|  | lla = "<nil>" | 
|  | } | 
|  | return fmt.Sprintf("(%v %d %s %s)", addrFamily(a.Family()), a.Index, name, lla) | 
|  | } | 
|  |  | 
|  | func (a *Inet4Addr) String() string { | 
|  | return fmt.Sprintf("(%v %v)", addrFamily(a.Family()), ipAddr(a.IP[:])) | 
|  | } | 
|  |  | 
|  | func (a *Inet6Addr) String() string { | 
|  | return fmt.Sprintf("(%v %v %d)", addrFamily(a.Family()), ipAddr(a.IP[:]), a.ZoneID) | 
|  | } | 
|  |  | 
|  | func (a *DefaultAddr) String() string { | 
|  | return fmt.Sprintf("(%v %s)", addrFamily(a.Family()), ipAddr(a.Raw[2:]).String()) | 
|  | } | 
|  |  | 
|  | type addrs []Addr | 
|  |  | 
|  | func (as addrs) String() string { | 
|  | var s string | 
|  | for _, a := range as { | 
|  | if a == nil { | 
|  | continue | 
|  | } | 
|  | if len(s) > 0 { | 
|  | s += " " | 
|  | } | 
|  | switch a := a.(type) { | 
|  | case *LinkAddr: | 
|  | s += a.String() | 
|  | case *Inet4Addr: | 
|  | s += a.String() | 
|  | case *Inet6Addr: | 
|  | s += a.String() | 
|  | case *DefaultAddr: | 
|  | s += a.String() | 
|  | } | 
|  | } | 
|  | if s == "" { | 
|  | return "<nil>" | 
|  | } | 
|  | return s | 
|  | } | 
|  |  | 
|  | func (as addrs) match(attrs addrAttrs) error { | 
|  | var ts addrAttrs | 
|  | af := syscall.AF_UNSPEC | 
|  | for i := range as { | 
|  | if as[i] != nil { | 
|  | ts |= 1 << uint(i) | 
|  | } | 
|  | switch as[i].(type) { | 
|  | case *Inet4Addr: | 
|  | if af == syscall.AF_UNSPEC { | 
|  | af = syscall.AF_INET | 
|  | } | 
|  | if af != syscall.AF_INET { | 
|  | return fmt.Errorf("got %v; want %v", addrs(as), addrFamily(af)) | 
|  | } | 
|  | case *Inet6Addr: | 
|  | if af == syscall.AF_UNSPEC { | 
|  | af = syscall.AF_INET6 | 
|  | } | 
|  | if af != syscall.AF_INET6 { | 
|  | return fmt.Errorf("got %v; want %v", addrs(as), addrFamily(af)) | 
|  | } | 
|  | } | 
|  | } | 
|  | if ts != attrs && ts > attrs { | 
|  | return fmt.Errorf("%v not included in %v", ts, attrs) | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func fetchAndParseRIB(af int, typ RIBType) ([]Message, error) { | 
|  | b, err := FetchRIB(af, typ, 0) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("%v %d %v", addrFamily(af), typ, err) | 
|  | } | 
|  | ms, err := ParseRIB(typ, b) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("%v %d %v", addrFamily(af), typ, err) | 
|  | } | 
|  | return ms, nil | 
|  | } | 
|  |  | 
|  | // propVirtual is a proprietary virtual network interface. | 
|  | type propVirtual struct { | 
|  | name         string | 
|  | addr, mask   string | 
|  | setupCmds    []*exec.Cmd | 
|  | teardownCmds []*exec.Cmd | 
|  | } | 
|  |  | 
|  | func (pv *propVirtual) setup() error { | 
|  | for _, cmd := range pv.setupCmds { | 
|  | if err := cmd.Run(); err != nil { | 
|  | pv.teardown() | 
|  | return err | 
|  | } | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func (pv *propVirtual) teardown() error { | 
|  | for _, cmd := range pv.teardownCmds { | 
|  | if err := cmd.Run(); err != nil { | 
|  | return err | 
|  | } | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func (pv *propVirtual) configure(suffix int) error { | 
|  | if runtime.GOOS == "openbsd" { | 
|  | pv.name = fmt.Sprintf("vether%d", suffix) | 
|  | } else { | 
|  | pv.name = fmt.Sprintf("vlan%d", suffix) | 
|  | } | 
|  | xname, err := exec.LookPath("ifconfig") | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | pv.setupCmds = append(pv.setupCmds, &exec.Cmd{ | 
|  | Path: xname, | 
|  | Args: []string{"ifconfig", pv.name, "create"}, | 
|  | }) | 
|  | if runtime.GOOS == "netbsd" { | 
|  | // NetBSD requires an underlying dot1Q-capable network | 
|  | // interface. | 
|  | pv.setupCmds = append(pv.setupCmds, &exec.Cmd{ | 
|  | Path: xname, | 
|  | Args: []string{"ifconfig", pv.name, "vlan", fmt.Sprintf("%d", suffix&0xfff), "vlanif", "wm0"}, | 
|  | }) | 
|  | } | 
|  | pv.setupCmds = append(pv.setupCmds, &exec.Cmd{ | 
|  | Path: xname, | 
|  | Args: []string{"ifconfig", pv.name, "inet", pv.addr, "netmask", pv.mask}, | 
|  | }) | 
|  | pv.teardownCmds = append(pv.teardownCmds, &exec.Cmd{ | 
|  | Path: xname, | 
|  | Args: []string{"ifconfig", pv.name, "destroy"}, | 
|  | }) | 
|  | return nil | 
|  | } |