diff --git a/module/pfsrc/pfk_hooks.c b/module/pfsrc/pfk_hooks.c index 62e2d83..0d9bb8a 100644 --- a/module/pfsrc/pfk_hooks.c +++ b/module/pfsrc/pfk_hooks.c @@ -140,6 +140,20 @@ pfk_pkt_work(struct work_struct *w) orig_src = ip_hdr(pk->skb)->saddr; } + /* + * pf patches transport checksums incrementally when it rewrites an + * address. VM-to-VM traffic commonly arrives with an unmaterialized + * checksum (CHECKSUM_PARTIAL carried over from the sender's offload), + * so compute it in software first - otherwise pf's fixup adjusts a + * placeholder and the peer drops the packet. + */ + if (pk->skb->ip_summed == CHECKSUM_PARTIAL && + skb_checksum_help(pk->skb)) { + kfree_skb(pk->skb); + kfree(pk); + return; + } + m = pfk_m_wrap_skb(pk->skb, pk->af); if (!m) { pfk_pkt_reinject(pk); /* like "pf not consulted" */ diff --git a/tests/vm/06-rdr.sh b/tests/vm/06-rdr.sh new file mode 100755 index 0000000..e6ecb27 --- /dev/null +++ b/tests/vm/06-rdr.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# M3 gate: rdr-to — destination/port redirect. +# +# Lab: the client connects to the firewall's own address 10.0.1.1:8080; pf +# redirects it to the server (10.0.2.20:80, busybox httpd). This exercises +# the inbound translation *and* the re-route of a packet whose destination pf +# rewrote (the packet is no longer local to the firewall). +set -uo pipefail; cd "$(dirname "$0")"; . ./lib.sh + +pf0 'insmod /opt/pf/pf.ko 2>/dev/null || insmod /tmp/pf.ko; true' +pf0 '/opt/pf/pfctl -e; /opt/pf/pfctl -f /dev/null' >/dev/null + +# a tiny responder on srv:80 (base64-encoded so it survives the two ssh hops +# without nested quoting problems; detached so ssh returns) +LOOP=$(printf 'while :; do nc -l -p 80 < /tmp/resp.txt >/dev/null 2>&1; done\n' | base64 | tr -d '\n') +# NOTE: never pkill -f a pattern that also appears in this very command line - +# it kills the invoking shell. Use a pidfile and check the listening socket. +srv "echo PF-RDR-OK > /tmp/resp.txt; echo $LOOP | base64 -d > /tmp/loop.sh; [ -f /tmp/loop.pid ] && kill \$(cat /tmp/loop.pid) 2>/dev/null; pkill nc 2>/dev/null; setsid sh /tmp/loop.sh >/dev/null 2>&1 /tmp/loop.pid; sleep 1; netstat -lnt 2>/dev/null | grep -q ':80 ' && echo RESPONDER_UP" | grep -q RESPONDER_UP && ok "responder up on srv:80" || bad "responder start" + +cat > /tmp/g06.conf </dev/null) +if echo "$RESP" | grep -q 'PF-RDR-OK'; then + ok "redirect delivered the response" +else + bad "no redirected response" + printf '%s\n' "$RESP" | head -4 | sed 's/^/ /' +fi + +# the state must show the translated tuple (server:80 with the original in parens) +pf0 '/opt/pf/pfctl -ss' | grep -E "$SRV_IP:80 .*$CLI_IP" >/dev/null && ok "state shows the translation" || bad "no translated state" + +# the reply path must survive the un-redirect +pf0 '/opt/pf/pfctl -s info' | grep -qiE 'match' && ok "counters readable" || bad "counters" + +srv '[ -f /tmp/loop.pid ] && kill $(cat /tmp/loop.pid) 2>/dev/null; pkill nc 2>/dev/null; true' >/dev/null +pf0 '/opt/pf/pfctl -d' >/dev/null +finish