Skip to content

Commit 4763432

Browse files
committed
apply tcpdump wrapper
1 parent 907456b commit 4763432

16 files changed

+228
-132
lines changed

nat-lab/bin/derp-server

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
set -e
44

5+
# Backup IP tables
6+
iptables-save -f iptables_backup
7+
ip6tables-save -f ip6tables_backup
8+
59
/usr/bin/nordderper &
610

711
sleep infinity

nat-lab/bin/dns-server.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
#!/bin/sh
2+
3+
# Backup IP tables
4+
iptables-save -f iptables_backup
5+
ip6tables-save -f ip6tables_backup
6+
27
/opt/bin/dns-server 2>&1 | tee /dns-server.log

nat-lab/docker-compose.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,8 @@ services:
423423
- net.netfilter.nf_conntrack_tcp_timeout_fin_wait=15
424424
cap_drop:
425425
- ALL
426+
cap_add:
427+
- NET_ADMIN
426428
security_opt:
427429
- no-new-privileges:true
428430
networks:
@@ -549,6 +551,10 @@ services:
549551
hostname: dns-server-1
550552
image: nat-lab:base
551553
entrypoint: /opt/bin/dns-server.sh
554+
cap_drop:
555+
- ALL
556+
cap_add:
557+
- NET_ADMIN
552558
environment:
553559
PYTHONUNBUFFERED: 1
554560
networks:
@@ -561,6 +567,10 @@ services:
561567
hostname: dns-server-2
562568
image: nat-lab:base
563569
entrypoint: /opt/bin/dns-server.sh
570+
cap_drop:
571+
- ALL
572+
cap_add:
573+
- NET_ADMIN
564574
environment:
565575
PYTHONUNBUFFERED: 1
566576
networks:

nat-lab/tests/conftest.py

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,16 @@
44
import random
55
import shutil
66
import subprocess
7+
from contextlib import AsyncExitStack
78
from datetime import datetime
89
from helpers import SetupParameters
910
from interderp_cli import InterDerpClient
1011
from itertools import combinations
11-
from mesh_api import start_tcpdump, stop_tcpdump
1212
from utils.bindings import TelioAdapterType
13-
from utils.connection import DockerConnection
14-
from utils.connection_util import (
15-
ConnectionTag,
16-
container_id,
17-
LAN_ADDR_MAP,
18-
new_connection_raw,
19-
)
13+
from utils.connection_util import ConnectionTag, LAN_ADDR_MAP, new_connection_raw
2014
from utils.process import ProcessExecError
2115
from utils.router import IPStack
16+
from utils.tcpdump import make_tcpdump
2217
from utils.vm import windows_vm_util, mac_vm_util
2318

2419
DERP_SERVER_1_ADDR = "http://10.0.10.1:8765"
@@ -30,6 +25,11 @@
3025
SETUP_CHECK_TIMEOUT_S = 30
3126
SETUP_CHECK_RETRIES = 5
3227

28+
# pylint: disable=unnecessary-dunder-call
29+
TEST_SCOPE_ASYNC_EXIT_STACK = asyncio.run(AsyncExitStack().__aenter__())
30+
# pylint: disable=unnecessary-dunder-call
31+
SESSION_ASYNC_EXIT_STACK = asyncio.run(AsyncExitStack().__aenter__())
32+
3333

3434
def _cancel_all_tasks(loop: asyncio.AbstractEventLoop):
3535
to_cancel = asyncio.tasks.all_tasks(loop)
@@ -197,24 +197,25 @@ def pytest_make_parametrize_id(config, val):
197197

198198

