Home | History | Annotate | Download | only in net
      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 
      5 package net
      6 
      7 import (
      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.
     16 func 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
     26 loop:
     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 
     48 const (
     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 
     58 func 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 
     97 func linkFlags(rawFlags uint32) Flags {
     98 	var f Flags
     99 	if rawFlags&syscall.IFF_UP != 0 {
    100 		f |= FlagUp
    101 	}
    102 	if rawFlags&syscall.IFF_BROADCAST != 0 {
    103 		f |= FlagBroadcast
    104 	}
    105 	if rawFlags&syscall.IFF_LOOPBACK != 0 {
    106 		f |= FlagLoopback
    107 	}
    108 	if rawFlags&syscall.IFF_POINTOPOINT != 0 {
    109 		f |= FlagPointToPoint
    110 	}
    111 	if rawFlags&syscall.IFF_MULTICAST != 0 {
    112 		f |= FlagMulticast
    113 	}
    114 	return f
    115 }
    116 
    117 // If the ifi is nil, interfaceAddrTable returns addresses for all
    118 // network interfaces. Otherwise it returns addresses for a specific
    119 // interface.
    120 func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
    121 	tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
    122 	if err != nil {
    123 		return nil, os.NewSyscallError("netlinkrib", err)
    124 	}
    125 	msgs, err := syscall.ParseNetlinkMessage(tab)
    126 	if err != nil {
    127 		return nil, os.NewSyscallError("parsenetlinkmessage", err)
    128 	}
    129 	var ift []Interface
    130 	if ifi == nil {
    131 		var err error
    132 		ift, err = interfaceTable(0)
    133 		if err != nil {
    134 			return nil, err
    135 		}
    136 	}
    137 	ifat, err := addrTable(ift, ifi, msgs)
    138 	if err != nil {
    139 		return nil, err
    140 	}
    141 	return ifat, nil
    142 }
    143 
    144 func addrTable(ift []Interface, ifi *Interface, msgs []syscall.NetlinkMessage) ([]Addr, error) {
    145 	var ifat []Addr
    146 loop:
    147 	for _, m := range msgs {
    148 		switch m.Header.Type {
    149 		case syscall.NLMSG_DONE:
    150 			break loop
    151 		case syscall.RTM_NEWADDR:
    152 			ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
    153 			if len(ift) != 0 || ifi.Index == int(ifam.Index) {
    154 				if len(ift) != 0 {
    155 					var err error
    156 					ifi, err = interfaceByIndex(ift, int(ifam.Index))
    157 					if err != nil {
    158 						return nil, err
    159 					}
    160 				}
    161 				attrs, err := syscall.ParseNetlinkRouteAttr(&m)
    162 				if err != nil {
    163 					return nil, os.NewSyscallError("parsenetlinkrouteattr", err)
    164 				}
    165 				ifa := newAddr(ifam, attrs)
    166 				if ifa != nil {
    167 					ifat = append(ifat, ifa)
    168 				}
    169 			}
    170 		}
    171 	}
    172 	return ifat, nil
    173 }
    174 
    175 func newAddr(ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRouteAttr) Addr {
    176 	var ipPointToPoint bool
    177 	// Seems like we need to make sure whether the IP interface
    178 	// stack consists of IP point-to-point numbered or unnumbered
    179 	// addressing.
    180 	for _, a := range attrs {
    181 		if a.Attr.Type == syscall.IFA_LOCAL {
    182 			ipPointToPoint = true
    183 			break
    184 		}
    185 	}
    186 	for _, a := range attrs {
    187 		if ipPointToPoint && a.Attr.Type == syscall.IFA_ADDRESS {
    188 			continue
    189 		}
    190 		switch ifam.Family {
    191 		case syscall.AF_INET:
    192 			return &IPNet{IP: IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv4len)}
    193 		case syscall.AF_INET6:
    194 			ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv6len)}
    195 			copy(ifa.IP, a.Value[:])
    196 			return ifa
    197 		}
    198 	}
    199 	return nil
    200 }
    201 
    202 // interfaceMulticastAddrTable returns addresses for a specific
    203 // interface.
    204 func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
    205 	ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi)
    206 	ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi)
    207 	return append(ifmat4, ifmat6...), nil
    208 }
    209 
    210 func parseProcNetIGMP(path string, ifi *Interface) []Addr {
    211 	fd, err := open(path)
    212 	if err != nil {
    213 		return nil
    214 	}
    215 	defer fd.close()
    216 	var (
    217 		ifmat []Addr
    218 		name  string
    219 	)
    220 	fd.readLine() // skip first line
    221 	b := make([]byte, IPv4len)
    222 	for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
    223 		f := splitAtBytes(l, " :\r\t\n")
    224 		if len(f) < 4 {
    225 			continue
    226 		}
    227 		switch {
    228 		case l[0] != ' ' && l[0] != '\t': // new interface line
    229 			name = f[1]
    230 		case len(f[0]) == 8:
    231 			if ifi == nil || name == ifi.Name {
    232 				// The Linux kernel puts the IP
    233 				// address in /proc/net/igmp in native
    234 				// endianness.
    235 				for i := 0; i+1 < len(f[0]); i += 2 {
    236 					b[i/2], _ = xtoi2(f[0][i:i+2], 0)
    237 				}
    238 				i := *(*uint32)(unsafe.Pointer(&b[:4][0]))
    239 				ifma := &IPAddr{IP: IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))}
    240 				ifmat = append(ifmat, ifma)
    241 			}
    242 		}
    243 	}
    244 	return ifmat
    245 }
    246 
    247 func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
    248 	fd, err := open(path)
    249 	if err != nil {
    250 		return nil
    251 	}
    252 	defer fd.close()
    253 	var ifmat []Addr
    254 	b := make([]byte, IPv6len)
    255 	for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
    256 		f := splitAtBytes(l, " \r\t\n")
    257 		if len(f) < 6 {
    258 			continue
    259 		}
    260 		if ifi == nil || f[1] == ifi.Name {
    261 			for i := 0; i+1 < len(f[2]); i += 2 {
    262 				b[i/2], _ = xtoi2(f[2][i:i+2], 0)
    263 			}
    264 			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]}}
    265 			ifmat = append(ifmat, ifma)
    266 		}
    267 	}
    268 	return ifmat
    269 }
    270