Browse Source

Add isolated STUN fallback scenario and equivalent NAT for test ports

master
evgeny 12 hours ago
parent
commit
a581cb2086
  1. 30
      tests/stream_netns/network.py
  2. 3
      tests/stream_netns/run.py
  3. 7
      tests/stream_netns/scenario.py

30
tests/stream_netns/network.py

@ -10,7 +10,7 @@ import time
from topology import COUNTRIES, NATS, NODES, WANS, wan
def nat_rules(gateway):
def nat_rules(gateway, stun_trial=False):
clients = [node for node in NODES if node.nat == gateway.name]
destinations = sorted({n.ip for n in NODES if not n.nat} | {g.ip for g in NATS})
lines = ["table ip lab {",
@ -27,6 +27,19 @@ def nat_rules(gateway):
for index, destination in enumerate(destinations):
port = 40000 + node.number * 100 + index
lines.append(f"{match} ip daddr {destination} update @contacts {{ ip daddr . udp dport . {port} timeout 60s }}")
# STUN-сценарий использует одного клиента шлюза. Его случайные тестовые порты
# получают тот же класс NAT, что рабочий 32000; внешние порты остаются другими.
trial = clients[0]
if stun_trial:
match = f' oifname "wan" ip saddr {trial.ip} udp sport 49152-65535'
if gateway.mode == "cone":
lines.append(f"{match} update @active {{ udp sport timeout 60s }}")
elif gateway.mode == "restricted":
lines.append(f"{match} update @contacts {{ ip daddr . udp dport . udp sport timeout 60s }}")
else:
for index, destination in enumerate(destinations):
port = 40000 + trial.number * 100 + index
lines.append(f"{match} ip daddr {destination} update @contacts {{ ip daddr . udp dport . {port} timeout 60s }}")
# Запрещённый вход отсекается ДО conntrack. Иначе первая неуспешная
# входящая проба закрепляет tuple без DNAT и блокирует последующее открытие.
lines += [" }", " chain ingress_guard { type filter hook prerouting priority raw; policy accept;"]
@ -40,6 +53,10 @@ def nat_rules(gateway):
else:
lines.append(f"{match} ip saddr . udp sport . udp dport @contacts accept")
lines.append(f"{match} counter drop")
if stun_trial and gateway.mode != "strict":
match = ' iifname "wan" udp dport 49152-65535'
guard = "udp dport @active" if gateway.mode == "cone" else "ip saddr . udp sport . udp dport @contacts"
lines += [f"{match} {guard} accept", f"{match} counter drop"]
lines += [" }", " chain prerouting { type nat hook prerouting priority dstnat; policy accept;"]
for node in clients:
match = f' iifname "wan" udp dport {node.mapped_port}'
@ -47,6 +64,9 @@ def nat_rules(gateway):
lines.append(f"{match} udp dport @active dnat to {node.ip}:{node.udp_port}")
elif gateway.mode == "restricted":
lines.append(f"{match} ip saddr . udp sport . udp dport @contacts dnat to {node.ip}:{node.udp_port}")
if stun_trial and gateway.mode != "strict":
guard = "udp dport @active" if gateway.mode == "cone" else "ip saddr . udp sport . udp dport @contacts"
lines.append(f' iifname "wan" udp dport 49152-65535 {guard} dnat to {trial.ip}')
lines += [" }", " chain postrouting { type nat hook postrouting priority srcnat; policy accept;"]
for node in clients:
match = f' oifname "wan" ip saddr {node.ip} udp sport {node.udp_port}'
@ -56,6 +76,14 @@ def nat_rules(gateway):
lines.append(f"{match} ip daddr {destination} snat to {gateway.ip}:{port}")
else:
lines.append(f"{match} snat to {gateway.ip}:{node.mapped_port}")
if stun_trial:
match = f' oifname "wan" ip saddr {trial.ip} udp sport 49152-65535'
if gateway.mode == "strict":
for index, destination in enumerate(destinations):
port = 40000 + trial.number * 100 + index
lines.append(f"{match} ip daddr {destination} snat to {gateway.ip}:{port}")
else:
lines.append(f"{match} snat to {gateway.ip}")
lines += [' oifname "wan" masquerade', " }",
" chain forward { type filter hook forward priority filter; policy drop;",
" ct state established,related accept", ' iifname "lan" oifname "wan" accept',

3
tests/stream_netns/run.py

@ -94,6 +94,7 @@ def main():
parser.add_argument("--network-only", action="store_true", help="Only topology, WAN and NAT calibration")
parser.add_argument("--nat-check", action="store_true", help="Verify owned endpoint profiles before real JOIN/streaming")
parser.add_argument("--nat-only", action="store_true", help="Only real uTun endpoint detection after network calibration")
parser.add_argument("--stun-only", action="store_true", help="STUN interface fallback and promotion to ETCP on three real NAT classes")
parser.add_argument("--binary", default=str(Path(__file__).parent / "stream_lab_node"))
parser.add_argument("--publisher", choices=[f"n{i:02d}" for i in range(1, 17)], default="n01")
parser.add_argument("--rate", type=int, default=1000000, help="Application bitrate in bits/s, before FEC")
@ -131,6 +132,8 @@ def main():
command.append("--nat-check")
if args.nat_only:
command.append("--nat-only")
if args.stun_only:
command.append("--stun-only")
if args.faults:
command.append("--faults")
if args.tcp:

7
tests/stream_netns/scenario.py

@ -11,7 +11,7 @@ from topology import NODES, WANS
class Agent:
def __init__(self, network, node, binary, seed, tcp, tcp_only, core_only=False):
def __init__(self, network, node, binary, seed, tcp, tcp_only, core_only=False, extra_config=""):
self.node = node
directory = network.directory / node.name
directory.mkdir()
@ -26,7 +26,7 @@ class Agent:
f"db_path={directory / 'db'}\ndebug_level=warn\n"
"[ntp]\nenabled=no\n[allowed_keys]\nallow_all=1\n"
"[chatserver]\ngroup_autoconnect=1\nstorage_autoload=0\nmember_files_enabled=0\n"
"[debug]\nstream=debug\nnat=debug\nchat_sync=info\nmember_sync=info\nbgp=info\nconnection=info\n" + sockets)
"[debug]\nstream=debug\nnat=debug\nchat_sync=info\nmember_sync=info\nbgp=info\nconnection=info\n" + sockets + extra_config)
path = directory / "node.conf"
path.write_text(config)
self.stderr = (directory / "stderr.log").open("w")
@ -136,6 +136,9 @@ class Scenario:
def run(self):
binary = Path(self.args.binary).resolve()
if self.args.stun_only:
from stun_scenario import check_stun
return check_stun(self)
nat_check = self.args.nat_check or self.args.nat_only
for node in NODES:
self.agents[node.name] = Agent(self.network, node, binary, self.args.seed, self.args.tcp, self.args.tcp_only, nat_check)

Loading…
Cancel
Save