github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/packetimpact/netdevs/netdevs.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package netdevs contains utilities for working with network devices. 16 package netdevs 17 18 import ( 19 "fmt" 20 "net" 21 "regexp" 22 "strconv" 23 "strings" 24 25 "github.com/SagerNet/gvisor/pkg/tcpip" 26 "github.com/SagerNet/gvisor/pkg/tcpip/header" 27 ) 28 29 // A DeviceInfo represents a network device. 30 type DeviceInfo struct { 31 ID uint32 32 MAC net.HardwareAddr 33 IPv4Addr net.IP 34 IPv4Net *net.IPNet 35 IPv6Addr net.IP 36 IPv6Net *net.IPNet 37 } 38 39 var ( 40 deviceLine = regexp.MustCompile(`^\s*(\d+): (\w+)`) 41 linkLine = regexp.MustCompile(`^\s*link/\w+ ([0-9a-fA-F:]+)`) 42 inetLine = regexp.MustCompile(`^\s*inet ([0-9./]+)`) 43 inet6Line = regexp.MustCompile(`^\s*inet6 ([0-9a-fA-F:/]+)`) 44 ) 45 46 // ParseDevicesWithRegex will parse the output with the given regexps to produce 47 // a map from device name to device information. It is assumed that deviceLine 48 // contains both a name and an ID. 49 func ParseDevicesWithRegex(cmdOutput string, deviceLine, linkLine, inetLine, inet6Line *regexp.Regexp) (map[string]DeviceInfo, error) { 50 var currentDevice string 51 var currentInfo DeviceInfo 52 deviceInfos := make(map[string]DeviceInfo) 53 for _, line := range strings.Split(cmdOutput, "\n") { 54 if m := deviceLine.FindStringSubmatch(line); m != nil { 55 if currentDevice != "" { 56 deviceInfos[currentDevice] = currentInfo 57 } 58 id, err := strconv.ParseUint(m[1], 10, 32) 59 if err != nil { 60 return nil, fmt.Errorf("parsing device ID %s: %w", m[1], err) 61 } 62 currentInfo = DeviceInfo{ID: uint32(id)} 63 currentDevice = m[2] 64 } else if m := linkLine.FindStringSubmatch(line); m != nil { 65 mac, err := net.ParseMAC(m[1]) 66 if err != nil { 67 return nil, err 68 } 69 currentInfo.MAC = mac 70 } else if m := inetLine.FindStringSubmatch(line); m != nil { 71 ipv4Addr, ipv4Net, err := net.ParseCIDR(m[1]) 72 if err != nil { 73 return nil, err 74 } 75 currentInfo.IPv4Addr = ipv4Addr 76 currentInfo.IPv4Net = ipv4Net 77 } else if m := inet6Line.FindStringSubmatch(line); m != nil { 78 ipv6Addr, ipv6Net, err := net.ParseCIDR(m[1]) 79 if err != nil { 80 return nil, err 81 } 82 currentInfo.IPv6Addr = ipv6Addr 83 currentInfo.IPv6Net = ipv6Net 84 } 85 } 86 if currentDevice != "" { 87 deviceInfos[currentDevice] = currentInfo 88 } 89 return deviceInfos, nil 90 } 91 92 // ParseDevices parses the output from `ip addr show` into a map from device 93 // name to information about the device. 94 // 95 // Note: if multiple IPv6 addresses are assigned to a device, the last address 96 // displayed by `ip addr show` will be used. This is fine for packetimpact 97 // because we will always only have at most one IPv6 address assigned to each 98 // device. 99 func ParseDevices(cmdOutput string) (map[string]DeviceInfo, error) { 100 return ParseDevicesWithRegex(cmdOutput, deviceLine, linkLine, inetLine, inet6Line) 101 } 102 103 // MACToIP converts the MAC address to an IPv6 link local address as described 104 // in RFC 4291 page 20: https://tools.ietf.org/html/rfc4291#page-20 105 func MACToIP(mac net.HardwareAddr) net.IP { 106 addr := make([]byte, header.IPv6AddressSize) 107 addr[0] = 0xfe 108 addr[1] = 0x80 109 header.EthernetAdddressToModifiedEUI64IntoBuf(tcpip.LinkAddress(mac), addr[8:]) 110 return net.IP(addr) 111 } 112 113 // FindDeviceByIP finds a DeviceInfo and device name from an IP address in the 114 // output of ParseDevices. 115 func FindDeviceByIP(ip net.IP, devices map[string]DeviceInfo) (string, DeviceInfo, error) { 116 for dev, info := range devices { 117 if info.IPv4Addr.Equal(ip) { 118 return dev, info, nil 119 } 120 } 121 return "", DeviceInfo{}, fmt.Errorf("can't find %s on any interface", ip) 122 }