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 addrlist = new ArrayList<>(); Ifnet(int rdomain) { this.rdomain = rdomain; } } // ---- SLOW: TAILQ_FOREACH(ifp) × TAILQ_FOREACH(ifa) ---- O(I×A) ---- static class SlowAddrTable { final List 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 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 populate(SlowAddrTable slow, FastAddrTable fast, int numIf, int addrsPerIf, int rdomain, Random rng) { List 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 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 queryTypes = new ArrayList<>(); List 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); } }