mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add packetdrill tests that use docker.
PiperOrigin-RevId: 292973224
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
load("defs.bzl", "packetdrill_test")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
packetdrill_test(
|
||||
name = "fin_wait2_timeout",
|
||||
scripts = ["fin_wait2_timeout.pkt"],
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
FROM ubuntu:bionic
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y net-tools git iptables iputils-ping netcat tcpdump jq tar
|
||||
RUN hash -r
|
||||
RUN git clone --branch packetdrill-v2.0 \
|
||||
https://github.com/google/packetdrill.git
|
||||
RUN cd packetdrill/gtests/net/packetdrill && ./configure && \
|
||||
apt-get install -y bison flex make && make
|
||||
@@ -0,0 +1,85 @@
|
||||
"""Defines a rule for packetdrill test targets."""
|
||||
|
||||
def _packetdrill_test_impl(ctx):
|
||||
test_runner = ctx.executable._test_runner
|
||||
runner = ctx.actions.declare_file("%s-runner" % ctx.label.name)
|
||||
|
||||
script_paths = []
|
||||
for script in ctx.files.scripts:
|
||||
script_paths.append(script.short_path)
|
||||
runner_content = "\n".join([
|
||||
"#!/bin/bash",
|
||||
# This test will run part in a distinct user namespace. This can cause
|
||||
# permission problems, because all runfiles may not be owned by the
|
||||
# current user, and no other users will be mapped in that namespace.
|
||||
# Make sure that everything is readable here.
|
||||
"find . -type f -exec chmod a+rx {} \\;",
|
||||
"find . -type d -exec chmod a+rx {} \\;",
|
||||
"%s %s --init_script %s -- %s\n" % (
|
||||
test_runner.short_path,
|
||||
" ".join(ctx.attr.flags),
|
||||
ctx.files._init_script[0].short_path,
|
||||
" ".join(script_paths),
|
||||
),
|
||||
])
|
||||
ctx.actions.write(runner, runner_content, is_executable = True)
|
||||
|
||||
transitive_files = depset()
|
||||
if hasattr(ctx.attr._test_runner, "data_runfiles"):
|
||||
transitive_files = depset(ctx.attr._test_runner.data_runfiles.files)
|
||||
runfiles = ctx.runfiles(
|
||||
files = [test_runner] + ctx.files._init_script + ctx.files.scripts,
|
||||
transitive_files = transitive_files,
|
||||
collect_default = True,
|
||||
collect_data = True,
|
||||
)
|
||||
return [DefaultInfo(executable = runner, runfiles = runfiles)]
|
||||
|
||||
_packetdrill_test = rule(
|
||||
attrs = {
|
||||
"_test_runner": attr.label(
|
||||
executable = True,
|
||||
cfg = "host",
|
||||
allow_files = True,
|
||||
default = "packetdrill_test.sh",
|
||||
),
|
||||
"_init_script": attr.label(
|
||||
allow_single_file = True,
|
||||
default = "packetdrill_setup.sh",
|
||||
),
|
||||
"flags": attr.string_list(
|
||||
mandatory = False,
|
||||
default = [],
|
||||
),
|
||||
"scripts": attr.label_list(
|
||||
mandatory = True,
|
||||
allow_files = True,
|
||||
),
|
||||
},
|
||||
test = True,
|
||||
implementation = _packetdrill_test_impl,
|
||||
)
|
||||
|
||||
_PACKETDRILL_TAGS = ["local", "manual"]
|
||||
|
||||
def packetdrill_linux_test(name, **kwargs):
|
||||
if "tags" not in kwargs:
|
||||
kwargs["tags"] = _PACKETDRILL_TAGS
|
||||
_packetdrill_test(
|
||||
name = name + "_linux_test",
|
||||
flags = ["--dut_platform", "linux"],
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def packetdrill_netstack_test(name, **kwargs):
|
||||
if "tags" not in kwargs:
|
||||
kwargs["tags"] = _PACKETDRILL_TAGS
|
||||
_packetdrill_test(
|
||||
name = name + "_netstack_test",
|
||||
flags = ["--dut_platform", "netstack"],
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def packetdrill_test(**kwargs):
|
||||
packetdrill_linux_test(**kwargs)
|
||||
packetdrill_netstack_test(**kwargs)
|
||||
@@ -0,0 +1,23 @@
|
||||
// Test that a socket in FIN_WAIT_2 eventually times out and a subsequent
|
||||
// packet generates a RST.
|
||||
|
||||
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
|
||||
+0 bind(3, ..., ...) = 0
|
||||
|
||||
+0 listen(3, 1) = 0
|
||||
|
||||
// Establish a connection without timestamps.
|
||||
+0 < S 0:0(0) win 32792 <mss 1460,sackOK,nop,nop,nop,wscale 7>
|
||||
+0 > S. 0:0(0) ack 1 <...>
|
||||
+0 < P. 1:1(0) ack 1 win 257
|
||||
|
||||
+0.100 accept(3, ..., ...) = 4
|
||||
// set FIN_WAIT2 timeout to 1 seconds.
|
||||
+0.100 setsockopt(4, SOL_TCP, TCP_LINGER2, [1], 4) = 0
|
||||
+0 close(4) = 0
|
||||
|
||||
+0 > F. 1:1(0) ack 1 <...>
|
||||
+0 < . 1:1(0) ack 2 win 257
|
||||
|
||||
+1.1 < . 1:1(0) ack 2 win 257
|
||||
+0 > R 2:2(0) win 0
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2018 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.
|
||||
|
||||
# This script runs both within the sentry context and natively. It should tweak
|
||||
# TCP parameters to match expectations found in the script files.
|
||||
sysctl -q net.ipv4.tcp_sack=1
|
||||
sysctl -q net.ipv4.tcp_rmem="4096 2097152 $((8*1024*1024))"
|
||||
sysctl -q net.ipv4.tcp_wmem="4096 2097152 $((8*1024*1024))"
|
||||
|
||||
# There may be errors from the above, but they will show up in the test logs and
|
||||
# we always want to proceed from this point. It's possible that values were
|
||||
# already set correctly and the nodes were not available in the namespace.
|
||||
exit 0
|
||||
Executable
+213
@@ -0,0 +1,213 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 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.
|
||||
|
||||
# Run a packetdrill test. Two docker containers are made, one for the
|
||||
# Device-Under-Test (DUT) and one for the test runner. Each is attached with
|
||||
# two networks, one for control packets that aid the test and one for test
|
||||
# packets which are sent as part of the test and observed for correctness.
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
function failure() {
|
||||
local lineno=$1
|
||||
local msg=$2
|
||||
local filename="$0"
|
||||
echo "FAIL: $filename:$lineno: $msg"
|
||||
}
|
||||
trap 'failure ${LINENO} "$BASH_COMMAND"' ERR
|
||||
|
||||
declare -r LONGOPTS="dut_platform:,init_script:"
|
||||
|
||||
# Don't use declare below so that the error from getopt will end the script.
|
||||
PARSED=$(getopt --options "" --longoptions=$LONGOPTS --name "$0" -- "$@")
|
||||
|
||||
eval set -- "$PARSED"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
--dut_platform)
|
||||
declare -r DUT_PLATFORM="$2"
|
||||
shift 2
|
||||
;;
|
||||
--init_script)
|
||||
declare -r INIT_SCRIPT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo "Programming error"
|
||||
exit 3
|
||||
esac
|
||||
done
|
||||
|
||||
# All the other arguments are scripts.
|
||||
declare -r scripts="$@"
|
||||
|
||||
# Check that the required flags are defined in a way that is safe for "set -u".
|
||||
if [[ "${DUT_PLATFORM-}" == "netstack" ]]; then
|
||||
declare -r RUNTIME="--runtime runsc-d"
|
||||
elif [[ "${DUT_PLATFORM-}" == "linux" ]]; then
|
||||
declare -r RUNTIME=""
|
||||
else
|
||||
echo "FAIL: Bad or missing --dut_platform argument: ${DUT_PLATFORM-}"
|
||||
exit 2
|
||||
fi
|
||||
if [[ ! -x "${INIT_SCRIPT-}" ]]; then
|
||||
echo "FAIL: Bad or missing --init_script: ${INIT_SCRIPT-}"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Variables specific to the control network and interface start with CTRL_.
|
||||
# Variables specific to the test network and interface start with TEST_.
|
||||
# Variables specific to the DUT start with DUT_.
|
||||
# Variables specific to the test runner start with TEST_RUNNER_.
|
||||
declare -r PACKETDRILL="/packetdrill/gtests/net/packetdrill/packetdrill"
|
||||
# Use random numbers so that test networks don't collide.
|
||||
declare -r CTRL_NET="ctrl_net-${RANDOM}${RANDOM}"
|
||||
declare -r TEST_NET="test_net-${RANDOM}${RANDOM}"
|
||||
declare -r tolerance_usecs=100000
|
||||
# On both DUT and test runner, testing packets are on the eth2 interface.
|
||||
declare -r TEST_DEVICE="eth2"
|
||||
# Number of bits in the *_NET_PREFIX variables.
|
||||
declare -r NET_MASK="24"
|
||||
function new_net_prefix() {
|
||||
# Class C, 192.0.0.0 to 223.255.255.255, transitionally has mask 24.
|
||||
echo "$(shuf -i 192-223 -n 1).$(shuf -i 0-255 -n 1).$(shuf -i 0-255 -n 1)"
|
||||
}
|
||||
# Last bits of the DUT's IP address.
|
||||
declare -r DUT_NET_SUFFIX=".10"
|
||||
# Control port.
|
||||
declare -r CTRL_PORT="40000"
|
||||
# Last bits of the test runner's IP address.
|
||||
declare -r TEST_RUNNER_NET_SUFFIX=".20"
|
||||
declare -r TIMEOUT="60"
|
||||
declare -r IMAGE_TAG="gcr.io/gvisor-presubmit/packetdrill"
|
||||
|
||||
# Make sure that docker is installed.
|
||||
docker --version
|
||||
|
||||
function finish {
|
||||
local cleanup_success=1
|
||||
for net in "${CTRL_NET}" "${TEST_NET}"; do
|
||||
# Kill all processes attached to ${net}.
|
||||
for docker_command in "kill" "rm"; do
|
||||
(docker network inspect "${net}" \
|
||||
--format '{{range $key, $value := .Containers}}{{$key}} {{end}}' \
|
||||
| xargs -r docker "${docker_command}") || \
|
||||
cleanup_success=0
|
||||
done
|
||||
# Remove the network.
|
||||
docker network rm "${net}" || \
|
||||
cleanup_success=0
|
||||
done
|
||||
|
||||
if ((!$cleanup_success)); then
|
||||
echo "FAIL: Cleanup command failed"
|
||||
exit 4
|
||||
fi
|
||||
}
|
||||
trap finish EXIT
|
||||
|
||||
# Subnet for control packets between test runner and DUT.
|
||||
declare CTRL_NET_PREFIX=$(new_net_prefix)
|
||||
while ! docker network create \
|
||||
"--subnet=${CTRL_NET_PREFIX}.0/${NET_MASK}" "${CTRL_NET}"; do
|
||||
sleep 0.1
|
||||
declare CTRL_NET_PREFIX=$(new_net_prefix)
|
||||
done
|
||||
|
||||
# Subnet for the packets that are part of the test.
|
||||
declare TEST_NET_PREFIX=$(new_net_prefix)
|
||||
while ! docker network create \
|
||||
"--subnet=${TEST_NET_PREFIX}.0/${NET_MASK}" "${TEST_NET}"; do
|
||||
sleep 0.1
|
||||
declare TEST_NET_PREFIX=$(new_net_prefix)
|
||||
done
|
||||
|
||||
docker pull "${IMAGE_TAG}"
|
||||
|
||||
# Create the DUT container and connect to network.
|
||||
DUT=$(docker create ${RUNTIME} --privileged --rm \
|
||||
--stop-timeout ${TIMEOUT} -it ${IMAGE_TAG})
|
||||
docker network connect "${CTRL_NET}" \
|
||||
--ip "${CTRL_NET_PREFIX}${DUT_NET_SUFFIX}" "${DUT}" \
|
||||
|| (docker kill ${DUT}; docker rm ${DUT}; false)
|
||||
docker network connect "${TEST_NET}" \
|
||||
--ip "${TEST_NET_PREFIX}${DUT_NET_SUFFIX}" "${DUT}" \
|
||||
|| (docker kill ${DUT}; docker rm ${DUT}; false)
|
||||
docker start "${DUT}"
|
||||
|
||||
# Create the test runner container and connect to network.
|
||||
TEST_RUNNER=$(docker create --privileged --rm \
|
||||
--stop-timeout ${TIMEOUT} -it ${IMAGE_TAG})
|
||||
docker network connect "${CTRL_NET}" \
|
||||
--ip "${CTRL_NET_PREFIX}${TEST_RUNNER_NET_SUFFIX}" "${TEST_RUNNER}" \
|
||||
|| (docker kill ${TEST_RUNNER}; docker rm ${REST_RUNNER}; false)
|
||||
docker network connect "${TEST_NET}" \
|
||||
--ip "${TEST_NET_PREFIX}${TEST_RUNNER_NET_SUFFIX}" "${TEST_RUNNER}" \
|
||||
|| (docker kill ${TEST_RUNNER}; docker rm ${REST_RUNNER}; false)
|
||||
docker start "${TEST_RUNNER}"
|
||||
|
||||
# Run tcpdump in the test runner unbuffered, without dns resolution, just on the
|
||||
# interface with the test packets.
|
||||
docker exec -t ${TEST_RUNNER} tcpdump -U -n -i "${TEST_DEVICE}" &
|
||||
|
||||
# Start a packetdrill server on the test_runner. The packetdrill server sends
|
||||
# packets and asserts that they are received.
|
||||
docker exec -d "${TEST_RUNNER}" \
|
||||
${PACKETDRILL} --wire_server --wire_server_dev="${TEST_DEVICE}" \
|
||||
--wire_server_ip="${CTRL_NET_PREFIX}${TEST_RUNNER_NET_SUFFIX}" \
|
||||
--wire_server_port="${CTRL_PORT}" \
|
||||
--local_ip="${TEST_NET_PREFIX}${TEST_RUNNER_NET_SUFFIX}" \
|
||||
--remote_ip="${TEST_NET_PREFIX}${DUT_NET_SUFFIX}"
|
||||
|
||||
# Because the Linux kernel receives the SYN-ACK but didn't send the SYN it will
|
||||
# issue a RST. To prevent this IPtables can be used to filter those out.
|
||||
docker exec "${TEST_RUNNER}" \
|
||||
iptables -A OUTPUT -p tcp --tcp-flags RST RST -j DROP
|
||||
|
||||
# Wait for the packetdrill server on the test runner to come. Attempt to
|
||||
# connect to it from the DUT every 100 milliseconds until success.
|
||||
while ! docker exec "${DUT}" \
|
||||
nc -zv "${CTRL_NET_PREFIX}${TEST_RUNNER_NET_SUFFIX}" "${CTRL_PORT}"; do
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
# Copy the packetdrill setup script to the DUT.
|
||||
docker cp -L "${INIT_SCRIPT}" "${DUT}:packetdrill_setup.sh"
|
||||
|
||||
# Copy the packetdrill scripts to the DUT.
|
||||
declare -a dut_scripts
|
||||
for script in $scripts; do
|
||||
docker cp -L "${script}" "${DUT}:$(basename ${script})"
|
||||
dut_scripts+=("/$(basename ${script})")
|
||||
done
|
||||
|
||||
# Start a packetdrill client on the DUT. The packetdrill client runs POSIX
|
||||
# socket commands and also sends instructions to the server.
|
||||
docker exec -t "${DUT}" \
|
||||
${PACKETDRILL} --wire_client --wire_client_dev="${TEST_DEVICE}" \
|
||||
--wire_server_ip="${CTRL_NET_PREFIX}${TEST_RUNNER_NET_SUFFIX}" \
|
||||
--wire_server_port="${CTRL_PORT}" \
|
||||
--local_ip="${TEST_NET_PREFIX}${DUT_NET_SUFFIX}" \
|
||||
--remote_ip="${TEST_NET_PREFIX}${TEST_RUNNER_NET_SUFFIX}" \
|
||||
--init_scripts=/packetdrill_setup.sh \
|
||||
--tolerance_usecs="${tolerance_usecs}" "${dut_scripts[@]}"
|
||||
|
||||
echo PASS: No errors.
|
||||
Reference in New Issue
Block a user