src/pkg/net/interface_linux.go - The Go Programming Language

Golang

Source file src/pkg/net/interface_linux.go

     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	// Network interface identification for Linux
     6	
     7	package net
     8	
     9	import (
    10		"os"
    11		"syscall"
    12		"unsafe"
    13	)
    14	
    15	// If the ifindex is zero, interfaceTable returns mappings of all
    16	// network interfaces.  Otherwise it returns a mapping of a specific
    17	// interface.
    18	func interfaceTable(ifindex int) ([]Interface, error) {
    19		tab, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC)
    20		if err != nil {
    21			return nil, os.NewSyscallError("netlink rib", err)
    22		}
    23	
    24		msgs, err := syscall.ParseNetlinkMessage(tab)
    25		if err != nil {
    26			return nil, os.NewSyscallError("netlink message", err)
    27		}
    28	
    29		var ift []Interface
    30		for _, m := range msgs {
    31			switch m.Header.Type {
    32			case syscall.NLMSG_DONE:
    33				goto done
    34			case syscall.RTM_NEWLINK:
    35				ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0]))
    36				if ifindex == 0 || ifindex == int(ifim.Index) {
    37					attrs, err := syscall.ParseNetlinkRouteAttr(&m)
    38					if err != nil {
    39						return nil, os.NewSyscallError("netlink routeattr", err)
    40					}
    41					ifi := newLink(ifim, attrs)
    42					ift = append(ift, ifi)
    43				}
    44			}
    45		}
    46	done:
    47		return ift, nil
    48	}
    49	
    50	func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) Interface {
    51		ifi := Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)}
    52		for _, a := range attrs {
    53			switch a.Attr.Type {
    54			case syscall.IFLA_ADDRESS:
    55				var nonzero bool
    56				for _, b := range a.Value {
    57					if b != 0 {
    58						nonzero = true
    59					}
    60				}
    61				if nonzero {
    62					ifi.HardwareAddr = a.Value[:]
    63				}
    64			case syscall.IFLA_IFNAME:
    65				ifi.Name = string(a.Value[:len(a.Value)-1])
    66			case syscall.IFLA_MTU:
    67				ifi.MTU = int(uint32(a.Value[3])<<24 | uint32(a.Value[2])<<16 | uint32(a.Value[1])<<8 | uint32(a.Value[0]))
    68			}
    69		}
    70		return ifi
    71	}
    72	
    73	func linkFlags(rawFlags uint32) Flags {
    74		var f Flags
    75		if rawFlags&syscall.IFF_UP != 0 {
    76			f |= FlagUp
    77		}
    78		if rawFlags&syscall.IFF_BROADCAST != 0 {
    79			f |= FlagBroadcast
    80		}
    81		if rawFlags&syscall.IFF_LOOPBACK != 0 {
    82			f |= FlagLoopback
    83		}
    84		if rawFlags&syscall.IFF_POINTOPOINT != 0 {
    85			f |= FlagPointToPoint
    86		}
    87		if rawFlags&syscall.IFF_MULTICAST != 0 {
    88			f |= FlagMulticast
    89		}
    90		return f
    91	}
    92	
    93	// If the ifindex is zero, interfaceAddrTable returns addresses
    94	// for all network interfaces.  Otherwise it returns addresses
    95	// for a specific interface.
    96	func interfaceAddrTable(ifindex int) ([]Addr, error) {
    97		tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
    98		if err != nil {
    99			return nil, os.NewSyscallError("netlink rib", err)
   100		}
   101	
   102		msgs, err := syscall.ParseNetlinkMessage(tab)
   103		if err != nil {
   104			return nil, os.NewSyscallError("netlink message", err)
   105		}
   106	
   107		ifat, err := addrTable(msgs, ifindex)
   108		if err != nil {
   109			return nil, err
   110		}
   111		return ifat, nil
   112	}
   113	
   114	func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, error) {
   115		var ifat []Addr
   116		for _, m := range msgs {
   117			switch m.Header.Type {
   118			case syscall.NLMSG_DONE:
   119				goto done
   120			case syscall.RTM_NEWADDR:
   121				ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
   122				if ifindex == 0 || ifindex == int(ifam.Index) {
   123					attrs, err := syscall.ParseNetlinkRouteAttr(&m)
   124					if err != nil {
   125						return nil, os.NewSyscallError("netlink routeattr", err)
   126					}
   127					ifat = append(ifat, newAddr(attrs, int(ifam.Family), int(ifam.Prefixlen)))
   128				}
   129			}
   130		}
   131	done:
   132		return ifat, nil
   133	}
   134	
   135	func newAddr(attrs []syscall.NetlinkRouteAttr, family, pfxlen int) Addr {
   136		ifa := &IPNet{}
   137		for _, a := range attrs {
   138			switch a.Attr.Type {
   139			case syscall.IFA_ADDRESS:
   140				switch family {
   141				case syscall.AF_INET:
   142					ifa.IP = IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3])
   143					ifa.Mask = CIDRMask(pfxlen, 8*IPv4len)
   144				case syscall.AF_INET6:
   145					ifa.IP = make(IP, IPv6len)
   146					copy(ifa.IP, a.Value[:])
   147					ifa.Mask = CIDRMask(pfxlen, 8*IPv6len)
   148				}
   149			}
   150		}
   151		return ifa
   152	}
   153	
   154	// If the ifindex is zero, interfaceMulticastAddrTable returns
   155	// addresses for all network interfaces.  Otherwise it returns
   156	// addresses for a specific interface.
   157	func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
   158		var (
   159			err error
   160			ifi *Interface
   161		)
   162		if ifindex > 0 {
   163			ifi, err = InterfaceByIndex(ifindex)
   164			if err != nil {
   165				return nil, err
   166			}
   167		}
   168		ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi)
   169		ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi)
   170		return append(ifmat4, ifmat6...), nil
   171	}
   172	
   173	func parseProcNetIGMP(path string, ifi *Interface) []Addr {
   174		fd, err := open(path)
   175		if err != nil {
   176			return nil
   177		}
   178		defer fd.close()
   179	
   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					for i := 0; i+1 < len(f[0]); i += 2 {
   197						b[i/2], _ = xtoi2(f[0][i:i+2], 0)
   198					}
   199					ifma := IPAddr{IP: IPv4(b[3], b[2], b[1], b[0])}
   200					ifmat = append(ifmat, ifma.toAddr())
   201				}
   202			}
   203		}
   204		return ifmat
   205	}
   206	
   207	func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
   208		fd, err := open(path)
   209		if err != nil {
   210			return nil
   211		}
   212		defer fd.close()
   213	
   214		var ifmat []Addr
   215		b := make([]byte, IPv6len)
   216		for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
   217			f := splitAtBytes(l, " \r\t\n")
   218			if len(f) < 6 {
   219				continue
   220			}
   221			if ifi == nil || f[1] == ifi.Name {
   222				for i := 0; i+1 < len(f[2]); i += 2 {
   223					b[i/2], _ = xtoi2(f[2][i:i+2], 0)
   224				}
   225				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]}}
   226				ifmat = append(ifmat, ifma.toAddr())
   227			}
   228		}
   229		return ifmat
   230	}