Skip to content

ip,ip6: add rate limiting for ICMP errors, ARP and ICMP input#576

Open
rjarry wants to merge 2 commits intoDPDK:mainfrom
rjarry:rate-limiting
Open

ip,ip6: add rate limiting for ICMP errors, ARP and ICMP input#576
rjarry wants to merge 2 commits intoDPDK:mainfrom
rjarry:rate-limiting

Conversation

@rjarry
Copy link
Collaborator

@rjarry rjarry commented Mar 26, 2026

Under a flood of bad packets (TTL=1, no-route, etc.), every packet triggers expensive ICMP error construction: address lookups, header prepend with potential memmove, and checksum computation. Similarly, ARP and ICMP/ICMPv6 input processing can saturate the control plane thread when flooded. None of this is rate limited today.

Add a per-node token bucket to all ICMP error nodes, ARP input, and ICMP/ICMPv6 input nodes. The bucket refills once per second and the check runs before any per-packet work. When the bucket is empty, the whole vector is moved to a dedicated drop node via rte_node_next_stream_move(), avoiding any unnecessary processing.

Three separate rates are configurable via the graph config API: icmp-error-rate, arp-rate and icmp-rate. All default to 1000 packets/sec per node per worker. Setting a rate to 0 disables the limit. Rate changes take effect immediately without graph reload.

Rate limiting for ICMP errors, ARP, and ICMP/ICMPv6 input

What changed (how):

  • Added three graph configuration fields to modules/infra/api/gr_infra.h:
    • uint16_t icmp_error_rate; uint16_t arp_rate; uint16_t icmp_rate;
    • Defaults set to 1000 in modules/infra/control/graph.c.
  • Introduced a token-bucket context and helper in modules/infra/datapath/gr_datapath.h:
    • struct rate_limit_ctx { uint16_t tokens; uint64_t last_refill; }
    • static inline bool rate_limited(struct rate_limit_ctx *ctx, uint16_t max_rate, uint16_t nb_pkts)
      • Uses rte_rdtsc() and rte_get_tsc_hz() to refill tokens once-per-second.
      • If max_rate == 0, limiting is disabled.
      • Refill sets tokens = max_rate; if tokens == 0 returns true (rate-limited).
      • On allowance, decrements tokens by min(tokens, nb_pkts).
  • Per-node integration in datapath nodes:
    • Added node-local contexts containing struct rate_limit_ctx and init hooks that set ctx->limit.tokens = graph_conf. and ctx->limit.last_refill = rte_rdtsc().
    • Nodes updated: arp_input, icmp_input, icmp6_input, ip_error (TTL/dest/frag), ip6_error (TTL/no-route).
    • Each node’s process function calls rate_limited(...) before any per-packet work; when it returns true the node moves the entire stream to a RATE_LIMITED edge via rte_node_next_stream_move(..., RATE_LIMITED) and returns without enqueueing/processing packets.
    • Each modified node maps RATE_LIMITED -> "error_rate_limited"; a drop node error_rate_limited is registered.
  • CLI and control plumbing:
    • graph config show/set extended to expose icmp_error_rate, arp_rate, and icmp_rate (modules/infra/cli/graph.c).
    • graph_conf made globally accessible (modules/infra/control/graph.c) with the new rate fields initialized to 1000.

Why / behavioral summary:

  • Rate checks run before any expensive per-packet work; when the per-node token bucket is empty the entire input vector is redirected to a drop node, avoiding address lookups, header prepends/memmove, and checksum work for the rejected stream.
  • Three independent rates (icmp-error-rate, arp-rate, icmp-rate) are configurable at runtime; a rate of 0 disables limiting. Defaults are 1000 packets/sec per node per worker. Rate changes take effect immediately via the graph configuration.

@coderabbitai
Copy link

coderabbitai bot commented Mar 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 621cce8a-f7c7-4225-83fa-15b2cf9a01ef

📥 Commits

Reviewing files that changed from the base of the PR and between d4bf7df and 0afe4da.

📒 Files selected for processing (10)
  • modules/infra/api/gr_infra.h
  • modules/infra/cli/graph.c
  • modules/infra/control/gr_graph.h
  • modules/infra/control/graph.c
  • modules/infra/datapath/gr_datapath.h
  • modules/ip/datapath/arp_input.c
  • modules/ip/datapath/icmp_input.c
  • modules/ip/datapath/ip_error.c
  • modules/ip6/datapath/icmp6_input.c
  • modules/ip6/datapath/ip6_error.c
