@echo "****************************"
@echo "Removing IP address files:"
@find . -type f -regextype egrep -regex '.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' -exec sudo rm -v {} \;
+ @find . -type f -name "fd00:0*" -exec sudo rm -v {} \;
@echo "****************************"
@echo "Removing network namespaces:"
@for ns in $$(ip netns list | grep $$(cat .last_hst_ppid) | awk '{print $$1}'); do \
func init() {
RegisterVethTests(EchoBuiltinTest, EchoBuiltinBandwidthTest)
RegisterSoloVethTests(TcpWithLossTest)
+ RegisterVeth6Tests(TcpWithLoss6Test)
}
func EchoBuiltinTest(s *VethsSuite) {
s.AssertNotEqual(len(output), 0)
s.AssertNotContains(output, "failed", output)
}
+
+func TcpWithLoss6Test(s *Veths6Suite) {
+ serverVpp := s.Containers.ServerVpp.VppInstance
+
+ serverVpp.Vppctl("test echo server uri tcp://%s/20022",
+ s.Interfaces.Server.Ip6AddressString())
+
+ clientVpp := s.Containers.ClientVpp.VppInstance
+
+ // Add loss of packets with Network Delay Simulator
+ clientVpp.Vppctl("set nsim poll-main-thread delay 0.01 ms bandwidth 40 gbit" +
+ " packet-size 1400 packets-per-drop 1000")
+
+ clientVpp.Vppctl("nsim output-feature enable-disable host-" + s.Interfaces.Server.Name())
+
+ // Do echo test from client-vpp container
+ output := clientVpp.Vppctl("test echo client uri tcp://%s/20022 verbose echo-bytes bytes 50m",
+ s.Interfaces.Server.Ip6AddressString())
+ s.Log(output)
+ s.AssertNotEqual(len(output), 0)
+ s.AssertNotContains(output, "failed", output)
+}
RegisterNoTopoSoloTests(HttpStaticPromTest, HttpGetTpsTest, HttpGetTpsInterruptModeTest, PromConcurrentConnectionsTest,
PromMemLeakTest, HttpClientPostMemLeakTest, HttpInvalidClientRequestMemLeakTest, HttpPostTpsTest, HttpPostTpsInterruptModeTest,
PromConsecutiveConnectionsTest, HttpGetTpsTlsTest, HttpPostTpsTlsTest, HttpClientGetRepeatMTTest, HttpClientPtrGetRepeatMTTest)
+ RegisterNoTopo6Tests(HttpClientGetResponseBody6Test, HttpClientGetTlsResponseBody6Test)
}
const wwwRootPath = "/tmp/www_root"
s.AssertContains(file_contents, response)
}
+func HttpClientGetResponseBody6Test(s *NoTopo6Suite) {
+ response := "<body>hello world</body>"
+ size := len(response)
+ httpClientGet6(s, response, size, "http")
+}
+
+func HttpClientGetTlsResponseBody6Test(s *NoTopo6Suite) {
+ response := "<body>hello world</body>"
+ size := len(response)
+ httpClientGet6(s, response, size, "https")
+}
+
+func httpClientGet6(s *NoTopo6Suite, response string, size int, proto string) {
+ var l net.Listener
+ var err error
+ var port string
+
+ vpp := s.Containers.Vpp.VppInstance
+ server := ghttp.NewUnstartedServer()
+ serverAddress := "[" + s.HostAddr() + "]"
+
+ if proto == "https" {
+ certFile := "resources/cert/localhost.crt"
+ keyFile := "resources/cert/localhost.key"
+ cer, err := tls.LoadX509KeyPair(certFile, keyFile)
+ s.AssertNil(err)
+ tlsConfig := &tls.Config{Certificates: []tls.Certificate{cer}}
+ server.HTTPTestServer.TLS = tlsConfig
+ port = "443"
+ l, err = tls.Listen("tcp", serverAddress+":443", tlsConfig)
+ } else {
+ port = "80"
+ l, err = net.Listen("tcp", serverAddress+":80")
+ }
+ s.AssertNil(err, fmt.Sprint(err))
+
+ server.HTTPTestServer.Listener = l
+ server.AppendHandlers(
+ ghttp.CombineHandlers(
+ s.LogHttpReq(false),
+ ghttp.VerifyRequest("GET", "/"),
+ ghttp.VerifyHeaderKV("Hello", "World"),
+ ghttp.VerifyHeaderKV("Test-H2", "Test-K2"),
+ ghttp.RespondWith(http.StatusOK, string(response), http.Header{"Content-Length": {strconv.Itoa(size)}}),
+ ))
+ server.Start()
+ defer server.Close()
+
+ uri := proto + "://" + serverAddress + ":" + port + "/"
+ cmd := "http client use-ptr verbose header Hello:World header Test-H2:Test-K2 save-to response.txt uri " + uri
+
+ o := vpp.Vppctl(cmd)
+ s.Log(o)
+ s.AssertContains(o, "200 OK")
+ s.AssertContains(o, response)
+ s.AssertContains(o, "Content-Length: "+strconv.Itoa(size))
+
+ file_contents, err := vpp.Container.Exec(false, "cat /tmp/response.txt")
+ s.AssertNil(err)
+ s.AssertContains(file_contents, response)
+}
+
func HttpClientGetRepeatMTTest(s *NoTopoSuite) {
httpClientRepeat(s, "", "sessions 2")
}
// recreate interfaces with RX-queues
s.AssertNil(vpp.DeleteTap(s.Interfaces.Tap))
- s.AssertNil(vpp.CreateTap(s.Interfaces.Tap, 2, 2))
+ s.AssertNil(vpp.CreateTap(s.Interfaces.Tap, false, 2, 2))
s.CreateNginxServer()
s.AssertNil(s.Containers.NginxServer.Start())
// If an address is not in use, 'counter' is then copied to 'chosenOctet' and it is used for the remaining tests.
// Also checks host IP addresses.
func (a *Ip4AddressAllocator) createIpAddress(networkNumber int, numberOfAddresses int) (string, error) {
- hostIps, _ := exechelper.CombinedOutput("ip a")
+ hostIps, _ := exechelper.CombinedOutput("ip -4 a")
counter := 10
var address string
ip4AddrAllocator.AddNetwork(0)
return ip4AddrAllocator
}
+
+type Ip6AddressAllocator struct {
+ networks map[int]AddressCounter
+ chosenSegment string
+ assignedIps []string
+}
+
+func (a *Ip6AddressAllocator) AddNetwork(networkNumber int) {
+ a.networks[networkNumber] = 1
+}
+
+func (a *Ip6AddressAllocator) NewIp6InterfaceAddress(inputNetworkNumber ...int) (string, error) {
+ var networkNumber int = 0
+ if len(inputNetworkNumber) > 0 {
+ networkNumber = inputNetworkNumber[0]
+ }
+
+ if _, ok := a.networks[networkNumber]; !ok {
+ a.AddNetwork(networkNumber)
+ }
+
+ numberOfAddresses := a.networks[networkNumber]
+
+ if numberOfAddresses == 65535 {
+ return "", fmt.Errorf("no available IPv6 addresses")
+ }
+
+ address, err := a.createIpAddress(networkNumber, numberOfAddresses)
+
+ a.networks[networkNumber] = numberOfAddresses + 1
+
+ return address + "/64", err
+}
+
+func (a *Ip6AddressAllocator) createIpAddress(networkNumber int, numberOfAddresses int) (string, error) {
+ hostIps, _ := exechelper.CombinedOutput("ip -6 a")
+ counter := 0xAAAA
+ var address string
+
+ for {
+ if a.chosenSegment != "" {
+ address = fmt.Sprintf("fd00:0:%s:%x::%x", a.chosenSegment, networkNumber, numberOfAddresses)
+ file, err := os.Create(address)
+ if err != nil {
+ return "", errors.New("unable to create file: " + fmt.Sprint(err))
+ }
+ file.Close()
+ break
+ } else {
+ address = fmt.Sprintf("fd00:0:%x:%x::%x", counter, networkNumber, numberOfAddresses)
+ _, err := os.Stat(address)
+ if err == nil || strings.Contains(string(hostIps), address) {
+ counter++
+ } else if os.IsNotExist(err) {
+ file, err := os.Create(address)
+ if err != nil {
+ return "", errors.New("unable to create file: " + fmt.Sprint(err))
+ }
+ file.Close()
+ a.chosenSegment = fmt.Sprintf("%x", counter)
+ break
+ } else {
+ return "", errors.New("an error occurred while checking if a file exists: " + fmt.Sprint(err))
+ }
+ }
+ }
+
+ a.assignedIps = append(a.assignedIps, address)
+ return address, nil
+}
+
+func (a *Ip6AddressAllocator) DeleteIpAddresses() {
+ for ip := range a.assignedIps {
+ os.Remove(a.assignedIps[ip])
+ }
+}
+
+func NewIp6AddressAllocator() *Ip6AddressAllocator {
+ var ip6AddrAllocator = new(Ip6AddressAllocator)
+ ip6AddrAllocator.networks = make(map[int]AddressCounter)
+ ip6AddrAllocator.AddNetwork(0)
+ return ip6AddrAllocator
+}
NetConfigs []NetConfig
NetInterfaces map[string]*NetInterface
Ip4AddrAllocator *Ip4AddressAllocator
+ Ip6AddrAllocator *Ip6AddressAllocator
TestIds map[string]string
CpuAllocator *CpuAllocatorT
CpuContexts []*CpuContext
s.Ip4AddrAllocator.DeleteIpAddresses()
}
+ if s.Ip6AddrAllocator != nil {
+ s.Ip6AddrAllocator.DeleteIpAddresses()
+ }
+
if coreDump {
Fail("VPP crashed")
}
Fail("unmarshal error: " + fmt.Sprint(err))
}
+ s.Ip6AddrAllocator = NewIp6AddressAllocator()
s.Ip4AddrAllocator = NewIp4AddressAllocator()
s.NetInterfaces = make(map[string]*NetInterface)
for _, elem := range yamlTopo.Devices {
+ if _, ok := elem["ipv6"]; ok {
+ elem["ipv6"] = elem["ipv6"].(bool)
+ } else {
+ elem["ipv6"] = false
+ }
if _, ok := elem["name"]; ok {
elem["name"] = s.ProcessIndex + elem["name"].(string) + s.Ppid
}
}
case Veth, Tap:
{
- if netIf, err := newNetworkInterface(elem, s.Ip4AddrAllocator); err == nil {
- s.NetConfigs = append(s.NetConfigs, netIf)
- s.NetInterfaces[netIf.Name()] = netIf
+ if elem["ipv6"].(bool) {
+ if netIf, err := newNetworkInterface6(elem, s.Ip6AddrAllocator); err == nil {
+ s.NetConfigs = append(s.NetConfigs, netIf)
+ s.NetInterfaces[netIf.Name()] = netIf
+ } else {
+ Fail("network config error: " + fmt.Sprint(err))
+ }
} else {
- Fail("network config error: " + fmt.Sprint(err))
+ if netIf, err := newNetworkInterface(elem, s.Ip4AddrAllocator); err == nil {
+ s.NetConfigs = append(s.NetConfigs, netIf)
+ s.NetInterfaces[netIf.Name()] = netIf
+ } else {
+ Fail("network config error: " + fmt.Sprint(err))
+ }
}
+
}
case Bridge:
{
MacAddress = ethernet_types.MacAddress
AddressWithPrefix = ip_types.AddressWithPrefix
IP4AddressWithPrefix = ip_types.IP4AddressWithPrefix
+ IP6AddressWithPrefix = ip_types.IP6AddressWithPrefix
InterfaceIndex = interface_types.InterfaceIndex
NetConfig interface {
NetConfigBase
Ip4AddrAllocator *Ip4AddressAllocator
Ip4Address string
+ Ip6AddrAllocator *Ip6AddressAllocator
+ Ip6Address string
Index InterfaceIndex
HwAddress MacAddress
NetworkNamespace string
return newInterface, nil
}
+func newNetworkInterface6(cfg NetDevConfig, a *Ip6AddressAllocator) (*NetInterface, error) {
+ var newInterface *NetInterface = &NetInterface{}
+ var err error
+ newInterface.Ip6AddrAllocator = a
+ newInterface.name = cfg["name"].(string)
+ newInterface.NetworkNumber = DEFAULT_NETWORK_NUM
+
+ if interfaceType, ok := cfg["type"]; ok {
+ newInterface.category = interfaceType.(string)
+ }
+
+ if presetHwAddress, ok := cfg["preset-hw-address"]; ok {
+ newInterface.HwAddress, err = ethernet_types.ParseMacAddress(presetHwAddress.(string))
+ if err != nil {
+ return &NetInterface{}, err
+ }
+ }
+
+ if netns, ok := cfg["netns"]; ok {
+ newInterface.NetworkNamespace = netns.(string)
+ }
+
+ if ip, ok := cfg["ip6"]; ok {
+ if n, ok := ip.(NetDevConfig)["network"]; ok {
+ newInterface.NetworkNumber = n.(int)
+ }
+ newInterface.Ip6Address, err = newInterface.Ip6AddrAllocator.NewIp6InterfaceAddress(
+ newInterface.NetworkNumber,
+ )
+ if err != nil {
+ return &NetInterface{}, err
+ }
+ }
+
+ if _, ok := cfg["peer"]; !ok {
+ return newInterface, nil
+ }
+
+ peer := cfg["peer"].(NetDevConfig)
+
+ if newInterface.Peer, err = newNetworkInterface6(peer, a); err != nil {
+ return &NetInterface{}, err
+ }
+
+ return newInterface, nil
+}
+
func (n *NetInterface) configureUpState() error {
err := setDevUp(n.Name(), "")
if err != nil {
return n.category
}
-func (n *NetInterface) AddressWithPrefix() AddressWithPrefix {
- address, _ := ip_types.ParseAddressWithPrefix(n.Ip4Address)
+func (n *NetInterface) AddressWithPrefix(IPv6 bool) AddressWithPrefix {
+ var address ip_types.AddressWithPrefix
+ if IPv6 {
+ address, _ = ip_types.ParseAddressWithPrefix(n.Ip6Address)
+ } else {
+ address, _ = ip_types.ParseAddressWithPrefix(n.Ip4Address)
+ }
return address
}
return Ip4AddressWithPrefix
}
+func (n *NetInterface) Ip6AddressWithPrefix() IP6AddressWithPrefix {
+ ip6Prefix, _ := ip_types.ParseIP6Prefix(n.Ip6Address)
+ Ip6AddressWithPrefix := ip_types.IP6AddressWithPrefix(ip6Prefix)
+ return Ip6AddressWithPrefix
+}
+
func (n *NetInterface) Ip4AddressString() string {
return strings.Split(n.Ip4Address, "/")[0]
}
+func (n *NetInterface) Ip6AddressString() string {
+ return strings.Split(n.Ip6Address, "/")[0]
+}
+
func (b *NetConfigBase) Name() string {
return b.name
}
s.AssertNil(vpp.Start())
// wait for VPP to start
time.Sleep(time.Second * 1)
- s.AssertNil(vpp.CreateTap(s.Interfaces.Client, 1, 1))
- s.AssertNil(vpp.CreateTap(s.Interfaces.Server, 1, 2))
+ s.AssertNil(vpp.CreateTap(s.Interfaces.Client, false, 1, 1))
+ s.AssertNil(vpp.CreateTap(s.Interfaces.Server, false, 1, 2))
s.Containers.Vpp.Exec(false, "chmod 777 -R %s", s.Containers.Vpp.GetContainerWorkDir())
// Add Ipv4 ARP entry for nginx HTTP server, otherwise first request fail (HTTP error 503)
vpp, _ := s.Containers.Vpp.newVppInstance(s.Containers.Vpp.AllocatedCpus, memoryConfig, sessionConfig)
s.AssertNil(vpp.Start())
- s.AssertNil(vpp.CreateTap(s.Interfaces.Tap, 1, 1), "failed to create tap interface")
+ s.AssertNil(vpp.CreateTap(s.Interfaces.Tap, false, 1, 1), "failed to create tap interface")
if *DryRun {
s.LogStartedContainers()
serverVpp := serverContainer.VppInstance
s.AssertNil(serverVpp.Start())
- idx, err := serverVpp.createAfPacket(s.Interfaces.Server)
+ idx, err := serverVpp.createAfPacket(s.Interfaces.Server, false)
s.AssertNil(err, fmt.Sprint(err))
s.AssertNotEqual(0, idx)
}
clientVpp := clientContainer.VppInstance
s.AssertNil(clientVpp.Start())
- idx, err := clientVpp.createAfPacket(s.Interfaces.Client)
+ idx, err := clientVpp.createAfPacket(s.Interfaces.Client, false)
s.AssertNil(err, fmt.Sprint(err))
s.AssertNotEqual(0, idx)
}
)
s.AssertNil(vpp.Start())
- s.AssertNil(vpp.CreateTap(s.Interfaces.Client, 1, 1))
- s.AssertNil(vpp.CreateTap(s.Interfaces.Server, 1, 2))
+ s.AssertNil(vpp.CreateTap(s.Interfaces.Client, false, 1, 1))
+ s.AssertNil(vpp.CreateTap(s.Interfaces.Server, false, 1, 2))
if *DryRun {
s.LogStartedContainers()
vpp, _ := s.Containers.Vpp.newVppInstance(s.Containers.Vpp.AllocatedCpus, sessionConfig)
s.AssertNil(vpp.Start())
- s.AssertNil(vpp.CreateTap(s.Interfaces.Tap, 1, 1), "failed to create tap interface")
+ s.AssertNil(vpp.CreateTap(s.Interfaces.Tap, false, 1, 1), "failed to create tap interface")
if *DryRun {
s.LogStartedContainers()
--- /dev/null
+package hst
+
+import (
+ "fmt"
+ "reflect"
+ "runtime"
+ "strings"
+
+ . "github.com/onsi/ginkgo/v2"
+)
+
+var noTopo6Tests = map[string][]func(s *NoTopo6Suite){}
+var noTopo6SoloTests = map[string][]func(s *NoTopo6Suite){}
+
+type NoTopo6Suite struct {
+ HstSuite
+ Interfaces struct {
+ Tap *NetInterface
+ }
+ Containers struct {
+ Vpp *Container
+ Nginx *Container
+ NginxHttp3 *Container
+ NginxServer *Container
+ Wrk *Container
+ Curl *Container
+ Ab *Container
+ }
+ NginxServerPort string
+}
+
+func RegisterNoTopo6Tests(tests ...func(s *NoTopo6Suite)) {
+ noTopo6Tests[getTestFilename()] = tests
+}
+func RegisterNoTopo6SoloTests(tests ...func(s *NoTopo6Suite)) {
+ noTopo6SoloTests[getTestFilename()] = tests
+}
+
+func (s *NoTopo6Suite) SetupSuite() {
+ s.HstSuite.SetupSuite()
+ s.LoadNetworkTopology("tap6")
+ s.LoadContainerTopology("single")
+ s.Interfaces.Tap = s.GetInterfaceByName("htaphost")
+ s.Containers.Vpp = s.GetContainerByName("vpp")
+ s.Containers.Nginx = s.GetContainerByName("nginx")
+ s.Containers.NginxHttp3 = s.GetContainerByName("nginx-http3")
+ s.Containers.NginxServer = s.GetTransientContainerByName("nginx-server")
+ s.Containers.Wrk = s.GetContainerByName("wrk")
+ s.Containers.Curl = s.GetContainerByName("curl")
+ s.Containers.Ab = s.GetContainerByName("ab")
+}
+
+func (s *NoTopo6Suite) SetupTest() {
+ s.HstSuite.SetupTest()
+
+ // Setup test conditions
+ var sessionConfig Stanza
+ sessionConfig.
+ NewStanza("session").
+ Append("enable").
+ Append("use-app-socket-api")
+
+ if strings.Contains(CurrentSpecReport().LeafNodeText, "InterruptMode") {
+ sessionConfig.Append("use-private-rx-mqs").Close()
+ s.Log("**********************INTERRUPT MODE**********************")
+ } else {
+ sessionConfig.Close()
+ }
+
+ vpp, _ := s.Containers.Vpp.newVppInstance(s.Containers.Vpp.AllocatedCpus, sessionConfig)
+
+ s.AssertNil(vpp.Start())
+ s.AssertNil(vpp.CreateTap(s.Interfaces.Tap, true, 1, 1), "failed to create tap interface")
+
+ if *DryRun {
+ s.LogStartedContainers()
+ s.Skip("Dry run mode = true")
+ }
+}
+
+func (s *NoTopo6Suite) TearDownTest() {
+ if CurrentSpecReport().Failed() {
+ s.CollectNginxLogs(s.Containers.NginxHttp3)
+ }
+ s.HstSuite.TearDownTest()
+}
+
+func (s *NoTopo6Suite) CreateNginxConfig(container *Container, multiThreadWorkers bool) {
+ var workers uint8
+ if multiThreadWorkers {
+ workers = 2
+ } else {
+ workers = 1
+ }
+ values := struct {
+ Workers uint8
+ }{
+ Workers: workers,
+ }
+ container.CreateConfigFromTemplate(
+ "/nginx.conf",
+ "./resources/nginx/nginx.conf",
+ values,
+ )
+}
+
+// Creates container and config.
+func (s *NoTopo6Suite) CreateNginxServer() {
+ s.AssertNil(s.Containers.NginxServer.Create())
+ s.NginxServerPort = s.GetPortFromPpid()
+ nginxSettings := struct {
+ LogPrefix string
+ Address string
+ Port string
+ Timeout int
+ }{
+ LogPrefix: s.Containers.NginxServer.Name,
+ Address: "[" + s.Interfaces.Tap.Ip6AddressString() + "]",
+ Port: s.NginxServerPort,
+ Timeout: 600,
+ }
+ s.Containers.NginxServer.CreateConfigFromTemplate(
+ "/nginx.conf",
+ "./resources/nginx/nginx_server.conf",
+ nginxSettings,
+ )
+}
+
+func (s *NoTopo6Suite) AddNginxVclConfig(multiThreadWorkers bool) {
+ vclFileName := s.Containers.Nginx.GetHostWorkDir() + "/vcl.conf"
+ appSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/default",
+ s.Containers.Nginx.GetContainerWorkDir())
+
+ var vclConf Stanza
+ vclConf.
+ NewStanza("vcl").
+ Append("heapsize 64M").
+ Append("rx-fifo-size 4000000").
+ Append("tx-fifo-size 4000000").
+ Append("segment-size 4000000000").
+ Append("add-segment-size 4000000000").
+ Append("event-queue-size 100000").
+ Append("use-mq-eventfd").
+ Append(appSocketApi)
+ if multiThreadWorkers {
+ vclConf.Append("multi-thread-workers")
+ }
+
+ err := vclConf.Close().SaveToFile(vclFileName)
+ s.AssertNil(err, fmt.Sprint(err))
+}
+
+func (s *NoTopo6Suite) VppAddr() string {
+ return s.Interfaces.Tap.Peer.Ip6AddressString()
+}
+
+func (s *NoTopo6Suite) VppIfName() string {
+ return s.Interfaces.Tap.Peer.Name()
+}
+
+func (s *NoTopo6Suite) HostAddr() string {
+ return s.Interfaces.Tap.Ip6AddressString()
+}
+
+func (s *NoTopo6Suite) CreateNginxHttp3Config(container *Container) {
+ nginxSettings := struct {
+ LogPrefix string
+ }{
+ LogPrefix: container.Name,
+ }
+ container.CreateConfigFromTemplate(
+ "/nginx.conf",
+ "./resources/nginx/nginx_http3.conf",
+ nginxSettings,
+ )
+}
+
+var _ = Describe("NoTopo6Suite", Ordered, ContinueOnFailure, Label("IPv6"), func() {
+ var s NoTopo6Suite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range noTopo6Tests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(TestTimeout))
+ }
+ }
+})
+
+var _ = Describe("NoTopo6SuiteSolo", Ordered, ContinueOnFailure, Serial, Label("IPv6"), func() {
+ var s NoTopo6Suite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ for filename, tests := range noTopo6SoloTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(TestTimeout))
+ }
+ }
+})
serverVpp := s.Containers.ServerVpp.VppInstance
s.AssertNil(serverVpp.Start())
- idx, err := serverVpp.createAfPacket(s.Interfaces.Server)
+ idx, err := serverVpp.createAfPacket(s.Interfaces.Server, false)
s.AssertNil(err, fmt.Sprint(err))
s.AssertNotEqual(0, idx)
}
clientVpp := s.GetContainerByName("client-vpp").VppInstance
s.AssertNil(clientVpp.Start())
- idx, err := clientVpp.createAfPacket(s.Interfaces.Client)
+ idx, err := clientVpp.createAfPacket(s.Interfaces.Client, false)
s.AssertNil(err, fmt.Sprint(err))
s.AssertNotEqual(0, idx)
}
--- /dev/null
+package hst
+
+import (
+ "fmt"
+ "reflect"
+ "runtime"
+ "strings"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+)
+
+var veth6Tests = map[string][]func(s *Veths6Suite){}
+var veth6SoloTests = map[string][]func(s *Veths6Suite){}
+
+type Veths6Suite struct {
+ HstSuite
+ Interfaces struct {
+ Server *NetInterface
+ Client *NetInterface
+ }
+ Containers struct {
+ ServerVpp *Container
+ ClientVpp *Container
+ ServerApp *Container
+ ClientApp *Container
+ }
+}
+
+func RegisterVeth6Tests(tests ...func(s *Veths6Suite)) {
+ veth6Tests[getTestFilename()] = tests
+}
+func RegisterSoloVeth6Tests(tests ...func(s *Veths6Suite)) {
+ veth6SoloTests[getTestFilename()] = tests
+}
+
+func (s *Veths6Suite) SetupSuite() {
+ time.Sleep(1 * time.Second)
+ s.HstSuite.SetupSuite()
+ s.ConfigureNetworkTopology("2peerVeth6")
+ s.LoadContainerTopology("2peerVeth")
+ s.Interfaces.Client = s.GetInterfaceByName("cln")
+ s.Interfaces.Server = s.GetInterfaceByName("srv")
+ s.Containers.ServerVpp = s.GetContainerByName("server-vpp")
+ s.Containers.ClientVpp = s.GetContainerByName("client-vpp")
+ s.Containers.ServerApp = s.GetContainerByName("server-app")
+ s.Containers.ClientApp = s.GetContainerByName("client-app")
+}
+
+func (s *Veths6Suite) SetupTest() {
+ s.HstSuite.SetupTest()
+
+ // Setup test conditions
+ var sessionConfig Stanza
+ sessionConfig.
+ NewStanza("session").
+ Append("enable").
+ Append("use-app-socket-api")
+
+ if strings.Contains(CurrentSpecReport().LeafNodeText, "InterruptMode") {
+ sessionConfig.Append("use-private-rx-mqs").Close()
+ s.Log("**********************INTERRUPT MODE**********************")
+ } else {
+ sessionConfig.Close()
+ }
+
+ // ... For server
+ serverVpp, err := s.Containers.ServerVpp.newVppInstance(s.Containers.ServerVpp.AllocatedCpus, sessionConfig)
+ s.AssertNotNil(serverVpp, fmt.Sprint(err))
+
+ // ... For client
+ clientVpp, err := s.Containers.ClientVpp.newVppInstance(s.Containers.ClientVpp.AllocatedCpus, sessionConfig)
+ s.AssertNotNil(clientVpp, fmt.Sprint(err))
+
+ s.SetupServerVpp()
+ s.setupClientVpp()
+ if *DryRun {
+ s.LogStartedContainers()
+ s.Skip("Dry run mode = true")
+ }
+}
+
+func (s *Veths6Suite) SetupServerVpp() {
+ serverVpp := s.Containers.ServerVpp.VppInstance
+ s.AssertNil(serverVpp.Start())
+
+ idx, err := serverVpp.createAfPacket(s.Interfaces.Server, true)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertNotEqual(0, idx)
+}
+
+func (s *Veths6Suite) setupClientVpp() {
+ clientVpp := s.GetContainerByName("client-vpp").VppInstance
+ s.AssertNil(clientVpp.Start())
+
+ idx, err := clientVpp.createAfPacket(s.Interfaces.Client, true)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertNotEqual(0, idx)
+}
+
+var _ = Describe("Veths6Suite", Ordered, ContinueOnFailure, func() {
+ var s Veths6Suite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ // https://onsi.github.io/ginkgo/#dynamically-generating-specs
+ for filename, tests := range veth6Tests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(TestTimeout))
+ }
+ }
+})
+
+var _ = Describe("Veths6SuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
+ var s Veths6Suite
+ BeforeAll(func() {
+ s.SetupSuite()
+ })
+ BeforeEach(func() {
+ s.SetupTest()
+ })
+ AfterAll(func() {
+ s.TearDownSuite()
+ })
+ AfterEach(func() {
+ s.TearDownTest()
+ })
+
+ // https://onsi.github.io/ginkgo/#dynamically-generating-specs
+ for filename, tests := range veth6SoloTests {
+ for _, test := range tests {
+ test := test
+ pc := reflect.ValueOf(test).Pointer()
+ funcValue := runtime.FuncForPC(pc)
+ testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
+ It(testName, Label("SOLO"), func(ctx SpecContext) {
+ s.Log(testName + ": BEGIN")
+ test(&s)
+ }, SpecTimeout(TestTimeout))
+ }
+ }
+})
s.AssertNotNil(vpp, fmt.Sprint(err))
s.AssertNil(vpp.Start())
- s.AssertNil(vpp.CreateTap(s.Interfaces.Client, 1, 1))
- s.AssertNil(vpp.CreateTap(s.Interfaces.Server, 1, 2))
+ s.AssertNil(vpp.CreateTap(s.Interfaces.Client, false, 1, 1))
+ s.AssertNil(vpp.CreateTap(s.Interfaces.Server, false, 1, 2))
if *DryRun {
s.LogStartedContainers()
s.AssertNotNil(vpp, fmt.Sprint(err))
s.AssertNil(vpp.Start())
- s.AssertNil(vpp.CreateTap(s.Interfaces.Client, 1, 1))
- s.AssertNil(vpp.CreateTap(s.Interfaces.Server, 1, 2))
+ s.AssertNil(vpp.CreateTap(s.Interfaces.Client, false, 1, 1))
+ s.AssertNil(vpp.CreateTap(s.Interfaces.Server, false, 1, 2))
s.proxyPort = 8080
s.serverPort = 80
import (
"context"
"encoding/json"
+ "errors"
"fmt"
"io"
"net"
vpp.getSuite().AssertNil(1, "Timeout while waiting for app '%s'", appName)
}
-func (vpp *VppInstance) createAfPacket(
- veth *NetInterface,
-) (interface_types.InterfaceIndex, error) {
+func (vpp *VppInstance) createAfPacket(veth *NetInterface, IPv6 bool) (interface_types.InterfaceIndex, error) {
+ var ipAddress string
+ var err error
+
if *DryRun {
- if ip4Address, err := veth.Ip4AddrAllocator.NewIp4InterfaceAddress(veth.Peer.NetworkNumber); err == nil {
- veth.Ip4Address = ip4Address
+ if IPv6 {
+ if ipAddress, err = veth.Ip6AddrAllocator.NewIp6InterfaceAddress(veth.Peer.NetworkNumber); err == nil {
+ veth.Ip6Address = ipAddress
+ }
} else {
+ if ipAddress, err = veth.Ip4AddrAllocator.NewIp4InterfaceAddress(veth.Peer.NetworkNumber); err == nil {
+ veth.Ip4Address = ipAddress
+ }
+ }
+ if err != nil {
return 0, err
}
+
vppCliConfig := fmt.Sprintf(
"create host-interface name %s\n"+
"set int state host-%s up\n"+
"set int ip addr host-%s %s\n",
veth.Name(),
veth.Name(),
- veth.Name(), veth.Ip4Address)
+ veth.Name(), ipAddress)
vpp.AppendToCliConfig(vppCliConfig)
vpp.getSuite().Log("%s* Interface added:\n%s%s", Colors.grn, vppCliConfig, Colors.rst)
return 1, nil
}
+
createReq := &af_packet.AfPacketCreateV3{
Mode: 1,
UseRandomHwAddr: true,
}
// Add address
- if veth.AddressWithPrefix() == (AddressWithPrefix{}) {
- var err error
- var ip4Address string
- if ip4Address, err = veth.Ip4AddrAllocator.NewIp4InterfaceAddress(veth.Peer.NetworkNumber); err == nil {
- veth.Ip4Address = ip4Address
+ if veth.AddressWithPrefix(IPv6) == (AddressWithPrefix{}) {
+ if IPv6 {
+ if ipAddress, err = veth.Ip6AddrAllocator.NewIp6InterfaceAddress(veth.Peer.NetworkNumber); err == nil {
+ veth.Ip6Address = ipAddress
+ }
} else {
+ if ipAddress, err = veth.Ip4AddrAllocator.NewIp4InterfaceAddress(veth.Peer.NetworkNumber); err == nil {
+ veth.Ip4Address = ipAddress
+ }
+ }
+ if err != nil {
return 0, err
}
}
addressReq := &interfaces.SwInterfaceAddDelAddress{
IsAdd: true,
SwIfIndex: veth.Index,
- Prefix: veth.AddressWithPrefix(),
+ Prefix: veth.AddressWithPrefix(IPv6),
}
- vpp.getSuite().Log("af-packet interface " + veth.Name() + " add address " + veth.Ip4Address)
+ vpp.getSuite().Log("af-packet interface " + veth.Name() + " add address " + ipAddress)
if err := vpp.ApiStream.SendMsg(addressReq); err != nil {
return 0, err
}
return nil
}
-func (vpp *VppInstance) CreateTap(tap *NetInterface, numRxQueues uint16, tapId uint32, flags ...uint32) error {
+func (vpp *VppInstance) CreateTap(tap *NetInterface, IPv6 bool, numRxQueues uint16, tapId uint32, flags ...uint32) error {
var tapFlags uint32 = 0
+
if len(flags) > 0 {
tapFlags = flags[0]
}
if *DryRun {
flagsCli := ""
+ ipAddress := ""
+ ipAddressPeer := ""
+
if tapFlags == Consistent_qp {
flagsCli = "consistent-qp"
}
- vppCliConfig := fmt.Sprintf("create tap id %d host-if-name %s host-ip4-addr %s num-rx-queues %d %s\n"+
+
+ if IPv6 {
+ ipAddress = "host-ip6-addr " + tap.Ip6Address
+ ipAddressPeer = tap.Peer.Ip6Address
+ } else {
+ ipAddress = "host-ip4-addr " + tap.Ip4Address
+ ipAddressPeer = tap.Peer.Ip4Address
+ }
+
+ vppCliConfig := fmt.Sprintf("create tap id %d host-if-name %s %s num-rx-queues %d %s\n"+
"set int ip addr tap%d %s\n"+
"set int state tap%d up\n",
tapId,
tap.name,
- tap.Ip4Address,
+ ipAddress,
numRxQueues,
flagsCli,
tapId,
- tap.Peer.Ip4Address,
+ ipAddressPeer,
tapId,
)
vpp.AppendToCliConfig(vppCliConfig)
HostIfName: tap.Name(),
HostIP4PrefixSet: true,
HostIP4Prefix: tap.Ip4AddressWithPrefix(),
+ HostIP6PrefixSet: true,
+ HostIP6Prefix: tap.Ip6AddressWithPrefix(),
NumRxQueues: numRxQueues,
TapFlags: tapv2.TapFlags(tapFlags),
}
addAddressReq := &interfaces.SwInterfaceAddDelAddress{
IsAdd: true,
SwIfIndex: reply.SwIfIndex,
- Prefix: tap.Peer.AddressWithPrefix(),
+ Prefix: tap.Peer.AddressWithPrefix(IPv6),
}
vpp.getSuite().Log("tap interface " + tap.Name() + " add address " + tap.Peer.Ip4Address)
tap.HwAddress, _ = ethernet_types.ParseMacAddress(netIntf.HardwareAddr.String())
}
+ if IPv6 {
+ timeoutCounter := 1.0
+ for {
+ if timeoutCounter <= 5 {
+ vpp.getSuite().Log("Waiting for 'tentative' flag to disappear [%vs/5s]", timeoutCounter)
+ out, err := vpp.Container.Exec(false, "ip -6 addr show dev %s", tap.Name())
+ if err != nil {
+ vpp.getSuite().Log(out)
+ return err
+ }
+ if !strings.Contains(out, "tentative") {
+ break
+ }
+ time.Sleep(time.Millisecond * 500)
+ timeoutCounter += 0.5
+ } else {
+ return errors.New("tentative flag did not disappear in time")
+ }
+ }
+ }
+
return nil
}
RegisterNoTopoTests(NginxHttp3Test, NginxAsServerTest, NginxPerfCpsTest, NginxPerfRpsTest, NginxPerfWrkTest,
NginxPerfCpsInterruptModeTest, NginxPerfRpsInterruptModeTest, NginxPerfWrkInterruptModeTest)
RegisterNoTopoSoloTests(NginxPerfRpsMultiThreadTest, NginxPerfCpsMultiThreadTest)
+ RegisterNoTopo6Tests(NginxPerfRps6Test)
}
func NginxHttp3Test(s *NoTopoSuite) {
func NginxPerfWrkTest(s *NoTopoSuite) {
s.AssertNil(runNginxPerf(s, "", "wrk", false))
}
+
+func runNginxPerf6(s *NoTopo6Suite, mode, ab_or_wrk string, multiThreadWorkers bool) error {
+ nRequests := 1000000
+ nClients := 1000
+
+ serverAddress := "[" + s.VppAddr() + "]"
+ vpp := s.Containers.Vpp.VppInstance
+
+ s.Containers.Nginx.Create()
+ s.AddNginxVclConfig(multiThreadWorkers)
+ s.CreateNginxConfig(s.Containers.Nginx, multiThreadWorkers)
+ s.Containers.Nginx.Start()
+ vpp.WaitForApp("nginx-", 5)
+
+ if ab_or_wrk == "ab" {
+ args := fmt.Sprintf("-n %d -c %d", nRequests, nClients)
+ if mode == "rps" {
+ args += " -k"
+ } else if mode != "cps" {
+ return fmt.Errorf("invalid mode %s; expected cps/rps", mode)
+ }
+ // don't exit on socket receive errors
+ args += " -r"
+ args += " http://" + serverAddress + ":80/64B.json"
+ s.Containers.Ab.ExtraRunningArgs = args
+ s.Log("Test might take up to 2 minutes to finish. Please wait")
+ s.Containers.Ab.Run()
+ o, err := s.Containers.Ab.GetOutput()
+ rps := parseString(o, "Requests per second:")
+ s.Log(rps)
+ s.AssertContains(err, "Finished "+fmt.Sprint(nRequests))
+ } else {
+ args := fmt.Sprintf("-c %d -t 2 -d 30 http://%s:80/64B.json", nClients,
+ serverAddress)
+ s.Containers.Wrk.ExtraRunningArgs = args
+ s.Containers.Wrk.Run()
+ s.Log("Please wait for 30s, test is running.")
+ o, err := s.Containers.Wrk.GetOutput()
+ rps := parseString(o, "requests")
+ s.Log(rps)
+ s.Log(err)
+ s.AssertEmpty(err, "err: '%s', output: '%s'", err, o)
+ }
+ return nil
+}
+
+func NginxPerfRps6Test(s *NoTopo6Suite) {
+ s.AssertNil(runNginxPerf6(s, "rps", "ab", false))
+}
// tap interfaces are created on test setup with 1 rx-queue,
// need to recreate them with 2 + consistent-qp
s.AssertNil(vppProxy.DeleteTap(s.Interfaces.Server))
- s.AssertNil(vppProxy.CreateTap(s.Interfaces.Server, 2, uint32(s.Interfaces.Server.Peer.Index), Consistent_qp))
+ s.AssertNil(vppProxy.CreateTap(s.Interfaces.Server, false, 2, uint32(s.Interfaces.Server.Peer.Index), Consistent_qp))
s.AssertNil(vppProxy.DeleteTap(s.Interfaces.Client))
- s.AssertNil(vppProxy.CreateTap(s.Interfaces.Client, 2, uint32(s.Interfaces.Client.Peer.Index), Consistent_qp))
+ s.AssertNil(vppProxy.CreateTap(s.Interfaces.Client, false, 2, uint32(s.Interfaces.Client.Peer.Index), Consistent_qp))
configureVppProxy(s, "tcp", uint16(proxyPort))
if proto == "udp" {
// tap interfaces are created on test setup with 1 rx-queue,
// need to recreate them with 2 + consistent-qp
s.AssertNil(vppProxy.DeleteTap(s.Interfaces.Server))
- s.AssertNil(vppProxy.CreateTap(s.Interfaces.Server, 2, uint32(s.Interfaces.Server.Peer.Index), Consistent_qp))
+ s.AssertNil(vppProxy.CreateTap(s.Interfaces.Server, false, 2, uint32(s.Interfaces.Server.Peer.Index), Consistent_qp))
s.AssertNil(vppProxy.DeleteTap(s.Interfaces.Client))
- s.AssertNil(vppProxy.CreateTap(s.Interfaces.Client, 2, uint32(s.Interfaces.Client.Peer.Index), Consistent_qp))
+ s.AssertNil(vppProxy.CreateTap(s.Interfaces.Client, false, 2, uint32(s.Interfaces.Client.Peer.Index), Consistent_qp))
configureVppProxy(s, "http", proxyPort)
sendfile on;
server {
listen 80;
+ listen [::]:80;
root /usr/share/nginx;
index index.html index.htm;
location /return_ok
--- /dev/null
+---
+devices:
+ - name: "hsns"
+ type: "netns"
+
+ - name: "srv"
+ ipv6: true
+ type: "veth"
+ preset-hw-address: "00:00:5e:00:53:01"
+ peer:
+ name: "srv_veth"
+ netns: "hsns"
+
+ - name: "cln"
+ ipv6: true
+ type: "veth"
+ peer:
+ name: "cln_veth"
+ netns: "hsns"
+
+ - name: "br"
+ type: "bridge"
+ netns: "hsns"
+ interfaces:
+ - srv_veth
+ - cln_veth
+
--- /dev/null
+---
+devices:
+ - name: "htaphost"
+ type: "tap"
+ ipv6: true
+ ip6:
+ network: 1
+ peer:
+ name: ""
+ ip6:
+ network: 1