M1: compat layer + pf.ko skeleton builds against Linux 7.3.4
- pfk.h: BSD types/ioctl encoding (OpenBSD bit layout), locks (NET_LOCK=rwsem), pools, timeouts, mbuf shim over sk_buff, ifnet registry, toeplitz/siphash - pfk_mod/pfk_hooks: misc /dev/pf (0600), IPv4 netfilter hook skeleton, DIOCGETSTATUS answered (M1 skeleton; engine ioctls land with pf_ioctl.c) - vendorize/stage-port scripts: deterministic upstream staging with seds (struct renames, IFF_*->PFIFF_*, sa_len drops) logged to docs/porting-notes - fixes: modules_prepare+Module.symvers for out-of-tree builds
This commit is contained in:
1 parent
ba81284bd3
commit
da2e9b679a
87 files changed
+47256
-3
No files matched your search
@@ -6,7 +6,11 @@ K="${KERNELDIR:-$(cd "$REPO/../linux-7.3.4" && pwd)}"
|
||||
cd "$K"
|
||||
"$REPO/scripts/kernel-config.sh" "${1:-}"
|
||||
JOBS="${JOBS:-$(($(nproc)-2))}"
|
||||
make -j"$JOBS" -s bzImage modules
|
||||
make -j"$JOBS" -s bzImage modules_prepare
|
||||
# External module builds need Module.symvers (kbuild only writes
|
||||
# vmlinux.symvers) and scripts/module.lds (from modules_prepare above).
|
||||
[ -f Module.symvers ] || cp vmlinux.symvers Module.symvers
|
||||
[ -f scripts/module.lds ] || { echo "FATAL: scripts/module.lds missing" >&2; exit 1; }
|
||||
mkdir -p "$REPO/out"
|
||||
KVER="$(make -s kernelversion)"
|
||||
cp -f arch/x86/boot/bzImage "$REPO/out/linux-${KVER}-bzImage"
|
||||
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build pf.ko out-of-tree against the patched kernel tree.
|
||||
set -euo pipefail
|
||||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
K="${KERNELDIR:-$(cd "$REPO/../linux-7.3.4" && pwd)}"
|
||||
[ -e "$K/Module.symvers" ] || {
|
||||
if [ -f "$K/vmlinux.symvers" ]; then cp "$K/vmlinux.symvers" "$K/Module.symvers";
|
||||
else echo "kernel tree not prepared — run scripts/build-kernel.sh first" >&2; exit 1; fi; }
|
||||
[ -f "$K/scripts/module.lds" ] || make -C "$K" -j4 modules_prepare >/dev/null
|
||||
make -C "$K" M="$REPO/module" "$@"
|
||||
echo MODULE_BUILD_OK
|
||||
Executable
+283
@@ -0,0 +1,283 @@
|
||||
#!/usr/bin/env bash
|
||||
# lab-lib.sh — helpers shared by vm-create.sh / vm-destroy.sh / vm-deploy.sh.
|
||||
# Source it; it never executes anything on its own. Topology single-source:
|
||||
# lab/ips.env. Disks: per-VM qcow2 with backing_file to the shared read-only
|
||||
# alpine-base.qcow2 (fast creates, ~MBs of delta storage; the base MUST NOT be
|
||||
# modified once children exist — vm-create only re-syncs it when the laptop
|
||||
# source hash differs).
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
LAB_LIB="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
: "${REPO:=$(cd "$(dirname "$LAB_LIB")/.." && pwd)}"
|
||||
export REPO
|
||||
|
||||
log() { printf 'pflab: %s\n' "$*"; }
|
||||
warn() { printf 'pflab WARN: %s\n' "$*" >&2; }
|
||||
die() { printf 'pflab FAIL: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
lab_env() {
|
||||
[ -f "$REPO/lab/ips.env" ] || die "missing $REPO/lab/ips.env"
|
||||
# shellcheck disable=SC1091
|
||||
. "$REPO/lab/ips.env"
|
||||
# bare-IP derivatives (route/via fields)
|
||||
PF0_LAN_IP=${PF0_LAN%%/*}; PF0_WAN_IP=${PF0_WAN%%/*}; PF0_WAN2_IP=${PF0_WAN2%%/*}
|
||||
CLI_IP=${CLI%%/*}; SRV_IP=${SRV%%/*}; GW2_WAN_IP=${GW2_WAN%%/*}; GW2_WAN2_IP=${GW2_WAN2%%/*}
|
||||
LAB_SEEDS="$REPO/lab/seeds"
|
||||
VMS="pf0 cli srv gw2"
|
||||
}
|
||||
|
||||
SSH_OPTS=(-o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10
|
||||
-o LogLevel=ERROR -o ControlMaster=auto -o ControlPersist=120
|
||||
-o "ControlPath=$HOME/.ssh/cm-pflab-%r@%h:%p")
|
||||
|
||||
vm_name() { echo "pftest-$1"; }
|
||||
vm_ip() { local v=${1^^}_SSH; [ -n "${!v-}" ] || die "unknown vm alias: $1"; echo "${!v}"; }
|
||||
|
||||
# Run command(s) on deb.
|
||||
deb() { ssh "${SSH_OPTS[@]}" "$DEB_USER@$DEB" "$@"; }
|
||||
deb_cmd() { deb "bash -s" <<<"$1"; }
|
||||
|
||||
# Run a shell script inside a lab VM (nested ssh from deb with the lab key).
|
||||
vm_cmd() { local ip=$1; deb "ssh -i $LAB_KEY_DEB ${SSH_OPTS[*]} root@$ip 'bash -s'" <<<"$2"; }
|
||||
|
||||
# --- template rendering: replace @KEY@ tokens from environment + extra K=V ---
|
||||
RENDER_KEYS=(
|
||||
DEB DEB_USER
|
||||
PF0_MGMT PF0_LAN PF0_WAN PF0_WAN2 CLI CLI_GW SRV SRV_GW GW2_WAN2 GW2_WAN
|
||||
PF0_SSH CLI_SSH SRV_SSH GW2_SSH
|
||||
PF0_LAN_IP PF0_WAN_IP PF0_WAN2_IP CLI_IP SRV_IP GW2_WAN_IP GW2_WAN2_IP
|
||||
MGMT_NET NET_PF_LAN NET_PF_WAN NET_PF_WAN2
|
||||
PF_LAN_HOSTIP PF_WAN_HOSTIP PF_WAN2_HOSTIP PF_NET_PREFIX
|
||||
VM_VCPUS VM_MEM_MB
|
||||
PF0_MAC_MGMT PF0_MAC_LAN PF0_MAC_WAN PF0_MAC_WAN2
|
||||
CLI_MAC_MGMT CLI_MAC_LAN SRV_MAC_MGMT SRV_MAC_WAN GW2_MAC_WAN GW2_MAC_WAN2
|
||||
ROOT_PASS LAB_PACKAGES
|
||||
IMG_DIR BASE_IMG SEED_DIR DEPLOY_DIR BOOT_DIR
|
||||
LAB_KEY_DEB LAB_KEY_LAPTOP LAPTOP_SSH_FRAG BASE_SRC_REL
|
||||
)
|
||||
_esc() { local s=$1; s=${s//\\/\\\\}; s=${s//&/\\&}; s=${s//\~/\\~}; s=${s//$'\n'/\\n}; printf '%s' "$s"; }
|
||||
render() { # render <template> <out> [KEY=VALUE ...]
|
||||
local tmpl=$1 out=$2 kv; shift 2
|
||||
local -a prog=()
|
||||
for kv in "$@"; do
|
||||
prog+=(-e "s~@${kv%%=*}@~$(_esc "${kv#*=}")~g")
|
||||
done
|
||||
for k in "${RENDER_KEYS[@]}"; do
|
||||
prog+=(-e "s~@$k@~$(_esc "${!k-}")~g")
|
||||
done
|
||||
sed "${prog[@]}" "$tmpl" > "$out"
|
||||
}
|
||||
|
||||
# --- deb host prerequisites ---
|
||||
host_prereqs() {
|
||||
deb true || die "cannot ssh $DEB_USER@$DEB (key auth? host up?)"
|
||||
deb_cmd "set -e
|
||||
systemctl is-active --quiet libvirtd || systemctl enable --now libvirtd 2>/dev/null || systemctl enable --now virtqemud
|
||||
virsh -c qemu:///system list --all >/dev/null
|
||||
command -v rsync >/dev/null || { apt-get update -qq; DEBIAN_FRONTEND=noninteractive apt-get install -y -qq rsync; }
|
||||
command -v genisoimage >/dev/null || { DEBIAN_FRONTEND=noninteractive apt-get install -y -qq genisoimage; }
|
||||
mkdir -p $SEED_DIR $BOOT_DIR"
|
||||
}
|
||||
|
||||
# --- libvirt networks: default (start+autostart) + isolated pf-* L2 nets ---
|
||||
# pf-* carry a host bridge IP (.254) so deb can ssh/icmp into VM data NICs;
|
||||
# no <dhcp> and forward=none -> pure isolated segments, guest gw IPs are
|
||||
# pf0 itself (no host/guest address conflict).
|
||||
ensure_networks() {
|
||||
deb "virsh net-start $MGMT_NET" 2>/dev/null || true
|
||||
deb "virsh net-autostart $MGMT_NET" || true
|
||||
local entry n b ip
|
||||
for entry in "pf-lan $NET_PF_LAN $PF_LAN_HOSTIP" "pf-wan $NET_PF_WAN $PF_WAN_HOSTIP" "pf-wan2 $NET_PF_WAN2 $PF_WAN2_HOSTIP"; do
|
||||
set -- $entry; b=$1; n=$2; ip=$3
|
||||
if ! deb "virsh net-info $n" >/dev/null 2>&1; then
|
||||
printf '<network>\n <name>%s</name>\n <forward mode="none"/>\n <bridge name="%s" stp="on" delay="0"/>\n <ip address="%s" netmask="%s"/>\n</network>\n' \
|
||||
"$n" "virbr-$b" "$ip" "$PF_NET_PREFIX" | deb "virsh net-define /dev/stdin" >/dev/null
|
||||
fi
|
||||
deb "virsh net-start $n" 2>/dev/null || true
|
||||
deb "virsh net-autostart $n" || true
|
||||
done
|
||||
}
|
||||
|
||||
# --- lab SSH keypair: private stays on deb; laptop keeps a copy for ProxyJump ---
|
||||
lab_keypair() {
|
||||
deb "test -f $LAB_KEY_DEB" || deb "ssh-keygen -q -t ed25519 -N '' -f $LAB_KEY_DEB -C pflab@deb"
|
||||
LAB_PUBKEY=$(deb "cat $LAB_KEY_DEB.pub")
|
||||
[ -n "$LAB_PUBKEY" ] || die "could not read lab pubkey on deb"
|
||||
if [ ! -f "$LAB_KEY_LAPTOP" ] || ! cmp -s <(deb "cat $LAB_KEY_DEB") "$LAB_KEY_LAPTOP"; then
|
||||
umask 077
|
||||
deb "cat $LAB_KEY_DEB" > "$LAB_KEY_LAPTOP"
|
||||
deb "cat $LAB_KEY_DEB.pub" > "$LAB_KEY_LAPTOP.pub"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- shared base image (laptop _vendor -> deb), synced once by sha256 ---
|
||||
base_image_sync() {
|
||||
local lsha rsha=""
|
||||
[ -f "$REPO/$BASE_SRC_REL" ] || die "base image missing: $REPO/$BASE_SRC_REL"
|
||||
lsha=$(sha256sum "$REPO/$BASE_SRC_REL" | cut -d' ' -f1)
|
||||
if [ "${RECREATE_BASE:-0}" != 1 ]; then
|
||||
rsha=$(deb "sha256sum $BASE_IMG 2>/dev/null" | cut -d' ' -f1 || true)
|
||||
fi
|
||||
if [ "$lsha" = "$rsha" ]; then log "base image current on deb"; return 0; fi
|
||||
log "syncing base image -> deb:$BASE_IMG"
|
||||
rsync -a --info=progress2 -e "ssh ${SSH_OPTS[*]}" "$REPO/$BASE_SRC_REL" "$DEB_USER@$DEB:$BASE_IMG"
|
||||
}
|
||||
|
||||
# --- guest boot kernel: the alpine cloud qcow2 ships an EMPTY ESP + no MBR
|
||||
# code, so it is not BIOS- nor UEFI-bootable in QEMU as delivered. We extract
|
||||
# the image's own kernel+initramfs (once, cached on deb) and direct-boot all
|
||||
# VMs with root=UUID of the base's ext4 partition (the same backing image).
|
||||
ensure_guest_kernel() {
|
||||
deb "test -f $BOOT_DIR/guest-vmlinuz -a -f $BOOT_DIR/guest-initramfs -a -f $BOOT_DIR/guest-root.uuid" \
|
||||
&& return 0
|
||||
log "extracting guest kernel/initramfs from base image"
|
||||
deb_cmd "set -e
|
||||
modprobe nbd max_part=8 2>/dev/null || true
|
||||
qemu-nbd -r -c /dev/nbd1 $BASE_IMG
|
||||
trap 'umount /mnt/pflab-extract 2>/dev/null || true; qemu-nbd -d /dev/nbd1' EXIT
|
||||
for i in 1 2 3 4 5; do [ -b /dev/nbd1p2 ] && break; sleep 1; done
|
||||
[ -b /dev/nbd1p2 ]
|
||||
mkdir -p /mnt/pflab-extract $BOOT_DIR
|
||||
mount -o ro /dev/nbd1p2 /mnt/pflab-extract
|
||||
cp /mnt/pflab-extract/boot/vmlinuz-lts $BOOT_DIR/guest-vmlinuz
|
||||
cp /mnt/pflab-extract/boot/initramfs-lts $BOOT_DIR/guest-initramfs
|
||||
blkid -s UUID -o value /dev/nbd1p2 > $BOOT_DIR/guest-root.uuid
|
||||
chmod a+r $BOOT_DIR/guest-vmlinuz $BOOT_DIR/guest-initramfs $BOOT_DIR/guest-root.uuid"
|
||||
}
|
||||
|
||||
default_kernelblock() { # prints kernelblock xml for extracted direct boot
|
||||
local uuid
|
||||
uuid=$(deb "cat $BOOT_DIR/guest-root.uuid")
|
||||
printf ' <kernel>%s/guest-vmlinuz</kernel>\n <initrd>%s/guest-initramfs</initrd>\n <cmdline>root=UUID=%s ro console=ttyS0,115200n8</cmdline>' \
|
||||
"$BOOT_DIR" "$BOOT_DIR" "$uuid"
|
||||
}
|
||||
|
||||
# --- per-role runcmd additions ---
|
||||
runcmd_extra() {
|
||||
case $1 in
|
||||
pf0)
|
||||
cat <<'EOF'
|
||||
- |
|
||||
mkdir -p /etc/local.d
|
||||
cat > /etc/local.d/pflab.start <<'EOS'
|
||||
#!/bin/sh
|
||||
sysctl -q -p /etc/sysctl.d/00-pflab.conf 2>/dev/null
|
||||
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
|
||||
EOS
|
||||
chmod +x /etc/local.d/pflab.start
|
||||
rc-update add local boot >/dev/null 2>&1 || true
|
||||
/etc/local.d/pflab.start
|
||||
EOF
|
||||
;;
|
||||
cli) echo " - ip route replace default via $CLI_GW" ;;
|
||||
srv) echo " - ip route replace default via $SRV_GW" ;;
|
||||
gw2) echo " - ip route replace default via $PF0_WAN_IP" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# --- one VM: seed iso + backing disk + domain define/start ---
|
||||
create_domain() { # create_domain <alias> [kernelblock]
|
||||
local a=$1 kblock=${2:-} name disk extra seeds
|
||||
name=$(vm_name "$a"); disk="$IMG_DIR/$name.qcow2"
|
||||
seeds="$LAB_SEEDS/$name"
|
||||
deb_cmd "virsh destroy $name 2>/dev/null; virsh undefine $name --nvram 2>/dev/null; virsh undefine $name 2>/dev/null; rm -f /var/lib/libvirt/qemu/nvram/$name*.raw; true"
|
||||
rm -rf "$seeds"; mkdir -p "$seeds"
|
||||
extra=$(runcmd_extra "$a")
|
||||
render "$REPO/lab/cloud-init/user-data.tmpl" "$seeds/user-data" \
|
||||
"VM=$a" "RUNCMD_EXTRA=$extra" "SSH_PUBKEY=$LAB_PUBKEY"
|
||||
render "$REPO/lab/cloud-init/meta-data.tmpl" "$seeds/meta-data" "VM=$a"
|
||||
render "$REPO/lab/cloud-init/network-config.$a.tmpl" "$seeds/network-config"
|
||||
render "$REPO/lab/domains/$name.xml.tmpl" "$seeds/$name.xml" \
|
||||
"NAME=$name" "VM=$a" "DISK=$disk" "SEED=$SEED_DIR/$name/seed.iso" "KERNELBLOCK=$kblock"
|
||||
rsync -rt --delete --chmod=D0755,F0644 -e "ssh ${SSH_OPTS[*]}" "$seeds/" "$DEB_USER@$DEB:$SEED_DIR/$name/"
|
||||
deb_cmd "cd '$SEED_DIR/$name' && rm -f seed.iso && \
|
||||
genisoimage -quiet -o seed.iso -volid CIDATA -joliet -rock user-data meta-data network-config"
|
||||
deb_cmd "chmod a+rX '$SEED_DIR' '$SEED_DIR/$name'"
|
||||
deb_cmd "rm -f '$disk' && qemu-img create -f qcow2 -F qcow2 -b '$BASE_IMG' '$disk' >/dev/null"
|
||||
deb "virsh define $SEED_DIR/$name/$name.xml" >/dev/null
|
||||
deb "virsh start $name" >/dev/null
|
||||
log "started $name"
|
||||
}
|
||||
|
||||
wait_ready() { # wait_ready <alias> [timeout-s] — polls cloud-init done marker
|
||||
local a=$1 t=${2:-300} ip name el=0
|
||||
ip=$(vm_ip "$a"); name=$(vm_name "$a")
|
||||
while :; do
|
||||
if vm_cmd "$ip" 'test -f /run/pf-lab-ready' 2>/dev/null; then
|
||||
log "$a ready"; return 0
|
||||
fi
|
||||
deb "LC_ALL=C virsh dominfo $name 2>/dev/null | grep -q running" \
|
||||
|| die "$name is not running (virsh console $name)"
|
||||
sleep 5; el=$((el+5))
|
||||
[ "$el" -ge "$t" ] && die "timeout waiting for $a cloud-init"
|
||||
done
|
||||
}
|
||||
|
||||
packages_fixup() { # safety net if cloud-init ran before pf0 had transit up
|
||||
local a ip
|
||||
for a in $VMS; do
|
||||
ip=$(vm_ip "$a")
|
||||
vm_cmd "$ip" 'command -v iperf3 >/dev/null 2>&1 || {
|
||||
. /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
|
||||
apk add -q iperf3 tcpdump python3 py3-scapy bash >/dev/null 2>&1 || warn "apk add failed on '"$a"'"
|
||||
}' || warn "no ssh to $a for package fixup"
|
||||
done
|
||||
}
|
||||
|
||||
# --- ssh access plumbing: aliases pf0|cli|srv|gw2 from deb and from laptop ---
|
||||
laptop_ssh_config() {
|
||||
mkdir -p "$(dirname "$LAPTOP_SSH_FRAG")"; chmod 700 "$(dirname "$LAPTOP_SSH_FRAG")" 2>/dev/null || true
|
||||
cat > "$LAPTOP_SSH_FRAG" <<EOF
|
||||
# >>> generated by pf_port/scripts/vm-create.sh (topology: lab/ips.env) >>>
|
||||
Host pf0 cli srv gw2
|
||||
User root
|
||||
IdentityFile $LAB_KEY_LAPTOP
|
||||
ProxyJump $DEB_USER@$DEB
|
||||
StrictHostKeyChecking accept-new
|
||||
UserKnownHostsFile $HOME/.ssh/known_hosts_pflab
|
||||
ConnectTimeout 15
|
||||
ServerAliveInterval 15
|
||||
Host pf0
|
||||
HostName $PF0_SSH
|
||||
Host cli
|
||||
HostName $CLI_SSH
|
||||
Host srv
|
||||
HostName $SRV_SSH
|
||||
Host gw2
|
||||
HostName $GW2_SSH
|
||||
# <<< generated <<<
|
||||
EOF
|
||||
touch ~/.ssh/config
|
||||
grep -qE '^Include[[:space:]]+~/.ssh/config.d' ~/.ssh/config || {
|
||||
cp -p ~/.ssh/config ~/.ssh/config.pflab-bak 2>/dev/null || true
|
||||
printf 'Include ~/.ssh/config.d/*\n' | cat - ~/.ssh/config > ~/.ssh/config.tmp && mv ~/.ssh/config.tmp ~/.ssh/config
|
||||
log "added Include ~/.ssh/config.d/* to ~/.ssh/config (backup: ~/.ssh/config.pflab-bak)"
|
||||
}
|
||||
}
|
||||
|
||||
deb_ssh_config() {
|
||||
deb_cmd 'f=$HOME/.ssh/config; touch "$f"
|
||||
sed -i "/# >>> pflab/,/# <<< pflab/d" "$f"
|
||||
cat >> "$f" <<EOF
|
||||
# >>> pflab (generated by vm-create.sh) >>>
|
||||
Host pf0 cli srv gw2
|
||||
User root
|
||||
IdentityFile '"$LAB_KEY_DEB"'
|
||||
StrictHostKeyChecking accept-new
|
||||
LogLevel ERROR
|
||||
ConnectTimeout 8
|
||||
Host pf0
|
||||
HostName '"$PF0_SSH"'
|
||||
Host cli
|
||||
HostName '"$CLI_SSH"'
|
||||
Host srv
|
||||
HostName '"$SRV_SSH"'
|
||||
Host gw2
|
||||
HostName '"$GW2_SSH"'
|
||||
# <<< pflab <<<
|
||||
EOF'
|
||||
}
|
||||
Executable
+109
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deterministic staging of OpenBSD 8.0 sources into module/ ports.
|
||||
# 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.
|
||||
set -euo pipefail
|
||||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
U="$REPO/_vendor/upstream"
|
||||
M="$REPO/module"
|
||||
LOG="$REPO/docs/porting-notes.md"
|
||||
|
||||
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).
|
||||
}
|
||||
|
||||
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() {
|
||||
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)"
|
||||
}
|
||||
|
||||
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'
|
||||
# NOTE: radix.c/rtable.c (ported by hand in T10 against our cleaned
|
||||
# compat/net/radix.h) and siphash/toeplitz/sha2 (own PFLINUX impls) are
|
||||
# deliberately NOT in the auto-port list.
|
||||
|
||||
log() { mkdir -p "$(dirname "$LOG")"; printf '\n%s\n```sh\n%s\n```\n' "## $(date -Is) staging" "$*" >> "$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";;
|
||||
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;;
|
||||
esac
|
||||
@@ -9,8 +9,9 @@ SYS_K=(net/pf.c net/pf_ioctl.c net/pf_norm.c net/pf_if.c net/pf_lb.c net/pf_rule
|
||||
net/pf_table.c net/pf_osfp.c net/pf_syncookies.c net/pfvar.h net/pfvar_priv.h
|
||||
net/if_pflog.c net/if_pflog.h net/ifq.c net/ifq.h net/hfsc.c net/hfsc.h
|
||||
net/fq_codel.c net/fq_codel.h netinet/ip_divert.c netinet/ip_divert.h
|
||||
net/radix.c net/radix.h net/radix_node.h net/rtable.c net/rtable.h
|
||||
sys/tree.h sys/queue.h sys/param.h sys/hash.h)
|
||||
net/radix.c net/radix.h net/rtable.c net/rtable.h net/toeplitz.c net/toeplitz.h
|
||||
crypto/sha2.c crypto/sha2.h crypto/siphash.c crypto/siphash.h
|
||||
sys/tree.h sys/queue.h sys/param.h sys/hash.h sys/_endian.h sys/_lock.h)
|
||||
SRC_K=(sbin/pfctl/parse.y sbin/pfctl/pfctl.c sbin/pfctl/pfctl.h sbin/pfctl/pfctl_parser.c
|
||||
sbin/pfctl/pfctl_parser.h sbin/pfctl/pfctl_optimize.c sbin/pfctl/pfctl_print.c
|
||||
sbin/pfctl/pfctl_osfp.c sbin/pfctl/pfctl_queue.c sbin/pfctl/pfctl_radix.c
|
||||
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
# vm-create.sh — build (or rebuild) the pftest-* libvirt lab on deb.
|
||||
# Topology + paths: lab/ips.env. Idempotent: pre-existing pftest-* domains,
|
||||
# disks and seeds are torn down and recreated; shared base image and data
|
||||
# networks are reused when already correct. Leave all VMs running at exit.
|
||||
#
|
||||
# --pf0-kernel=PATH direct kernel boot for pf0 (laptop path is rsynced to
|
||||
# deb:BOOT_DIR/bzImage; a deb-local path is used as-is).
|
||||
# cmdline: root=/dev/vda2 ro console=ttyS0
|
||||
# --pf0-initrd=PATH optional initrd alongside --pf0-kernel
|
||||
# --recreate-base force re-sync of alpine-base.qcow2 from _vendor
|
||||
# -h help
|
||||
set -euo pipefail
|
||||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
# shellcheck disable=SC1091
|
||||
. "$REPO/scripts/lab-lib.sh"
|
||||
|
||||
PF0_KERNEL=""; PF0_INITRD=""; RECREATE_BASE=0
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--pf0-kernel=*) PF0_KERNEL=${arg#*=}; [ -n "$PF0_KERNEL" ] || die "--pf0-kernel needs a value" ;;
|
||||
--pf0-initrd=*) PF0_INITRD=${arg#*=} ;;
|
||||
--recreate-base) RECREATE_BASE=1 ;;
|
||||
-h|--help) sed -n '2,12p' "$0"; exit 0 ;;
|
||||
*) die "unknown arg: $arg (-h for usage)" ;;
|
||||
esac
|
||||
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
|
||||
lab_keypair
|
||||
base_image_sync
|
||||
|
||||
# direct kernel boot: every VM boots via the image's own extracted kernel
|
||||
# (base image is not BIOS/UEFI bootable, see lab-lib.sh); --pf0-kernel
|
||||
# replaces pf0's kernel with a built bzImage (T6), keeping its root on vda2.
|
||||
ensure_guest_kernel
|
||||
DEFAULT_KB=$(default_kernelblock)
|
||||
PF0_KB="$DEFAULT_KB"
|
||||
if [ -n "$PF0_KERNEL" ]; then
|
||||
deb "mkdir -p $BOOT_DIR"
|
||||
KPATH="$PF0_KERNEL"
|
||||
if [ -f "$PF0_KERNEL" ]; then
|
||||
rsync -a -e "ssh ${SSH_OPTS[*]}" "$PF0_KERNEL" "$DEB_USER@$DEB:$BOOT_DIR/bzImage"
|
||||
KPATH="$BOOT_DIR/bzImage"
|
||||
fi
|
||||
PF0_KB=" <kernel>$KPATH</kernel>"$'\n'" <cmdline>root=/dev/vda2 ro console=ttyS0,115200n8</cmdline>"
|
||||
if [ -n "$PF0_INITRD" ]; then
|
||||
IPATH="$PF0_INITRD"
|
||||
if [ -f "$PF0_INITRD" ]; 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>"
|
||||
fi
|
||||
log "pf0 direct kernel boot override: $KPATH"
|
||||
fi
|
||||
|
||||
# pf0 first: cli/srv/gw2 cloud-init needs pf0's transit+masq for pkgs
|
||||
create_domain pf0 "$PF0_KB"
|
||||
wait_ready pf0 420
|
||||
|
||||
for a in cli srv gw2; do create_domain "$a" "$DEFAULT_KB"; done
|
||||
for a in cli srv gw2; do wait_ready "$a"; done
|
||||
|
||||
packages_fixup
|
||||
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"
|
||||
log "LAB UP: aliases on laptop: ssh pf0|cli|srv|gw2 (ProxyJump $DEB_USER@$DEB)"
|
||||
deb "virsh list" || true
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
# vm-deploy.sh — ship build artifacts laptop -> deb -> pf0.
|
||||
# Default sources (tolerated when absent): out/linux-*-bzImage / out/bzImage,
|
||||
# module/*.ko, tools/out/* (files or dirs). Explicit paths as args override.
|
||||
# deb landing zone: $DEPLOY_DIR (/var/lib/pflab/deploy); pf0 gets /opt/pf/.
|
||||
# Transfer into the guest uses rsync when the guest has it, else scp.
|
||||
# Exit 0 with a warning when there is nothing to deploy (T6/T7 call freely).
|
||||
set -euo pipefail
|
||||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
# shellcheck disable=SC1091
|
||||
. "$REPO/scripts/lab-lib.sh"
|
||||
lab_env
|
||||
|
||||
cd "$REPO"
|
||||
FILES=()
|
||||
add() { # add <path...> — files only; dirs expanded
|
||||
local p
|
||||
for p in "$@"; do
|
||||
if [ -d "$p" ]; then add "$p"/*
|
||||
elif [ -f "$p" ]; then FILES+=("$p")
|
||||
fi
|
||||
done
|
||||
}
|
||||
if [ $# -gt 0 ]; then
|
||||
add "$@"
|
||||
else
|
||||
shopt -s nullglob
|
||||
add out/linux-*-bzImage out/bzImage out/*.ko module/*.ko tools/out/*
|
||||
fi
|
||||
if [ ${#FILES[@]} -eq 0 ]; then
|
||||
log "nothing to deploy (out/ bzImage, module/*.ko, tools/out/* all absent) — OK"
|
||||
exit 0
|
||||
fi
|
||||
log "deploying ${#FILES[@]} artifact(s): ${FILES[*]#$REPO/}"
|
||||
|
||||
vm_cmd "$(vm_ip pf0)" 'true' 2>/dev/null || die "pf0 unreachable — run ./scripts/vm-create.sh first"
|
||||
|
||||
deb "mkdir -p $DEPLOY_DIR"
|
||||
rsync -a -e "ssh ${SSH_OPTS[*]}" "${FILES[@]}" "$DEB_USER@$DEB:$DEPLOY_DIR/"
|
||||
log "staged on deb: $DEPLOY_DIR"
|
||||
|
||||
vm_cmd "$(vm_ip pf0)" 'mkdir -p /opt/pf'
|
||||
if deb "command -v rsync" >/dev/null 2>&1 && \
|
||||
vm_cmd "$(vm_ip pf0)" 'command -v rsync' >/dev/null 2>&1; then
|
||||
deb_cmd "rsync -a -e 'ssh -i $LAB_KEY_DEB ${SSH_OPTS[*]}' '$DEPLOY_DIR/' root@$(vm_ip pf0):/opt/pf/"
|
||||
else
|
||||
# no rsync in guest: per-file scp
|
||||
deb_cmd "for f in '$DEPLOY_DIR'/*; do scp -i $LAB_KEY_DEB ${SSH_OPTS[*]} -q \"\$f\" root@$(vm_ip pf0):/opt/pf/; done"
|
||||
fi
|
||||
log "pf0:/opt/pf ->"
|
||||
vm_cmd "$(vm_ip pf0)" 'ls -l /opt/pf'
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# vm-destroy.sh — tear the lab down: pftest-* domains, their qcow2 disks,
|
||||
# seed ISOs/dirs, the pf-lan/pf-wan/pf-wan2 networks, and the generated
|
||||
# laptop ssh fragment. The shared alpine-base.qcow2 and 'default' network are
|
||||
# KEPT by default (re-create is then fast); --purge-base removes the base,
|
||||
# --purge-keys also drops the lab keypair on deb + laptop copies.
|
||||
# Never touches non-pftest domains or the 'default' network state.
|
||||
set -euo pipefail
|
||||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
# shellcheck disable=SC1091
|
||||
. "$REPO/scripts/lab-lib.sh"
|
||||
|
||||
PURGE_BASE=0; PURGE_KEYS=0; QUIET=0
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--purge-base) PURGE_BASE=1 ;;
|
||||
--purge-keys) PURGE_KEYS=1 ;;
|
||||
--quiet) QUIET=1 ;;
|
||||
-h|--help) sed -n '2,8p' "$0"; exit 0 ;;
|
||||
*) die "unknown arg: $arg (-h for usage)" ;;
|
||||
esac
|
||||
done
|
||||
[ $QUIET = 1 ] && exec >/dev/null 2>&1
|
||||
|
||||
lab_env
|
||||
deb true || { warn "deb unreachable; removing laptop artifacts only"; rm -f "$LAPTOP_SSH_FRAG"; exit 0; }
|
||||
|
||||
for a in $VMS; do
|
||||
name=$(vm_name "$a")
|
||||
deb_cmd "virsh destroy $name 2>/dev/null; virsh undefine $name --nvram 2>/dev/null; virsh undefine $name 2>/dev/null; rm -f /var/lib/libvirt/qemu/nvram/$name*.raw; true"
|
||||
deb_cmd "rm -rf '$SEED_DIR/$name' '$IMG_DIR/$name.qcow2' /tmp/$name.xml"
|
||||
log "removed domain+disk+seed: $name"
|
||||
done
|
||||
rm -rf "$LAB_SEEDS"
|
||||
|
||||
for n in "$NET_PF_LAN" "$NET_PF_WAN" "$NET_PF_WAN2"; do
|
||||
deb_cmd "virsh net-destroy $n 2>/dev/null; virsh net-undefine $n 2>/dev/null; true"
|
||||
done
|
||||
log "removed networks: $NET_PF_LAN $NET_PF_WAN $NET_PF_WAN2 (default left as-is)"
|
||||
|
||||
deb_cmd 'f=$HOME/.ssh/config; [ -f "$f" ] && sed -i "/# >>> pflab/,/# <<< pflab/d" "$f" || true'
|
||||
rm -f "$LAPTOP_SSH_FRAG" "$HOME/.ssh/known_hosts_pflab"
|
||||
log "removed generated ssh fragments (laptop + deb config blocks)"
|
||||
|
||||
if [ $PURGE_BASE = 1 ]; then
|
||||
deb "rm -f $BASE_IMG"
|
||||
log "removed base image on deb"
|
||||
fi
|
||||
if [ $PURGE_KEYS = 1 ]; then
|
||||
deb "rm -f $LAB_KEY_DEB $LAB_KEY_DEB.pub"
|
||||
rm -f "$LAB_KEY_LAPTOP" "$LAB_KEY_LAPTOP.pub"
|
||||
log "removed lab keypair (deb + laptop)"
|
||||
fi
|
||||
|
||||
log "VERIFY state on deb:"
|
||||
deb 'virsh list --all; echo ---; virsh net-list --all; echo ---; ls -l /var/lib/libvirt/images/ /var/lib/libvirt/pflab/ 2>/dev/null || true'
|
||||
Reference in new issue
Block a user