🚧 Files skipped from review as they are similar to previous changes (7)
  • modules/infra/control/gr_graph.h
  • modules/ip/datapath/icmp_input.c
  • modules/ip6/datapath/ip6_error.c
  • modules/infra/datapath/gr_datapath.h
  • modules/infra/cli/graph.c
  • modules/ip/datapath/arp_input.c
  • modules/infra/api/gr_infra.h

📝 Walkthrough

Walkthrough

Changes add selectable graph configuration updates via a new gr_graph_conf_set_attr_t bitmask and gr_graph_conf_set_req request struct, and extend struct gr_graph_conf with icmp_error_rate, arp_rate, and icmp_rate. graph_conf becomes a non-static global. A token-bucket struct rate_limit_ctx and rate_limited() helper using DPDK cycle counters are introduced. Rate limiting is applied to ARP, ICMP, ICMPv6, IP error, and IPv6 error nodes with init hooks and a new error_rate_limited next-node; the CLI is updated to set and show the new parameters.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@modules/infra/api/gr_infra.h`:
- Around line 391-393: The three rate fields (icmp_error_rate, arp_rate,
icmp_rate) in struct gr_graph_conf cannot use 0 both as “disable” and
“unchanged” because GR_GRAPH_CONF_SET treats zeroed members as “unchanged”;
modify the API to add explicit presence flags (e.g., a bitmask or bools like
has_icmp_error_rate/has_arp_rate/has_icmp_rate) to the struct and update the
setter/serialization logic (the code path used by GR_GRAPH_CONF_SET and callers
such as modules/infra/cli/graph.c) to consult these presence bits when encoding
requests so a caller can send has_*=true with value 0 to mean “disable” while
has_*=false means “leave unchanged.”

In `@modules/infra/datapath/gr_datapath.h`:
- Around line 18-31: The rate limiter currently allows a whole vector when
tokens<nb_pkts and doesn't apply a runtime decrease of max_rate; in rate_limited
(and struct rate_limit_ctx usage) change the logic so: if max_rate == 0 return
true (reject all); on refill (now - last_refill >= rte_get_tsc_hz()) set
ctx->tokens = max_rate and update last_refill; otherwise clamp ctx->tokens =
RTE_MIN(ctx->tokens, max_rate) to handle runtime decreases; then if ctx->tokens
< nb_pkts return true (reject the batch) else subtract nb_pkts from ctx->tokens
(ctx->tokens -= nb_pkts); this ensures batches larger than remaining tokens are
rejected and lowers-of-rate take effect immediately.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6713d6cb-850c-47c1-a541-4e2c5fcf04e0

📥 Commits

Reviewing files that changed from the base of the PR and between a5520bf and d4bf7df.

📒 Files selected for processing (10)
  • modules/infra/api/gr_infra.h
  • modules/infra/cli/graph.c
  • modules/infra/control/gr_graph.h
  • modules/infra/control/graph.c
  • modules/infra/datapath/gr_datapath.h
  • modules/ip/datapath/arp_input.c
  • modules/ip/datapath/icmp_input.c
  • modules/ip/datapath/ip_error.c
  • modules/ip6/datapath/icmp6_input.c
  • modules/ip6/datapath/ip6_error.c

rjarry added 2 commits March 27, 2026 10:37
The graph config API uses zero to mean "don't change this field" for
rx_burst_max and vector_max. Future fields may need zero as a valid
value, so the convention does not scale.

Add a set_attrs bitmask to the config set request so that the handler
knows which fields the client explicitly provided. Move validation
(zero and overflow checks) inside the per-field blocks so they only
apply to fields being changed.

Signed-off-by: Robin Jarry <rjarry@redhat.com>
Under a flood of bad packets (TTL=1, no-route, etc.), every packet
triggers expensive ICMP error construction: address lookups, header
prepend with potential memmove, and checksum computation. Similarly, ARP
and ICMP/ICMPv6 input processing can saturate the control plane thread
when flooded. None of this is rate limited today.

Add a per-node token bucket to all ICMP error nodes, ARP input, and
ICMP/ICMPv6 input nodes. The bucket refills once per second and the
check runs before any per-packet work. When the bucket is empty, the
whole vector is moved to a dedicated drop node via
rte_node_next_stream_move(), avoiding any unnecessary processing.

Three separate rates are configurable via the graph config API:
icmp-error-rate, arp-rate and icmp-rate. All default to 1000 packets/sec
per node per worker. Setting a rate to 0 disables the limit. Rate
changes take effect immediately without graph reload.

Signed-off-by: Robin Jarry <rjarry@redhat.com>
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