test: add the rtnetlink test suit

It is much easier to create complex configurations from bash scripts with help
of the iproute2 tools.

PiperOrigin-RevId: 646286415
This commit is contained in:
Andrei Vagin
2024-06-24 18:25:26 -07:00
committed by gVisor bot
parent 8ef3239b0b
commit c3fe152a48
10 changed files with 317 additions and 3 deletions
+1 -1
View File
@@ -242,7 +242,7 @@ network-tests: iptables-tests packetdrill-tests packetimpact-tests
.PHONY: network-tests
syscall-tests: $(RUNTIME_BIN) ## Run all system call tests.
@$(call test,--test_env=RUNTIME=$(RUNTIME_BIN) --cxxopt=-Werror $(PARTITIONS) test/syscalls/...)
@$(call test,--test_env=RUNTIME=$(RUNTIME_BIN) --cxxopt=-Werror $(PARTITIONS) test/syscalls/... test/rtnetlink/...)
.PHONY: syscall-tests
packetimpact-tests:
+2 -1
View File
@@ -8,7 +8,8 @@ RUN apt-get update && apt-get install -y curl gnupg2 git \
apt-transport-https ca-certificates gnupg-agent \
software-properties-common \
pkg-config libffi-dev patch diffutils libssl-dev iptables kmod \
clang crossbuild-essential-amd64 erofs-utils busybox-static libbpf-dev
clang crossbuild-essential-amd64 erofs-utils busybox-static libbpf-dev \
iproute2 netcat
# This package is needed to build eBPF on amd64, but not on arm64 where it
# doesn't exist.
+24
View File
@@ -0,0 +1,24 @@
load("//test/runner:defs.bzl", "syscall_test")
package(
default_applicable_licenses = ["//:license"],
licenses = ["notice"],
)
syscall_test(
size = "small",
container = True,
overlay = True,
save = False,
test = "//test/rtnetlink/linux:veth_test",
use_tmpfs = True,
)
syscall_test(
size = "small",
container = True,
overlay = True,
save = False,
test = "//test/rtnetlink/linux:bridge_test",
use_tmpfs = True,
)
+40
View File
@@ -0,0 +1,40 @@
load("//tools:defs.bzl", "go_binary")
package(
default_applicable_licenses = ["//:license"],
default_visibility = [
"//:sandbox",
],
licenses = ["notice"],
)
sh_library(
name = "rtnetlink_test",
srcs = ["rtnetlink_test.sh"],
)
sh_binary(
name = "veth_test",
srcs = ["veth_test.sh"],
deps = [":rtnetlink_test"],
)
sh_binary(
name = "bridge_test",
srcs = ["bridge_test.sh"],
data = [
":tcp_serv",
],
deps = [
":rtnetlink_test",
],
)
go_binary(
name = "tcp_serv",
srcs = ["tcp_serv.go"],
pure = True,
deps = [
"@org_golang_x_sys//unix:go_default_library",
],
)
+59
View File
@@ -0,0 +1,59 @@
#!/bin/bash
# Copyright 2024 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.
set -xeo pipefail
source "$(dirname "$0")/rtnetlink_test.sh"
TCP_SRV="$(dirname "$0")/tcp_serv"
if [[ ! -f "$TCP_SRV" ]]; then
TCP_SRV="$(dirname "$0")/tcp_serv_/tcp_serv"
fi
ip netns attach rootns "$$"
ip link add br0 type bridge
ip netns add test0
ip netns add test1
ip link add hveth0 type veth peer name veth0 netns test0
ip link add hveth1 type veth peer name veth1 netns test1
ip link set up dev hveth0
ip link set up dev hveth1
ip link set hveth0 master br0
ip link set hveth1 master br0
ip addr add 192.168.0.3/24 dev br0
ip link set up dev br0
ip netns exec test0 ip link set up dev veth0
ip netns exec test1 ip link set up dev veth1
ip netns exec test0 ip addr add 192.168.0.1/24 dev veth0
ip netns exec test1 ip addr add 192.168.0.2/24 dev veth1
check_connectivity test1 192.168.0.2 8800 test0 "ping from test0"
check_connectivity test1 192.168.0.2 8801 rootns "ping from rootns"
# Destroy namespaces and the bridge.
ip netns del test0
ip link del br0
ip netns del test1
if ! wait_for ! ip link show hveth0 2>/dev/null; then
fail "hveth0 hasn't been destroyed"
fi
if ! wait_for ! ip link show hveth1 2>/dev/null; then
fail "hveth1 hasn't been destroyed"
fi
if ! wait_for ! ip link show br0 2>/dev/null; then
fail "br0 hasn't been destroyed"
fi
+80
View File
@@ -0,0 +1,80 @@
#!/bin/bash
# Copyright 2024 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.
set -xeo pipefail
fail() {
echo "FAIL: $*"
exit 1
}
# Don't report any test cases.
echo "$@" | grep list_tests && exit 1
if [[ -z "$TEST_ON_GVISOR" ]]; then
if [[ ! -d /var/run ]]; then
echo "SKIP: /var/run doesn't exist but it is required for the ip tool."
exit 0
fi
mount -t tmpfs test /var/run
else
mkdir -p /var/run
mount -t tmpfs test /var/run
fi
# check_connectivity checks that a TCP connection can be established between two
# specified namespaces.
# Arguments:
# * network namespace where a server will be started
# * IP address in the server network namespace.
# * port for a test TCP connection
# * network namespace where a client will be started
# * test message that will be send from server to client
check_connectivity() {
local srv_netns="$1"
local srv_ip="$2"
local port="$3"
local clt_netns="$4"
local test_msg="$5"
# Create a sync pipe that will be closed when TCP_SRV creates a listen socket.
exec {pipe}<> <(:)
exec {pipe_r}</proc/self/fd/$pipe
exec {pipe_w}>/proc/self/fd/$pipe
exec {pipe}>&-
echo "$test_msg" | ip netns exec "$srv_netns" "$TCP_SRV" "--port=$port" "--sync-fd=$pipe_w" 1>&2 &
pid=$!
exec {pipe_w}>&-
cat <&$pipe_r
exec {pipe_r}>&-
# The server has been started.
out=$(ip netns exec "$clt_netns" nc -nvd "$srv_ip" "$port")
if [[ "$out" != "$test_msg" ]]; then
fail "unexpected output '$out' (expected '$test_msg'"
fi
wait "$pid"
}
wait_for() {
for _ in $(seq 50); do
if eval "$@"; then
return 0
fi
sleep 0.1
done
return 1
}
+65
View File
@@ -0,0 +1,65 @@
// Copyright 2024 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.
// tcp_serv creates a TCP server socket, reads data from stdin, and sends that
// data to the socket. The same thing can be done with the nc tool, but
// tcp_serv can signal when a tcp socket is created.
package main
import (
"flag"
"fmt"
"io"
"log"
"net"
"os"
"golang.org/x/sys/unix"
)
func main() {
port := flag.Int("port", 8888, "port to listen on")
syncFD := flag.Int("sync-fd", -1, "file descriptor that will be closed after creating a socket")
flag.Parse()
address := fmt.Sprintf(":%d", *port)
listener, err := net.Listen("tcp", address)
if err != nil {
log.Fatalf("Error creating listener: %s", err)
return
}
defer listener.Close()
if *syncFD >= 0 {
unix.Close(*syncFD)
}
conn, err := listener.Accept()
if err != nil {
log.Fatalf("Error accepting connection: %s", err)
}
defer conn.Close()
data := make([]byte, 1024)
for {
count, err := os.Stdin.Read(data)
if err == io.EOF {
break
}
_, err = conn.Write(data[:count])
if err != nil {
log.Fatalf("Error sending data: %s", err)
}
}
}
+41
View File
@@ -0,0 +1,41 @@
#!/bin/bash
# Copyright 2024 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.
set -xeo pipefail
source "$(dirname "$0")/rtnetlink_test.sh"
# Create a new veth pair in the current namespace.
ip link add name test_veth01 type veth peer name test_veth02
ip a
ip a | grep test_veth01
ip a | grep test_veth02
ip link del name test_veth01
ip a
# Check that test_veth02 has been destroyed.
if ! wait_for ! ip link show test_veth02; then
fail "test_veth02 hasn't been destroyed"
exit 1
fi
ip netns add test
ip link add test_veth01 type veth peer name test_veth02 netns test
ip link show test_veth01
ip netns exec test ip link show test_veth02
ip netns del test
if ! wait_for ! ip link show test_veth01; then
fail "test_veth01 hasn't been destroyed"
fi
+4
View File
@@ -211,6 +211,7 @@ def syscall_test_variants(
save_resume = False,
size = "medium",
timeout = None,
overlay = False,
**kwargs):
"""Generates syscall tests for all variants.
@@ -258,6 +259,7 @@ def syscall_test_variants(
save_resume = save_resume,
size = size,
timeout = timeout,
overlay = overlay,
**kwargs
)
@@ -367,6 +369,7 @@ def syscall_test(
tags = None,
save = True,
size = "medium",
overlay = False,
**kwargs):
"""syscall_test is a macro that will create targets for all platforms.
@@ -430,6 +433,7 @@ def syscall_test(
False, # save, generate all tests without save variant.
False, # save_resume, generate all tests without save_resume variant.
size,
overlay = overlay,
**kwargs
)
+1 -1
View File
@@ -186,7 +186,7 @@ func runTestCaseNative(testBin string, tc *gtest.TestCase, args []string, t *tes
// setup_container takes in its target argv as positional arguments.
cmd.Path = getSetupContainerPath()
cmd.Args = append([]string{cmd.Path}, cmd.Args...)
cmd.SysProcAttr.Cloneflags |= unix.CLONE_NEWUSER | unix.CLONE_NEWNET | unix.CLONE_NEWIPC | unix.CLONE_NEWUTS
cmd.SysProcAttr.Cloneflags |= unix.CLONE_NEWUSER | unix.CLONE_NEWNET | unix.CLONE_NEWIPC | unix.CLONE_NEWUTS | unix.CLONE_NEWNS
// Set current user/group as root inside the namespace.
cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{
{ContainerID: 0, HostID: os.Getuid(), Size: 1},