github.com/spotify/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/pkg/net/interface_linux.go (about)

     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("netlink rib", err)
    20  	}
    21  	msgs, err := syscall.ParseNetlinkMessage(tab)
    22  	if err != nil {
    23  		return nil, os.NewSyscallError("netlink message", 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("netlink routeattr", 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  func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) *Interface {
    49  	ifi := &Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)}
    50  	for _, a := range attrs {
    51  		switch a.Attr.Type {
    52  		case syscall.IFLA_ADDRESS:
    53  			var nonzero bool
    54  			for _, b := range a.Value {
    55  				if b != 0 {
    56  					nonzero = true
    57  				}
    58  			}
    59  			if nonzero {
    60  				ifi.HardwareAddr = a.Value[:]
    61  			}
    62  		case syscall.IFLA_IFNAME:
    63  			ifi.Name = string(a.Value[:len(a.Value)-1])
    64  		case syscall.IFLA_MTU:
    65  			ifi.MTU = int(*(*uint32)(unsafe.Pointer(&a.Value[:4][0])))
    66  		}
    67  	}
    68  	return ifi
    69  }
    70  
    71  func linkFlags(rawFlags uint32) Flags {
    72  	var f Flags
    73  	if rawFlags&syscall.IFF_UP != 0 {
    74  		f |= FlagUp
    75  	}
    76  	if rawFlags&syscall.IFF_BROADCAST != 0 {
    77  		f |= FlagBroadcast
    78  	}
    79  	if rawFlags&syscall.IFF_LOOPBACK != 0 {
    80  		f |= FlagLoopback
    81  	}
    82  	if rawFlags&syscall.IFF_POINTOPOINT != 0 {
    83  		f |= FlagPointToPoint
    84  	}
    85  	if rawFlags&syscall.IFF_MULTICAST != 0 {
    86  		f |= FlagMulticast
    87  	}
    88  	return f
    89  }
    90  
    91  // If the ifi is nil, interfaceAddrTable returns addresses for all
    92  // network interfaces.  Otherwise it returns addresses for a specific
    93  // interface.
    94  func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
    95  	tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
    96  	if err != nil {
    97  		return nil, os.NewSyscallError("netlink rib", err)
    98  	}
    99  	msgs, err := syscall.ParseNetlinkMessage(tab)
   100  	if err != nil {
   101  		return nil, os.NewSyscallError("netlink message", err)
   102  	}
   103  	var ift []Interface
   104  	if ifi == nil {
   105  		var err error
   106  		ift, err = interfaceTable(0)
   107  		if err != nil {
   108  			return nil, err
   109  		}
   110  	}
   111  	ifat, err := addrTable(ift, ifi, msgs)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	return ifat, nil
   116  }
   117  
   118  func addrTable(ift []Interface, ifi *Interface, msgs []syscall.NetlinkMessage) ([]Addr, error) {
   119  	var ifat []Addr
   120  loop:
   121  	for _, m := range msgs {
   122  		switch m.Header.Type {
   123  		case syscall.NLMSG_DONE:
   124  			break loop
   125  		case syscall.RTM_NEWADDR:
   126  			ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
   127  			if len(ift) != 0 || ifi.Index == int(ifam.Index) {
   128  				if len(ift) != 0 {
   129  					var err error
   130  					ifi, err = interfaceByIndex(ift, int(ifam.Index))
   131  					if err != nil {
   132  						return nil, err
   133  					}
   134  				}
   135  				attrs, err := syscall.ParseNetlinkRouteAttr(&m)
   136  				if err != nil {
   137  					return nil, os.NewSyscallError("netlink routeattr", err)
   138  				}
   139  				ifa := newAddr(ifi, ifam, attrs)
   140  				if ifa != nil {
   141  					ifat = append(ifat, ifa)
   142  				}
   143  			}
   144  		}
   145  	}
   146  	return ifat, nil
   147  }
   148  
   149  func newAddr(ifi *Interface, ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRouteAttr) Addr {
   150  	for _, a := range attrs {
   151  		if ifi.Flags&FlagPointToPoint != 0 && a.Attr.Type == syscall.IFA_LOCAL ||
   152  			ifi.Flags&FlagPointToPoint == 0 && a.Attr.Type == syscall.IFA_ADDRESS {
   153  			switch ifam.Family {
   154  			case syscall.AF_INET:
   155  				return &IPNet{IP: IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv4len)}
   156  			case syscall.AF_INET6:
   157  				ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv6len)}
   158  				copy(ifa.IP, a.Value[:])
   159  				return ifa
   160  			}
   161  		}
   162  	}
   163  	return nil
   164  }
   165  
   166  // interfaceMulticastAddrTable returns addresses for a specific
   167  // interface.
   168  func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
   169  	ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi)
   170  	ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi)
   171  	return append(ifmat4, ifmat6...), nil
   172  }
   173  
   174  func parseProcNetIGMP(path string, ifi *Interface) []Addr {
   175  	fd, err := open(path)
   176  	if err != nil {
   177  		return nil
   178  	}
   179  	defer fd.close()
   180  	var (
   181  		ifmat []Addr
   182  		name  string
   183  	)
   184  	fd.readLine() // skip first line
   185  	b := make([]byte, IPv4len)
   186  	for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
   187  		f := splitAtBytes(l, " :\r\t\n")
   188  		if len(f) < 4 {
   189  			continue
   190  		}
   191  		switch {
   192  		case l[0] != ' ' && l[0] != '\t': // new interface line
   193  			name = f[1]
   194  		case len(f[0]) == 8:
   195  			if ifi == nil || name == ifi.Name {
   196  				// The Linux kernel puts the IP
   197  				// address in /proc/net/igmp in native
   198  				// endianness.
   199  				for i := 0; i+1 < len(f[0]); i += 2 {
   200  					b[i/2], _ = xtoi2(f[0][i:i+2], 0)
   201  				}
   202  				i := *(*uint32)(unsafe.Pointer(&b[:4][0]))
   203  				ifma := IPAddr{IP: IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))}
   204  				ifmat = append(ifmat, ifma.toAddr())
   205  			}
   206  		}
   207  	}
   208  	return ifmat
   209  }
   210  
   211  func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
   212  	fd, err := open(path)
   213  	if err != nil {
   214  		return nil
   215  	}
   216  	defer fd.close()
   217  	var ifmat []Addr
   218  	b := make([]byte, IPv6len)
   219  	for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
   220  		f := splitAtBytes(l, " \r\t\n")
   221  		if len(f) < 6 {
   222  			continue
   223  		}
   224  		if ifi == nil || f[1] == ifi.Name {
   225  			for i := 0; i+1 < len(f[2]); i += 2 {
   226  				b[i/2], _ = xtoi2(f[2][i:i+2], 0)
   227  			}
   228  			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]}}
   229  			ifmat = append(ifmat, ifma.toAddr())
   230  		}
   231  	}
   232  	return ifmat
   233  }