199199
async def setup_check_interderp():
200-
async with new_connection_raw(ConnectionTag.DOCKER_CONE_CLIENT_1) as connection:
201-
if not isinstance(connection, DockerConnection):
202-
raise Exception("Not docker connection")
203-
containers = [
204-
connection.container_name(),
205-
"nat-lab-derp-01-1",
206-
"nat-lab-derp-02-1",
207-
"nat-lab-derp-03-1",
200+
async with AsyncExitStack() as exit_stack:
201+
connections = [
202+
await exit_stack.enter_async_context(new_connection_raw(conn_tag))
203+
for conn_tag in [
204+
ConnectionTag.DOCKER_CONE_CLIENT_1,
205+
ConnectionTag.DOCKER_DERP_1,
206+
ConnectionTag.DOCKER_DERP_2,
207+
ConnectionTag.DOCKER_DERP_3,
208+
]
208209
]
209-
start_tcpdump(containers)
210-
try:
210+
211+
async with make_tcpdump(connections):
211212
for idx, (server1, server2) in enumerate(
212213
combinations(
213214
[DERP_SERVER_1_ADDR, DERP_SERVER_2_ADDR, DERP_SERVER_3_ADDR], 2
214215
)
215216
):
216217
derp_test = InterDerpClient(
217-
connection,
218+
connections[0],
218219
server1,
219220
server2,
220221
DERP_SERVER_1_SECRET_KEY,
@@ -223,8 +224,6 @@ async def setup_check_interderp():
223224
)
224225
await derp_test.execute()
225226
await derp_test.save_logs()
226-
finally:
227-
stop_tcpdump(containers)
228227

229228

230229
SETUP_CHECKS = [
@@ -405,24 +404,56 @@ def pytest_runtest_setup():
405404

406405
# pylint: disable=unused-argument
407406
def pytest_runtest_call(item):
408-
start_tcpdump([f"nat-lab-dns-server-{i}-1" for i in range(1, 3)])
407+
if os.environ.get("NATLAB_SAVE_LOGS") is None:
408+
return
409+
410+
async def async_code():
411+
connections = [
412+
await TEST_SCOPE_ASYNC_EXIT_STACK.enter_async_context(
413+
new_connection_raw(conn_tag)
414+
)
415+
for conn_tag in [
416+
ConnectionTag.DOCKER_DNS_SERVER_1,
417+
ConnectionTag.DOCKER_DNS_SERVER_2,
418+
]
419+
]
420+
await TEST_SCOPE_ASYNC_EXIT_STACK.enter_async_context(make_tcpdump(connections))
421+
422+
asyncio.run(async_code())
409423

410424

411425
# pylint: disable=unused-argument
412426
def pytest_runtest_makereport(item, call):
427+
if os.environ.get("NATLAB_SAVE_LOGS") is None:
428+
return
429+
430+
async def async_code():
431+
global TEST_SCOPE_ASYNC_EXIT_STACK
432+
await TEST_SCOPE_ASYNC_EXIT_STACK.aclose()
433+
# pylint: disable=unnecessary-dunder-call
434+
TEST_SCOPE_ASYNC_EXIT_STACK = await AsyncExitStack().__aenter__()
435+
413436
if call.when == "call":
414-
stop_tcpdump([f"nat-lab-dns-server-{i}-1" for i in range(1, 3)])
437+
asyncio.run(async_code())
415438

416439

417440
# pylint: disable=unused-argument
418441
def pytest_sessionstart(session):
419442
if os.environ.get("NATLAB_SAVE_LOGS") is None:
420443
return
421444

445+
async def async_code():
446+
connections = [
447+
await SESSION_ASYNC_EXIT_STACK.enter_async_context(
448+
new_connection_raw(gw_tag)
449+
)
450+
for gw_tag in ConnectionTag
451+
if "_GW" in gw_tag.name
452+
]
453+
await SESSION_ASYNC_EXIT_STACK.enter_async_context(make_tcpdump(connections))
454+
422455
if not session.config.option.collectonly:
423-
start_tcpdump(
424-
{container_id(gw_tag) for gw_tag in ConnectionTag if "_GW" in gw_tag.name}
425-
)
456+
asyncio.run(async_code())
426457

427458

428459
# pylint: disable=unused-argument
@@ -431,11 +462,7 @@ def pytest_sessionfinish(session, exitstatus):
431462
return
432463

433464
if not session.config.option.collectonly:
434-
stop_tcpdump(
435-
{container_id(gw_tag) for gw_tag in ConnectionTag if "_GW" in gw_tag.name},
436-
"./logs",
437-
)
438-
465+
asyncio.run(SESSION_ASYNC_EXIT_STACK.aclose())
439466
collect_nordderper_logs()
440467
collect_dns_server_logs()
441468
asyncio.run(collect_kernel_logs(session.items, "after_tests"))

nat-lab/tests/helpers.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22
import itertools
33
import json
44
import pytest
5-
from config import WG_SERVERS
65
from contextlib import AsyncExitStack, asynccontextmanager
76
from dataclasses import dataclass, field
87
from datetime import datetime
98
from itertools import product, zip_longest
10-
from mesh_api import Node, API, stop_tcpdump
9+
from mesh_api import Node, API
1110
from telio import Client
1211
from typing import AsyncIterator, List, Tuple, Optional, Union
1312
from utils.bindings import (
@@ -26,9 +25,11 @@
2625
ConnectionManager,
2726
ConnectionTag,
2827
new_connection_manager_by_tag,
28+
new_connection_raw,
2929
)
3030
from utils.ping import ping
3131
from utils.router import IPStack
32+
from utils.tcpdump import make_tcpdump
3233
from uuid import UUID
3334

3435

@@ -294,6 +295,15 @@ async def setup_environment(
294295
)
295296

296297
if prepare_vpn:
298+
connections = [
299+
await exit_stack.enter_async_context(new_connection_raw(conn_tag))
300+
for conn_tag in [
301+
ConnectionTag.DOCKER_NLX_1,
302+
ConnectionTag.DOCKER_VPN_1,
303+
ConnectionTag.DOCKER_VPN_2,
304+
]
305+
]
306+
await exit_stack.enter_async_context(make_tcpdump(connections))
297307
api.prepare_all_vpn_servers()
298308

299309
clients = await setup_clients(
@@ -317,18 +327,15 @@ async def setup_environment(
317327
),
318328
)
319329

320-
try:
321-
yield Environment(api, nodes, connection_managers, clients)
322-
323-
print(datetime.now(), "Checking connection limits")
324-
for conn_manager in connection_managers:
325-
if conn_manager.tracker:
326-
violations = await conn_manager.tracker.find_conntracker_violations()
327-
assert (
328-
violations is None
329-
), f"conntracker reported out of limits {violations}"
330-
finally:
331-
stop_tcpdump([server["container"] for server in WG_SERVERS])
330+
yield Environment(api, nodes, connection_managers, clients)
331+
332+
print(datetime.now(), "Checking connection limits")
333+
for conn_manager in connection_managers:
334+
if conn_manager.tracker:
335+
violations = await conn_manager.tracker.find_conntracker_violations()
336+
assert (
337+
violations is None
338+
), f"conntracker reported out of limits {violations}"
332339

333340

334341
async def setup_mesh_nodes(

nat-lab/tests/mesh_api.py

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@
55
import random
66
import time
77
import uuid
8-
from collections.abc import Iterable
9-
from concurrent.futures import ThreadPoolExecutor
108
from config import DERP_SERVERS, LIBTELIO_IPV6_WG_SUBNET, WG_SERVERS
119
from datetime import datetime
1210
from ipaddress import ip_address
1311
from typing import Dict, Any, List, Tuple, Optional
1412
from utils.bindings import Config, Server, Peer, PeerBase
1513
from utils.router import IPStack, IPProto, get_ip_address_type
16-
from utils.testing import get_current_test_log_path
1714

1815
if platform.machine() != "x86_64":
1916
import pure_wg as Key
@@ -373,7 +370,6 @@ def generate_peer_config(node: Node, allowed_ips: str) -> str:
373370
)
374371

375372
for node in node_list:
376-
start_tcpdump([server_config["container"]])
377373
if "type" in server_config and server_config["type"] == "nordlynx":
378374
priv_key = server_config["private_key"]
379375
commands = [
@@ -445,51 +441,3 @@ def config_dynamic_nodes(
445441
def prepare_all_vpn_servers(self):
446442
for wg_server in WG_SERVERS:
447443
self.setup_vpn_servers(list(self.nodes.values()), wg_server)
448-
449-
450-
def start_tcpdump(container_names: Iterable[str]):
451-
if os.environ.get("NATLAB_SAVE_LOGS") is None:
452-
return
453-
454-
def aux(container_name):
455-
# First make sure that no leftover processes/files will interfere
456-
cmd = f"docker exec --privileged {container_name} killall -w tcpdump"
457-
os.system(cmd)
458-
cmd = f"docker exec --privileged {container_name} rm {PCAP_FILE_PATH}"
459-
os.system(cmd)
460-
cmd = f"docker exec -d --privileged {container_name} tcpdump -i any -U -w {PCAP_FILE_PATH}"
461-
os.system(cmd)
462-
463-
with ThreadPoolExecutor() as executor:
464-
executor.map(aux, container_names)
465-
466-
467-
def stop_tcpdump(container_names, store_in=None):
468-
if os.environ.get("NATLAB_SAVE_LOGS") is None:
469-
return
470-
log_dir = get_current_test_log_path()
471-
os.makedirs(log_dir, exist_ok=True)
472-
473-
def aux(container_name):
474-
cmd = f"docker exec --privileged {container_name} killall -w tcpdump"
475-
os.system(cmd)
476-
path = find_unique_path_for_tcpdump(
477-
store_in if store_in else log_dir, container_name
478-
)
479-
cmd = f"docker container cp {container_name}:{PCAP_FILE_PATH} {path}"
480-
os.system(cmd)
481-
482-
with ThreadPoolExecutor() as executor:
483-
executor.map(aux, container_names)
484-
485-
486-
def find_unique_path_for_tcpdump(log_dir, container_name):
487-
candidate_path = f"{log_dir}/{container_name}.pcap"
488-
counter = 1
489-
# NOTE: counter starting from '1' means that the file will either have no suffix or
490-
# will have a suffix starting from '2'. This is to make it clear that it's not the
491-
# first log for that container/client.
492-
while os.path.isfile(candidate_path):
493-
counter += 1
494-
candidate_path = f"./{log_dir}/{container_name}-{counter}.pcap"
495-
return candidate_path

0 commit comments

Comments
 (0)