3 Commits
5 changed files with 105 additions and 7 deletions

No files matched your search

+14 -6
View File
@@ -95,6 +95,18 @@ pfk_divert_pkt_free(struct pfk_divert_pkt *dp)
}
/* PF engine entry points (called from the ported pf.c) */
static struct pfk_divert_sock *
pfk_divert_lookup(u_int16_t port)
{
struct pfk_divert_sock *ds;
list_for_each_entry(ds, &pfk_divert_list, link) {
if (ds->port == port)
return ds;
}
return NULL;
}
static int
pfk_divert_queue(struct sk_buff *skb, int dir, int type, u16 port)
{
@@ -114,12 +126,8 @@ pfk_divert_queue(struct sk_buff *skb, int dir, int type, u16 port)
dp->ifindex = skb->dev ? skb->dev->ifindex : 0;
spin_lock_irqsave(&pfk_divert_lock, flags);
ds = NULL;
list_for_each_entry(ds, &pfk_divert_list, link) {
if (ds->port == port)
break;
}
if (&ds->link == &pfk_divert_list) {
ds = pfk_divert_lookup(port);
if (ds == NULL) {
pfk_divert_global.divs_noport++;
spin_unlock_irqrestore(&pfk_divert_lock, flags);
pr_info_ratelimited("pfk: divert port %u: no socket bound\n",
+2
View File
@@ -42,7 +42,9 @@ pf_disable_pf0() {
>/dev/null 2>&1 || true
}
trap pf_disable_pf0 EXIT INT TERM
ONLY="${1:-}"
for t in "$REPO"/tests/vm/[0-9]*.sh; do
[ -n "$ONLY" ] && case "$t" in *"$ONLY"*) ;; *) continue ;; esac
[ -e "$t" ] || continue
name="$(basename "$t")"
echo "=== $name ==="
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# M3 gate: nat-to — source NAT of transit traffic to the egress address.
#
# Scenario: cli (10.0.1.10) -> srv (10.0.2.20) through pf0, rule:
# nat on egress proto icmp from 10.0.1.0/24 to any -> (egress)
# Expected: srv sees src 192.168.122.8 (pf0 egress IP) instead of 10.0.1.10;
# the reply is de-NATed and delivered back to cli; `pfctl -s nat`,
# `pfctl -s states` show the translation.
set -uo pipefail; cd "$(dirname "$0")"; . ./lib.sh
FAILED=0
pf0 "insmod /tmp/pf.ko 2>/dev/null || insmod /opt/pf/pf.ko; true"
pf0 '/opt/pf/pfctl -e; /opt/pf/pfctl -f /dev/null' >/dev/null
cat > /tmp/g05.conf <<C
nat on egress proto icmp from 10.0.1.0/24 to any -> (egress)
pass in quick on ingress proto icmp keep state
pass out quick proto icmp keep state
C
pfload /tmp/g05.conf || { bad "nat rule load"; finish; }
pf0 '/opt/pf/pfctl -sn' | grep -q 'nat on egress' && ok "nat rule listed" || bad "nat not listed"
# capture on pf0's egress while cli pings srv
pf0 'tcpdump -ni egress -c 4 -e icmp > /tmp/nat.td 2>&1 &'
sleep 1
cli "ping -c 2 -W 2 $SRV_IP >/dev/null 2>&1" && ok "transit ping through NAT" || bad "transit ping"
sleep 2
TD=$(pf0 'cat /tmp/nat.td')
echo "$TD" | grep -q "$CLI_IP" && bad "cli source address leaked to egress" || ok "source rewritten (no $CLI_IP on egress)"
echo "$TD" | grep -q '192.168.122.8' && ok "nat applied to $(pf0 'ip -4 addr show egress | grep -o "192[0-9.]*"' | head -1)" || bad "egress src ip not seen"
pf0 '/opt/pf/pfctl -s states' | grep -q "$SRV_IP" && ok "state present" || bad "no state"
pf0 '/opt/pf/pfctl -d' >/dev/null
finish
+3 -1
View File
@@ -16,9 +16,11 @@ FAILED=0
pf0 "insmod /tmp/pf.ko 2>/dev/null || insmod /opt/pf/pf.ko; true"
pf0 '/opt/pf/pfctl -e; /opt/pf/pfctl -f /dev/null' >/dev/null
pfput ../../tools/out/pdivert /opt/pf/pdivert || bad "pdivert deploy"
pf0 'chmod +x /opt/pf/pdivert' >/dev/null
cat > /tmp/g11.conf <<C
pass in quick proto icmp from $CLI_IP to $PFLAN divert-to $PFLAN $PORT keep state
pass in quick proto icmp from $CLI_IP to $PFLAN divert-to "$PFLAN" port $PORT keep state
pass out quick
C
pfload /tmp/g11.conf || { bad "divert rule load"; finish; }
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
# M6 gate: soak + unload/leak discipline.
#
# 1) load pf, ruleset with states/tables/anchors, drive mixed traffic ~20s
# 2) assert no BUG/WARNING/Oops in dmesg
# 3) disable, rmmod; assert module unloads cleanly (no refcount hang)
# 4) insmod/rmmod cycle x2; assert slab objects for pf caches return to the
# pre-load baseline (no leak), and that a reload works (timer/notifier
# re-registration is idempotent)
set -uo pipefail; cd "$(dirname "$0")"; . ./lib.sh
FAILED=0
slab_snapshot() {
pf0 'grep -E "^(pf|pfk)[^ ]* " /proc/slabinfo | awk "{print \$1, \$3}"' \
| sort
}
pf0 'insmod /tmp/pf.ko 2>/dev/null || insmod /opt/pf/pf.ko; true'
pf0 '/opt/pf/pfctl -e; /opt/pf/pfctl -f /dev/null' >/dev/null
BEFORE=$(slab_snapshot)
cat > /tmp/g14.conf <<C
table <peers> { $CLI_IP }
anchor "s"
pass in quick on ingress proto icmp from <peers> keep state
pass out quick proto icmp keep state
pass out quick proto tcp from any to any port 22 keep state
C
pfload /tmp/g14.conf || { bad soak ruleset; finish; }
# mixed traffic: ICMP + TCP handshakes, 20s
cli "ping -q -c 30 -i 0.2 $SRV_IP >/dev/null 2>&1 &" 2>/dev/null
cli "for i in 1 2 3 4 5; do nc -w1 -z $SRV_IP 22; done" 2>/dev/null
sleep 22
ST=$(pf0 '/opt/pf/pfctl -s states | wc -l')
[ "${ST:-0}" -ge 1 ] && ok "states under load ($ST)" || bad "no states under load"
DMESG=$(pf0 'dmesg | grep -cE "BUG:|WARNING:|Oops|general protection"')
[ "${DMESG:-1}" = 0 ] && ok "no kernel BUG/WARN/Oops" || bad "dmesg anomalies ($DMESG)"
pf0 '/opt/pf/pfctl -d; /opt/pf/pfctl -f /dev/null' >/dev/null
pf0 'rmmod pf && echo UNLOAD_OK' | grep -q UNLOAD_OK && ok "clean unload (refcounts released)" || bad "rmmod failed"
sleep 1
pf0 'insmod /tmp/pf.ko 2>/dev/null || insmod /opt/pf/pf.ko; true; (/opt/pf/pfctl -s info >/dev/null && echo RELOAD_OK)' | grep -q RELOAD_OK && ok "reload after unload" || bad "reload failed"
pf0 'rmmod pf && echo UNLOAD_OK2' | grep -q UNLOAD_OK2 && ok "second unload" || bad "second rmmod"
sleep 1
AFTER=$(slab_snapshot)
# compare per-cache object counts if they exist on both sides
LEAK=$(join <(echo "$BEFORE") <(echo "$AFTER") 2>/dev/null | awk '$3-$2 > 0 {print $1, $3-$2}')
if [ -n "$LEAK" ]; then bad "slab growth after cycles: $LEAK"; else ok "no slab growth after unload cycles"; fi
finish