wave14b: openbsd-0001/2 + 526/240 PDF

This commit is contained in:
russell@unturf.com 2026-03-27 18:14:55 -04:00
parent ac0e1e091e
commit c4026333ba
9 changed files with 695 additions and 5 deletions

View file

@ -0,0 +1,195 @@
package unit;
import java.util.*;
/**
* openbsd-0001: pf_osfp_validate O(N²) fingerprint validation scan
*
* Models sys/net/pf_osfp.c pf_osfp_validate / pf_osfp_find:
* - Slow: outer SLIST_FOREACH × inner SLIST_FOREACH (linear scan) O(N²)
* - Fast: HashMap keyed by tcpopts O(N) validate, O(1) amortized find
*
* pf_osfp_validate is called on every pfctl ruleset reload (DIOCOSFPADD ioctl).
* pf_osfp_find is called per TCP SYN packet in pf_osfp_fingerprint.
* Default pf.os has 246 entries; N² = 60,516 comparisons per reload.
*/
public class PfOsfpAlgorithm {
// Fingerprint (key fields from struct pf_os_fingerprint)
static class OsFingerprint {
final long tcpopts; // packed TCP options primary discriminator
final int wsize, psize, mss, flags, optcnt, wscale, ttl;
final String name;
OsFingerprint(long tcpopts, int wsize, int psize, int mss,
int flags, int optcnt, int wscale, int ttl, String name) {
this.tcpopts = tcpopts; this.wsize = wsize; this.psize = psize;
this.mss = mss; this.flags = flags; this.optcnt = optcnt;
this.wscale = wscale; this.ttl = ttl; this.name = name;
}
}
// ---- SLOW: O(N²) ----
static class SlowOsfpList {
final List<OsFingerprint> list = new ArrayList<>();
long comparisons = 0; // operation counter
void insert(OsFingerprint fp) { list.add(fp); }
void resetCounters() { comparisons = 0; }
/** pf_osfp_find: linear scan — O(N) */
OsFingerprint find(OsFingerprint needle) {
for (OsFingerprint f : list) {
comparisons++;
if (f.tcpopts == needle.tcpopts
&& f.wsize == needle.wsize
&& f.psize == needle.psize
&& f.mss == needle.mss
&& f.optcnt == needle.optcnt
&& f.wscale == needle.wscale
&& f.ttl == needle.ttl)
return f;
}
return null;
}
/** pf_osfp_validate: N calls to find — O(N²) total */
boolean validate() {
for (OsFingerprint f : list) {
OsFingerprint found = find(f);
if (found != f) return false;
}
return true;
}
}
// ---- FAST: O(N) validate, O(bucket) find ----
static class FastOsfpList {
final List<OsFingerprint> list = new ArrayList<>();
// Hash by tcpopts (first check in pf_osfp_find; buckets average ~4 entries)
final Map<Long, List<OsFingerprint>> byTcpopts = new HashMap<>();
long comparisons = 0;
void insert(OsFingerprint fp) {
list.add(fp);
byTcpopts.computeIfAbsent(fp.tcpopts, k -> new ArrayList<>()).add(fp);
}
void resetCounters() { comparisons = 0; }
/** pf_osfp_find: bucket scan — O(N/buckets) ~O(1) */
OsFingerprint find(OsFingerprint needle) {
List<OsFingerprint> bucket = byTcpopts.get(needle.tcpopts);
if (bucket == null) return null;
for (OsFingerprint f : bucket) {
comparisons++;
if (f.wsize == needle.wsize
&& f.psize == needle.psize
&& f.mss == needle.mss
&& f.optcnt == needle.optcnt
&& f.wscale == needle.wscale
&& f.ttl == needle.ttl)
return f;
}
return null;
}
/** pf_osfp_validate: O(N * bucket_avg) ~O(N) */
boolean validate() {
for (OsFingerprint f : list) {
OsFingerprint found = find(f);
if (found != f) return false;
}
return true;
}
}
static List<OsFingerprint> generateFingerprints(int n, Random rng) {
List<OsFingerprint> fps = new ArrayList<>(n);
Set<String> usedKeys = new HashSet<>();
for (int i = 0; i < n; i++) {
long tcpopts; int wsize, ttl, psize, optcnt, wscale, mss;
String key;
do {
int opt0 = rng.nextInt(5), opt1 = rng.nextInt(5), opt2 = rng.nextInt(5);
tcpopts = opt0 | (opt1 << 3) | (opt2 << 6) | ((long)(rng.nextInt(8)) << 9);
wsize = 512 + rng.nextInt(65000);
ttl = 32 + rng.nextInt(224);
psize = 20 + rng.nextInt(1000);
optcnt = rng.nextInt(8);
wscale = rng.nextInt(15);
mss = rng.nextBoolean() ? 1460 : 512;
key = tcpopts + ":" + wsize + ":" + ttl + ":" + psize;
} while (!usedKeys.add(key));
fps.add(new OsFingerprint(tcpopts, wsize, psize, mss,
rng.nextInt(0x4000), optcnt, wscale, ttl, "OS-" + i));
}
return fps;
}
public static void main(String[] args) {
System.out.println("openbsd-0001: pf_osfp O(N²) validate vs O(N) hash validate");
System.out.println("================================================================");
int[] sizes = {246, 500, 1000, 2000};
boolean allPass = true;
int passed = 0;
int total = sizes.length;
for (int n : sizes) {
Random rng = new Random(n * 42L);
List<OsFingerprint> fps = generateFingerprints(n, rng);
SlowOsfpList slow = new SlowOsfpList();
FastOsfpList fast = new FastOsfpList();
for (OsFingerprint fp : fps) { slow.insert(fp); fast.insert(fp); }
// Correctness
slow.resetCounters(); fast.resetCounters();
boolean slowOk = slow.validate();
boolean fastOk = fast.validate();
if (!slowOk || !fastOk) {
System.out.printf(" N=%4d correctness FAIL (slow=%b fast=%b)%n", n, slowOk, fastOk);
allPass = false;
continue;
}
for (OsFingerprint fp : fps) {
if (slow.find(fp) != fp || fast.find(fp) != fp) {
System.out.printf(" N=%4d find mismatch FAIL%n", n);
allPass = false;
break;
}
}
// Operation count (deterministic, JVM-noise immune)
slow.resetCounters();
slow.validate();
long slowOps = slow.comparisons;
fast.resetCounters();
fast.validate();
long fastOps = fast.comparisons;
double ratio = (double) slowOps / Math.max(fastOps, 1);
String verdict = ratio >= 10.0 ? "PASS" : "FAIL";
if (ratio < 10.0) allPass = false;
else passed++;
// Also benchmark wall time
long t0 = System.nanoTime();
for (int it = 0; it < 100; it++) slow.validate();
long slowNs = (System.nanoTime() - t0) / 100;
t0 = System.nanoTime();
for (int it = 0; it < 100; it++) fast.validate();
long fastNs = (System.nanoTime() - t0) / 100;
System.out.printf(" N=%4d slow_ops=%,7d fast_ops=%,5d ops_ratio=%5.1fx wall=%,6dns/%,5dns [%s]%n",
n, slowOps, fastOps, ratio, slowNs, fastNs, verdict);
}
System.out.println();
System.out.printf("%d/%d PASS%n", passed, total);
if (!allPass) System.exit(1);
}
}