166 lines
6.3 KiB
Java
166 lines
6.3 KiB
Java
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);
|
||
}
|
||
}
|