M1 progress: engine port compiles except pf.c; AF_DIVERT socket family; ifq framework

- compat layer extended through the full BSD API surface pf_ioctl/pf_lb/
  pf_if/pf_norm/pf_table/pf_ruleset/radix/md5/sha2 need (all compile):
  cpumem/counters model, ifaddr/ifg groups, cred view, endian+in6 names,
  NAT46/64, SipHash24, mbuf_list, frag6 helpers, ip6 forward/checksum,
  tcp options/FSM names, taskqueue & sysctl shims (documented in
  docs/porting-notes.md)
- pfsrc/divert_sock.c: real BSD divert(4) as runtime-registered PF_DIVERT=32
  socket family inside the module (sock_register; NPROTO=AF_MAX=45 -> zero
  core patches); divert_packet/divert6_packet wired for pf.c
- compat/pfk_ifq.c: OpenBSD 8.0 ifq framework with priq back end; hfsc and
  fq_codel aliases pending M5 (recorded in feature matrix as pending)
- design + plan updated for the divert decision (user-requested)
- stage-port.sh: single deterministic rename program for headers and .c
This commit is contained in:
lgcosta committed 2026-11-11 13:35:53 -03:00
1 parent a84b2c91f3
commit 1fcd7b3046
77 files changed
+28002 -1114

No files matched your search

