1// Generate a local routing table structure following the code at 2// https://github.com/google/gopacket/blob/master/routing/routing.go 3// 4// Plan 9 networking is described here: http://9p.io/magic/man2html/3/ip 5 6package netroute 7 8import ( 9 "bytes" 10 "fmt" 11 "io/ioutil" 12 "net" 13 "strconv" 14 "strings" 15 16 "github.com/google/gopacket/routing" 17) 18 19const netdir = "/net" 20 21func New() (routing.Router, error) { 22 rtr := &router{} 23 rtr.ifaces = make(map[int]net.Interface) 24 rtr.addrs = make(map[int]ipAddrs) 25 ifaces, err := net.Interfaces() 26 if err != nil { 27 return nil, fmt.Errorf("could not get interfaces: %v", err) 28 } 29 for _, iface := range ifaces { 30 rtr.ifaces[iface.Index] = iface 31 var addrs ipAddrs 32 ifaceAddrs, err := iface.Addrs() 33 if err != nil { 34 return nil, err 35 } 36 for _, addr := range ifaceAddrs { 37 if inet, ok := addr.(*net.IPNet); ok { 38 // Go has a nasty habit of giving you IPv4s as ::ffff:1.2.3.4 instead of 1.2.3.4. 39 // We want to use mapped v4 addresses as v4 preferred addresses, never as v6 40 // preferred addresses. 41 if v4 := inet.IP.To4(); v4 != nil { 42 if addrs.v4 == nil { 43 addrs.v4 = v4 44 } 45 } else if addrs.v6 == nil { 46 addrs.v6 = inet.IP 47 } 48 } 49 } 50 rtr.addrs[iface.Index] = addrs 51 } 52 53 rtr.v4, rtr.v6, err = parseIPRoutes() 54 if err != nil { 55 return nil, err 56 } 57 return rtr, nil 58} 59 60func parseIPRoutes() (v4, v6 routeSlice, err error) { 61 buf, err := ioutil.ReadFile(netdir + "/iproute") 62 if err != nil { 63 return nil, nil, err 64 } 65 66 for { 67 i := bytes.IndexRune(buf, '\n') 68 if i <= 0 { 69 break 70 } 71 f := strings.Fields(string(buf[:i])) 72 buf = buf[i+1:] 73 74 if len(f) < 8 { 75 return nil, nil, fmt.Errorf("iproute entry contains %d fields", len(f)) 76 } 77 flags, rt, err := parseRoute(f) 78 if err != nil { 79 return nil, nil, err 80 } 81 if rt.Dst != nil { 82 // If gateway for destination 127.0.0.1/32 is 127.0.0.1, set it to nil. 83 if m, n := rt.Dst.Mask.Size(); n > 0 && m == n && rt.Dst.IP.Equal(rt.Gateway) { 84 rt.Gateway = nil 85 } 86 } 87 if strings.ContainsRune(flags, '4') { // IPv4 88 v4 = append(v4, rt) 89 } 90 if strings.ContainsRune(flags, '6') { // IPv6 91 v6 = append(v6, rt) 92 } 93 } 94 return v4, v6, nil 95} 96 97func parseRoute(f []string) (flags string, rt *rtInfo, err error) { 98 rt = new(rtInfo) 99 isV4 := strings.ContainsRune(f[3], '4') // flags 100 101 rt.PrefSrc, rt.Src, err = parsePlan9CIDR(f[6], f[7], isV4) 102 if err != nil { 103 return "", nil, err 104 } 105 _, rt.Dst, err = parsePlan9CIDR(f[0], f[1], isV4) 106 if err != nil { 107 return "", nil, err 108 } 109 rt.Gateway = net.ParseIP(f[2]) 110 111 n, err := strconv.ParseUint(f[5], 10, 32) 112 if err != nil { 113 return "", nil, err 114 } 115 rt.InputIface = 0 116 rt.OutputIface = uint32(n) + 1 // starts at 0, so net package adds 1 117 rt.Priority = 0 118 return f[3], rt, nil 119} 120 121func parsePlan9CIDR(addr, mask string, isV4 bool) (net.IP, *net.IPNet, error) { 122 if len(mask) == 0 || mask[0] != '/' { 123 return nil, nil, fmt.Errorf("invalid CIDR mask %v", mask) 124 } 125 n, err := strconv.ParseUint(mask[1:], 10, 32) 126 if err != nil { 127 return nil, nil, err 128 } 129 ip := net.ParseIP(addr) 130 iplen := net.IPv6len 131 if isV4 { 132 // Plan 9 uses IPv6 mask for IPv4 addresses 133 n -= 8 * (net.IPv6len - net.IPv4len) 134 iplen = net.IPv4len 135 } 136 if n == 0 && ip.IsUnspecified() { 137 return nil, nil, nil 138 } 139 m := net.CIDRMask(int(n), 8*iplen) 140 return ip, &net.IPNet{IP: ip.Mask(m), Mask: m}, nil 141} 142