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 // +build darwin dragonfly freebsd netbsd openbsd
      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.RouteRIB(syscall.NET_RT_IFLIST, ifindex)
     20 	if err != nil {
     21 		return nil, os.NewSyscallError("routerib", err)
     22 	}
     23 	msgs, err := syscall.ParseRoutingMessage(tab)
     24 	if err != nil {
     25 		return nil, os.NewSyscallError("parseroutingmessage", err)
     26 	}
     27 	return parseInterfaceTable(ifindex, msgs)
     28 }
     29 
     30 func parseInterfaceTable(ifindex int, msgs []syscall.RoutingMessage) ([]Interface, error) {
     31 	var ift []Interface
     32 loop:
     33 	for _, m := range msgs {
     34 		switch m := m.(type) {
     35 		case *syscall.InterfaceMessage:
     36 			if ifindex == 0 || ifindex == int(m.Header.Index) {
     37 				ifi, err := newLink(m)
     38 				if err != nil {
     39 					return nil, err
     40 				}
     41 				ift = append(ift, *ifi)
     42 				if ifindex == int(m.Header.Index) {
     43 					break loop
     44 				}
     45 			}
     46 		}
     47 	}
     48 	return ift, nil
     49 }
     50 
     51 func newLink(m *syscall.InterfaceMessage) (*Interface, error) {
     52 	sas, err := syscall.ParseRoutingSockaddr(m)
     53 	if err != nil {
     54 		return nil, os.NewSyscallError("parseroutingsockaddr", err)
     55 	}
     56 	ifi := &Interface{Index: int(m.Header.Index), Flags: linkFlags(m.Header.Flags)}
     57 	sa, _ := sas[syscall.RTAX_IFP].(*syscall.SockaddrDatalink)
     58 	if sa != nil {
     59 		// NOTE: SockaddrDatalink.Data is minimum work area,
     60 		// can be larger.
     61 		m.Data = m.Data[unsafe.Offsetof(sa.Data):]
     62 		var name [syscall.IFNAMSIZ]byte
     63 		for i := 0; i < int(sa.Nlen); i++ {
     64 			name[i] = byte(m.Data[i])
     65 		}
     66 		ifi.Name = string(name[:sa.Nlen])
     67 		ifi.MTU = int(m.Header.Data.Mtu)
     68 		addr := make([]byte, sa.Alen)
     69 		for i := 0; i < int(sa.Alen); i++ {
     70 			addr[i] = byte(m.Data[int(sa.Nlen)+i])
     71 		}
     72 		ifi.HardwareAddr = addr[:sa.Alen]
     73 	}
     74 	return ifi, nil
     75 }
     76 
     77 func linkFlags(rawFlags int32) Flags {
     78 	var f Flags
     79 	if rawFlags&syscall.IFF_UP != 0 {
     80 		f |= FlagUp
     81 	}
     82 	if rawFlags&syscall.IFF_BROADCAST != 0 {
     83 		f |= FlagBroadcast
     84 	}
     85 	if rawFlags&syscall.IFF_LOOPBACK != 0 {
     86 		f |= FlagLoopback
     87 	}
     88 	if rawFlags&syscall.IFF_POINTOPOINT != 0 {
     89 		f |= FlagPointToPoint
     90 	}
     91 	if rawFlags&syscall.IFF_MULTICAST != 0 {
     92 		f |= FlagMulticast
     93 	}
     94 	return f
     95 }
     96 
     97 // If the ifi is nil, interfaceAddrTable returns addresses for all
     98 // network interfaces.  Otherwise it returns addresses for a specific
     99 // interface.
    100 func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
    101 	index := 0
    102 	if ifi != nil {
    103 		index = ifi.Index
    104 	}
    105 	tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, index)
    106 	if err != nil {
    107 		return nil, os.NewSyscallError("routerib", err)
    108 	}
    109 	msgs, err := syscall.ParseRoutingMessage(tab)
    110 	if err != nil {
    111 		return nil, os.NewSyscallError("parseroutingmessage", err)
    112 	}
    113 	var ift []Interface
    114 	if index == 0 {
    115 		ift, err = parseInterfaceTable(index, msgs)
    116 		if err != nil {
    117 			return nil, err
    118 		}
    119 	}
    120 	var ifat []Addr
    121 	for _, m := range msgs {
    122 		switch m := m.(type) {
    123 		case *syscall.InterfaceAddrMessage:
    124 			if index == 0 || index == int(m.Header.Index) {
    125 				if index == 0 {
    126 					var err error
    127 					ifi, err = interfaceByIndex(ift, int(m.Header.Index))
    128 					if err != nil {
    129 						return nil, err
    130 					}
    131 				}
    132 				ifa, err := newAddr(ifi, m)
    133 				if err != nil {
    134 					return nil, err
    135 				}
    136 				if ifa != nil {
    137 					ifat = append(ifat, ifa)
    138 				}
    139 			}
    140 		}
    141 	}
    142 	return ifat, nil
    143 }
    144 
    145 func newAddr(ifi *Interface, m *syscall.InterfaceAddrMessage) (*IPNet, error) {
    146 	sas, err := syscall.ParseRoutingSockaddr(m)
    147 	if err != nil {
    148 		return nil, os.NewSyscallError("parseroutingsockaddr", err)
    149 	}
    150 	ifa := &IPNet{}
    151 	switch sa := sas[syscall.RTAX_NETMASK].(type) {
    152 	case *syscall.SockaddrInet4:
    153 		ifa.Mask = IPv4Mask(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3])
    154 	case *syscall.SockaddrInet6:
    155 		ifa.Mask = make(IPMask, IPv6len)
    156 		copy(ifa.Mask, sa.Addr[:])
    157 	}
    158 	switch sa := sas[syscall.RTAX_IFA].(type) {
    159 	case *syscall.SockaddrInet4:
    160 		ifa.IP = IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3])
    161 	case *syscall.SockaddrInet6:
    162 		ifa.IP = make(IP, IPv6len)
    163 		copy(ifa.IP, sa.Addr[:])
    164 		// NOTE: KAME based IPv6 protcol stack usually embeds
    165 		// the interface index in the interface-local or
    166 		// link-local address as the kernel-internal form.
    167 		if ifa.IP.IsLinkLocalUnicast() {
    168 			ifa.IP[2], ifa.IP[3] = 0, 0
    169 		}
    170 	}
    171 	if ifa.IP == nil || ifa.Mask == nil {
    172 		return nil, nil // Sockaddrs contain syscall.SockaddrDatalink on NetBSD
    173 	}
    174 	return ifa, nil
    175 }
    176