feat: compute and chart IPv6 MAAD#110
Note
🤖 Claude Opus 5.5 on behalf of Oliver
ELI5
Runs the MAAD address-structure analysis on IPv6 traffic too, not only IPv4, and adds an IPv4/IPv6 switch to the dashboard spectrum card and the file detail page.
Setup
This PR bumps the product contract: both family MAAD configs are part of the pipeline identity. The pipeline rejects an existing product database, so build a fresh one.
- Build the nfdump fork (
./vendor/scripts/compile-nfdump.sh). - Choose an input window that contains IPv6 flows. Any nfcapd day from a source that carries IPv6 works. To check, run
target/nfdump/libexec/nfdump -r <file> 'ipv6' -c 5and confirm it prints flows. - Build a new product for one day:Then point the dashboard at that database.bash
1 ./scripts/netflow-db.sh pipeline --dataset <id> --start-date <day> --end-date <day> \ 2 --database-path data/<id>-ipv6/netflow.sqlite --nfdump target/nfdump/libexec/nfdump - CLI-only check without captures:
./scripts/netflow-db.sh maad --ipv6 tools/netflow-db/tests/fixtures/maad-conformance/ipv6-mixed/input.txt.
Flows to exercise
- Pipeline output.
address_structure_statshasip_version = 6rows alongside the v4 rows, with prefix lengths /23–/64.verify --require-maad-datapasses, and fails when v6 traffic exists but v6 MAAD rows are missing. - Dashboard. On
/datasets/<id>, pick IPv6 on the spectrum card:- The URL gains
ipVersion=6and the card refetches. - Reload keeps IPv6.
- Reset view returns to IPv4, which removes the param.
- The URL gains
- Drilldown. At 5-minute grouping, click any dashboard chart. The file page opens with
ipVersion=6, and its family switch shows IPv6. - File page. Reload keeps the family, and Next File keeps it too. Switching the family replaces the URL (no new history entry) and refetches MAAD.
- Validation.
?ipVersion=5on MAAD API routes or the file page returns 400. On the dashboard, the search-param schema rejects it and falls back to IPv4. - Compare.
compareagainst an older reference with no IPv6 rows now fails withunexpected_candidate_only_rows. Compare IPv6-era products only with IPv6-era references.
Decisions and edge cases
- IP family is a URL search param (
ipVersion,4or6, default4, left out of the URL when4). Every dashboard→file drilldown and the Next File link carry it. - Next File is now a link instead of a GET form. The form dropped every query param (
dataset,direction,ipVersion), because GET submission replaces the action's query string. comparehas no exemption for a missing IP family. An earlier revision accepted candidate-only rows whenever the reference lacked that family, which also let unrelated-source rows through. That was removed as greenfield (no compat code).- IPv6 uses upstream's /23–/64 range, and the nearly-full test uses
128 − pl. IPv4 output is unchanged. The two families are separate series, not comparable points. On anonymized internal v6 sources the structure below ~/40 mostly reflects the anonymizer; that labeling question is out of scope.
Verification
- Automated:
bun run format,bun run lint,bun run typecheck,bun run test:db(v6 goldens, compare rejection tests),bun run test:web,bun run test:e2e(newmaad-ip-version.spec.tscovers IPv6 selection → reload → drilldown → reload → next file). - Manual, still needed: a real-capture product build with IPv6 traffic (steps above), then a visual check of the IPv6 spectrum and the file-page charts.
Made by Claude Opus 5.5 (with Opus 5.5 subagents) via Claude Code.
Checks
3 successful