-
Notifications
You must be signed in to change notification settings - Fork 2
ddos
Pre-Alpha. This page describes behavior that may change.
Ze can automatically detect volumetric DDoS attacks on its interfaces, mitigate them on the host or upstream, and report incidents to the Flowtriq cloud. Detection learns a per-interface baseline, characterizes the attack, and routes the response by direction so a surgical rule lands where it can actually help.
This page covers the volumetric (rate and bandwidth) subsystem. For the behavioral, per-source detector that is a separate domain, see Behavioral anomaly detection.
The subsystem is a set of independent plugins, each enabled separately. You can run any combination: detector alone (monitoring only), detector plus local (on-host protection), detector plus flowspec (upstream), or all of them.
| Plugin | Role |
|---|---|
ddos-detect |
Watches interface rates, learns a baseline, triggers on the dynamic threshold, characterizes the attack |
ddos-observe |
Holds the incident ring and serves show ddos status / show ddos incidents
|
ddos-local |
Installs an nftables drop rule on the host when an attack is detected |
ddos-flowspec |
Announces a surgical FlowSpec (or RTBH) rule upstream over BGP |
ddos-flowtriq |
Reports incidents to the Flowtriq cloud API for dashboarding and server-driven mitigation |
The detector runs two parallel triggers against a rolling per-interface baseline. Samples collected during an active attack, or above the current threshold, are excluded from the baseline so an attack cannot poison it.
threshold = max(baseline_p99 * threshold-multiplier, absolute-floor)
The baseline is a rolling window of the last baseline-window non-attack samples. The p99 is recalculated every 10 samples. absolute-floor (default 5000 PPS) sets a minimum so a very quiet baseline cannot make the detector fire on trivial spikes.
Amplification floods (NTP, memcached, CLDAP) are low-packet-rate but very high bandwidth, so a packet-rate threshold alone misses them. A parallel bandwidth baseline (same rolling window and poisoning guard, in bytes per second) fires when observed bandwidth exceeds max(bps_p99 * bps-threshold-multiplier, bps-floor).
The BPS trigger only engages once its baseline has warmed, and bps-floor (default 50 Mbps, expressed in bits per second for convenience) keeps it inert below that bandwidth so legitimate high-throughput bursts are not flagged. Set bps-trigger-enable false to disable just the bandwidth path.
The detector saves both baselines to <config-dir>/state/ddos-detect-baseline.json on shutdown or reconfigure, and periodically, and restores them on startup. A restart or a config change resumes detection without re-warming over baseline-window. A stale, too-few-sample, or corrupt file is rejected and the baseline warms fresh.
On characterization the detector computes a 0 to 100 confidence from the peak-to-threshold ratio, family specificity, and source spread. It is stored on the incident (shown in show ddos incidents), reported to Flowtriq, and can gate the responders with confidence-min. Confidence is computed once at attack start, so attack duration is not a factor.
Each signal also carries a graded severity from the peak-to-threshold ratio: medium (at least 1x), high (at least 2x), critical (at least 5x). The FlowSpec responder can use critical to engage an immediate blackhole.
The fast trigger fires first with a coarse target. A second, finer pass then classifies the attack and narrows the rule in place. Characterization feeds the surgical responders so they install a rule that matches the attack, not the whole victim prefix.
| Family | Signature | Match |
|---|---|---|
| reflection | UDP with a dominant amplifier source port (DNS 53, NTP 123, SNMP 161, CLDAP 389, SSDP 1900, Memcached 11211) | protocol plus source port |
| syn-flood | TCP dominated by half-open conntrack states | protocol plus SYN flag |
| icmp-flood | ICMP dominant | protocol |
| udp-flood | UDP dominant without a reflector | protocol (and dominant destination port) |
| generic-flood | no clear signature (includes fragment floods) | coarse destination-prefix drop |
The attack-target prefix is pulled from live on-box traffic stats. The detector reads the attacked destination from traffic usage (with track-ip enabled on the exposed interfaces) for the fast IPv4 target, and from the flow-export conntrack recent-flow ring for the classifier (protocol, ports, TCP flags, IPv6, top sources). The recent-flow ring is IPv4 and IPv6 capable, so IPv6 victims (invisible to the IPv4-only traffic-usage map) are resolved from the ring.
Without a reachable flow source the detector still fires but emits a generic signal with no target, and the coarse destination-prefix drop is used. If characterization is enabled with neither source configured, ze doctor reports doctor-ddos-detect-no-flow-source.
The recent-flow ring is normally refreshed only at each conntrack dump (every flow-export active-timeout seconds, default 60s). To avoid classifying against a pre-attack ring, detection forces the refresh: ddos-detect emits AttackDetected, flow-export responds with an immediate out-of-band conntrack dump, and the classifier polls the ring for up to characterize-timeout until it reflects the attack. A just-started flood is therefore characterized promptly regardless of active-timeout.
On the gokrazy appliance nothing but ze runs, so nothing else loads nf_conntrack, registers a tracking hook, or enables accounting. Without those the ctnetlink dump reads an empty table and characterization has no data. On the appliance build flow-export performs a one-time nf_conntrack init: it best-effort loads the conntrack modules, enables nf_conntrack_acct, and installs a minimal ct state nftables rule whose presence registers the conntrack tracking hooks for the netns. It is best-effort throughout and degrades to the empty-ring behavior on failure rather than blocking startup.
The detector classifies each attack victim as local (an address the box terminates, for example control-plane traffic, on the netfilter INPUT hook) or remote (a downstream host it forwards, on the FORWARD hook). The class is shown in show ddos incidents as direction, and mitigation is routed by it:
- Local victim:
ddos-localinstalls an INPUT-hook drop. - Remote victim:
ddos-flowspecannounces upstream.ddos-localadditionally installs a FORWARD-hook drop only whenddos local forward-mitigationis enabled. - Unresolved victim: treated as remote, since a local INPUT drop cannot protect an address the box does not own.
Detection carries a single allow/deny policy, indexed by prefix, that governs how detected attacks are handled. It replaces the old per-responder allowlist leaves (a config still using them fails validation). The detector is the single enforcement point and encodes the decision on the emitted event, so every responder honors one policy without duplicating it.
Each rule is keyed by a prefix and carries:
| Leaf | Values | Meaning |
|---|---|---|
action |
allow, deny
|
allow exempts matching traffic; deny subjects it to DDoS handling |
match |
source, destination, any (default) |
Match the prefix against the attack source, the victim, or either |
scope |
detection, mitigation (default) |
For allow: detection suppresses the incident entirely; mitigation still records it but never blocks |
default-action (default deny) applies when no rule matches. Rules are evaluated longest-prefix-match: the most specific rule wins, so a /24 deny inside a /16 allow is decided by the /24 with no ordering needed. Ties resolve to deny. Configure allow rules at minimum for management and SSH prefixes, DNS servers, and BGP session endpoints.
ddos {
detect {
enabled true;
policy {
default-action deny;
rule 198.51.100.0/24 { action allow; match destination; scope mitigation; }
rule 203.0.113.7/32 { action allow; match source; scope detection; }
}
}
}
ddos-local in enforce mode installs an nftables drop for the characterized target and narrows it in place as characterization refines the vector. The rule is removed automatically when the attack stops: nftables drops occur after the kernel NIC RX counter increments, so the detector still sees the arriving flood and clears only when RxPps genuinely falls below threshold.
ddos {
detect { enabled true; policy { default-action deny; rule 10.0.0.0/8 { action allow; match destination; } } }
local { response-level enforce; }
}
traffic {
usage {
enabled true;
track-ip true;
interfaces { interface eth0 { enabled true; } }
}
}
Start ddos-local in alert mode (log only, no rules installed) until you trust detection accuracy, then switch to enforce.
Once the attack is characterized, ddos-flowspec announces a surgical BGP FlowSpec rule to the configured upstream, matching the attack protocol, ports, and flags. The action is mandatory (no default, because neither choice is universally safe): rate-limit announces an RFC 8955 traffic-rate of rate-limit-bytes bytes per second, while discard drops the whole characterized flow. A rate-limit-bytes of 0 is valid and equals discard.
ddos {
flowspec {
response-level enforce;
action rate-limit;
rate-limit-bytes 1000000;
hold-down 300;
probe-interval 60;
}
}
Blackhole fallback. With blackhole-fallback true, a critical attack (peak at least 5x threshold) engages an immediate upstream discard on the fast signal without waiting for characterization. This fast path is never gated by confidence-min.
Leak-probe. Once the upstream drops the attack, Ze's local sensors go blind (the traffic never arrives), so the detector's clear signal is not trustworthy. The responder ignores it and runs its own leak-probe cycle: after hold-down it periodically narrows the rule to a small non-zero rate (probe-rate, default 1 Mbps) and observes whether the leaked traffic saturates it. If it does, the flood is still arriving and the rule is re-tightened with exponential backoff. If not, the rule is withdrawn.
FlowSpec rules are originated onto the wire through the BGP reactor. ddos-flowspec renders an update text command carrying the RFC 8955 traffic-rate extended community and the FlowSpec NLRI. The same origination is available manually through the announce flowspec CLI verb:
announce flowspec destination 203.0.113.42/32 protocol udp source-port 53 rate-limit 1000000
announce flowspec destination 203.0.113.42/32 protocol udp discard
The match components are encoded into a FlowSpec NLRI through the family registration seam (the same encoder the update text path uses). An action is mandatory: rate-limit <bytes-per-sec> maps to a traffic-rate function, and discard to a traffic-rate of 0. Announcements can be tracked and auto-expired with trailing tag <k> <v> and for <duration> options, and withdrawn with withdraw.
ddos-flowtriq reports incidents to the Flowtriq cloud API in real time: it opens an incident on detection, updates it every few seconds with current rates, and resolves it when the attack ends. The dashboard provides historical analysis, alerting, and optional server-driven mitigation commands.
ddos {
flowtriq {
enabled true;
api-key YOUR_API_KEY;
node-uuid YOUR_NODE_UUID;
}
}
The reporter authenticates with a bearer api-key and an X-Node-UUID header, against api-base (default https://flowtriq.com/api/v1). Incidents flow through open (POST /agent/incidents), update (POST /agent/incidents/{uuid}), and resolve (POST /agent/incidents/{uuid}/resolve) endpoints, carrying peak PPS and BPS, attack family, top source IPs, and top destination ports. See Flowtriq API reference for the full field list.
Detection and mitigation defend forwarded and box-local traffic. Three lower-level features keep the BGP control plane itself alive under load.
Per-peer TTL settings implement the Generalized TTL Security Mechanism. ttl-security sets the minimum accepted TTL (or hop limit) for inbound packets, so a spoofed packet from more than the expected number of hops away is dropped by the kernel (IP_MINTTL / IPV6_MINHOPCOUNT). outgoing-ttl pins the TTL Ze sets on its own outbound packets. Both are configured on the peer:
bgp {
peer upstream {
session {
ttl-security 254; // accept only packets arriving with TTL >= 254 (one hop)
outgoing-ttl 255;
}
}
}
The copp plugin rate-limits new TCP connections to the BGP listen port so a connection-flood cannot exhaust the control plane, while exempting established sessions and trusted sources. It lowers to an nftables input chain: an accept for established/related, an accept per trusted source, and a rate-limit term for new connections to the protected ports.
control-plane-protection {
bgp {
rate 100/second;
burst 20;
trusted-source 192.0.2.1/32;
trusted-source 198.51.100.0/24;
over-limit-policy accept; // safe default; drop to police hard
}
}
protected-port defaults to 179 and is only needed when the BGP listener uses an alternate port. over-limit-policy defaults to accept to avoid a lock-out risk.
Ze marks its own BGP TCP traffic with DSCP CS6 (Internet Control, IP_TOS / IPV6_TCLASS = 0xC0) on every session socket, as RFC 4271 recommends. Network devices with QoS policies prioritize CS6 over ordinary data, which reduces the risk of a hold-timer expiry when the link is congested by an attack.
show ddos is a namespace whose subcommands are owned by the plugin holding the data, so a subcommand appears only while its plugin is loaded.
| Command | Owner | Shows |
|---|---|---|
show ddos status |
ddos-observe |
Whether observation is running, active attack count, incidents held in the ring |
show ddos incidents |
ddos-observe |
The incident ring (newest first): target vector, family, top sources, peak pps/bps, confidence, direction, start/end |
show ddos local |
ddos-local |
Whether an on-host nft drop is installed and the vector it covers |
show ddos flowspec |
ddos-flowspec |
Whether an upstream FlowSpec rule is announced, its vector, and whether the leak-probe is running |
Inspect the flow ring directly with show flow-recent and show flow-recent dst <prefix>.
| Parameter | Default | Range | Description |
|---|---|---|---|
enabled |
false |
bool | Enable the detector |
check-interval |
1 |
1-3600 s | Seconds between detection evaluations |
confirm-duration |
3 |
0-3600 | Consecutive ticks above threshold before triggering (0 = immediate) |
clear-consecutive-checks |
10 |
1-100 | Consecutive ticks below threshold before clearing |
baseline-window |
300 |
10-86400 | Rolling baseline window in samples |
threshold-multiplier |
3.00 |
1.00-100.00 | Baseline p99 multiplier for the PPS threshold |
absolute-floor |
5000 |
1+ PPS | Minimum PPS threshold regardless of baseline |
startup-grace |
90 |
0-3600 s | Seconds after startup where only extreme spikes trigger |
bps-trigger-enable |
true |
bool | Enable the bandwidth trigger alongside the PPS threshold |
bps-threshold-multiplier |
3.00 |
1.00-100.00 | Baseline p99 multiplier for the bandwidth trigger |
bps-floor |
50000000 |
1+ bits/s | Bandwidth below which the BPS trigger is inert (default 50 Mbps) |
characterize-enable |
true |
bool | Run Stage-2 flow characterization |
characterize-timeout |
2000 |
50-5000 ms | Budget for the on-trigger flow queries |
policy |
default-action deny
|
container | Allow/deny traffic policy (see Traffic policy) |
| Parameter | Default | Range | Description |
|---|---|---|---|
response-level |
alert |
alert, enforce |
alert logs only; enforce installs nft drop rules |
max-mitigation-duration |
3600 |
0-86400 s | Force-remove the rule after this many seconds (0 = no cap) |
confidence-min |
0 |
0-100 | Minimum incident confidence to mitigate a characterized attack (0 = no gate) |
forward-mitigation |
false |
bool | Also drop a remote victim's traffic on the FORWARD hook |
| Parameter | Default | Range | Description |
|---|---|---|---|
response-level |
alert |
alert, enforce |
alert logs only; enforce announces FlowSpec |
action |
(required) | rate-limit, discard | Mandatory traffic-action. rate-limit needs rate-limit-bytes; discard drops the flow |
rate-limit-bytes |
(none) | 0..max bytes/s | Required for rate-limit; 0 equals discard
|
hold-down |
300 |
1-86400 s | Minimum seconds before the first leak-probe |
probe-interval |
60 |
1-3600 s | Seconds between leak-probe attempts |
probe-rate |
1000000 |
1+ bps | Bits per second allowed during a leak-probe |
blackhole-fallback |
false |
bool | Immediate upstream discard on a critical fast signal |
confidence-min |
0 |
0-100 | Minimum confidence to announce a characterized rule (fast path never gated) |
| Parameter | Default | Range | Description |
|---|---|---|---|
enabled |
false |
bool | Enable Flowtriq reporting |
api-key |
(required) | 1-512 chars | Flowtriq API bearer token |
node-uuid |
(required) | 1-128 chars | Node UUID from Flowtriq setup |
api-base |
https://flowtriq.com/api/v1 |
URL | API base URL |
- Behavioral anomaly detection for the report-only per-source detector, a separate domain from volumetric DDoS.
- FlowSpec injection for hand-constructing and injecting FlowSpec rules.
- Firewall for the nftables backend that local drops and CoPP lower onto.
- Traffic Control for per-interface queueing and scheduling.
Adapted from docs/guide/ddos-mitigation.md .
Unreviewed draft. This wiki was authored in bulk and has not been reviewed. File corrections on the issue tracker.
- Overview
- YANG Model
- Editor Workflow
- Archive and Rollback
- System
- Interfaces
- VRRP
- BFD
- FIB
- OSPF
- IS-IS
- MPLS / LDP / RSVP-TE
- RSVP-TE
- SRv6
- Static Routes
- Policy Routing
- Firewall
- Traffic Control
- Class of Service
- L2TP/PPP
- PPPoE
- VPP Data Plane
- RPKI
- IPsec VPN
- TACACS+ AAA
- RADIUS AAA
- AS112 DNS
- Authorization
- Fleet
- BGP
- Starting and Stopping
- Show Commands
- Monitoring
- Flow Export
- DDoS Mitigation
- Anomaly Detection
- Health Checks
- Audit Trail
- Production Diagnostics
- Logging
- Operational Reports
- Healthcheck
- Self-Update
- Zero-Touch Provisioning
- MRT Analysis
- Upgrade and Restart
- Storage
- Policy
- Core
- Resilience
- Validation
- Capabilities
- Address Families
- Protocol
- Subsystems
- Infrastructure
- Route Server at an IXP
- Transit Edge with RPKI
- Public Looking Glass
- ExaBGP Migration Walkthrough
- FlowSpec Injection
- Chaos-Tested Peering
- AS Path Topology