+3 -1
View File
@@ -40,7 +40,7 @@ deb_cmd() { deb "bash -s" <<<"$1"; }
# Run a shell script inside a lab VM (nested ssh from deb with the lab key).
# Guest host keys change every lab rebuild -> pin-noise disabled for this hop.
VMSSH_OPTS="-o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -o LogLevel=ERROR"
vm_cmd() { local ip=$1; deb "ssh -i $LAB_KEY_DEB $VMSSH_OPTS root@$ip 'bash -s'" <<<"$2"; }
vm_cmd() { local ip=$1; deb "ssh -i $LAB_KEY_DEB $VMSSH_OPTS root@$ip 'sh -s'" <<<"$2"; }
# --- template rendering: replace @KEY@ tokens from environment + extra K=V ---
RENDER_KEYS=(
@@ -164,6 +164,7 @@ runcmd_extra() {
cat > /etc/local.d/pflab.start <<'EOS'
#!/bin/sh
sysctl -q -p /etc/sysctl.d/00-pflab.conf 2>/dev/null
for f in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > "$f" 2>/dev/null; done
IF=$(ip route show default 2>/dev/null | awk '{print $5; exit}')
[ -n "$IF" ] || exit 0
iptables -t nat -A POSTROUTING -o "$IF" -j MASQUERADE
@@ -222,6 +223,7 @@ packages_fixup() { # safety net if cloud-init ran before pf0 had transit up
for a in $VMS; do
ip=$(vm_ip "$a")
vm_cmd "$ip" 'command -v iperf3 >/dev/null 2>&1 && command -v iptables >/dev/null 2>&1 || {
grep -q nameserver /etc/resolv.conf 2>/dev/null || echo nameserver 192.168.122.1 >> /etc/resolv.conf
. /etc/os-release
grep -qE "^[^#]*/community" /etc/apk/repositories || echo "${HOME}/v${VERSION_ID}/community" >> /etc/apk/repositories
apk update -q >/dev/null 2>&1
+120 -89
View File
@@ -1,112 +1,143 @@
#!/usr/bin/env bash
# Deterministic staging of OpenBSD 8.0 sources into module/ ports.
# Deterministic staging of OpenBSD 8.0 sources into the module/ port.
#
# Every transformation is logged in docs/porting-notes.md ("porting log").
# Re-running refreshes pristine copies while PRESERVING our port edits is
# NOT done here — ported files are committed and edited in-tree thereafter;
# this script is used (a) first-time staging, (b) `--diff <file>` to compare
# our ported copy against pristine upstream for rebase forensics.
# Ported files are committed and edited in-tree afterwards; this script is
# used (a) for first-time staging, (b) with `--diff <file>` to compare our
# ported copy against the pristine upstream file (rebase forensics).
set -euo pipefail
REPO="$(cd "$(dirname "$0")/.." && pwd)"
U="$REPO/_vendor/upstream"
M="$REPO/module"
LOG="$REPO/docs/porting-notes.md"
# --- shared rename program -------------------------------------------------
# BSD type/macro names that collide with Linux uapi or with the kernel's own
# macros are renamed to our pf_* equivalents. Applied to the staged headers
# and to every ported .c file.
pf_sed() {
sed -i -E \
-e 's/\bstruct ip6_hdr\b/struct pf_ip6_hdr/g' \
-e 's/\bstruct ip\b/struct pf_ip/g' \
-e 's/\bstruct tcphdr\b/struct pf_tcphdr/g' \
-e 's/\bstruct udphdr\b/struct pf_udphdr/g' \
-e 's/\bstruct icmp\b/struct pf_icmp/g' \
-e 's/\bstruct icmp6_hdr\b/struct pf_icmp6/g' \
-e 's/\bstruct mld_hdr\b/struct pf_mld_hdr/g' \
-e 's/\bstruct nd_neighbor_solicit\b/struct pf_nd_neighbor_solicit/g' \
-e 's/\bstruct rtentry\b/struct pf_rtentry/g' \
-e 's/\bstruct ifnet\b/struct pfk_ifnet/g' \
-e 's/\bIFF_([A-Z0-9_]+)/PFIFF_\1/g' \
-e 's/\bIP_OPTIONS\b/PF_IP_OPTIONS/g' \
-e 's/\bIP_RECORD_ROUTE\b/PF_IP_RECORD_ROUTE/g' \
-e 's/\bIP_TIMESTAMP\b/PF_IP_TIMESTAMP/g' \
-e 's/ip6_fragment[(]/pfk_ip6_fragment(/g' \
-e 's/in6_proto_cksum_out[(]/pfk_in6_proto_cksum_out(/g' \
-e 's/ip6_forward[(]/pfk_ip6_forward(/g' \
-e 's/ifp->if_output[(]/pfk_if_output(/g' \
-e 's/pfk_if_output[(]ifp, m, sin6tosa[(]dst[)]/pfk_if_output(ifp, m, (struct sockaddr *)sin6tosa(dst)/g' \
-e 's/pfk_if_output[(]ifp, m, sintosa[(]dst[)]/pfk_if_output(ifp, m, (struct sockaddr *)sintosa(dst)/g' \
-e 's/^([ \t]*)[^;]*\.(sa|sin|sin6|six|sun)[0-9]*_len[[:space:]]*=[^;]*;/\1;/' \
-e 's/struct radix_node \[2\], u_int8_t\);/struct radix_node *, u_int8_t);/' \
-e '/^#define[[:space:]]+isupper\(c\)/d' \
-e '/^#define[[:space:]]+islower\(c\)/d' \
-e '/^#define[[:space:]]+isalpha\(c\)/d' \
-e 's/\btcp_mssdflt\b/pfk_tcp_mssdflt/g' \
-e 's/([a-z0-9_]+)->p_ucred->cr_ruid/pfk_pruid(\1)/g' \
-e 's/([a-z0-9_]+)->p_ucred->cr_uid/pfk_pruid(\1)/g' \
-e 's/([a-z0-9_]+)->p_p->ps_pid/pfk_ppid(\1)/g' \
-e 's/^([a-zA-Z_][a-zA-Z0-9_ \t\*]*\b[a-zA-Z_][a-zA-Z0-9_]*)\(\);/\1(void);/' \
"$@"
}
stage_hdrs() {
mkdir -p "$M/include/net" "$M/include/compat/net" "$M/include/compat/sys" "$M/include/compat/crypto"
cp -f "$U/sys/net/pfvar.h" "$M/include/net/pfvar.h"
cp -f "$U/sys/net/pfvar_priv.h" "$M/include/net/pfvar_priv.h"
cp -f "$U/sys/net/if_pflog.h" "$M/include/compat/net/if_pflog.h"
cp -f "$U/sys/net/radix.h" "$M/include/compat/net/radix.h"
cp -f "$U/sys/net/rtable.h" "$M/include/compat/net/rtable.h"
cp -f "$U/sys/net/ifq.h" "$M/include/compat/net/ifq.h"
cp -f "$U/sys/net/toeplitz.h" "$M/include/compat/net/toeplitz.h"
cp -f "$U/sys/sys/queue.h" "$M/include/compat/sys/queue.h"
cp -f "$U/sys/sys/tree.h" "$M/include/compat/sys/tree.h"
# NOTE: radix.h / siphash.h / toeplitz.h are NOT copied verbatim — our
# cleaned PFLINUX versions live in module/include/compat/{net,crypto}/
# (BSD syntax like __bounded / _BYTE_ORDER does not compile in-kernel).
mkdir -p "$M/include/net" "$M/include/compat/net" \
"$M/include/compat/sys" "$M/include/compat/crypto"
cp -f "$U/sys/net/pfvar.h" "$M/include/net/pfvar.h"
cp -f "$U/sys/net/pfvar_priv.h" "$M/include/net/pfvar_priv.h"
pf_sed "$M/include/net/pfvar.h" "$M/include/net/pfvar_priv.h"
cp -f "$U/sys/sys/queue.h" "$M/include/compat/sys/queue.h"
cp -f "$U/sys/sys/tree.h" "$M/include/compat/sys/tree.h"
# GCC requires the attribute AFTER the storage class when 'inline'
# follows (OpenBSD writes '__unused static inline').
sed -i 's/^__unused static inline/static inline/' \
"$M/include/compat/sys/tree.h"
# NOTE: radix.h / rtable.h / ifq.h / toeplitz.h / siphash.h are NOT
# copied verbatim — our cleaned PFLINUX versions live in
# module/include/compat/{net,crypto}/ (BSD syntax such as __bounded
# or _BYTE_ORDER does not compile in-kernel). Never overwrite them.
}
stage_c() { # relpath upstream -> module/compat|pf
local up="$1" dst="$2"
cp -f "$up" "$dst"
}
# port_c <upstream-sys-rel> <module-rel> : first-time staging of an OpenBSD
# .c file with deterministic transformations (all logged to PORT notes):
# 1. all original #include lines -> single '#include <pfk.h>' (+ keep
# none: pfvar is pulled by pfk.h chain when needed via -I)
# 2. BSD wire struct renames (avoid Linux-uapi collisions):
# ip6_hdr→pf_ip6_hdr, struct ip→pf_ip, tcphdr→pf_tcphdr,
# udphdr→pf_udphdr, struct icmp→pf_icmp, icmp6_hdr→icmp6hdr
# 3. BSD IFF_* flag names -> PFIFF_* (Linux owns IFF_*)
# 4. sa_len/sin_len/six_len assignment statements commented out
# 5. '#if NPFLOG > 0'-style option headers were never included (dropped)
# port_c <upstream-sys-rel> <module-rel>
# 1. every '#include' line is removed and replaced by pfk.h + the pfvar
# headers; all '#ifdef _KERNEL' guards stay intact (the module is built
# with -D_KERNEL=1, so the kernel branches are compiled).
# 2. the shared rename program is applied.
port_c() {
local up="$U/sys/$1" dst="$M/$2"
[ -f "$up" ] || { echo "no upstream $1" >&2; return 1; }
mkdir -p "$(dirname "$dst")"
if [ -f "$dst" ]; then echo "EXISTS: $2 (not re-staged; use --diff)"; return 0; fi
{
printf '/* PFLINUX port of OpenBSD 8.0 %s — see docs/porting-notes.md */\n' "$1"
printf '#include <pfk.h>\n'
# keep everything after the last original #include (body)
awk 'BEGIN{last=0} /^#include/{last=NR} END{print last}' "$up" |
{ read n; tail -n +$((n+1)) "$up"; }
} > "$dst.part"
sed -i -E \
-e 's/\bstruct ip6_hdr\b/struct pf_ip6_hdr/g' \
-e 's/\bstruct ip\b/struct pf_ip/g' \
-e 's/\bstruct tcphdr\b/struct pf_tcphdr/g' \
-e 's/\bstruct udphdr\b/struct pf_udphdr/g' \
-e 's/\bstruct icmp\b/struct pf_icmp/g' \
-e 's/\bstruct icmp6_hdr\b/struct icmp6hdr/g' \
-e 's/\bIFF_([A-Z0-9_]+)/PFIFF_\1/g' \
-e 's/\bIP_OPTIONS\b/PF_IP_OPTIONS/g' \
-e 's/\bIP_RECORD_ROUTE\b/PF_IP_RECORD_ROUTE/g' \
-e 's/\bIP_TIMESTAMP\b/PF_IP_TIMESTAMP/g' \
-e 's/->sa_len[[:space:]]*=[^;]*;/\/* PFLINUX sa_len: *\/;/g' \
-e 's/([a-z0-9_]*(sa|sin|six|sun)_len)[[:space:]]*=[^;]*;/\/* PFLINUX \1 *\/;/g' \
"$dst.part"
mv "$dst.part" "$dst"
log "port_c $1 -> $2 (includes→pfk.h; struct renames; sa_len nulled)"
local up="$U/sys/$1" dst="$M/$2"
[ -f "$up" ] || { echo "no upstream $1" >&2; return 1; }
mkdir -p "$(dirname "$dst")"
if [ -f "$dst" ]; then
echo "EXISTS: $2 (not re-staged; use --diff)"
return 0
fi
{
printf '/* PFLINUX port of OpenBSD 8.0 %s — see docs/porting-notes.md */\n' "$1"
printf '#include <pfk.h>\n'
printf '#include <net/pfvar.h>\n'
printf '#include <net/pfvar_priv.h>\n\n'
sed -E '/^[[:space:]]*#include/d' "$up"
} > "$dst.part"
pf_sed "$dst.part"
mv "$dst.part" "$dst"
log "port_c $1 -> $2 (includes dropped -> pfk.h; -D_KERNEL mode; renames)"
}
PORTED_LIST='net/pf.c net/pf_ioctl.c net/pf_norm.c net/pf_if.c net/pf_lb.c
net/pf_ruleset.c net/pf_table.c net/pf_osfp.c net/pf_syncookies.c
net/ifq.c net/hfsc.c net/fq_codel.c net/if_pflog.c netinet/ip_divert.c
crypto/md5.c crypto/sha2.c'
# NOTE: radix.c/rtable.c (ported by hand in T10 against our cleaned
# compat/net/radix.h) and siphash/toeplitz (own PFLINUX impls) are
# deliberately NOT in the auto-port list.
# radix.c/rtable.c (hand-ported against our cleaned compat/net/radix.h) and
# siphash/toeplitz (own PFLinux implementations) are deliberately NOT in the
# automatic list.
log() { mkdir -p "$(dirname "$LOG")"; printf '\n%s\n```sh\n%s\n```\n' "## $(date -Is) staging" "$*" >> "$LOG"; }
log() {
mkdir -p "$(dirname "$LOG")"
printf '\n## %s staging\n\n```sh\n%s\n```\n' "$(date -Is)" "$*" >> "$LOG"
}
cmd="${1:-stage}"
case "$cmd" in
stage)
log "stage: headers + skeletons"
stage_hdrs
echo "headers staged: $(find "$M/include/net" "$M/include/compat" -type f | wc -l)"
;;
port)
shift
for f in ${*:-$PORTED_LIST}; do
case "$f" in
net/pf.c|net/pf_ioctl.c|net/pf_norm.c|net/pf_if.c|net/pf_lb.c|\
net/pf_ruleset.c|net/pf_table.c|net/pf_osfp.c|net/pf_syncookies.c|\
net/ifq.c|net/hfsc.c|net/fq_codel.c|net/if_pflog.c)
port_c "$f" "pfsrc/${f##*/}";;
netinet/ip_divert.c)
port_c "$f" "pfsrc/ip_divert.c";;
crypto/md5.c|crypto/sha2.c)
port_c "$f" "compat/${f##*/}";;
esac
done
;;
--diff)
[ -f "$U/$2" ] || { echo "no pristine $2"; exit 1; }
diff -u "$U/$2" "$M/$2" | head -80
;;
*) echo "usage: stage-port.sh [stage|--diff relpath]"; exit 1;;
stage)
log "stage: headers"
stage_hdrs
echo "headers staged: $(find "$M/include/net" "$M/include/compat" -type f | wc -l)"
;;
port)
shift
for f in ${*:-$PORTED_LIST}; do
case "$f" in
net/pf.c|net/pf_ioctl.c|net/pf_norm.c|net/pf_if.c|net/pf_lb.c|\
net/pf_ruleset.c|net/pf_table.c|net/pf_osfp.c|net/pf_syncookies.c|\
net/ifq.c|net/hfsc.c|net/fq_codel.c|net/if_pflog.c)
port_c "$f" "pfsrc/${f##*/}" ;;
net/radix.c|net/rtable.c)
port_c "$f" "compat/${f##*/}" ;;
netinet/ip_divert.c)
port_c "$f" "pfsrc/ip_divert.c" ;;
crypto/md5.c|crypto/sha2.c)
port_c "$f" "compat/${f##*/}" ;;
esac
done
;;
--diff)
[ -f "$U/$2" ] || { echo "no pristine $2"; exit 1; }
diff -u "$U/$2" "$M/$2" | head -80
;;
*)
echo "usage: stage-port.sh [stage|port [files...]|--diff relpath]"
exit 1
;;
esac
+4 -3
View File
@@ -28,7 +28,6 @@ done
lab_env
command -v rsync >/dev/null || die "rsync missing on laptop"
command -v genisoimage >/dev/null 2>&1 || true # iso is built on deb, not here
host_prereqs
ensure_networks
@@ -55,7 +54,9 @@ if [ -n "$PF0_KERNEL" ]; then
rsync -a -e "ssh ${SSH_OPTS[*]}" "$PF0_INITRD" "$DEB_USER@$DEB:$BOOT_DIR/initrd"
IPATH="$BOOT_DIR/initrd"
fi
PF0_KB=" <kernel>$KPATH</kernel>"$'\n'" <initrd>$IPATH</initrd>"$'\n'" <cmdline>root=/dev/vda2 ro console=ttyS0,115200n8</cmdline>"
# with an initrd, Alpine's initramfs needs its module list on the cmdline;
# the no-initrd (built-in drivers, T6) case keeps the plain contract cmdline
PF0_KB=" <kernel>$KPATH</kernel>"$'\n'" <initrd>$IPATH</initrd>"$'\n'" <cmdline>root=/dev/vda2 ro console=ttyS0,115200n8 modules=sd-mod,usb-storage,ext4,ena,gve</cmdline>"
fi
log "pf0 direct kernel boot override: $KPATH"
fi
@@ -72,6 +73,6 @@ deb_ssh_config
laptop_ssh_config
log "validate: ssh pf0 'ip -br a'"
ssh "${SSH_OPTS[@]}" pf0 'ip -br a' || warn "ssh pf0 failed — check $LAPTOP_SSH_FRAG"
ssh "${SSH_OPTS[@]}" pf0 'ip -br a' || die "ssh pf0 failed — check $LAPTOP_SSH_FRAG"
log "LAB UP: aliases on laptop: ssh pf0|cli|srv|gw2 (ProxyJump $DEB_USER@$DEB)"
deb "virsh list" || true
+4 -3
View File
@@ -13,16 +13,17 @@ lab_env
cd "$REPO"
FILES=()
add() { # add <path...> — files only; dirs expanded
add() { # add <path...> — files only; dirs expanded; warns on explicit misses
local p
for p in "$@"; do
if [ -d "$p" ]; then add "$p"/*
elif [ -f "$p" ]; then FILES+=("$p")
elif [ "$EXPLICIT" = 1 ]; then warn "skip missing: $p"
fi
done
}
if [ $# -gt 0 ]; then
add "$@"
EXPLICIT=0
if [ $# -gt 0 ]; then EXPLICIT=1; add "$@"
else
shopt -s nullglob
add out/linux-*-bzImage out/bzImage out/*.ko module/*.ko tools/out/*