105 lines
3.1 KiB
Go
105 lines
3.1 KiB
Go
|
// Copyright 2020 The gVisor Authors.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
// Package netdevs contains utilities for working with network devices.
|
||
|
package netdevs
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"net"
|
||
|
"regexp"
|
||
|
"strings"
|
||
|
|
||
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||
|
)
|
||
|
|
||
|
// A DeviceInfo represents a network device.
|
||
|
type DeviceInfo struct {
|
||
|
MAC net.HardwareAddr
|
||
|
IPv4Addr net.IP
|
||
|
IPv4Net *net.IPNet
|
||
|
IPv6Addr net.IP
|
||
|
IPv6Net *net.IPNet
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
deviceLine = regexp.MustCompile(`^\s*\d+: (\w+)`)
|
||
|
linkLine = regexp.MustCompile(`^\s*link/\w+ ([0-9a-fA-F:]+)`)
|
||
|
inetLine = regexp.MustCompile(`^\s*inet ([0-9./]+)`)
|
||
|
inet6Line = regexp.MustCompile(`^\s*inet6 ([0-9a-fA-Z:/]+)`)
|
||
|
)
|
||
|
|
||
|
// ParseDevices parses the output from `ip addr show` into a map from device
|
||
|
// name to information about the device.
|
||
|
func ParseDevices(cmdOutput string) (map[string]DeviceInfo, error) {
|
||
|
var currentDevice string
|
||
|
var currentInfo DeviceInfo
|
||
|
deviceInfos := make(map[string]DeviceInfo)
|
||
|
for _, line := range strings.Split(cmdOutput, "\n") {
|
||
|
if m := deviceLine.FindStringSubmatch(line); m != nil {
|
||
|
if currentDevice != "" {
|
||
|
deviceInfos[currentDevice] = currentInfo
|
||
|
}
|
||
|
currentInfo = DeviceInfo{}
|
||
|
currentDevice = m[1]
|
||
|
} else if m := linkLine.FindStringSubmatch(line); m != nil {
|
||
|
mac, err := net.ParseMAC(m[1])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
currentInfo.MAC = mac
|
||
|
} else if m := inetLine.FindStringSubmatch(line); m != nil {
|
||
|
ipv4Addr, ipv4Net, err := net.ParseCIDR(m[1])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
currentInfo.IPv4Addr = ipv4Addr
|
||
|
currentInfo.IPv4Net = ipv4Net
|
||
|
} else if m := inet6Line.FindStringSubmatch(line); m != nil {
|
||
|
ipv6Addr, ipv6Net, err := net.ParseCIDR(m[1])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
currentInfo.IPv6Addr = ipv6Addr
|
||
|
currentInfo.IPv6Net = ipv6Net
|
||
|
}
|
||
|
}
|
||
|
if currentDevice != "" {
|
||
|
deviceInfos[currentDevice] = currentInfo
|
||
|
}
|
||
|
return deviceInfos, nil
|
||
|
}
|
||
|
|
||
|
// MACToIP converts the MAC address to an IPv6 link local address as described
|
||
|
// in RFC 4291 page 20: https://tools.ietf.org/html/rfc4291#page-20
|
||
|
func MACToIP(mac net.HardwareAddr) net.IP {
|
||
|
addr := make([]byte, header.IPv6AddressSize)
|
||
|
addr[0] = 0xfe
|
||
|
addr[1] = 0x80
|
||
|
header.EthernetAdddressToModifiedEUI64IntoBuf(tcpip.LinkAddress(mac), addr[8:])
|
||
|
return net.IP(addr)
|
||
|
}
|
||
|
|
||
|
// FindDeviceByIP finds a DeviceInfo and device name from an IP address in the
|
||
|
// output of ParseDevices.
|
||
|
func FindDeviceByIP(ip net.IP, devices map[string]DeviceInfo) (string, DeviceInfo, error) {
|
||
|
for dev, info := range devices {
|
||
|
if info.IPv4Addr.Equal(ip) {
|
||
|
return dev, info, nil
|
||
|
}
|
||
|
}
|
||
|
return "", DeviceInfo{}, fmt.Errorf("can't find %s on any interface", ip)
|
||
|
}
|