interface_linux.go 5.71 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 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.

// Network interface identification for Linux

package net

import (
	"os"
	"syscall"
	"unsafe"
)

// 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 20 21
	tab, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC)
	if err != nil {
		return nil, os.NewSyscallError("netlink rib", err)
22 23
	}

24 25 26
	msgs, err := syscall.ParseNetlinkMessage(tab)
	if err != nil {
		return nil, os.NewSyscallError("netlink message", err)
27 28
	}

29
	var ift []Interface
30 31 32 33 34 35 36
	for _, m := range msgs {
		switch m.Header.Type {
		case syscall.NLMSG_DONE:
			goto done
		case syscall.RTM_NEWLINK:
			ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0]))
			if ifindex == 0 || ifindex == int(ifim.Index) {
37 38 39
				attrs, err := syscall.ParseNetlinkRouteAttr(&m)
				if err != nil {
					return nil, os.NewSyscallError("netlink routeattr", err)
40
				}
41
				ifi := newLink(ifim, attrs)
42 43 44 45 46 47 48 49
				ift = append(ift, ifi)
			}
		}
	}
done:
	return ift, nil
}

50
func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) Interface {
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
	ifi := Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)}
	for _, a := range attrs {
		switch a.Attr.Type {
		case syscall.IFLA_ADDRESS:
			var nonzero bool
			for _, b := range a.Value {
				if b != 0 {
					nonzero = true
				}
			}
			if nonzero {
				ifi.HardwareAddr = a.Value[:]
			}
		case syscall.IFLA_IFNAME:
			ifi.Name = string(a.Value[:len(a.Value)-1])
		case syscall.IFLA_MTU:
67
			ifi.MTU = int(*(*uint32)(unsafe.Pointer(&a.Value[:4][0])))
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
		}
	}
	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 ifindex is zero, interfaceAddrTable returns addresses
// for all network interfaces.  Otherwise it returns addresses
// for a specific interface.
96
func interfaceAddrTable(ifindex int) ([]Addr, error) {
97 98 99
	tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
	if err != nil {
		return nil, os.NewSyscallError("netlink rib", err)
100 101
	}

102 103 104
	msgs, err := syscall.ParseNetlinkMessage(tab)
	if err != nil {
		return nil, os.NewSyscallError("netlink message", err)
105
	}
106

107 108 109
	ifat, err := addrTable(msgs, ifindex)
	if err != nil {
		return nil, err
110
	}
111
	return ifat, nil
112 113
}

114
func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, error) {
115 116 117 118 119 120 121 122
	var ifat []Addr
	for _, m := range msgs {
		switch m.Header.Type {
		case syscall.NLMSG_DONE:
			goto done
		case syscall.RTM_NEWADDR:
			ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
			if ifindex == 0 || ifindex == int(ifam.Index) {
123 124 125
				attrs, err := syscall.ParseNetlinkRouteAttr(&m)
				if err != nil {
					return nil, os.NewSyscallError("netlink routeattr", err)
126
				}
127
				ifat = append(ifat, newAddr(attrs, int(ifam.Family), int(ifam.Prefixlen)))
128 129 130 131 132 133 134
			}
		}
	}
done:
	return ifat, nil
}

135 136
func newAddr(attrs []syscall.NetlinkRouteAttr, family, pfxlen int) Addr {
	ifa := &IPNet{}
137 138 139 140 141
	for _, a := range attrs {
		switch a.Attr.Type {
		case syscall.IFA_ADDRESS:
			switch family {
			case syscall.AF_INET:
142 143
				ifa.IP = IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3])
				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 155 156
}

// If the ifindex is zero, interfaceMulticastAddrTable returns
// addresses for all network interfaces.  Otherwise it returns
// addresses for a specific interface.
157
func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
158
	var (
159
		err error
160
		ifi *Interface
161 162 163 164 165 166 167
	)
	if ifindex > 0 {
		ifi, err = InterfaceByIndex(ifindex)
		if err != nil {
			return nil, err
		}
	}
168 169
	ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi)
	ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi)
170 171 172
	return append(ifmat4, ifmat6...), nil
}

173 174
func parseProcNetIGMP(path string, ifi *Interface) []Addr {
	fd, err := open(path)
175 176 177 178 179
	if err != nil {
		return nil
	}
	defer fd.close()

180 181 182 183
	var (
		ifmat []Addr
		name  string
	)
184 185 186
	fd.readLine() // skip first line
	b := make([]byte, IPv4len)
	for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
187 188 189 190 191 192 193 194
		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:
195
			if ifi == nil || name == ifi.Name {
196 197 198
				// The Linux kernel puts the IP
				// address in /proc/net/igmp in native
				// endianness.
199 200 201
				for i := 0; i+1 < len(f[0]); i += 2 {
					b[i/2], _ = xtoi2(f[0][i:i+2], 0)
				}
202 203
				i := *(*uint32)(unsafe.Pointer(&b[:4][0]))
				ifma := IPAddr{IP: IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))}
204 205 206 207 208 209 210
				ifmat = append(ifmat, ifma.toAddr())
			}
		}
	}
	return ifmat
}

211 212
func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
	fd, err := open(path)
213 214 215 216 217
	if err != nil {
		return nil
	}
	defer fd.close()

218
	var ifmat []Addr
219 220
	b := make([]byte, IPv6len)
	for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
221 222 223 224
		f := splitAtBytes(l, " \r\t\n")
		if len(f) < 6 {
			continue
		}
225
		if ifi == nil || f[1] == ifi.Name {
226 227 228
			for i := 0; i+1 < len(f[2]); i += 2 {
				b[i/2], _ = xtoi2(f[2][i:i+2], 0)
			}
229 230 231 232 233 234
			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.toAddr())
		}
	}
	return ifmat
}