1
0
mirror of https://github.com/golang/go synced 2024-11-19 07:44:49 -07:00
go/src/net/interface_linux.go
Paul Marks a5dec3859a net: make multi-IP resolution more flexible.
Remove the "netaddr" type, which ambiguously represented either one
address, or a list of addresses. Instead, use "addrList" wherever
multiple addresses are supported.

The "first" method returns the first address matching some condition
(e.g. "is it IPv4?"), primarily to support legacy code that can't handle
multiple addresses.

The "partition" method splits an addrList into two categories, as
defined by some strategy function. This is useful for implementing
Happy Eyeballs, and similar two-channel algorithms.

Finally, internetAddrList (formerly resolveInternetAddr) no longer
mangles the ordering defined by getaddrinfo. In the future, this may
be used by a sequential Dial implementation.

Updates #8453, #8455.

Change-Id: I7375f4c34481580ab40e31d33002a4073a0474f3
Reviewed-on: https://go-review.googlesource.com/8360
Reviewed-by: Mikio Hara <mikioh.mikioh@gmail.com>
Run-TryBot: Mikio Hara <mikioh.mikioh@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-04-10 09:03:25 +00:00

272 lines
7.0 KiB
Go

// 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.
package net
import (
"os"
"syscall"
"unsafe"
)
// If the ifindex is zero, interfaceTable returns mappings of all
// network interfaces. Otherwise it returns a mapping of a specific
// interface.
func interfaceTable(ifindex int) ([]Interface, error) {
tab, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC)
if err != nil {
return nil, os.NewSyscallError("netlink rib", err)
}
msgs, err := syscall.ParseNetlinkMessage(tab)
if err != nil {
return nil, os.NewSyscallError("netlink message", err)
}
var ift []Interface
loop:
for _, m := range msgs {
switch m.Header.Type {
case syscall.NLMSG_DONE:
break loop
case syscall.RTM_NEWLINK:
ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0]))
if ifindex == 0 || ifindex == int(ifim.Index) {
attrs, err := syscall.ParseNetlinkRouteAttr(&m)
if err != nil {
return nil, os.NewSyscallError("netlink routeattr", err)
}
ift = append(ift, *newLink(ifim, attrs))
if ifindex == int(ifim.Index) {
break loop
}
}
}
}
return ift, nil
}
const (
// See linux/if_arp.h.
// Note that Linux doesn't support IPv4 over IPv6 tunneling.
sysARPHardwareIPv4IPv4 = 768 // IPv4 over IPv4 tunneling
sysARPHardwareIPv6IPv6 = 769 // IPv6 over IPv6 tunneling
sysARPHardwareIPv6IPv4 = 776 // IPv6 over IPv4 tunneling
sysARPHardwareGREIPv4 = 778 // any over GRE over IPv4 tunneling
sysARPHardwareGREIPv6 = 823 // any over GRE over IPv6 tunneling
)
func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) *Interface {
ifi := &Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)}
for _, a := range attrs {
switch a.Attr.Type {
case syscall.IFLA_ADDRESS:
// We never return any /32 or /128 IP address
// prefix on any IP tunnel interface as the
// hardware address.
switch len(a.Value) {
case IPv4len:
switch ifim.Type {
case sysARPHardwareIPv4IPv4, sysARPHardwareGREIPv4, sysARPHardwareIPv6IPv4:
continue
}
case IPv6len:
switch ifim.Type {
case sysARPHardwareIPv6IPv6, sysARPHardwareGREIPv6:
continue
}
}
var nonzero bool
for _, b := range a.Value {
if b != 0 {
nonzero = true
break
}
}
if nonzero {
ifi.HardwareAddr = a.Value[:]
}
case syscall.IFLA_IFNAME:
ifi.Name = string(a.Value[:len(a.Value)-1])
case syscall.IFLA_MTU:
ifi.MTU = int(*(*uint32)(unsafe.Pointer(&a.Value[:4][0])))
}
}
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 ifi is nil, interfaceAddrTable returns addresses for all
// network interfaces. Otherwise it returns addresses for a specific
// interface.
func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
if err != nil {
return nil, os.NewSyscallError("netlink rib", err)
}
msgs, err := syscall.ParseNetlinkMessage(tab)
if err != nil {
return nil, os.NewSyscallError("netlink message", err)
}
var ift []Interface
if ifi == nil {
var err error
ift, err = interfaceTable(0)
if err != nil {
return nil, err
}
}
ifat, err := addrTable(ift, ifi, msgs)
if err != nil {
return nil, err
}
return ifat, nil
}
func addrTable(ift []Interface, ifi *Interface, msgs []syscall.NetlinkMessage) ([]Addr, error) {
var ifat []Addr
loop:
for _, m := range msgs {
switch m.Header.Type {
case syscall.NLMSG_DONE:
break loop
case syscall.RTM_NEWADDR:
ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
if len(ift) != 0 || ifi.Index == int(ifam.Index) {
if len(ift) != 0 {
var err error
ifi, err = interfaceByIndex(ift, int(ifam.Index))
if err != nil {
return nil, err
}
}
attrs, err := syscall.ParseNetlinkRouteAttr(&m)
if err != nil {
return nil, os.NewSyscallError("netlink routeattr", err)
}
ifa := newAddr(ifi, ifam, attrs)
if ifa != nil {
ifat = append(ifat, ifa)
}
}
}
}
return ifat, nil
}
func newAddr(ifi *Interface, ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRouteAttr) Addr {
var ipPointToPoint bool
// Seems like we need to make sure whether the IP interface
// stack consists of IP point-to-point numbered or unnumbered
// addressing over point-to-point link encapsulation.
if ifi.Flags&FlagPointToPoint != 0 {
for _, a := range attrs {
if a.Attr.Type == syscall.IFA_LOCAL {
ipPointToPoint = true
break
}
}
}
for _, a := range attrs {
if ipPointToPoint && a.Attr.Type == syscall.IFA_ADDRESS || !ipPointToPoint && a.Attr.Type == syscall.IFA_LOCAL {
continue
}
switch ifam.Family {
case syscall.AF_INET:
return &IPNet{IP: IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv4len)}
case syscall.AF_INET6:
ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv6len)}
copy(ifa.IP, a.Value[:])
return ifa
}
}
return nil
}
// interfaceMulticastAddrTable returns addresses for a specific
// interface.
func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi)
ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi)
return append(ifmat4, ifmat6...), nil
}
func parseProcNetIGMP(path string, ifi *Interface) []Addr {
fd, err := open(path)
if err != nil {
return nil
}
defer fd.close()
var (
ifmat []Addr
name string
)
fd.readLine() // skip first line
b := make([]byte, IPv4len)
for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
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:
if ifi == nil || name == ifi.Name {
// The Linux kernel puts the IP
// address in /proc/net/igmp in native
// endianness.
for i := 0; i+1 < len(f[0]); i += 2 {
b[i/2], _ = xtoi2(f[0][i:i+2], 0)
}
i := *(*uint32)(unsafe.Pointer(&b[:4][0]))
ifma := &IPAddr{IP: IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))}
ifmat = append(ifmat, ifma)
}
}
}
return ifmat
}
func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
fd, err := open(path)
if err != nil {
return nil
}
defer fd.close()
var ifmat []Addr
b := make([]byte, IPv6len)
for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
f := splitAtBytes(l, " \r\t\n")
if len(f) < 6 {
continue
}
if ifi == nil || f[1] == ifi.Name {
for i := 0; i+1 < len(f[2]); i += 2 {
b[i/2], _ = xtoi2(f[2][i:i+2], 0)
}
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)
}
}
return ifmat
}