mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Internal change.
PiperOrigin-RevId: 432024122
This commit is contained in:
@@ -1,23 +0,0 @@
|
||||
load("//tools:defs.bzl", "go_library", "go_test")
|
||||
|
||||
package(
|
||||
licenses = ["notice"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "netdevs",
|
||||
srcs = ["netdevs.go"],
|
||||
visibility = ["//test/packetimpact:__subpackages__"],
|
||||
deps = [
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/header",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "netdevs_test",
|
||||
size = "small",
|
||||
srcs = ["netdevs_test.go"],
|
||||
library = ":netdevs",
|
||||
deps = ["@com_github_google_go_cmp//cmp:go_default_library"],
|
||||
)
|
||||
@@ -1,122 +0,0 @@
|
||||
// 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"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
|
||||
// A DeviceInfo represents a network device.
|
||||
type DeviceInfo struct {
|
||||
ID uint32
|
||||
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-F:/]+)`)
|
||||
)
|
||||
|
||||
// ParseDevicesWithRegex will parse the output with the given regexps to produce
|
||||
// a map from device name to device information. It is assumed that deviceLine
|
||||
// contains both a name and an ID.
|
||||
func ParseDevicesWithRegex(cmdOutput string, deviceLine, linkLine, inetLine, inet6Line *regexp.Regexp) (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
|
||||
}
|
||||
id, err := strconv.ParseUint(m[1], 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing device ID %s: %w", m[1], err)
|
||||
}
|
||||
currentInfo = DeviceInfo{ID: uint32(id)}
|
||||
currentDevice = m[2]
|
||||
} 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
|
||||
}
|
||||
|
||||
// ParseDevices parses the output from `ip addr show` into a map from device
|
||||
// name to information about the device.
|
||||
//
|
||||
// Note: if multiple IPv6 addresses are assigned to a device, the last address
|
||||
// displayed by `ip addr show` will be used. This is fine for packetimpact
|
||||
// because we will always only have at most one IPv6 address assigned to each
|
||||
// device.
|
||||
func ParseDevices(cmdOutput string) (map[string]DeviceInfo, error) {
|
||||
return ParseDevicesWithRegex(cmdOutput, deviceLine, linkLine, inetLine, inet6Line)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func mustParseMAC(s string) net.HardwareAddr {
|
||||
mac, err := net.ParseMAC(s)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to parse test MAC %q: %s", s, err))
|
||||
}
|
||||
return mac
|
||||
}
|
||||
|
||||
func TestParseDevices(t *testing.T) {
|
||||
for _, v := range []struct {
|
||||
desc string
|
||||
cmdOutput string
|
||||
want map[string]DeviceInfo
|
||||
}{
|
||||
{
|
||||
desc: "v4 and v6",
|
||||
cmdOutput: `
|
||||
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||
inet 127.0.0.1/8 scope host lo
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 ::1/128 scope host
|
||||
valid_lft forever preferred_lft forever
|
||||
2613: eth0@if2614: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:c0:a8:09:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
|
||||
inet 192.168.9.2/24 brd 192.168.9.255 scope global eth0
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:c0ff:fea8:902/64 scope link tentative
|
||||
valid_lft forever preferred_lft forever
|
||||
2615: eth2@if2616: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:df:f5:e1:0a brd ff:ff:ff:ff:ff:ff link-netnsid 0
|
||||
inet 223.245.225.10/24 brd 223.245.225.255 scope global eth2
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:dfff:fef5:e10a/64 scope link tentative
|
||||
valid_lft forever preferred_lft forever
|
||||
2617: eth1@if2618: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:da:33:13:0a brd ff:ff:ff:ff:ff:ff link-netnsid 0
|
||||
inet 218.51.19.10/24 brd 218.51.19.255 scope global eth1
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:daff:fe33:130a/64 scope link tentative
|
||||
valid_lft forever preferred_lft forever`,
|
||||
want: map[string]DeviceInfo{
|
||||
"lo": {
|
||||
ID: 1,
|
||||
MAC: mustParseMAC("00:00:00:00:00:00"),
|
||||
IPv4Addr: net.IPv4(127, 0, 0, 1),
|
||||
IPv4Net: &net.IPNet{
|
||||
IP: net.IPv4(127, 0, 0, 0),
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
},
|
||||
IPv6Addr: net.ParseIP("::1"),
|
||||
IPv6Net: &net.IPNet{
|
||||
IP: net.ParseIP("::1"),
|
||||
Mask: net.CIDRMask(128, 128),
|
||||
},
|
||||
},
|
||||
"eth0": {
|
||||
ID: 2613,
|
||||
MAC: mustParseMAC("02:42:c0:a8:09:02"),
|
||||
IPv4Addr: net.IPv4(192, 168, 9, 2),
|
||||
IPv4Net: &net.IPNet{
|
||||
IP: net.IPv4(192, 168, 9, 0),
|
||||
Mask: net.CIDRMask(24, 32),
|
||||
},
|
||||
IPv6Addr: net.ParseIP("fe80::42:c0ff:fea8:902"),
|
||||
IPv6Net: &net.IPNet{
|
||||
IP: net.ParseIP("fe80::"),
|
||||
Mask: net.CIDRMask(64, 128),
|
||||
},
|
||||
},
|
||||
"eth1": {
|
||||
ID: 2617,
|
||||
MAC: mustParseMAC("02:42:da:33:13:0a"),
|
||||
IPv4Addr: net.IPv4(218, 51, 19, 10),
|
||||
IPv4Net: &net.IPNet{
|
||||
IP: net.IPv4(218, 51, 19, 0),
|
||||
Mask: net.CIDRMask(24, 32),
|
||||
},
|
||||
IPv6Addr: net.ParseIP("fe80::42:daff:fe33:130a"),
|
||||
IPv6Net: &net.IPNet{
|
||||
IP: net.ParseIP("fe80::"),
|
||||
Mask: net.CIDRMask(64, 128),
|
||||
},
|
||||
},
|
||||
"eth2": {
|
||||
ID: 2615,
|
||||
MAC: mustParseMAC("02:42:df:f5:e1:0a"),
|
||||
IPv4Addr: net.IPv4(223, 245, 225, 10),
|
||||
IPv4Net: &net.IPNet{
|
||||
IP: net.IPv4(223, 245, 225, 0),
|
||||
Mask: net.CIDRMask(24, 32),
|
||||
},
|
||||
IPv6Addr: net.ParseIP("fe80::42:dfff:fef5:e10a"),
|
||||
IPv6Net: &net.IPNet{
|
||||
IP: net.ParseIP("fe80::"),
|
||||
Mask: net.CIDRMask(64, 128),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "v4 only",
|
||||
cmdOutput: `
|
||||
2613: eth0@if2614: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:c0:a8:09:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
|
||||
inet 192.168.9.2/24 brd 192.168.9.255 scope global eth0
|
||||
valid_lft forever preferred_lft forever`,
|
||||
want: map[string]DeviceInfo{
|
||||
"eth0": {
|
||||
ID: 2613,
|
||||
MAC: mustParseMAC("02:42:c0:a8:09:02"),
|
||||
IPv4Addr: net.IPv4(192, 168, 9, 2),
|
||||
IPv4Net: &net.IPNet{
|
||||
IP: net.IPv4(192, 168, 9, 0),
|
||||
Mask: net.CIDRMask(24, 32),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "v6 only",
|
||||
cmdOutput: `
|
||||
2615: eth2@if2616: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:df:f5:e1:0a brd ff:ff:ff:ff:ff:ff link-netnsid 0
|
||||
inet6 fe80::42:dfff:fef5:e10a/64 scope link tentative
|
||||
valid_lft forever preferred_lft forever`,
|
||||
want: map[string]DeviceInfo{
|
||||
"eth2": {
|
||||
ID: 2615,
|
||||
MAC: mustParseMAC("02:42:df:f5:e1:0a"),
|
||||
IPv6Addr: net.ParseIP("fe80::42:dfff:fef5:e10a"),
|
||||
IPv6Net: &net.IPNet{
|
||||
IP: net.ParseIP("fe80::"),
|
||||
Mask: net.CIDRMask(64, 128),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(v.desc, func(t *testing.T) {
|
||||
got, err := ParseDevices(v.cmdOutput)
|
||||
if err != nil {
|
||||
t.Errorf("ParseDevices(\n%s\n) got unexpected error: %s", v.cmdOutput, err)
|
||||
}
|
||||
if diff := cmp.Diff(v.want, got); diff != "" {
|
||||
t.Errorf("ParseDevices(\n%s\n) got output diff (-want, +got):\n%s", v.cmdOutput, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseDevicesErrors(t *testing.T) {
|
||||
for _, v := range []struct {
|
||||
desc string
|
||||
cmdOutput string
|
||||
}{
|
||||
{
|
||||
desc: "invalid MAC addr",
|
||||
cmdOutput: `
|
||||
2617: eth1@if2618: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:da:33:13:0a:ffffffff brd ff:ff:ff:ff:ff:ff link-netnsid 0
|
||||
inet 218.51.19.10/24 brd 218.51.19.255 scope global eth1
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:daff:fe33:130a/64 scope link tentative
|
||||
valid_lft forever preferred_lft forever`,
|
||||
},
|
||||
{
|
||||
desc: "invalid v4 addr",
|
||||
cmdOutput: `
|
||||
2617: eth1@if2618: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:da:33:13:0a brd ff:ff:ff:ff:ff:ff link-netnsid 0
|
||||
inet 1234.4321.424242.0/24 brd 218.51.19.255 scope global eth1
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:daff:fe33:130a/64 scope link tentative
|
||||
valid_lft forever preferred_lft forever`,
|
||||
},
|
||||
{
|
||||
desc: "invalid v6 addr",
|
||||
cmdOutput: `
|
||||
2617: eth1@if2618: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:da:33:13:0a brd ff:ff:ff:ff:ff:ff link-netnsid 0
|
||||
inet 218.51.19.10/24 brd 218.51.19.255 scope global eth1
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80:ffffffff::42:daff:fe33:130a/64 scope link tentative
|
||||
valid_lft forever preferred_lft forever`,
|
||||
},
|
||||
{
|
||||
desc: "invalid CIDR missing prefixlen",
|
||||
cmdOutput: `
|
||||
2617: eth1@if2618: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
|
||||
link/ether 02:42:da:33:13:0a brd ff:ff:ff:ff:ff:ff link-netnsid 0
|
||||
inet 218.51.19.10 brd 218.51.19.255 scope global eth1
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::42:daff:fe33:130a scope link tentative
|
||||
valid_lft forever preferred_lft forever`,
|
||||
},
|
||||
} {
|
||||
t.Run(v.desc, func(t *testing.T) {
|
||||
if _, err := ParseDevices(v.cmdOutput); err == nil {
|
||||
t.Errorf("ParseDevices(\n%s\n) succeeded unexpectedly, want error", v.cmdOutput)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user