egress: refresh a leased pair on dhcp renewal and default anti-spoof to log
dnsmasq runs the dhcp-script on a renewal only with script-on-renewal, so a pair loaded at the first lease timed out while the lease kept renewing and the bridge table dropped the container's traffic. Set script-on-renewal, time pairs out remaining + 300s, default antiSpoof to log, and run the vm test through renewals with drop.
This commit is contained in:
parent
cf360b74b9
commit
5189e8fb4b
1 changed files with 107 additions and 39 deletions
|
|
@ -13,6 +13,9 @@
|
|||
# - another container's IPv4 address, another container's MAC, an IPv6 address
|
||||
# that is not link-local and a DHCP message with another's `chaddr` are
|
||||
# dropped, and each leaves a `hive-egress-spoof ` line that names the port;
|
||||
# - a container that keeps talking across several renewals of a 2-minute lease
|
||||
# is never dropped, and its pair is still in `lease_v4` after the first
|
||||
# lease and its margin would have ended, with `antiSpoof = "drop"`;
|
||||
# - a DHCP renewal, a link-local IPv6 packet and a restored MAC go through;
|
||||
# - a port with no bound MAC sends nothing, and the timer binds it again;
|
||||
# - `antiSpoof = "log"`, reached by a reload of the unit, brings the bindings
|
||||
|
|
@ -95,6 +98,7 @@ pkgs.testers.runNixOSTest {
|
|||
../host-modules/hive-priv.nix
|
||||
];
|
||||
virtualisation.memorySize = 2048;
|
||||
services.hyperhive.network.egress.antiSpoof = "drop";
|
||||
networking.bridges.${bridge}.interfaces = [ ];
|
||||
networking.interfaces.${bridge}.ipv4.addresses = [
|
||||
{
|
||||
|
|
@ -116,9 +120,11 @@ pkgs.testers.runNixOSTest {
|
|||
interface = bridge;
|
||||
bind-interfaces = true;
|
||||
port = 0;
|
||||
dhcp-range = "10.42.0.2,10.42.0.254,1h";
|
||||
# dnsmasq's shortest lease: the test runs through renewals in minutes.
|
||||
dhcp-range = "10.42.0.2,10.42.0.254,2m";
|
||||
dhcp-leasefile = "/var/lib/dnsmasq/dnsmasq.leases";
|
||||
dhcp-script = toString config.services.hyperhive.network.egress.leaseScript;
|
||||
script-on-renewal = true;
|
||||
log-dhcp = true;
|
||||
};
|
||||
};
|
||||
|
|
@ -192,6 +198,7 @@ pkgs.testers.runNixOSTest {
|
|||
MAC_A, MAC_B = mac("a"), mac("b")
|
||||
IP_A, IP_B = ipv4("a"), ipv4("b")
|
||||
assert MAC_A != MAC_B and IP_A != IP_B
|
||||
T0 = int(host.succeed("date +%s").strip())
|
||||
|
||||
with subtest("each port is bound to its container's MAC and each lease is loaded, with no help from the test"):
|
||||
host.wait_until_succeeds(f"nft list set {TABLE} port_mac | grep -q '\"vb-a\" . {MAC_A}'")
|
||||
|
|
@ -207,6 +214,26 @@ pkgs.testers.runNixOSTest {
|
|||
assert udp_reaches_host("a", IP_A), "control: a datagram from a's own address arrives"
|
||||
assert not spoof_lines(), spoof_lines()
|
||||
|
||||
with subtest("a container that keeps talking across renewals is never dropped, and its pair outlives the first lease and its margin"):
|
||||
# The leases run 2 minutes and the pair outlives its lease by 300
|
||||
# seconds (LEASE_MARGIN_SECS in hive-priv's egress.rs). The pairs were
|
||||
# loaded before T0, so `T0 + 420` is the latest the first load alone
|
||||
# would have kept them; the loop runs past it, and only the refresh at
|
||||
# each renewal keeps them in the set.
|
||||
def now():
|
||||
return int(host.succeed("date +%s").strip())
|
||||
def ack_count(ip):
|
||||
return host.succeed("journalctl -u dnsmasq -o cat --no-pager").count("DHCPACK(${bridge}) " + ip)
|
||||
while now() < T0 + 450:
|
||||
assert ct_ok("a", "ping -c1 -W2 ${gateway}"), "a's traffic is dropped while it renews"
|
||||
assert ct_ok("b", "ping -c1 -W2 ${gateway}"), "b's traffic is dropped while it renews"
|
||||
host.sleep(15)
|
||||
assert not spoof_lines(), spoof_lines()
|
||||
assert ack_count(IP_A) >= 4 and ack_count(IP_B) >= 4, "each container renewed several times"
|
||||
host.succeed(f"nft list set {TABLE} lease_v4 | grep -q '{MAC_A} . {IP_A}'")
|
||||
host.succeed(f"nft list set {TABLE} lease_v4 | grep -q '{MAC_B} . {IP_B}'")
|
||||
assert udp_reaches_host("a", IP_A), "a's own address still reaches the host after its first lease ended"
|
||||
|
||||
with subtest("another container's IPv4 address is dropped, and the line names the port"):
|
||||
ct("a", f"ip addr add {IP_B}/24 dev eth0")
|
||||
assert not udp_reaches_host("a", IP_B), "a datagram from b's address must not arrive"
|
||||
|
|
|
|||
Loading…
Reference in a new issue