1// Copyright 2011 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package net 6 7import ( 8 "os" 9 "syscall" 10 "unsafe" 11) 12 13// If the ifindex is zero, interfaceTable returns mappings of all 14// network interfaces. Otherwise it returns a mapping of a specific 15// interface. 16func interfaceTable(ifindex int) ([]Interface, error) { 17 tab, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC) 18 if err != nil { 19 return nil, os.NewSyscallError("netlinkrib", err) 20 } 21 msgs, err := syscall.ParseNetlinkMessage(tab) 22 if err != nil { 23 return nil, os.NewSyscallError("parsenetlinkmessage", err) 24 } 25 var ift []Interface 26loop: 27 for _, m := range msgs { 28 switch m.Header.Type { 29 case syscall.NLMSG_DONE: 30 break loop 31 case syscall.RTM_NEWLINK: 32 ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0])) 33 if ifindex == 0 || ifindex == int(ifim.Index) { 34 attrs, err := syscall.ParseNetlinkRouteAttr(&m) 35 if err != nil { 36 return nil, os.NewSyscallError("parsenetlinkrouteattr", err) 37 } 38 ift = append(ift, *newLink(ifim, attrs)) 39 if ifindex == int(ifim.Index) { 40 break loop 41 } 42 } 43 } 44 } 45 return ift, nil 46} 47 48const ( 49 // See linux/if_arp.h. 50 // Note that Linux doesn't support IPv4 over IPv6 tunneling. 51 sysARPHardwareIPv4IPv4 = 768 // IPv4 over IPv4 tunneling 52 sysARPHardwareIPv6IPv6 = 769 // IPv6 over IPv6 tunneling 53 sysARPHardwareIPv6IPv4 = 776 // IPv6 over IPv4 tunneling 54 sysARPHardwareGREIPv4 = 778 // any over GRE over IPv4 tunneling 55 sysARPHardwareGREIPv6 = 823 // any over GRE over IPv6 tunneling 56) 57 58func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) *Interface { 59 ifi := &Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)} 60 for _, a := range attrs { 61 switch a.Attr.Type { 62 case syscall.IFLA_ADDRESS: 63 // We never return any /32 or /128 IP address 64 // prefix on any IP tunnel interface as the 65 // hardware address. 66 switch len(a.Value) { 67 case IPv4len: 68 switch ifim.Type { 69 case sysARPHardwareIPv4IPv4, sysARPHardwareGREIPv4, sysARPHardwareIPv6IPv4: 70 continue 71 } 72 case IPv6len: 73 switch ifim.Type { 74 case sysARPHardwareIPv6IPv6, sysARPHardwareGREIPv6: 75 continue 76 } 77 } 78 var nonzero bool 79 for _, b := range a.Value { 80 if b != 0 { 81 nonzero = true 82 break 83 } 84 } 85 if nonzero { 86 ifi.HardwareAddr = a.Value[:] 87 } 88 case syscall.IFLA_IFNAME: 89 ifi.Name = string(a.Value[:len(a.Value)-1]) 90 case syscall.IFLA_MTU: 91 ifi.MTU = int(*(*uint32)(unsafe.Pointer(&a.Value[:4][0]))) 92 } 93 } 94 return ifi 95} 96 97func linkFlags(rawFlags uint32) Flags { 98 var f Flags 99 if rawFlags&syscall.IFF_UP != 0 { 100 f |= FlagUp 101 } 102 if rawFlags&syscall.IFF_RUNNING != 0 { 103 f |= FlagRunning 104 } 105 if rawFlags&syscall.IFF_BROADCAST != 0 { 106 f |= FlagBroadcast 107 } 108 if rawFlags&syscall.IFF_LOOPBACK != 0 { 109 f |= FlagLoopback 110 } 111 if rawFlags&syscall.IFF_POINTOPOINT != 0 { 112 f |= FlagPointToPoint 113 } 114 if rawFlags&syscall.IFF_MULTICAST != 0 { 115 f |= FlagMulticast 116 } 117 return f 118} 119 120// If the ifi is nil, interfaceAddrTable returns addresses for all 121// network interfaces. Otherwise it returns addresses for a specific 122// interface. 123func interfaceAddrTable(ifi *Interface) ([]Addr, error) { 124 tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC) 125 if err != nil { 126 return nil, os.NewSyscallError("netlinkrib", err) 127 } 128 msgs, err := syscall.ParseNetlinkMessage(tab) 129 if err != nil { 130 return nil, os.NewSyscallError("parsenetlinkmessage", err) 131 } 132 var ift []Interface 133 if ifi == nil { 134 var err error 135 ift, err = interfaceTable(0) 136 if err != nil { 137 return nil, err 138 } 139 } 140 ifat, err := addrTable(ift, ifi, msgs) 141 if err != nil { 142 return nil, err 143 } 144 return ifat, nil 145} 146 147func addrTable(ift []Interface, ifi *Interface, msgs []syscall.NetlinkMessage) ([]Addr, error) { 148 var ifat []Addr 149loop: 150 for _, m := range msgs { 151 switch m.Header.Type { 152 case syscall.NLMSG_DONE: 153 break loop 154 case syscall.RTM_NEWADDR: 155 ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0])) 156 if len(ift) != 0 || ifi.Index == int(ifam.Index) { 157 if len(ift) != 0 { 158 var err error 159 ifi, err = interfaceByIndex(ift, int(ifam.Index)) 160 if err != nil { 161 return nil, err 162 } 163 } 164 attrs, err := syscall.ParseNetlinkRouteAttr(&m) 165 if err != nil { 166 return nil, os.NewSyscallError("parsenetlinkrouteattr", err) 167 } 168 ifa := newAddr(ifam, attrs) 169 if ifa != nil { 170 ifat = append(ifat, ifa) 171 } 172 } 173 } 174 } 175 return ifat, nil 176} 177 178func newAddr(ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRouteAttr) Addr { 179 var ipPointToPoint bool 180 // Seems like we need to make sure whether the IP interface 181 // stack consists of IP point-to-point numbered or unnumbered 182 // addressing. 183 for _, a := range attrs { 184 if a.Attr.Type == syscall.IFA_LOCAL { 185 ipPointToPoint = true 186 break 187 } 188 } 189 for _, a := range attrs { 190 if ipPointToPoint && a.Attr.Type == syscall.IFA_ADDRESS { 191 continue 192 } 193 switch ifam.Family { 194 case syscall.AF_INET: 195 return &IPNet{IP: IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv4len)} 196 case syscall.AF_INET6: 197 ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv6len)} 198 copy(ifa.IP, a.Value[:]) 199 return ifa 200 } 201 } 202 return nil 203} 204 205// interfaceMulticastAddrTable returns addresses for a specific 206// interface. 207func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { 208 ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi) 209 ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi) 210 return append(ifmat4, ifmat6...), nil 211} 212 213func parseProcNetIGMP(path string, ifi *Interface) []Addr { 214 fd, err := open(path) 215 if err != nil { 216 return nil 217 } 218 defer fd.close() 219 var ( 220 ifmat []Addr 221 name string 222 ) 223 fd.readLine() // skip first line 224 b := make([]byte, IPv4len) 225 for l, ok := fd.readLine(); ok; l, ok = fd.readLine() { 226 f := splitAtBytes(l, " :\r\t\n") 227 if len(f) < 4 { 228 continue 229 } 230 switch { 231 case l[0] != ' ' && l[0] != '\t': // new interface line 232 name = f[1] 233 case len(f[0]) == 8: 234 if ifi == nil || name == ifi.Name { 235 // The Linux kernel puts the IP 236 // address in /proc/net/igmp in native 237 // endianness. 238 for i := 0; i+1 < len(f[0]); i += 2 { 239 b[i/2], _ = xtoi2(f[0][i:i+2], 0) 240 } 241 i := *(*uint32)(unsafe.Pointer(&b[:4][0])) 242 ifma := &IPAddr{IP: IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))} 243 ifmat = append(ifmat, ifma) 244 } 245 } 246 } 247 return ifmat 248} 249 250func parseProcNetIGMP6(path string, ifi *Interface) []Addr { 251 fd, err := open(path) 252 if err != nil { 253 return nil 254 } 255 defer fd.close() 256 var ifmat []Addr 257 b := make([]byte, IPv6len) 258 for l, ok := fd.readLine(); ok; l, ok = fd.readLine() { 259 f := splitAtBytes(l, " \r\t\n") 260 if len(f) < 6 { 261 continue 262 } 263 if ifi == nil || f[1] == ifi.Name { 264 for i := 0; i+1 < len(f[2]); i += 2 { 265 b[i/2], _ = xtoi2(f[2][i:i+2], 0) 266 } 267 ifma := &IPAddr{IP: IP{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]}} 268 ifmat = append(ifmat, ifma) 269 } 270 } 271 return ifmat 272} 273