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,166 @@
package unit;
import java.util.*;
/**
* openbsd-0002: ifa_ifwithaddr O(I×A) nested interface+address scan in if.c
*
* Models sys/net/if.c ifa_ifwithaddr:
* - Slow: TAILQ_FOREACH(interfaces) × TAILQ_FOREACH(addresses) O(I×A)
* - Fast: HashMap keyed by (af, addr_bytes, rdomain) O(1)
*
* Called per-packet in ip_input.c, icmp6.c, in_pcb.c, ip_output.c.
* On I=50 interfaces with A=10 addresses each: 500 comparisons per packet.
* Under 100k pps SYN flood: 50M comparisons/sec in kernel address lookup.
*/
public class IfaIfwithAddrAlgorithm {
static class Ifaddr {
final int af;
final byte[] addr;
final int rdomain;
Ifaddr(int af, byte[] addr, int rdomain) {
this.af = af; this.addr = Arrays.copyOf(addr, addr.length);
this.rdomain = rdomain;
}
@Override public boolean equals(Object o) {
if (!(o instanceof Ifaddr)) return false;
Ifaddr x = (Ifaddr) o;
return af == x.af && rdomain == x.rdomain && Arrays.equals(addr, x.addr);
}
@Override public int hashCode() {
int h = af * 31 + rdomain;
for (byte b : addr) h = h * 31 + b;
return h;
}
}
static class Ifnet {
final int rdomain;
final List<Ifaddr> addrlist = new ArrayList<>();
Ifnet(int rdomain) { this.rdomain = rdomain; }
}
// ---- SLOW: TAILQ_FOREACH(ifp) × TAILQ_FOREACH(ifa) ---- O(I×A) ----
static class SlowAddrTable {
final List<Ifnet> ifnetlist = new ArrayList<>();
long comparisons = 0;
void addInterface(Ifnet ifp) { ifnetlist.add(ifp); }
void resetCounters() { comparisons = 0; }
Ifaddr lookup(int af, byte[] addr, int rdomain) {
for (Ifnet ifp : ifnetlist) {
if (ifp.rdomain != rdomain) continue;
for (Ifaddr ifa : ifp.addrlist) {
comparisons++;
if (ifa.af == af && Arrays.equals(ifa.addr, addr))
return ifa;
}
}
return null;
}
}
// ---- FAST: HashMap O(1) ----
static class FastAddrTable {
final Map<Ifaddr, Ifaddr> hashTable = new HashMap<>();
long comparisons = 0;
void addAddr(Ifaddr ifa) { hashTable.put(ifa, ifa); }
void resetCounters() { comparisons = 0; }
Ifaddr lookup(int af, byte[] addr, int rdomain) {
comparisons++; // single hash probe
return hashTable.get(new Ifaddr(af, addr, rdomain));
}
}
static byte[] randomAddr(Random rng, int len) {
byte[] b = new byte[len]; rng.nextBytes(b); return b;
}
static List<Ifaddr> populate(SlowAddrTable slow, FastAddrTable fast,
int numIf, int addrsPerIf, int rdomain, Random rng) {
List<Ifaddr> allAddrs = new ArrayList<>();
for (int i = 0; i < numIf; i++) {
Ifnet ifp = new Ifnet(rdomain);
for (int a = 0; a < addrsPerIf; a++) {
int af = rng.nextBoolean() ? 2 : 10; // AF_INET / AF_INET6
byte[] addrBytes = randomAddr(rng, af == 2 ? 4 : 16);
Ifaddr ifa = new Ifaddr(af, addrBytes, rdomain);
ifp.addrlist.add(ifa);
fast.addAddr(ifa);
allAddrs.add(ifa);
}
slow.addInterface(ifp);
}
return allAddrs;
}
public static void main(String[] args) {
System.out.println("openbsd-0002: ifa_ifwithaddr O(I×A) nested scan vs O(1) hash");
System.out.println("================================================================");
int[][] configs = {{20, 5}, {50, 10}, {100, 10}, {200, 20}};
int LOOKUPS = 2000;
boolean allPass = true;
int passed = 0;
for (int[] cfg : configs) {
int numIf = cfg[0], addrsPerIf = cfg[1], rdomain = 0;
Random rng = new Random(numIf * 1000L + addrsPerIf);
SlowAddrTable slow = new SlowAddrTable();
FastAddrTable fast = new FastAddrTable();
List<Ifaddr> allAddrs = populate(slow, fast, numIf, addrsPerIf, rdomain, rng);
// Correctness
boolean ok = true;
for (Ifaddr ifa : allAddrs) {
Ifaddr sf = slow.lookup(ifa.af, ifa.addr, rdomain);
Ifaddr ff = fast.lookup(ifa.af, ifa.addr, rdomain);
if (sf == null || ff == null || !sf.equals(ifa) || !ff.equals(ifa)) {
System.out.printf(" I=%3d A=%2d FAIL correctness%n", numIf, addrsPerIf);
ok = false; allPass = false; break;
}
}
if (!ok) continue;
// Build lookup queries (mix hit/miss)
List<int[]> queryTypes = new ArrayList<>();
List<byte[]> queryAddrs = new ArrayList<>();
for (int q = 0; q < LOOKUPS; q++) {
if (rng.nextInt(3) == 0) {
queryTypes.add(new int[]{2, rdomain});
queryAddrs.add(randomAddr(rng, 4)); // likely miss
} else {
Ifaddr ifa = allAddrs.get(rng.nextInt(allAddrs.size()));
queryTypes.add(new int[]{ifa.af, rdomain});
queryAddrs.add(ifa.addr); // hit
}
}
// Operation count (deterministic)
slow.resetCounters(); fast.resetCounters();
for (int q = 0; q < LOOKUPS; q++) {
slow.lookup(queryTypes.get(q)[0], queryAddrs.get(q), queryTypes.get(q)[1]);
fast.lookup(queryTypes.get(q)[0], queryAddrs.get(q), queryTypes.get(q)[1]);
}
long slowOps = slow.comparisons;
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++;
System.out.printf(" I=%3d A=%2d (total=%5d) slow_ops=%,8d fast_ops=%,5d ratio=%5.1fx [%s]%n",
numIf, addrsPerIf, numIf * addrsPerIf, slowOps, fastOps, ratio, verdict);
}
System.out.println();
System.out.printf("%d/4 PASS%n", passed);
if (!allPass) System.exit(1);
}
}