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  }