|  | // Copyright 2011 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. | 
|  |  | 
|  | package net | 
|  |  | 
|  | import ( | 
|  | "os" | 
|  | "syscall" | 
|  | "unsafe" | 
|  | ) | 
|  |  | 
|  | // If the ifindex is zero, interfaceTable returns mappings of all | 
|  | // network interfaces. Otherwise it returns a mapping of a specific | 
|  | // interface. | 
|  | func interfaceTable(ifindex int) ([]Interface, error) { | 
|  | tab, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC) | 
|  | if err != nil { | 
|  | return nil, os.NewSyscallError("netlinkrib", err) | 
|  | } | 
|  | msgs, err := syscall.ParseNetlinkMessage(tab) | 
|  | if err != nil { | 
|  | return nil, os.NewSyscallError("parsenetlinkmessage", err) | 
|  | } | 
|  | var ift []Interface | 
|  | loop: | 
|  | for _, m := range msgs { | 
|  | switch m.Header.Type { | 
|  | case syscall.NLMSG_DONE: | 
|  | break loop | 
|  | case syscall.RTM_NEWLINK: | 
|  | ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0])) | 
|  | if ifindex == 0 || ifindex == int(ifim.Index) { | 
|  | attrs, err := syscall.ParseNetlinkRouteAttr(&m) | 
|  | if err != nil { | 
|  | return nil, os.NewSyscallError("parsenetlinkrouteattr", err) | 
|  | } | 
|  | ift = append(ift, *newLink(ifim, attrs)) | 
|  | if ifindex == int(ifim.Index) { | 
|  | break loop | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return ift, nil | 
|  | } | 
|  |  | 
|  | const ( | 
|  | // See linux/if_arp.h. | 
|  | // Note that Linux doesn't support IPv4 over IPv6 tunneling. | 
|  | sysARPHardwareIPv4IPv4 = 768 // IPv4 over IPv4 tunneling | 
|  | sysARPHardwareIPv6IPv6 = 769 // IPv6 over IPv6 tunneling | 
|  | sysARPHardwareIPv6IPv4 = 776 // IPv6 over IPv4 tunneling | 
|  | sysARPHardwareGREIPv4  = 778 // any over GRE over IPv4 tunneling | 
|  | sysARPHardwareGREIPv6  = 823 // any over GRE over IPv6 tunneling | 
|  | ) | 
|  |  | 
|  | func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) *Interface { | 
|  | ifi := &Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)} | 
|  | for _, a := range attrs { | 
|  | switch a.Attr.Type { | 
|  | case syscall.IFLA_ADDRESS: | 
|  | // We never return any /32 or /128 IP address | 
|  | // prefix on any IP tunnel interface as the | 
|  | // hardware address. | 
|  | switch len(a.Value) { | 
|  | case IPv4len: | 
|  | switch ifim.Type { | 
|  | case sysARPHardwareIPv4IPv4, sysARPHardwareGREIPv4, sysARPHardwareIPv6IPv4: | 
|  | continue | 
|  | } | 
|  | case IPv6len: | 
|  | switch ifim.Type { | 
|  | case sysARPHardwareIPv6IPv6, sysARPHardwareGREIPv6: | 
|  | continue | 
|  | } | 
|  | } | 
|  | var nonzero bool | 
|  | for _, b := range a.Value { | 
|  | if b != 0 { | 
|  | nonzero = true | 
|  | break | 
|  | } | 
|  | } | 
|  | if nonzero { | 
|  | ifi.HardwareAddr = a.Value[:] | 
|  | } | 
|  | case syscall.IFLA_IFNAME: | 
|  | ifi.Name = string(a.Value[:len(a.Value)-1]) | 
|  | case syscall.IFLA_MTU: | 
|  | ifi.MTU = int(*(*uint32)(unsafe.Pointer(&a.Value[:4][0]))) | 
|  | } | 
|  | } | 
|  | return ifi | 
|  | } | 
|  |  | 
|  | func linkFlags(rawFlags uint32) Flags { | 
|  | var f Flags | 
|  | if rawFlags&syscall.IFF_UP != 0 { | 
|  | f |= FlagUp | 
|  | } | 
|  | if rawFlags&syscall.IFF_BROADCAST != 0 { | 
|  | f |= FlagBroadcast | 
|  | } | 
|  | if rawFlags&syscall.IFF_LOOPBACK != 0 { | 
|  | f |= FlagLoopback | 
|  | } | 
|  | if rawFlags&syscall.IFF_POINTOPOINT != 0 { | 
|  | f |= FlagPointToPoint | 
|  | } | 
|  | if rawFlags&syscall.IFF_MULTICAST != 0 { | 
|  | f |= FlagMulticast | 
|  | } | 
|  | return f | 
|  | } | 
|  |  | 
|  | // If the ifi is nil, interfaceAddrTable returns addresses for all | 
|  | // network interfaces. Otherwise it returns addresses for a specific | 
|  | // interface. | 
|  | func interfaceAddrTable(ifi *Interface) ([]Addr, error) { | 
|  | tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC) | 
|  | if err != nil { | 
|  | return nil, os.NewSyscallError("netlinkrib", err) | 
|  | } | 
|  | msgs, err := syscall.ParseNetlinkMessage(tab) | 
|  | if err != nil { | 
|  | return nil, os.NewSyscallError("parsenetlinkmessage", err) | 
|  | } | 
|  | var ift []Interface | 
|  | if ifi == nil { | 
|  | var err error | 
|  | ift, err = interfaceTable(0) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | } | 
|  | ifat, err := addrTable(ift, ifi, msgs) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | return ifat, nil | 
|  | } | 
|  |  | 
|  | func addrTable(ift []Interface, ifi *Interface, msgs []syscall.NetlinkMessage) ([]Addr, error) { | 
|  | var ifat []Addr | 
|  | loop: | 
|  | for _, m := range msgs { | 
|  | switch m.Header.Type { | 
|  | case syscall.NLMSG_DONE: | 
|  | break loop | 
|  | case syscall.RTM_NEWADDR: | 
|  | ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0])) | 
|  | if len(ift) != 0 || ifi.Index == int(ifam.Index) { | 
|  | if len(ift) != 0 { | 
|  | var err error | 
|  | ifi, err = interfaceByIndex(ift, int(ifam.Index)) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | } | 
|  | attrs, err := syscall.ParseNetlinkRouteAttr(&m) | 
|  | if err != nil { | 
|  | return nil, os.NewSyscallError("parsenetlinkrouteattr", err) | 
|  | } | 
|  | ifa := newAddr(ifam, attrs) | 
|  | if ifa != nil { | 
|  | ifat = append(ifat, ifa) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return ifat, nil | 
|  | } | 
|  |  | 
|  | func newAddr(ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRouteAttr) Addr { | 
|  | var ipPointToPoint bool | 
|  | // Seems like we need to make sure whether the IP interface | 
|  | // stack consists of IP point-to-point numbered or unnumbered | 
|  | // addressing. | 
|  | for _, a := range attrs { | 
|  | if a.Attr.Type == syscall.IFA_LOCAL { | 
|  | ipPointToPoint = true | 
|  | break | 
|  | } | 
|  | } | 
|  | for _, a := range attrs { | 
|  | if ipPointToPoint && a.Attr.Type == syscall.IFA_ADDRESS { | 
|  | continue | 
|  | } | 
|  | switch ifam.Family { | 
|  | case syscall.AF_INET: | 
|  | return &IPNet{IP: IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv4len)} | 
|  | case syscall.AF_INET6: | 
|  | ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv6len)} | 
|  | copy(ifa.IP, a.Value[:]) | 
|  | return ifa | 
|  | } | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // interfaceMulticastAddrTable returns addresses for a specific | 
|  | // interface. | 
|  | func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { | 
|  | ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi) | 
|  | ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi) | 
|  | return append(ifmat4, ifmat6...), nil | 
|  | } | 
|  |  | 
|  | func parseProcNetIGMP(path string, ifi *Interface) []Addr { | 
|  | fd, err := open(path) | 
|  | if err != nil { | 
|  | return nil | 
|  | } | 
|  | defer fd.close() | 
|  | var ( | 
|  | ifmat []Addr | 
|  | name  string | 
|  | ) | 
|  | fd.readLine() // skip first line | 
|  | b := make([]byte, IPv4len) | 
|  | for l, ok := fd.readLine(); ok; l, ok = fd.readLine() { | 
|  | f := splitAtBytes(l, " :\r\t\n") | 
|  | if len(f) < 4 { | 
|  | continue | 
|  | } | 
|  | switch { | 
|  | case l[0] != ' ' && l[0] != '\t': // new interface line | 
|  | name = f[1] | 
|  | case len(f[0]) == 8: | 
|  | if ifi == nil || name == ifi.Name { | 
|  | // The Linux kernel puts the IP | 
|  | // address in /proc/net/igmp in native | 
|  | // endianness. | 
|  | for i := 0; i+1 < len(f[0]); i += 2 { | 
|  | b[i/2], _ = xtoi2(f[0][i:i+2], 0) | 
|  | } | 
|  | i := *(*uint32)(unsafe.Pointer(&b[:4][0])) | 
|  | ifma := &IPAddr{IP: IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))} | 
|  | ifmat = append(ifmat, ifma) | 
|  | } | 
|  | } | 
|  | } | 
|  | return ifmat | 
|  | } | 
|  |  | 
|  | func parseProcNetIGMP6(path string, ifi *Interface) []Addr { | 
|  | fd, err := open(path) | 
|  | if err != nil { | 
|  | return nil | 
|  | } | 
|  | defer fd.close() | 
|  | var ifmat []Addr | 
|  | b := make([]byte, IPv6len) | 
|  | for l, ok := fd.readLine(); ok; l, ok = fd.readLine() { | 
|  | f := splitAtBytes(l, " \r\t\n") | 
|  | if len(f) < 6 { | 
|  | continue | 
|  | } | 
|  | if ifi == nil || f[1] == ifi.Name { | 
|  | for i := 0; i+1 < len(f[2]); i += 2 { | 
|  | b[i/2], _ = xtoi2(f[2][i:i+2], 0) | 
|  | } | 
|  | 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]}} | 
|  | ifmat = append(ifmat, ifma) | 
|  | } | 
|  | } | 
|  | return ifmat | 
|  | } |