gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/packetimpact/testbench/testbench.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 testbench has utilities to send and receive packets, and also command 16 // the DUT to run POSIX functions. It is the packetimpact test API. 17 package testbench 18 19 import ( 20 "encoding/json" 21 "flag" 22 "fmt" 23 "math/rand" 24 "net" 25 "testing" 26 "time" 27 ) 28 29 var ( 30 // Native indicates that the test is being run natively. 31 Native = false 32 // RPCKeepalive is the gRPC keepalive. 33 RPCKeepalive = 10 * time.Second 34 35 // dutInfosJSON is the json string that describes information about all the 36 // duts available to use. 37 dutInfosJSON string 38 // dutInfo is the pool among which the testbench can choose a DUT to work 39 // with. 40 dutInfo chan *DUTInfo 41 ) 42 43 // DUTInfo has both network and uname information about the DUT. 44 type DUTInfo struct { 45 Uname *DUTUname 46 Net *DUTTestNet 47 } 48 49 // DUTUname contains information about the DUT from uname. 50 type DUTUname struct { 51 Machine string 52 KernelName string 53 KernelRelease string 54 KernelVersion string 55 OperatingSystem string 56 } 57 58 // IsLinux returns true if the DUT is running Linux. 59 func (n *DUTUname) IsLinux() bool { 60 return Native && n.OperatingSystem == "GNU/Linux" 61 } 62 63 // IsGvisor returns true if the DUT is running gVisor. 64 func (*DUTUname) IsGvisor() bool { 65 return !Native 66 } 67 68 // IsFuchsia returns true if the DUT is running Fuchsia. 69 func (n *DUTUname) IsFuchsia() bool { 70 return Native && n.OperatingSystem == "Fuchsia" 71 } 72 73 // DUTTestNet describes the test network setup on dut and how the testbench 74 // should connect with an existing DUT. 75 type DUTTestNet struct { 76 // LocalMAC is the local MAC address on the test network. 77 LocalMAC net.HardwareAddr 78 // RemoteMAC is the DUT's MAC address on the test network. 79 RemoteMAC net.HardwareAddr 80 // LocalIPv4 is the local IPv4 address on the test network. 81 LocalIPv4 net.IP 82 // RemoteIPv4 is the DUT's IPv4 address on the test network. 83 RemoteIPv4 net.IP 84 // IPv4PrefixLength is the network prefix length of the IPv4 test network. 85 IPv4PrefixLength int 86 // LocalIPv6 is the local IPv6 address on the test network. 87 LocalIPv6 net.IP 88 // RemoteIPv6 is the DUT's IPv6 address on the test network. 89 RemoteIPv6 net.IP 90 // LocalDevID is the ID of the local interface on the test network. 91 LocalDevID uint32 92 // RemoteDevID is the ID of the remote interface on the test network. 93 RemoteDevID uint32 94 // LocalDevName is the device that testbench uses to inject traffic. 95 LocalDevName string 96 // RemoteDevName is the device name on the DUT, individual tests can 97 // use the name to construct tests. 98 RemoteDevName string 99 100 // The following two fields on actually on the control network instead 101 // of the test network, including them for convenience. 102 103 // POSIXServerIP is the POSIX server's IP address on the control network. 104 POSIXServerIP net.IP 105 // POSIXServerPort is the UDP port the POSIX server is bound to on the 106 // control network. 107 POSIXServerPort uint16 108 } 109 110 // SubnetBroadcast returns the test network's subnet broadcast address. 111 func (n *DUTTestNet) SubnetBroadcast() net.IP { 112 addr := append([]byte(nil), n.RemoteIPv4...) 113 mask := net.CIDRMask(n.IPv4PrefixLength, net.IPv4len*8) 114 for i := range addr { 115 addr[i] |= ^mask[i] 116 } 117 return addr 118 } 119 120 // registerFlags defines flags and associates them with the package-level 121 // exported variables above. It should be called by tests in their init 122 // functions. 123 func registerFlags(fs *flag.FlagSet) { 124 fs.BoolVar(&Native, "native", Native, "whether the test is running natively") 125 fs.DurationVar(&RPCKeepalive, "rpc_keepalive", RPCKeepalive, "gRPC keepalive") 126 fs.StringVar(&dutInfosJSON, "dut_infos_json", dutInfosJSON, "json that describes the DUTs") 127 } 128 129 // Initialize initializes the testbench, it parse the flags and sets up the 130 // pool of test networks for testbench's later use. 131 func Initialize(fs *flag.FlagSet) { 132 testing.Init() 133 registerFlags(fs) 134 flag.Parse() 135 if err := loadDUTInfos(); err != nil { 136 panic(err) 137 } 138 } 139 140 // loadDUTInfos loads available DUT test infos from the json file, it 141 // must be called after flag.Parse(). 142 func loadDUTInfos() error { 143 var dutInfos []DUTInfo 144 if err := json.Unmarshal([]byte(dutInfosJSON), &dutInfos); err != nil { 145 return fmt.Errorf("failed to unmarshal JSON: %w", err) 146 } 147 if got, want := len(dutInfos), 1; got < want { 148 return fmt.Errorf("got %d DUTs, the test requires at least %d DUTs", got, want) 149 } 150 // Using a buffered channel as semaphore 151 dutInfo = make(chan *DUTInfo, len(dutInfos)) 152 for i := range dutInfos { 153 dutInfos[i].Net.LocalIPv4 = dutInfos[i].Net.LocalIPv4.To4() 154 dutInfos[i].Net.RemoteIPv4 = dutInfos[i].Net.RemoteIPv4.To4() 155 dutInfo <- &dutInfos[i] 156 } 157 return nil 158 } 159 160 // GenerateRandomPayload generates a random byte slice of the specified length, 161 // causing a fatal test failure if it is unable to do so. 162 func GenerateRandomPayload(t *testing.T, n int) []byte { 163 t.Helper() 164 buf := make([]byte, n) 165 if _, err := rand.Read(buf); err != nil { 166 t.Fatalf("rand.Read(buf) failed: %s", err) 167 } 168 return buf 169 } 170 171 // getDUTInfo returns information about an available DUT from the pool. If no 172 // DUT is readily available, getDUTInfo blocks until one becomes available. 173 func getDUTInfo() *DUTInfo { 174 return <-dutInfo 175 } 176 177 // release returns the DUTInfo back to the pool. 178 func (info *DUTInfo) release() { 179 dutInfo <- info 180 }