Skip to content

feat(flow-export): path-mtu sizes datagrams for a tunnel to the collectors - #353

Merged
lunarthegrey merged 2 commits into
mainfrom
claude/flow-export-collector-mtu
Oct 11, 2026
Merged

lunarthegrey merged 2 commits into
mainfrom
claude/flow-export-collector-mtu

Conversation

@lunarthegrey

Copy link
Copy Markdown
Contributor

Why

flow-export's datagrams are a fixed 1,400 bytes. That fits a 1500-byte path, but not the tunnel the production collectors will most likely sit behind: Tailscale's MTU is 1280.

  • Every full datagram would leave fragmented.
  • Losing either fragment loses the whole datagram: a batch of sFlow samples, or IPFIX records the flow cache has already exported.

What changes

  • New directive path-mtu <bytes> in module flow-export.
    • Hot, range 576–9000.
    • Each datagram becomes that MTU less 48 (IPv6 and UDP headers, so either family fits). path-mtu 1280 sends 1,232-byte datagrams.
    • Unset, nothing changes: 1,400 bytes.
  • One value per section, not per collector.
    • The sFlow exporter encodes each datagram once for every sFlow collector, under one agent sequence. IPFIX encodes once per privacy profile.
    • A router's collectors share a path anyway.
    • A per-collector size would need an encode and a sequence stream per size.
  • Floor of 576.
    • It still holds a sample of the most header-bytes allows (256) in sFlow's expanded encoding with an IPv6 agent.
    • It also holds the IPFIX announcement with its records.
    • Tests cover both.
  • The exporters take the size per call instead of sflow_out::MAX_DATAGRAM, so a reload applies from the next tick.
  • Docs:
    • The runbook's Collectors section gains a "set path-mtu over a tunnel" note.
    • It also covers what Tailscale needs: collector and source-address on Tailscale addresses, tailnet ACLs for the UDP ports, and Docker's default port publishing so the exporter's address survives.
    • example.conf and the CHANGELOG are updated.

Testing

  • Config: path-mtu parses; values outside 576–9000 and duplicates are refused. path-mtu 1280 becomes 1,232-byte datagrams, unset 1,400.
  • sFlow: at the smallest size, every sample of the largest header still goes (expanded encoding, IPv6 agent), with no datagram larger than the size.
  • IPFIX: at the smallest size, the announcement comes first, all 500 records go, none are unencodable, and the messages are more and smaller.
  • Worker: a reload from 1,400 to 528 applies on the next tick. The same 20 samples arrive in more datagrams, none over 528.
  • Checks run: cargo fmt; clippy on the host and both Linux targets (--workspace --all-targets --all-features -D warnings); host cargo test --workspace.
  • Not run locally: the root end-to-end suite in Docker (Docker Desktop wasn't running). CI's qemu jobs run it.

🤖 Generated with Claude Code

…ctors

Datagrams were a fixed 1,400 bytes: right for a 1500-byte path, too big
for the tunnel the production collectors will most likely sit behind.
Tailscale's MTU is 1280, so every full datagram would leave fragmented,
and losing either fragment loses the datagram: whole batches of samples,
or IPFIX records the cache has already let go.

`path-mtu <bytes>` makes each datagram that MTU less 48 (IPv6 and UDP
headers, so either family fits). It is one value for the section rather
than per collector: the sFlow exporter encodes each datagram once for
every sFlow collector under one agent sequence, and IPFIX once per
privacy profile, and the collectors one router reaches share a path.
Unset, nothing changes. 576 is the floor because it still holds a sample
of the most header-bytes allows in sFlow's expanded encoding with an
IPv6 agent, and the IPFIX announcement with its records. It is hot: each
tick encodes with the configuration in place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T03:08:44.844667Z ddaf967 PR opened
🔒 Security Review ✅ Completed 2026-10-11T03:07:59.769890Z ddaf967 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ddaf967e34

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/modules/flow-export/src/worker.rs
…ends

A tick took a fixed 8,192 records from the flow cache, sized for 1,400-byte
messages: 16 IPv6 records each, 512 messages, exactly the per-collector
send budget. With path-mtu 1280 a message holds 14, so a full take needs
about 586 messages, and the budget dropped the rest after their records
had left the cache. A rate change makes a full take certain: it queues
every flow at once. In the test, 20,000 IPv6 flows at 1,232-byte
datagrams lost 148 messages. At 1,400 the old take had no room for the
announcement either.

The take is now derived from the datagram size: what the budget's
messages carry of the largest record any template sends (85 bytes,
IPv6), keeping 64 messages back for announcements and the part-filled
message each domain, template and rate change ends a tick with. At 1,400
that is 7,168 records a tick, still 71,680 a second.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lunarthegrey
lunarthegrey merged commit 279d9ce into main Oct 11, 2026
14 checks passed
@lunarthegrey
lunarthegrey deleted the claude/flow-export-collector-mtu branch October 11, 2026 04:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant