java-topology/defects/openbsd/unit/IfaIfwithAddrAlgorithm.java

166 lines
6.3 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}