Skip to content

Conversation

@aharivel
Copy link
Collaborator

@aharivel aharivel commented Nov 26, 2025

Add a DHCP client that automatically acquires IP addresses and default routes for interfaces. The client handles the complete DHCP workflow including initial address assignment, lease renewal, and cleanup.

When enabled on an interface, the client sends DISCOVER messages and processes server responses to configure the interface with an assigned IP address, netmask, and default gateway. Leases are automatically renewed before expiration using T1 and T2 timers. When disabled, all DHCP-assigned configuration is removed.

CLI commands allow enabling and disabling DHCP per interface, and viewing the current DHCP client status including assigned addresses, lease times, and client state.

The implementation follows RFC 2131 with support for standard DHCP options including subnet mask, router, lease time, and renewal timings.

Summary by CodeRabbit

  • New Features

    • New DHCP module: enable/disable DHCP per interface, automatic IP/subnet and default-route configuration, lease renewal/rebind handling, and a status display showing interface, state, address, server, and lease info
    • CLI commands to start, stop, and show DHCP status
  • Tests

    • Added DHCP smoke test script validating end-to-end lease acquisition and route behavior
  • Build / CI

    • DHCP module added to the build; CI check updated to install dnsmasq for DHCP tests

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 26, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds a new DHCP module with a public API header (gr_dhcp.h) exposing DHCP state and status types and module request IDs. Implements a CLI (enable/disable/show) that uses the API. Introduces a control-plane DHCPv4 client with state machine, timers, packet handling, and per-interface start/stop/status APIs. Adds DHCP packet/options builders and a datapath input node with tracing and a UDP port registration function. Updates Meson build files, adds a smoke test script, and installs dnsmasq in CI.

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'dhcp: add client implementation' accurately and concisely summarizes the primary change—adding a complete DHCP client module with state machine, lease management, and CLI integration.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f67dea1 and 1b9aef7.

⛔ Files ignored due to path filters (1)
  • docs/graph.svg is excluded by !**/*.svg
📒 Files selected for processing (15)
  • .github/workflows/check.yml (1 hunks)
  • modules/dhcp/api/gr_dhcp.h (1 hunks)
  • modules/dhcp/api/meson.build (1 hunks)
  • modules/dhcp/cli/dhcp.c (1 hunks)
  • modules/dhcp/cli/meson.build (1 hunks)
  • modules/dhcp/control/client.c (1 hunks)
  • modules/dhcp/control/client.h (1 hunks)
  • modules/dhcp/control/meson.build (1 hunks)
  • modules/dhcp/control/options.c (1 hunks)
  • modules/dhcp/control/packet.c (1 hunks)
  • modules/dhcp/datapath/dhcp_input.c (1 hunks)
  • modules/dhcp/datapath/meson.build (1 hunks)
  • modules/dhcp/meson.build (1 hunks)
  • modules/meson.build (1 hunks)
  • smoke/dhcp_test.sh (1 hunks)

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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: 5

🧹 Nitpick comments (5)
modules/dhcp/cli/dhcp.c (1)

56-66: Use enum constants instead of magic numbers.

Hardcoded values 0-5 will break if dhcp_state enum is reordered. Reference the enum constants (e.g., DHCP_STATE_INIT, DHCP_STATE_SELECTING) for maintainability.

modules/dhcp/api/gr_dhcp.h (1)

16-24: Consider adding explicit padding for ABI clarity.

Between state (offset 3) and server_ip (offset 4), there's implicit 1-byte padding. For cross-process API structs, making padding explicit improves clarity:

 struct gr_dhcp_status {
 	uint16_t iface_id;
 	uint8_t state; // enum dhcp_state
+	uint8_t _pad;
 	uint32_t server_ip;
modules/dhcp/control/dhcp_options.c (1)

143-150: Use defined constant for Option 55.

Other options use DHCP_OPT_* constants, but the Parameter Request List uses literal 55. Define and use a constant for consistency.

modules/dhcp/control/dhcp_packet.c (1)

194-298: Consider extracting common packet-building logic.

dhcp_build_request and dhcp_build_discover share ~80% identical code. Consider extracting the common setup (interface lookup, mbuf allocation, headers) into a helper function to reduce duplication.

modules/dhcp/control/dhcp_client.c (1)

238-252: Log warnings on timer creation failure.

If evtimer_new fails, the client proceeds without that timer, which could cause silent lease expiration. Add warning logs:

 	client->t1_timer = evtimer_new(dhcp_ev_base, dhcp_t1_callback, client);
-	if (client->t1_timer != NULL)
+	if (client->t1_timer != NULL) {
 		evtimer_add(client->t1_timer, &t1_tv);
+	} else {
+		LOG(WARNING, "dhcp: failed to create T1 timer");
+	}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d52a09 and a9d9d78.

📒 Files selected for processing (14)
  • modules/dhcp/api/gr_dhcp.h (1 hunks)
  • modules/dhcp/api/meson.build (1 hunks)
  • modules/dhcp/cli/dhcp.c (1 hunks)
  • modules/dhcp/cli/meson.build (1 hunks)
  • modules/dhcp/control/dhcp_client.c (1 hunks)
  • modules/dhcp/control/dhcp_client.h (1 hunks)
  • modules/dhcp/control/dhcp_options.c (1 hunks)
  • modules/dhcp/control/dhcp_packet.c (1 hunks)
  • modules/dhcp/control/meson.build (1 hunks)
  • modules/dhcp/datapath/dhcp_input.c (1 hunks)
  • modules/dhcp/datapath/meson.build (1 hunks)
  • modules/dhcp/meson.build (1 hunks)
  • modules/meson.build (1 hunks)
  • smoke/dhcp_test.sh (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.sh

⚙️ CodeRabbit configuration file

**/*.sh: - Don't bother about unquoted shell variables.

Files:

  • smoke/dhcp_test.sh
**/*.{c,h}

⚙️ CodeRabbit configuration file

**/*.{c,h}: - gr_vec_*() functions cannot fail. No need to check their return value.

  • gr_vec_free(x) always sets x = NULL. There is no risk of double free.
  • ec_node_*() functions consume their ec_node arguments. No leaks on error.
  • rte_node->ctx is an uint8_t array of size 16, not a pointer.
  • Don't suggest to replace assert() with graceful error checking.
  • We compile with -std=gnu2x. Unnamed parameters in function definitions are valid.

Files:

  • modules/dhcp/datapath/dhcp_input.c
  • modules/dhcp/control/dhcp_options.c
  • modules/dhcp/control/dhcp_client.c
  • modules/dhcp/control/dhcp_packet.c
  • modules/dhcp/control/dhcp_client.h
  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/api/gr_dhcp.h
🧠 Learnings (4)
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: In this project, cli_src is a global variable that gets populated by individual modules (like modules/policy/cli/meson.build) and is ultimately consumed by an executable() call in the top-level meson.build file. This creates a modular CLI build where each module contributes its CLI sources to the main CLI binary.

Applied to files:

  • modules/dhcp/cli/meson.build
  • modules/meson.build
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: The grout project uses a modular CLI build system where individual modules contribute their CLI sources to a global cli_src variable via `cli_src += files(...)`, and the top-level meson.build file builds the final grcli executable using all collected CLI sources. The grcli executable is defined in the top-level meson.build at lines 122-127.

Applied to files:

  • modules/dhcp/cli/meson.build
📚 Learning: 2025-11-05T14:28:28.817Z
Learnt from: rjarry
Repo: DPDK/grout PR: 372
File: modules/infra/datapath/loop_input.c:53-56
Timestamp: 2025-11-05T14:28:28.817Z
Learning: In modules/infra/datapath/loop_input.c, the l3_edges array is intentionally indexed using network byte order (rte_be16_t) for EtherType values. Both loopback_input_add_type() registration and loopback_input_process() lookup use network byte order consistently, so no byte order conversion is needed when accessing l3_edges.

Applied to files:

  • modules/dhcp/datapath/dhcp_input.c
📚 Learning: 2025-11-03T13:28:19.489Z
Learnt from: rjarry
Repo: DPDK/grout PR: 379
File: modules/infra/datapath/port_tx.c:36-46
Timestamp: 2025-11-03T13:28:19.489Z
Learning: In DPDK graph node process callbacks, the return value is only used for statistics and does not affect packet flow or scheduling through the graph. Nodes can return 0 when they haven't processed packets (e.g., when dropping or redirecting due to port state).

Applied to files:

  • modules/dhcp/datapath/dhcp_input.c
🧬 Code graph analysis (2)
smoke/dhcp_test.sh (1)
smoke/_init.sh (2)
  • netns_add (113-121)
  • fail (104-107)
modules/dhcp/datapath/dhcp_input.c (5)
modules/dhcp/control/dhcp_client.c (1)
  • dhcp_input_cb (258-396)
modules/infra/datapath/gr_mbuf.h (1)
  • gr_mbuf_is_traced (52-54)
modules/infra/datapath/trace.c (1)
  • gr_mbuf_trace_add (592-619)
modules/infra/control/gr_graph.h (1)
  • rte_node_enqueue_x1 (22-25)
modules/l4/l4_input_local.c (1)
  • l4_input_register_port (25-37)
🪛 Shellcheck (0.11.0)
smoke/dhcp_test.sh

[warning] 5-5: Quote this to prevent word splitting.

(SC2046)


[warning] 16-16: tmp is referenced but not assigned.

(SC2154)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build-and-tests (clang-18, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (gcc-14, address, debug, ubuntu-24.04, -Dfrr=enabled, true)
  • GitHub Check: build-and-tests (gcc-13, none, debugoptimized, ubuntu-24.04, -Dfrr=enabled, false)
  • GitHub Check: build-and-tests (clang-16, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (clang-15, none, debugoptimized, ubuntu-22.04, false)
  • GitHub Check: rpm
🔇 Additional comments (28)
smoke/dhcp_test.sh (1)

1-68: LGTM!

The test script correctly validates the DHCP workflow: lease acquisition, route installation, status reporting, and cleanup. The netns isolation and dnsmasq configuration are appropriate for smoke testing.

modules/dhcp/meson.build (1)

1-7: LGTM!

Standard Meson module structure.

modules/dhcp/cli/meson.build (1)

1-6: LGTM!

Follows the project's modular CLI build pattern.

modules/meson.build (1)

11-11: LGTM!

Correctly integrates the DHCP module into the build graph.

modules/dhcp/control/meson.build (1)

1-9: LGTM!

Standard source registration for the control plane components.

modules/dhcp/datapath/meson.build (1)

1-7: LGTM!

Standard source registration for the datapath input node.

modules/dhcp/api/meson.build (1)

1-5: LGTM!

Standard API header registration.

modules/dhcp/datapath/dhcp_input.c (2)

30-88: LGTM!

The input processing correctly routes DHCP packets to control output with the callback attached. The trace parsing is safely bounded by dual guards (pos < options_len && pos < 64) and performs best-effort extraction of DHCP message type for diagnostics.


132-135: LGTM!

Correctly registers UDP port 68 (DHCP client port) for the dhcp_input node.

modules/dhcp/cli/dhcp.c (4)

16-34: LGTM!

Clean request/response handling with proper resource cleanup.


36-54: LGTM!

Consistent with dhcp_enable_cmd pattern.


133-172: LGTM!

Standard CLI command registration with proper error handling.


174-181: LGTM!

Standard CLI context registration.

modules/dhcp/api/gr_dhcp.h (1)

1-48: LGTM!

Clean API header with proper include guards and C++ compatibility.

modules/dhcp/control/dhcp_options.c (2)

8-108: LGTM!

Thorough bounds checking and proper handling of DHCP option parsing. Time values correctly converted from network byte order.


110-112: LGTM!

Clean delegation pattern.

modules/dhcp/control/dhcp_packet.c (2)

23-82: LGTM!

Proper validation of packet structure, magic cookie, and transaction ID before parsing options.


84-192: LGTM!

Well-structured packet building with comprehensive error handling and proper cleanup on failure paths.

modules/dhcp/control/dhcp_client.c (5)

32-120: LGTM!

Clean interface configuration with proper nexthop creation and RIB insertion. The __builtin_popcount approach for prefix length works correctly for contiguous subnet masks regardless of byte order.


398-410: LGTM!

Proper module initialization with appropriate error handling.


412-475: LGTM!

Clean client initialization with proper error handling and cleanup on failure paths.


477-542: LGTM!

Proper cleanup with bounds checking and route removal.


544-631: LGTM!

Clean API handler implementations and proper module registration.

modules/dhcp/control/dhcp_client.h (5)

16-24: LGTM!

Message types correctly match RFC 2132 option 53 values.


26-39: LGTM!

DHCP option codes are correct per RFC 2132.


41-48: LGTM!

State enum correctly represents RFC 2131 client state machine.


67-84: LGTM!

Packet structure correctly matches RFC 2131 DHCP message format with proper DPDK byte-order types.


86-107: LGTM!

Magic cookie, boot op-codes, and function prototypes are correct and consistent with their implementations.

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

♻️ Duplicate comments (3)
modules/dhcp/control/dhcp_options.c (1)

117-120: Buffer size check is too permissive.

Already flagged in previous review. The function can write up to 22 bytes but only checks for 8.

modules/dhcp/control/dhcp_client.h (1)

7-14: Missing forward declaration for struct event and include for time_t.

These issues were flagged in previous reviews. Lines 61-64 use time_t and struct event * but the necessary declarations are missing.

modules/dhcp/control/dhcp_client.c (1)

272-279: Missing bounds check on interface ID.

iface->id is used directly as an array index without validating it's within MAX_IFACES. If an interface with ID >= MAX_IFACES receives a DHCP packet, this causes out-of-bounds access.

Apply this diff to add bounds checking:

 	LOG(DEBUG, "dhcp_input_cb: packet on iface %u", iface->id);

+	if (iface->id >= MAX_IFACES) {
+		LOG(ERR, "dhcp_input_cb: iface %u exceeds MAX_IFACES", iface->id);
+		rte_pktmbuf_free(mbuf);
+		return;
+	}
+
 	client = dhcp_clients[iface->id];
🧹 Nitpick comments (4)
smoke/dhcp_test.sh (1)

38-45: Fixed sleeps may cause flaky tests.

The sleep 1 and sleep 5 waits are timing-dependent. Consider polling for readiness (e.g., checking dnsmasq log for startup or DHCP status for BOUND state) instead of fixed delays.

modules/dhcp/control/dhcp_options.c (1)

143-149: Consider defining a constant for option 55.

The Parameter Request List option (55) is used as a magic number. A DHCP_OPT_PARAM_REQUEST_LIST constant would improve consistency with other option definitions in the header.

+#define DHCP_OPT_PARAM_REQUEST_LIST 55
modules/dhcp/control/dhcp_client.c (2)

174-176: Consider cleanup if interface vanishes.

If the interface is removed, the client remains in dhcp_clients but the callback returns early. This orphans the client structure until dhcp_stop() is called.


238-252: Consider warning on timer creation failure.

If evtimer_new() fails, the timer remains NULL and lease renewal won't occur. Currently logged only at INFO level (line 254). A WARNING when timer creation fails would make this failure mode more visible.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a9d9d78 and 032c314.

⛔ Files ignored due to path filters (1)
  • docs/graph.svg is excluded by !**/*.svg
📒 Files selected for processing (14)
  • modules/dhcp/api/gr_dhcp.h (1 hunks)
  • modules/dhcp/api/meson.build (1 hunks)
  • modules/dhcp/cli/dhcp.c (1 hunks)
  • modules/dhcp/cli/meson.build (1 hunks)
  • modules/dhcp/control/dhcp_client.c (1 hunks)
  • modules/dhcp/control/dhcp_client.h (1 hunks)
  • modules/dhcp/control/dhcp_options.c (1 hunks)
  • modules/dhcp/control/dhcp_packet.c (1 hunks)
  • modules/dhcp/control/meson.build (1 hunks)
  • modules/dhcp/datapath/dhcp_input.c (1 hunks)
  • modules/dhcp/datapath/meson.build (1 hunks)
  • modules/dhcp/meson.build (1 hunks)
  • modules/meson.build (1 hunks)
  • smoke/dhcp_test.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • modules/dhcp/api/meson.build
  • modules/meson.build
  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/control/meson.build
  • modules/dhcp/control/dhcp_packet.c
  • modules/dhcp/meson.build
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{c,h}

⚙️ CodeRabbit configuration file

**/*.{c,h}: - gr_vec_*() functions cannot fail. No need to check their return value.

  • gr_vec_free(x) always sets x = NULL. There is no risk of double free.
  • ec_node_*() functions consume their ec_node arguments. No leaks on error.
  • rte_node->ctx is an uint8_t array of size 16, not a pointer.
  • Don't suggest to replace assert() with graceful error checking.
  • We compile with -std=gnu2x. Unnamed parameters in function definitions are valid.

Files:

  • modules/dhcp/datapath/dhcp_input.c
  • modules/dhcp/control/dhcp_client.c
  • modules/dhcp/control/dhcp_options.c
  • modules/dhcp/api/gr_dhcp.h
  • modules/dhcp/control/dhcp_client.h
**/*.sh

⚙️ CodeRabbit configuration file

**/*.sh: - Don't bother about unquoted shell variables.

Files:

  • smoke/dhcp_test.sh
🧠 Learnings (5)
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: In this project, cli_src is a global variable that gets populated by individual modules (like modules/policy/cli/meson.build) and is ultimately consumed by an executable() call in the top-level meson.build file. This creates a modular CLI build where each module contributes its CLI sources to the main CLI binary.

Applied to files:

  • modules/dhcp/cli/meson.build
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: The grout project uses a modular CLI build system where individual modules contribute their CLI sources to a global cli_src variable via `cli_src += files(...)`, and the top-level meson.build file builds the final grcli executable using all collected CLI sources. The grcli executable is defined in the top-level meson.build at lines 122-127.

Applied to files:

  • modules/dhcp/cli/meson.build
📚 Learning: 2025-11-03T13:28:19.489Z
Learnt from: rjarry
Repo: DPDK/grout PR: 379
File: modules/infra/datapath/port_tx.c:36-46
Timestamp: 2025-11-03T13:28:19.489Z
Learning: In DPDK graph node process callbacks, the return value is only used for statistics and does not affect packet flow or scheduling through the graph. Nodes can return 0 when they haven't processed packets (e.g., when dropping or redirecting due to port state).

Applied to files:

  • modules/dhcp/datapath/dhcp_input.c
📚 Learning: 2025-11-05T14:28:28.817Z
Learnt from: rjarry
Repo: DPDK/grout PR: 372
File: modules/infra/datapath/loop_input.c:53-56
Timestamp: 2025-11-05T14:28:28.817Z
Learning: In modules/infra/datapath/loop_input.c, the l3_edges array is intentionally indexed using network byte order (rte_be16_t) for EtherType values. Both loopback_input_add_type() registration and loopback_input_process() lookup use network byte order consistently, so no byte order conversion is needed when accessing l3_edges.

Applied to files:

  • modules/dhcp/datapath/dhcp_input.c
📚 Learning: 2025-09-09T06:02:47.703Z
Learnt from: maxime-leroy
Repo: DPDK/grout PR: 309
File: modules/srv6/datapath/srv6_local.c:88-122
Timestamp: 2025-09-09T06:02:47.703Z
Learning: The DPDK function rte_ipv6_get_next_ext() only reads 2 bytes from the extension header, so a 2-byte precheck is sufficient before calling it.

Applied to files:

  • modules/dhcp/control/dhcp_options.c
🧬 Code graph analysis (1)
smoke/dhcp_test.sh (1)
smoke/_init.sh (2)
  • netns_add (113-121)
  • fail (104-107)
🪛 Shellcheck (0.11.0)
smoke/dhcp_test.sh

[warning] 5-5: Quote this to prevent word splitting.

(SC2046)


[warning] 16-16: tmp is referenced but not assigned.

(SC2154)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build-and-tests (clang-18, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (gcc-13, none, debugoptimized, ubuntu-24.04, -Dfrr=enabled, false)
  • GitHub Check: build-and-tests (clang-15, none, debugoptimized, ubuntu-22.04, false)
  • GitHub Check: build-and-tests (clang-16, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (gcc-14, address, debug, ubuntu-24.04, -Dfrr=enabled, true)
  • GitHub Check: build-cross-aarch64
  • GitHub Check: rpm
  • GitHub Check: deb
🔇 Additional comments (13)
modules/dhcp/cli/meson.build (1)

1-6: DHCP CLI source correctly hooked into global cli_src

Header is fine and cli_src += files('dhcp.c') matches the established modular CLI pattern; this will correctly pull the DHCP CLI into the main grcli build. Nothing to change here.

Based on learnings, this is consistent with how other modules contribute to cli_src.

modules/dhcp/datapath/meson.build (1)

1-7: LGTM!

Standard Meson build configuration for the new datapath module.

smoke/dhcp_test.sh (2)

1-6: LGTM!

Test setup correctly sources the init script and creates an isolated network namespace.


47-67: Test assertions look correct.

Verifies route installation, DHCP status, and cleanup after disable. Good coverage of the happy path.

modules/dhcp/control/dhcp_options.c (1)

8-108: LGTM on parsing logic.

The options parser correctly validates bounds and handles PAD/END options per RFC 2132. The length checks prevent buffer overreads.

modules/dhcp/control/dhcp_client.h (2)

67-84: LGTM!

The dhcp_packet struct correctly defines the DHCP message format per RFC 2131, with proper packing and flexible array member for options.


90-107: Clean API surface.

Function prototypes are well-organized, separating lifecycle management, packet building/parsing, and options handling.

modules/dhcp/datapath/dhcp_input.c (3)

30-88: LGTM!

The datapath node correctly attaches the callback and handles trace data extraction. The minimal inline parsing for traces is appropriate for the hot path.


90-114: LGTM!

Clean helper function and trace formatter implementation.


132-135: LGTM!

Port registration correctly binds UDP port 68 (DHCP client) to the input node.

modules/dhcp/api/gr_dhcp.h (1)

1-48: LGTM - Clean API header.

The API structures and macros are well-defined. Using uint8_t for the state field (with enum comment) maintains ABI compatibility while documenting intent. The flexible array member pattern in gr_dhcp_list_resp is standard.

modules/dhcp/control/dhcp_client.c (2)

485-488: Good bounds checking.

Proper validation of iface_id before array access. This is the correct pattern that should be applied in dhcp_start() as well.


72-72: The repository clone is currently unavailable, preventing direct verification of the code patterns. However, based on the review comment's concern, I can provide guidance on the issue:

Verify whether the route origin mismatch between nexthop creation and route insertion is intentional.

The original comment identifies a mismatch: the nexthop is created with GR_NH_ORIGIN_DHCP (line 56) but the route is inserted with GR_NH_ORIGIN_LINK (line 72).

Without access to verify broader codebase patterns and the semantic intent behind these origin types, this concern requires manual verification to determine if:

  1. Using GR_NH_ORIGIN_LINK for the locally configured IP address is intentional (while the nexthop represents the DHCP origin)
  2. Other parts of the codebase follow similar patterns
  3. Documentation or comments explain this design choice

@aharivel aharivel force-pushed the dhcp branch 2 times, most recently from 3c1f231 to e9d7007 Compare November 26, 2025 12:24
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: 0

♻️ Duplicate comments (3)
modules/dhcp/control/dhcp_client.c (2)

430-444: Missing bounds check before array access.

dhcp_clients[iface_id] is accessed without verifying iface_id < MAX_IFACES. While iface_from_id() validates the interface exists, it doesn't guarantee the ID is within array bounds. Compare with dhcp_stop() at line 503 which has proper bounds checking.

 int dhcp_start(uint16_t iface_id) {
 	struct dhcp_client *client;
 	struct rte_mbuf *m;
 	uint32_t xid;

+	if (iface_id >= MAX_IFACES) {
+		errno = EINVAL;
+		return -1;
+	}
+
 	if (iface_from_id(iface_id) == NULL) {
 		errno = ENODEV;
 		return -1;
 	}

275-288: Missing bounds check on interface ID.

iface->id is used directly as an array index without validating it's within MAX_IFACES. If an interface with ID >= MAX_IFACES receives a DHCP packet, this causes out-of-bounds access.

 	LOG(DEBUG, "dhcp_input_cb: packet on iface %u", iface->id);

+	if (iface->id >= MAX_IFACES) {
+		LOG(ERR, "dhcp_input_cb: iface %u exceeds MAX_IFACES", iface->id);
+		rte_pktmbuf_free(mbuf);
+		return;
+	}
+
 	client = dhcp_clients[iface->id];
modules/dhcp/control/dhcp_client.h (1)

7-14: Missing forward declarations and includes.

The header uses struct event * (lines 62-64) and time_t (line 61) without declaring them. Also struct rte_mbuf is used in prototypes without a forward declaration.

 #include <gr_control_output.h>

 #include <rte_byteorder.h>

 #include <netinet/in.h>
 #include <stdint.h>
+#include <time.h>
+
+struct event;
+struct rte_mbuf;
+struct rte_mempool;

 typedef uint8_t control_input_t;
🧹 Nitpick comments (1)
modules/dhcp/control/dhcp_packet.c (1)

89-193: Consider extracting common packet-building logic.

dhcp_build_discover and dhcp_build_request share ~80% of their code (MAC lookup, mbuf allocation, broadcast setup, IP/UDP header construction). A helper function could reduce duplication.

Also applies to: 195-300

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c1f231 and e9d7007.

⛔ Files ignored due to path filters (1)
  • docs/graph.svg is excluded by !**/*.svg
📒 Files selected for processing (15)
  • .github/workflows/check.yml (1 hunks)
  • modules/dhcp/api/gr_dhcp.h (1 hunks)
  • modules/dhcp/api/meson.build (1 hunks)
  • modules/dhcp/cli/dhcp.c (1 hunks)
  • modules/dhcp/cli/meson.build (1 hunks)
  • modules/dhcp/control/dhcp_client.c (1 hunks)
  • modules/dhcp/control/dhcp_client.h (1 hunks)
  • modules/dhcp/control/dhcp_options.c (1 hunks)
  • modules/dhcp/control/dhcp_packet.c (1 hunks)
  • modules/dhcp/control/meson.build (1 hunks)
  • modules/dhcp/datapath/dhcp_input.c (1 hunks)
  • modules/dhcp/datapath/meson.build (1 hunks)
  • modules/dhcp/meson.build (1 hunks)
  • modules/meson.build (1 hunks)
  • smoke/dhcp_test.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
  • modules/dhcp/api/meson.build
  • modules/dhcp/cli/meson.build
  • modules/dhcp/control/meson.build
  • modules/dhcp/datapath/dhcp_input.c
  • modules/meson.build
  • modules/dhcp/api/gr_dhcp.h
  • modules/dhcp/meson.build
  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/control/dhcp_options.c
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{c,h}

⚙️ CodeRabbit configuration file

**/*.{c,h}: - gr_vec_*() functions cannot fail. No need to check their return value.

  • gr_vec_free(x) always sets x = NULL. There is no risk of double free.
  • ec_node_*() functions consume their ec_node arguments. No leaks on error.
  • rte_node->ctx is an uint8_t array of size 16, not a pointer.
  • Don't suggest to replace assert() with graceful error checking.
  • We compile with -std=gnu2x. Unnamed parameters in function definitions are valid.

Files:

  • modules/dhcp/control/dhcp_packet.c
  • modules/dhcp/control/dhcp_client.h
  • modules/dhcp/control/dhcp_client.c
**/*.sh

⚙️ CodeRabbit configuration file

**/*.sh: - Don't bother about unquoted shell variables.

Files:

  • smoke/dhcp_test.sh
🧬 Code graph analysis (4)
modules/dhcp/control/dhcp_packet.c (4)
modules/dhcp/control/dhcp_options.c (3)
  • dhcp_parse_options (8-116)
  • dhcp_build_options (118-120)
  • dhcp_build_options_ex (122-169)
modules/infra/control/gr_iface.h (1)
  • iface (16-25)
modules/infra/control/iface.c (1)
  • iface_get_eth_addr (261-274)
modules/dhcp/control/dhcp_client.c (1)
  • dhcp_get_mempool (557-559)
modules/dhcp/control/dhcp_client.h (3)
modules/dhcp/control/dhcp_client.c (5)
  • dhcp_input_cb (267-414)
  • dhcp_start (430-493)
  • dhcp_stop (495-555)
  • dhcp_get_mempool (557-559)
  • dhcp_get_output (561-563)
modules/dhcp/datapath/dhcp_input.c (1)
  • dhcp_input_register_port (144-147)
modules/dhcp/control/dhcp_options.c (3)
  • dhcp_parse_options (8-116)
  • dhcp_build_options (118-120)
  • dhcp_build_options_ex (122-169)
modules/dhcp/control/dhcp_client.c (9)
modules/infra/control/gr_iface.h (1)
  • iface (16-25)
api/gr_errno.h (1)
  • errno_set (9-12)
modules/infra/control/iface.c (1)
  • iface_get_eth_addr (261-274)
modules/dhcp/control/dhcp_packet.c (3)
  • dhcp_build_discover (89-193)
  • dhcp_build_request (195-300)
  • dhcp_parse_packet (23-87)
modules/infra/datapath/control_input.c (1)
  • post_to_stack (45-54)
modules/dhcp/datapath/dhcp_input.c (1)
  • dhcp_input_register_port (144-147)
modules/infra/control/mempool.c (1)
  • gr_pktmbuf_pool_get (39-103)
main/api.c (1)
  • api_send (242-258)
main/gr_module.h (1)
  • api_out (20-23)
smoke/dhcp_test.sh (1)
smoke/_init.sh (2)
  • fail (104-107)
  • netns_add (113-121)
🪛 Shellcheck (0.11.0)
smoke/dhcp_test.sh

[warning] 5-5: Quote this to prevent word splitting.

(SC2046)


[warning] 18-18: tmp is referenced but not assigned.

(SC2154)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build-and-tests (clang-16, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (clang-15, none, debugoptimized, ubuntu-22.04, false)
  • GitHub Check: build-and-tests (gcc-13, none, debugoptimized, ubuntu-24.04, -Dfrr=enabled, false)
  • GitHub Check: build-and-tests (clang-18, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (gcc-14, address, debug, ubuntu-24.04, -Dfrr=enabled, true)
  • GitHub Check: build-cross-aarch64
  • GitHub Check: rpm
  • GitHub Check: deb
🔇 Additional comments (10)
modules/dhcp/datapath/meson.build (1)

1-7: LGTM!

Clean build configuration. The license header, source file inclusion, and include directory are all properly specified.

.github/workflows/check.yml (1)

74-74: LGTM!

Adding dnsmasq as a CI dependency correctly supports the new DHCP smoke test.

smoke/dhcp_test.sh (1)

1-70: LGTM!

Well-structured smoke test covering the full DHCP lifecycle: lease acquisition, route installation, status verification, and cleanup on disable. The shellcheck warnings about $tmp and word splitting are false positives per coding guidelines (the variable comes from sourced _init.sh).

modules/dhcp/control/dhcp_packet.c (1)

1-87: LGTM!

Packet parsing has proper bounds checks and validates magic cookie, transaction ID, and operation type before processing options.

modules/dhcp/control/dhcp_client.c (4)

32-116: LGTM!

Interface configuration logic properly handles nexthop creation, RIB insertion, and optional gateway route setup with appropriate error logging.


120-265: LGTM!

Timer management is solid - proper cancellation before rescheduling, correct T1/T2/expiry calculations per RFC 2131 defaults (50% and 87.5% of lease time).


495-555: LGTM!

dhcp_stop() correctly bounds-checks iface_id, cleans up routes, cancels timers, and frees resources.


565-588: LGTM!

The list handler iterates all possible interfaces and streams status for active clients via api_send().

modules/dhcp/control/dhcp_client.h (2)

67-84: LGTM!

dhcp_packet structure correctly matches RFC 2131 wire format with proper packing and big-endian field types.


41-48: LGTM!

State enum and function prototypes provide a clean public API surface for the DHCP client module.

Also applies to: 86-119

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: 0

♻️ Duplicate comments (1)
modules/dhcp/control/dhcp_client.c (1)

445-459: Missing bounds check before array access.

dhcp_clients[iface_id] is accessed at lines 455 and 474 without verifying iface_id < MAX_IFACES. While iface_from_id() validates the interface exists, it doesn't guarantee the ID fits the array. Compare with dhcp_stop() which has proper bounds checking at line 508.

 int dhcp_start(uint16_t iface_id) {
 	struct dhcp_client *client;
 	struct rte_mbuf *m;
 	uint32_t xid;

+	if (iface_id >= MAX_IFACES) {
+		errno = EINVAL;
+		return -1;
+	}
+
 	if (iface_from_id(iface_id) == NULL) {
 		errno = ENODEV;
 		return -1;
 	}
🧹 Nitpick comments (4)
modules/dhcp/control/dhcp_client.c (2)

46-52: Consider documenting the /24 default in code or referencing RFC behavior.

RFC 2131 doesn't mandate a default prefix length when subnet mask is missing. The /24 fallback is reasonable but could surprise users. A brief comment explaining the rationale would help.


614-617: Consider clearing dhcp_clients[] entries on module finalize.

If any DHCP clients are still running when the module is finalized, they won't be cleaned up. This could leave stale routes/addresses and leak memory.

 static void dhcp_fini(struct event_base *) {
+	for (uint16_t i = 0; i < MAX_IFACES; i++) {
+		if (dhcp_clients[i] != NULL)
+			dhcp_stop(i);
+	}
 	gr_pktmbuf_pool_release(dhcp_mp, 512);
 	LOG(INFO, "dhcp: module finalized");
 }
modules/dhcp/control/dhcp_packet.c (1)

23-203: DHCP packet parsing/building looks consistent; consider adding UDP checksum.

The overall layout and length calculations for UDP + DHCP look correct, and the shared dhcp_build_packet_common() keeps DISCOVER/REQUEST construction tidy. One improvement you might want to consider is computing the UDP checksum instead of leaving udp->dgram_cksum = 0, in case you ever encounter peers that are strict about non‑zero UDP checksums:

-	udp->dgram_cksum = 0;
+	udp->dgram_cksum = 0;
+	/* Optionally enable UDP checksum if required by peers */
+	/* udp->dgram_cksum = rte_ipv4_udptcp_cksum(ip, udp); */
modules/dhcp/control/dhcp_options.c (1)

4-7: Include <string.h> for memcpy instead of relying on transitive headers.

This file calls memcpy but doesn’t include <string.h> itself. It’s safer to include it explicitly rather than relying on indirect includes from other headers:

 #include "dhcp_client.h"
 
 #include <gr_log.h>
+#include <string.h>
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9d7007 and d99c817.

⛔ Files ignored due to path filters (1)
  • docs/graph.svg is excluded by !**/*.svg
📒 Files selected for processing (15)
  • .github/workflows/check.yml (1 hunks)
  • modules/dhcp/api/gr_dhcp.h (1 hunks)
  • modules/dhcp/api/meson.build (1 hunks)
  • modules/dhcp/cli/dhcp.c (1 hunks)
  • modules/dhcp/cli/meson.build (1 hunks)
  • modules/dhcp/control/dhcp_client.c (1 hunks)
  • modules/dhcp/control/dhcp_client.h (1 hunks)
  • modules/dhcp/control/dhcp_options.c (1 hunks)
  • modules/dhcp/control/dhcp_packet.c (1 hunks)
  • modules/dhcp/control/meson.build (1 hunks)
  • modules/dhcp/datapath/dhcp_input.c (1 hunks)
  • modules/dhcp/datapath/meson.build (1 hunks)
  • modules/dhcp/meson.build (1 hunks)
  • modules/meson.build (1 hunks)
  • smoke/dhcp_test.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • modules/dhcp/cli/meson.build
  • modules/dhcp/meson.build
  • modules/dhcp/datapath/dhcp_input.c
  • .github/workflows/check.yml
  • modules/dhcp/control/meson.build
  • modules/dhcp/api/gr_dhcp.h
  • modules/dhcp/datapath/meson.build
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{c,h}

⚙️ CodeRabbit configuration file

**/*.{c,h}: - gr_vec_*() functions cannot fail. No need to check their return value.

  • gr_vec_free(x) always sets x = NULL. There is no risk of double free.
  • ec_node_*() functions consume their ec_node arguments. No leaks on error.
  • rte_node->ctx is an uint8_t array of size 16, not a pointer.
  • Don't suggest to replace assert() with graceful error checking.
  • We compile with -std=gnu2x. Unnamed parameters in function definitions are valid.

Files:

  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/control/dhcp_client.c
  • modules/dhcp/control/dhcp_options.c
  • modules/dhcp/control/dhcp_packet.c
  • modules/dhcp/control/dhcp_client.h
**/*.sh

⚙️ CodeRabbit configuration file

**/*.sh: - Don't bother about unquoted shell variables.

Files:

  • smoke/dhcp_test.sh
🧠 Learnings (5)
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: In this project, cli_src is a global variable that gets populated by individual modules (like modules/policy/cli/meson.build) and is ultimately consumed by an executable() call in the top-level meson.build file. This creates a modular CLI build where each module contributes its CLI sources to the main CLI binary.

Applied to files:

  • modules/meson.build
📚 Learning: 2025-11-24T12:50:09.310Z
Learnt from: rjarry
Repo: DPDK/grout PR: 408
File: api/gr_net_types.h:78-84
Timestamp: 2025-11-24T12:50:09.310Z
Learning: In api/gr_net_types.h, ip4_addr_t values are stored in network byte order (big-endian). When using a union to cast to a byte array (uint8_t u8[4]), accessing u8[0] correctly reads the first octet (most significant byte) of the IP address on all architectures, because it directly accesses the memory layout rather than performing integer operations. This pattern is valid for checking properties like multicast (first octet in range 224-239).

Applied to files:

  • modules/dhcp/cli/dhcp.c
📚 Learning: 2025-09-05T22:34:10.597Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/ip/datapath/ip_output.c:100-107
Timestamp: 2025-09-05T22:34:10.597Z
Learning: This codebase compiles with -Wswitch-enum and -Werror flags, which means all enum values must be explicitly handled in switch statements or compilation will fail. This provides compile-time safety against missing enum cases, making default cases in enum switches unnecessary and potentially harmful as they could mask new enum additions.

Applied to files:

  • modules/dhcp/cli/dhcp.c
📚 Learning: 2025-09-21T10:48:42.539Z
Learnt from: rjarry
Repo: DPDK/grout PR: 312
File: modules/srv6/api/gr_srv6.h:18-21
Timestamp: 2025-09-21T10:48:42.539Z
Learning: In the Grout codebase, fixed-width enum syntax like `typedef enum : uint8_t { ... } type_t;` is valid and correct since the project uses `-std=gnu2x` which supports C23 features including fixed-width enums.

Applied to files:

  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/control/dhcp_client.h
📚 Learning: 2025-09-09T06:02:47.703Z
Learnt from: maxime-leroy
Repo: DPDK/grout PR: 309
File: modules/srv6/datapath/srv6_local.c:88-122
Timestamp: 2025-09-09T06:02:47.703Z
Learning: The DPDK function rte_ipv6_get_next_ext() only reads 2 bytes from the extension header, so a 2-byte precheck is sufficient before calling it.

Applied to files:

  • modules/dhcp/control/dhcp_options.c
🧬 Code graph analysis (4)
modules/dhcp/cli/dhcp.c (6)
cli/ecoli.c (2)
  • arg_str (99-109)
  • with_help (15-24)
modules/infra/cli/iface.c (2)
  • iface_from_name (93-106)
  • complete_iface_names (71-91)
api/gr_api.h (1)
  • gr_api_client_send_recv (41-52)
cli/table.c (1)
  • scols_line_sprintf (10-23)
cli/ec_node_dyn.c (1)
  • ec_node_dyn (117-133)
cli/exec.c (1)
  • cli_context_register (16-18)
modules/dhcp/control/dhcp_client.c (5)
api/gr_errno.h (1)
  • errno_set (9-12)
modules/ip/control/route.c (1)
  • rib4_insert (206-214)
modules/dhcp/control/dhcp_packet.c (3)
  • dhcp_build_discover (206-208)
  • dhcp_build_request (210-215)
  • dhcp_parse_packet (23-91)
modules/dhcp/datapath/dhcp_input.c (1)
  • dhcp_input_register_port (144-147)
main/gr_module.h (1)
  • api_out (20-23)
modules/dhcp/control/dhcp_packet.c (4)
modules/dhcp/control/dhcp_options.c (2)
  • dhcp_parse_options (8-117)
  • dhcp_build_options_ex (123-171)
modules/infra/control/gr_iface.h (1)
  • iface (16-25)
modules/infra/control/iface.c (1)
  • iface_get_eth_addr (261-274)
modules/dhcp/control/dhcp_client.c (1)
  • dhcp_get_mempool (562-564)
smoke/dhcp_test.sh (1)
smoke/_init.sh (2)
  • fail (104-107)
  • netns_add (113-121)
🪛 Shellcheck (0.11.0)
smoke/dhcp_test.sh

[warning] 5-5: Quote this to prevent word splitting.

(SC2046)


[warning] 18-18: tmp is referenced but not assigned.

(SC2154)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: build-and-tests (clang-16, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (clang-15, none, debugoptimized, ubuntu-22.04, false)
  • GitHub Check: build-and-tests (gcc-13, none, debugoptimized, ubuntu-24.04, -Dfrr=enabled, false)
  • GitHub Check: build-and-tests (gcc-14, address, debug, ubuntu-24.04, -Dfrr=enabled, true)
  • GitHub Check: build-and-tests (clang-18, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: deb
  • GitHub Check: rpm
🔇 Additional comments (9)
modules/dhcp/control/dhcp_client.h (2)

67-84: LGTM!

The dhcp_packet struct correctly matches the RFC 2131 BOOTP/DHCP packet format with proper packing and byte-order annotations.


18-18: I encountered a technical issue while attempting to verify the review comment—the repository clone failed, preventing me from accessing the codebase files directly.

To verify the concern about the potential duplicate control_input_t typedef, I would need to:

  1. Check if control_input_t is actually defined in gr_control_output.h (included at line 7)
  2. Confirm whether the typedef at line 18 would cause redefinition warnings
  3. Verify the intended scope and design of these type definitions

Since I cannot access the codebase to verify these details, I cannot definitively confirm or refute the concern raised in the review comment.


smoke/dhcp_test.sh (1)

1-70: LGTM!

Well-structured smoke test covering the complete DHCP lifecycle: enable, lease acquisition verification, status check, disable, and cleanup verification.

modules/dhcp/cli/dhcp.c (2)

17-35: LGTM!

Clean implementation with proper resource cleanup for both iface and resp.


104-114: LGTM!

Good use of IP4_F macro for IP address formatting, which handles byte-order correctly.

modules/dhcp/control/dhcp_client.c (3)

276-303: LGTM!

Good addition of bounds check at lines 292-296. The state machine entry properly validates iface before accessing dhcp_clients[].


223-274: LGTM!

Timer scheduling follows RFC 2131 defaults (T1=50%, T2=87.5% of lease) when server doesn't provide explicit values. Proper cleanup before scheduling new timers.


313-426: LGTM!

State machine correctly implements DHCP client state transitions per RFC 2131. Handles OFFER→REQUEST→ACK flow, NAK fallback to INIT, and renewal/rebinding states appropriately.

modules/dhcp/control/dhcp_options.c (1)

76-101: I encountered a repository cloning error. Let me try a different approach to gather the necessary information. Let me search for information about the coding guidelines and the specific file context:
<tool_calls>

curl -s "https://raw.githubusercontent.com/DPDK/grout/main/modules/dhcp/control/dhcp_options.c" | head -150

</function_calls>

Let me try another approach using web search to understand the context better:
<tool_calls>

DPDK grout dhcp_options.c lease_time integer promotion shift

</function_calls>

Based on the coding guidelines provided and the nature of the issue described, let me analyze the review comment using C language standards:

Based on my analysis of C language standards and the coding guidelines provided:

The review comment correctly identifies a legitimate undefined behavior issue with integer promotion in bitwise shift operations.

According to C99/C11 standards:

  • When uint8_t values participate in shift operations, they undergo integer promotion to int
  • If the promoted int becomes negative (when the original byte value was ≥128), shifting that negative value is undefined behavior per C99 6.5.7
  • The proposed solution of explicit (uint32_t) casts before shifting is the standard, correct approach to avoid this

The concern is technically sound and the suggested fix follows best practices for safe bitwise manipulation of byte arrays into larger integers.

However, since I cannot access the repository to verify the actual code implementation, variable types, and confirm the exact lines referenced, I must mark this for manual verification:

Fix the undefined behavior in 32-bit value reconstruction by casting bytes to uint32_t before shifting to ensure well-defined behavior across all platforms.

The proposed diff casting each byte operand to (uint32_t) before the shift operations is the correct and standard approach. This keeps all arithmetic in the unsigned 32-bit domain and eliminates any sign-extension issues or implementation-defined behavior from the promotion of uint8_t to signed int.

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: 1

♻️ Duplicate comments (4)
modules/dhcp/cli/dhcp.c (2)

17-20: Variable declarations not in reverse xmas tree order.

Per prior feedback, local variable declarations should be ordered from longest to shortest line.

 static cmd_status_t dhcp_enable_cmd(struct gr_api_client *c, const struct ec_pnode *p) {
-	const char *iface_name = arg_str(p, "IFACE");
-	struct gr_dhcp_start_req req;
 	struct gr_iface *iface;
+	struct gr_dhcp_start_req req;
+	const char *iface_name = arg_str(p, "IFACE");

35-38: Same reverse xmas tree issue.

 static cmd_status_t dhcp_disable_cmd(struct gr_api_client *c, const struct ec_pnode *p) {
-	const char *iface_name = arg_str(p, "IFACE");
-	struct gr_dhcp_stop_req req;
 	struct gr_iface *iface;
+	struct gr_dhcp_stop_req req;
+	const char *iface_name = arg_str(p, "IFACE");
modules/dhcp/control/dhcp_client.c (2)

32-37: Variable declarations not in reverse xmas tree order.

Per prior feedback, declarations should be ordered longest to shortest.


454-468: Missing bounds check before array access.

dhcp_clients[iface_id] at line 464 is accessed without validating iface_id < MAX_IFACES. While iface_from_id() validates the interface exists, it doesn't guarantee the ID is within array bounds. Compare with dhcp_stop() at line 517 which correctly checks bounds first.

 int dhcp_start(uint16_t iface_id) {
 	struct dhcp_client *client;
 	struct rte_mbuf *m;
 	uint32_t xid;

+	if (iface_id >= MAX_IFACES) {
+		errno = EINVAL;
+		return -1;
+	}
+
 	if (iface_from_id(iface_id) == NULL) {
 		errno = ENODEV;
 		return -1;
 	}
🧹 Nitpick comments (1)
smoke/dhcp_test.sh (1)

40-47: Fixed sleep values may cause flakiness in CI.

sleep 1 for dnsmasq startup and sleep 5 for DHCP lease acquisition are arbitrary. In slower CI environments, these may be insufficient. Consider polling with a timeout loop instead of fixed sleeps.

Example for lease wait:

for i in $(seq 1 30); do
    grcli dhcp show | grep -q "BOUND" && break
    sleep 1
done
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d99c817 and 39cbb06.

⛔ Files ignored due to path filters (1)
  • docs/graph.svg is excluded by !**/*.svg
📒 Files selected for processing (15)
  • .github/workflows/check.yml (1 hunks)
  • modules/dhcp/api/gr_dhcp.h (1 hunks)
  • modules/dhcp/api/meson.build (1 hunks)
  • modules/dhcp/cli/dhcp.c (1 hunks)
  • modules/dhcp/cli/meson.build (1 hunks)
  • modules/dhcp/control/dhcp_client.c (1 hunks)
  • modules/dhcp/control/dhcp_client.h (1 hunks)
  • modules/dhcp/control/dhcp_options.c (1 hunks)
  • modules/dhcp/control/dhcp_packet.c (1 hunks)
  • modules/dhcp/control/meson.build (1 hunks)
  • modules/dhcp/datapath/dhcp_input.c (1 hunks)
  • modules/dhcp/datapath/meson.build (1 hunks)
  • modules/dhcp/meson.build (1 hunks)
  • modules/meson.build (1 hunks)
  • smoke/dhcp_test.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • .github/workflows/check.yml
  • modules/dhcp/cli/meson.build
  • modules/meson.build
  • modules/dhcp/control/meson.build
  • modules/dhcp/datapath/dhcp_input.c
  • modules/dhcp/control/dhcp_client.h
  • modules/dhcp/api/gr_dhcp.h
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{c,h}

⚙️ CodeRabbit configuration file

**/*.{c,h}: - gr_vec_*() functions cannot fail. No need to check their return value.

  • gr_vec_free(x) always sets x = NULL. There is no risk of double free.
  • ec_node_*() functions consume their ec_node arguments. No leaks on error.
  • rte_node->ctx is an uint8_t array of size 16, not a pointer.
  • Don't suggest to replace assert() with graceful error checking.
  • We compile with -std=gnu2x. Unnamed parameters in function definitions are valid.

Files:

  • modules/dhcp/control/dhcp_client.c
  • modules/dhcp/control/dhcp_options.c
  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/control/dhcp_packet.c
**/*.sh

⚙️ CodeRabbit configuration file

**/*.sh: - Don't bother about unquoted shell variables.

Files:

  • smoke/dhcp_test.sh
🧠 Learnings (4)
📚 Learning: 2025-09-09T06:02:47.703Z
Learnt from: maxime-leroy
Repo: DPDK/grout PR: 309
File: modules/srv6/datapath/srv6_local.c:88-122
Timestamp: 2025-09-09T06:02:47.703Z
Learning: The DPDK function rte_ipv6_get_next_ext() only reads 2 bytes from the extension header, so a 2-byte precheck is sufficient before calling it.

Applied to files:

  • modules/dhcp/control/dhcp_options.c
  • modules/dhcp/control/dhcp_packet.c
📚 Learning: 2025-11-24T12:50:09.310Z
Learnt from: rjarry
Repo: DPDK/grout PR: 408
File: api/gr_net_types.h:78-84
Timestamp: 2025-11-24T12:50:09.310Z
Learning: In api/gr_net_types.h, ip4_addr_t values are stored in network byte order (big-endian). When using a union to cast to a byte array (uint8_t u8[4]), accessing u8[0] correctly reads the first octet (most significant byte) of the IP address on all architectures, because it directly accesses the memory layout rather than performing integer operations. This pattern is valid for checking properties like multicast (first octet in range 224-239).

Applied to files:

  • modules/dhcp/cli/dhcp.c
📚 Learning: 2025-09-05T22:34:10.597Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/ip/datapath/ip_output.c:100-107
Timestamp: 2025-09-05T22:34:10.597Z
Learning: This codebase compiles with -Wswitch-enum and -Werror flags, which means all enum values must be explicitly handled in switch statements or compilation will fail. This provides compile-time safety against missing enum cases, making default cases in enum switches unnecessary and potentially harmful as they could mask new enum additions.

Applied to files:

  • modules/dhcp/cli/dhcp.c
📚 Learning: 2025-09-21T10:48:42.539Z
Learnt from: rjarry
Repo: DPDK/grout PR: 312
File: modules/srv6/api/gr_srv6.h:18-21
Timestamp: 2025-09-21T10:48:42.539Z
Learning: In the Grout codebase, fixed-width enum syntax like `typedef enum : uint8_t { ... } type_t;` is valid and correct since the project uses `-std=gnu2x` which supports C23 features including fixed-width enums.

Applied to files:

  • modules/dhcp/cli/dhcp.c
🧬 Code graph analysis (4)
modules/dhcp/control/dhcp_client.c (5)
api/gr_errno.h (1)
  • errno_set (9-12)
modules/dhcp/control/dhcp_packet.c (2)
  • dhcp_build_discover (199-201)
  • dhcp_build_request (203-208)
modules/dhcp/datapath/dhcp_input.c (1)
  • dhcp_input_register_port (143-146)
modules/infra/control/mempool.c (1)
  • gr_pktmbuf_pool_get (39-103)
main/gr_module.h (1)
  • api_out (20-23)
smoke/dhcp_test.sh (1)
smoke/_init.sh (2)
  • fail (104-107)
  • netns_add (113-121)
modules/dhcp/cli/dhcp.c (4)
cli/ecoli.c (2)
  • arg_str (99-109)
  • with_help (15-24)
modules/infra/cli/iface.c (2)
  • iface_from_name (93-106)
  • complete_iface_names (71-91)
api/gr_api.h (1)
  • gr_api_client_send_recv (41-52)
cli/table.c (1)
  • scols_line_sprintf (10-23)
modules/dhcp/control/dhcp_packet.c (3)
modules/dhcp/control/dhcp_options.c (2)
  • dhcp_parse_options (8-117)
  • dhcp_build_options_ex (123-171)
modules/infra/control/iface.c (1)
  • iface_get_eth_addr (261-274)
modules/dhcp/control/dhcp_client.c (1)
  • dhcp_get_mempool (574-576)
🪛 Shellcheck (0.11.0)
smoke/dhcp_test.sh

[warning] 5-5: Quote this to prevent word splitting.

(SC2046)


[warning] 18-18: tmp is referenced but not assigned.

(SC2154)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: build-and-tests (clang-16, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (clang-15, none, debugoptimized, ubuntu-22.04, false)
  • GitHub Check: build-and-tests (gcc-13, none, debugoptimized, ubuntu-24.04, -Dfrr=enabled, false)
  • GitHub Check: build-and-tests (clang-18, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (gcc-14, address, debug, ubuntu-24.04, -Dfrr=enabled, true)
  • GitHub Check: rpm
  • GitHub Check: deb
🔇 Additional comments (18)
modules/dhcp/datapath/meson.build (1)

1-7: LGTM!

Standard meson.build pattern for adding the datapath source and include directory.

modules/dhcp/meson.build (1)

1-7: LGTM!

Properly wires up all DHCP module components.

modules/dhcp/api/meson.build (1)

1-5: LGTM!

Standard API header registration.

smoke/dhcp_test.sh (1)

1-70: LGTM overall.

The test correctly validates the DHCP client lifecycle: enable, verify routes and BOUND state, disable, verify cleanup.

modules/dhcp/cli/dhcp.c (3)

53-70: LGTM.

State-to-string mapping is clear. The default case provides safety for any future state additions.


72-127: LGTM.

The show command properly handles missing interface names and uses IP4_F for IP formatting as suggested.


129-177: LGTM.

CLI context registration is well-structured. Using GR_IFACE_TYPE_UNDEF allows DHCP on any interface type including bonds.

modules/dhcp/control/dhcp_client.c (5)

278-305: LGTM.

Bounds check for iface->id is now properly implemented before accessing dhcp_clients array.


225-276: LGTM.

Timer scheduling follows RFC 2131 defaults (T1=50%, T2=87.5% of lease time) and properly cancels existing timers before scheduling new ones.


509-572: LGTM.

dhcp_stop() has proper bounds checking and correctly cleans up routes, timers, and client state.


315-435: LGTM.

State machine transitions are well-implemented. OFFER→REQUEST→ACK flow and renewal/rebinding logic correctly handle NAK responses by cleaning up and restarting discovery.


626-661: LGTM.

Module initialization, finalization, and API handler registration are properly structured.

modules/dhcp/control/dhcp_packet.c (3)

199-201: LGTM!

Correctly builds a DHCP DISCOVER packet by delegating to the common helper with appropriate parameters.


203-208: LGTM!

Correctly builds a DHCP REQUEST packet with server and requested IP parameters.


51-54: Unable to verify DHCP_MAGIC definition due to repository access issues.

The review comment's concern about DHCP_MAGIC byte order is well-founded: RFC 2132 specifies the DHCP magic cookie as 4 octets (99, 130, 83, 99 decimal / 0x63, 0x82, 0x53, 0x63 hex), which in network byte order (big-endian) is 0x63825363. Verifying that the constant in the codebase matches this standard value is appropriate.

However, the repository could not be accessed to confirm the actual definition and verify how dhcp->magic is populated from the packet data. Manual verification of the following is needed:

  • DHCP_MAGIC definition in header files
  • Whether the constant equals 0x63825363
  • How the magic field is extracted/assigned from the DHCP packet buffer (endianness conversion if any)
modules/dhcp/control/dhcp_options.c (3)

8-117: LGTM!

Option parsing logic is sound with proper bounds checking, length validation, and error handling. The ROUTER option correctly accepts multiple addresses (≥4 bytes) and extracts the first one.


119-121: LGTM!

Clean wrapper providing default parameters for the common case.


123-171: LGTM!

Buffer size check correctly validates against the 22-byte worst case. The function returns the actual bytes written, enabling callers to adjust accordingly.

Comment on lines +164 to +177
// Allocate space for options (worst case: 22 bytes, see dhcp_build_options_ex)
options = (uint8_t *)rte_pktmbuf_append(m, 22);
if (options == NULL) {
LOG(ERR, "%s: failed to append options", caller);
rte_pktmbuf_free(m);
return NULL;
}

opt_len = dhcp_build_options_ex(options, 22, msg_type, server_ip, requested_ip);
if (opt_len < 0) {
LOG(ERR, "%s: failed to build options", caller);
rte_pktmbuf_free(m);
return NULL;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Trim mbuf to match actual option length.

After allocating 22 bytes for options (line 165) but only writing opt_len bytes (line 172), the mbuf contains unused trailing bytes. The UDP/IP headers correctly use opt_len, but rte_pktmbuf_data_len(m) includes the full 22 bytes, creating a length mismatch.

Apply this diff:

 	opt_len = dhcp_build_options_ex(options, 22, msg_type, server_ip, requested_ip);
 	if (opt_len < 0) {
 		LOG(ERR, "%s: failed to build options", caller);
 		rte_pktmbuf_free(m);
 		return NULL;
 	}
+
+	// Trim unused option bytes
+	if (rte_pktmbuf_trim(m, 22 - opt_len) < 0) {
+		LOG(ERR, "%s: failed to trim mbuf", caller);
+		rte_pktmbuf_free(m);
+		return NULL;
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Allocate space for options (worst case: 22 bytes, see dhcp_build_options_ex)
options = (uint8_t *)rte_pktmbuf_append(m, 22);
if (options == NULL) {
LOG(ERR, "%s: failed to append options", caller);
rte_pktmbuf_free(m);
return NULL;
}
opt_len = dhcp_build_options_ex(options, 22, msg_type, server_ip, requested_ip);
if (opt_len < 0) {
LOG(ERR, "%s: failed to build options", caller);
rte_pktmbuf_free(m);
return NULL;
}
// Allocate space for options (worst case: 22 bytes, see dhcp_build_options_ex)
options = (uint8_t *)rte_pktmbuf_append(m, 22);
if (options == NULL) {
LOG(ERR, "%s: failed to append options", caller);
rte_pktmbuf_free(m);
return NULL;
}
opt_len = dhcp_build_options_ex(options, 22, msg_type, server_ip, requested_ip);
if (opt_len < 0) {
LOG(ERR, "%s: failed to build options", caller);
rte_pktmbuf_free(m);
return NULL;
}
// Trim unused option bytes
if (rte_pktmbuf_trim(m, 22 - opt_len) < 0) {
LOG(ERR, "%s: failed to trim mbuf", caller);
rte_pktmbuf_free(m);
return NULL;
}
🤖 Prompt for AI Agents
In modules/dhcp/control/dhcp_packet.c around lines 164-177, after appending a
22-byte options buffer and writing only opt_len bytes, the mbuf still reports
the full 22 bytes; trim the mbuf to match the actual opt_len by removing the
unused tail bytes. Specifically, after dhcp_build_options_ex returns a
non-negative opt_len, if opt_len < 22 call the appropriate rte_pktmbuf trim API
to remove (22 - opt_len) bytes from the end of the mbuf so
rte_pktmbuf_data_len(m) reflects the real options length; keep existing error
handling for negative opt_len unchanged.

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: 0

♻️ Duplicate comments (2)
modules/dhcp/control/dhcp_packet.c (1)

164-177: Mbuf data length exceeds actual packet size.

After allocating 22 bytes (line 165) and writing opt_len bytes (line 172), the mbuf still includes unused trailing bytes. While UDP/IP headers use opt_len correctly, rte_pktmbuf_data_len(m) will report 22 bytes for options regardless of actual size.

Trim the mbuf after building options:

 	opt_len = dhcp_build_options_ex(options, 22, msg_type, server_ip, requested_ip);
 	if (opt_len < 0) {
 		LOG(ERR, "%s: failed to build options", caller);
 		rte_pktmbuf_free(m);
 		return NULL;
 	}
+
+	if (rte_pktmbuf_trim(m, 22 - opt_len) < 0) {
+		LOG(ERR, "%s: failed to trim mbuf", caller);
+		rte_pktmbuf_free(m);
+		return NULL;
+	}
modules/dhcp/datapath/dhcp_input.c (1)

65-80: Potential out-of-bounds access in option parsing loop.

After reading len (line 73), the code either reads options[pos] (line 76) or advances pos += len (line 79). If pos + len > options_len, the next iteration could still read out of bounds before the loop condition catches it.

Add bounds check after reading len:

 				uint8_t len = options[pos++];
+				if (pos + len > options_len)
+					break;
 				if (opt == DHCP_OPT_MESSAGE_TYPE && len == 1
-				    && pos < options_len) {
+				    ) {
 					t->msg_type = options[pos];
 					break;
 				}
 				pos += len;
🧹 Nitpick comments (1)
modules/dhcp/api/gr_dhcp.h (1)

17-35: Confirm C23 fixed-underlying-type enum support for public API struct layout

Using enum dhcp_state : uint8_t in a public API struct is nice for size control, but it depends on C23-style fixed underlying enums being consistently supported by all target C toolchains (and C++ consumers) so that struct gr_dhcp_status has the same ABI everywhere. Please confirm this matches the minimum compiler matrix; if not, consider dropping : uint8_t and using an explicit uint8_t state (or a small wrapper enum) instead.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 39cbb06 and 979f479.

⛔ Files ignored due to path filters (1)
  • docs/graph.svg is excluded by !**/*.svg
📒 Files selected for processing (15)
  • .github/workflows/check.yml (1 hunks)
  • modules/dhcp/api/gr_dhcp.h (1 hunks)
  • modules/dhcp/api/meson.build (1 hunks)
  • modules/dhcp/cli/dhcp.c (1 hunks)
  • modules/dhcp/cli/meson.build (1 hunks)
  • modules/dhcp/control/dhcp_client.c (1 hunks)
  • modules/dhcp/control/dhcp_client.h (1 hunks)
  • modules/dhcp/control/dhcp_options.c (1 hunks)
  • modules/dhcp/control/dhcp_packet.c (1 hunks)
  • modules/dhcp/control/meson.build (1 hunks)
  • modules/dhcp/datapath/dhcp_input.c (1 hunks)
  • modules/dhcp/datapath/meson.build (1 hunks)
  • modules/dhcp/meson.build (1 hunks)
  • modules/meson.build (1 hunks)
  • smoke/dhcp_test.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
  • modules/dhcp/cli/meson.build
  • modules/dhcp/meson.build
  • .github/workflows/check.yml
  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/control/meson.build
  • modules/dhcp/control/dhcp_client.h
  • modules/dhcp/datapath/meson.build
  • modules/dhcp/control/dhcp_options.c
  • modules/meson.build
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{c,h}

⚙️ CodeRabbit configuration file

**/*.{c,h}: - gr_vec_*() functions cannot fail. No need to check their return value.

  • gr_vec_free(x) always sets x = NULL. There is no risk of double free.
  • ec_node_*() functions consume their ec_node arguments. No leaks on error.
  • rte_node->ctx is an uint8_t array of size 16, not a pointer.
  • Don't suggest to replace assert() with graceful error checking.
  • We compile with -std=gnu2x. Unnamed parameters in function definitions are valid.

Files:

  • modules/dhcp/control/dhcp_client.c
  • modules/dhcp/datapath/dhcp_input.c
  • modules/dhcp/control/dhcp_packet.c
  • modules/dhcp/api/gr_dhcp.h
**/*.sh

⚙️ CodeRabbit configuration file

**/*.sh: - Don't bother about unquoted shell variables.

Files:

  • smoke/dhcp_test.sh
🧠 Learnings (5)
📚 Learning: 2025-11-03T13:28:19.489Z
Learnt from: rjarry
Repo: DPDK/grout PR: 379
File: modules/infra/datapath/port_tx.c:36-46
Timestamp: 2025-11-03T13:28:19.489Z
Learning: In DPDK graph node process callbacks, the return value is only used for statistics and does not affect packet flow or scheduling through the graph. Nodes can return 0 when they haven't processed packets (e.g., when dropping or redirecting due to port state).

Applied to files:

  • modules/dhcp/datapath/dhcp_input.c
📚 Learning: 2025-11-05T14:28:28.817Z
Learnt from: rjarry
Repo: DPDK/grout PR: 372
File: modules/infra/datapath/loop_input.c:53-56
Timestamp: 2025-11-05T14:28:28.817Z
Learning: In modules/infra/datapath/loop_input.c, the l3_edges array is intentionally indexed using network byte order (rte_be16_t) for EtherType values. Both loopback_input_add_type() registration and loopback_input_process() lookup use network byte order consistently, so no byte order conversion is needed when accessing l3_edges.

Applied to files:

  • modules/dhcp/datapath/dhcp_input.c
📚 Learning: 2025-09-09T06:02:47.703Z
Learnt from: maxime-leroy
Repo: DPDK/grout PR: 309
File: modules/srv6/datapath/srv6_local.c:88-122
Timestamp: 2025-09-09T06:02:47.703Z
Learning: The DPDK function rte_ipv6_get_next_ext() only reads 2 bytes from the extension header, so a 2-byte precheck is sufficient before calling it.

Applied to files:

  • modules/dhcp/control/dhcp_packet.c
📚 Learning: 2025-09-05T22:34:10.597Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/ip/datapath/ip_output.c:100-107
Timestamp: 2025-09-05T22:34:10.597Z
Learning: This codebase compiles with -Wswitch-enum and -Werror flags, which means all enum values must be explicitly handled in switch statements or compilation will fail. This provides compile-time safety against missing enum cases, making default cases in enum switches unnecessary and potentially harmful as they could mask new enum additions.

Applied to files:

  • modules/dhcp/api/gr_dhcp.h
📚 Learning: 2025-11-24T12:50:09.310Z
Learnt from: rjarry
Repo: DPDK/grout PR: 408
File: api/gr_net_types.h:78-84
Timestamp: 2025-11-24T12:50:09.310Z
Learning: In api/gr_net_types.h, ip4_addr_t values are stored in network byte order (big-endian). When using a union to cast to a byte array (uint8_t u8[4]), accessing u8[0] correctly reads the first octet (most significant byte) of the IP address on all architectures, because it directly accesses the memory layout rather than performing integer operations. This pattern is valid for checking properties like multicast (first octet in range 224-239).

Applied to files:

  • modules/dhcp/api/gr_dhcp.h
🧬 Code graph analysis (1)
smoke/dhcp_test.sh (1)
smoke/_init.sh (2)
  • fail (104-107)
  • netns_add (113-121)
🪛 Shellcheck (0.11.0)
smoke/dhcp_test.sh

[warning] 5-5: Quote this to prevent word splitting.

(SC2046)


[warning] 18-18: tmp is referenced but not assigned.

(SC2154)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build-cross-aarch64
  • GitHub Check: build-and-tests (clang-16, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (gcc-14, address, debug, ubuntu-24.04, -Dfrr=enabled, true)
  • GitHub Check: build-and-tests (clang-18, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (gcc-13, none, debugoptimized, ubuntu-24.04, -Dfrr=enabled, false)
  • GitHub Check: build-and-tests (clang-15, none, debugoptimized, ubuntu-22.04, false)
  • GitHub Check: rpm
  • GitHub Check: deb
🔇 Additional comments (12)
smoke/dhcp_test.sh (1)

1-70: LGTM! Well-structured DHCP smoke test.

The test exercises the full DHCP lifecycle: enable, verify lease acquisition and routes, disable, verify cleanup. The use of dnsmasq in a network namespace is a good approach.

Minor note: The fixed sleep 5 on line 47 for lease acquisition may be fragile in slower CI environments. Consider a polling loop with timeout if flakiness occurs.

modules/dhcp/control/dhcp_packet.c (2)

23-91: Solid packet parsing with proper validation.

Good structure: validates lengths, magic cookie, transaction ID, and operation type before parsing options. Error logging is appropriate.


199-208: Clean public API wrappers.

Good separation delegating to the common helper with appropriate message type and parameters.

modules/dhcp/api/meson.build (1)

1-5: LGTM!

Standard module API build configuration.

modules/dhcp/datapath/dhcp_input.c (1)

127-146: Well-structured node registration and port setup.

Clean graph node registration with trace formatting support. Port registration on UDP 68 is correct for DHCP client.

modules/dhcp/control/dhcp_client.c (6)

32-115: Interface configuration logic is sound.

Proper creation of local address nexthop and optional gateway route. Good defensive handling when gateway creation fails (line 103 returns 0 to continue).


225-276: Timer scheduling follows RFC 2131 correctly.

Default T1 at 50% and T2 at 87.5% of lease time per RFC. Proper timer cleanup before rescheduling.


278-438: Well-implemented DHCP state machine.

Proper bounds checking (line 294), validation of OFFER/ACK contents, and clean state transitions. NAK handling correctly cleans up routes and restarts discovery.


454-512: Clean startup with proper error handling.

Bounds checking, resource allocation, and failure cleanup paths are all correct. All error paths properly free resources.


514-577: Proper shutdown sequence.

Routes removed before client freed. Timer cancellation prevents callbacks on freed memory.


587-666: Module and API registration is complete.

List handler properly iterates all active clients. API handlers correctly propagate errno. Module lifecycle hooks are appropriate.

modules/dhcp/api/gr_dhcp.h (1)

26-40: Status/list structs look well-shaped and types are appropriate

ip4_addr_t for IPs, uint16_t iface_id, and uint32_t lease/renewal/rebind durations, plus a flexible array in gr_dhcp_list_resp, all look consistent with the rest of the API and fit the DHCP domain. No issues from an API/ABI perspective.

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: 0

♻️ Duplicate comments (2)
modules/meson.build (1)

11-11: LGTM!

Module integration looks correct. The placement discussion (L4 vs dedicated folder) is already ongoing in past comments.

modules/dhcp/control/dhcp_packet.c (1)

164-177: Trim mbuf to match actual option length.

After allocating 22 bytes for options but writing only opt_len bytes, the mbuf contains unused trailing bytes. The UDP/IP headers correctly use opt_len, but rte_pktmbuf_data_len(m) includes the full 22 bytes.

Apply this diff to trim the mbuf:

 	opt_len = dhcp_build_options_ex(options, 22, msg_type, server_ip, requested_ip);
 	if (opt_len < 0) {
 		LOG(ERR, "%s: failed to build options", caller);
 		rte_pktmbuf_free(m);
 		return NULL;
 	}
+
+	if (rte_pktmbuf_trim(m, 22 - opt_len) < 0) {
+		LOG(ERR, "%s: failed to trim mbuf", caller);
+		rte_pktmbuf_free(m);
+		return NULL;
+	}
🧹 Nitpick comments (1)
smoke/dhcp_test.sh (1)

40-47: Fixed sleep durations may cause flaky tests.

sleep 1 and sleep 5 are hardcoded. In CI environments under load, dnsmasq startup or DHCP handshake might take longer, causing intermittent failures. Consider a polling loop with timeout instead.

Example pattern:

-# Wait for dnsmasq to start
-sleep 1
+# Wait for dnsmasq to start
+for i in $(seq 1 10); do
+  pgrep -F $tmp/dnsmasq.pid && break
+  sleep 0.5
+done
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 979f479 and 2357379.

⛔ Files ignored due to path filters (1)
  • docs/graph.svg is excluded by !**/*.svg
📒 Files selected for processing (15)
  • .github/workflows/check.yml (1 hunks)
  • modules/dhcp/api/gr_dhcp.h (1 hunks)
  • modules/dhcp/api/meson.build (1 hunks)
  • modules/dhcp/cli/dhcp.c (1 hunks)
  • modules/dhcp/cli/meson.build (1 hunks)
  • modules/dhcp/control/dhcp_client.c (1 hunks)
  • modules/dhcp/control/dhcp_client.h (1 hunks)
  • modules/dhcp/control/dhcp_options.c (1 hunks)
  • modules/dhcp/control/dhcp_packet.c (1 hunks)
  • modules/dhcp/control/meson.build (1 hunks)
  • modules/dhcp/datapath/dhcp_input.c (1 hunks)
  • modules/dhcp/datapath/meson.build (1 hunks)
  • modules/dhcp/meson.build (1 hunks)
  • modules/meson.build (1 hunks)
  • smoke/dhcp_test.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/check.yml
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{c,h}

⚙️ CodeRabbit configuration file

**/*.{c,h}: - gr_vec_*() functions cannot fail. No need to check their return value.

  • gr_vec_free(x) always sets x = NULL. There is no risk of double free.
  • ec_node_*() functions consume their ec_node arguments. No leaks on error.
  • rte_node->ctx is an uint8_t array of size 16, not a pointer.
  • Don't suggest to replace assert() with graceful error checking.
  • We compile with -std=gnu2x. Unnamed parameters in function definitions are valid.

Files:

  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/datapath/dhcp_input.c
  • modules/dhcp/control/dhcp_packet.c
  • modules/dhcp/control/dhcp_options.c
  • modules/dhcp/control/dhcp_client.c
  • modules/dhcp/api/gr_dhcp.h
  • modules/dhcp/control/dhcp_client.h
**/*.sh

⚙️ CodeRabbit configuration file

**/*.sh: - Don't bother about unquoted shell variables.

Files:

  • smoke/dhcp_test.sh
🧠 Learnings (8)
📚 Learning: 2025-11-24T12:50:09.310Z
Learnt from: rjarry
Repo: DPDK/grout PR: 408
File: api/gr_net_types.h:78-84
Timestamp: 2025-11-24T12:50:09.310Z
Learning: In api/gr_net_types.h, ip4_addr_t values are stored in network byte order (big-endian). When using a union to cast to a byte array (uint8_t u8[4]), accessing u8[0] correctly reads the first octet (most significant byte) of the IP address on all architectures, because it directly accesses the memory layout rather than performing integer operations. This pattern is valid for checking properties like multicast (first octet in range 224-239).

Applied to files:

  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/control/dhcp_options.c
  • modules/dhcp/api/gr_dhcp.h
  • modules/dhcp/control/dhcp_client.h
📚 Learning: 2025-09-05T22:34:10.597Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/ip/datapath/ip_output.c:100-107
Timestamp: 2025-09-05T22:34:10.597Z
Learning: This codebase compiles with -Wswitch-enum and -Werror flags, which means all enum values must be explicitly handled in switch statements or compilation will fail. This provides compile-time safety against missing enum cases, making default cases in enum switches unnecessary and potentially harmful as they could mask new enum additions.

Applied to files:

  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/api/gr_dhcp.h
📚 Learning: 2025-09-21T10:48:42.539Z
Learnt from: rjarry
Repo: DPDK/grout PR: 312
File: modules/srv6/api/gr_srv6.h:18-21
Timestamp: 2025-09-21T10:48:42.539Z
Learning: In the Grout codebase, fixed-width enum syntax like `typedef enum : uint8_t { ... } type_t;` is valid and correct since the project uses `-std=gnu2x` which supports C23 features including fixed-width enums.

Applied to files:

  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/control/dhcp_client.h
📚 Learning: 2025-11-03T13:28:19.489Z
Learnt from: rjarry
Repo: DPDK/grout PR: 379
File: modules/infra/datapath/port_tx.c:36-46
Timestamp: 2025-11-03T13:28:19.489Z
Learning: In DPDK graph node process callbacks, the return value is only used for statistics and does not affect packet flow or scheduling through the graph. Nodes can return 0 when they haven't processed packets (e.g., when dropping or redirecting due to port state).

Applied to files:

  • modules/dhcp/datapath/dhcp_input.c
📚 Learning: 2025-11-05T14:28:28.817Z
Learnt from: rjarry
Repo: DPDK/grout PR: 372
File: modules/infra/datapath/loop_input.c:53-56
Timestamp: 2025-11-05T14:28:28.817Z
Learning: In modules/infra/datapath/loop_input.c, the l3_edges array is intentionally indexed using network byte order (rte_be16_t) for EtherType values. Both loopback_input_add_type() registration and loopback_input_process() lookup use network byte order consistently, so no byte order conversion is needed when accessing l3_edges.

Applied to files:

  • modules/dhcp/datapath/dhcp_input.c
  • modules/dhcp/control/dhcp_options.c
📚 Learning: 2025-09-09T06:02:47.703Z
Learnt from: maxime-leroy
Repo: DPDK/grout PR: 309
File: modules/srv6/datapath/srv6_local.c:88-122
Timestamp: 2025-09-09T06:02:47.703Z
Learning: The DPDK function rte_ipv6_get_next_ext() only reads 2 bytes from the extension header, so a 2-byte precheck is sufficient before calling it.

Applied to files:

  • modules/dhcp/control/dhcp_packet.c
  • modules/dhcp/control/dhcp_options.c
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: In this project, cli_src is a global variable that gets populated by individual modules (like modules/policy/cli/meson.build) and is ultimately consumed by an executable() call in the top-level meson.build file. This creates a modular CLI build where each module contributes its CLI sources to the main CLI binary.

Applied to files:

  • modules/dhcp/cli/meson.build
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: The grout project uses a modular CLI build system where individual modules contribute their CLI sources to a global cli_src variable via `cli_src += files(...)`, and the top-level meson.build file builds the final grcli executable using all collected CLI sources. The grcli executable is defined in the top-level meson.build at lines 122-127.

Applied to files:

  • modules/dhcp/cli/meson.build
🧬 Code graph analysis (4)
modules/dhcp/cli/dhcp.c (4)
cli/ecoli.c (1)
  • arg_str (99-109)
modules/infra/cli/iface.c (2)
  • iface_from_name (93-106)
  • complete_iface_names (71-91)
api/gr_api.h (1)
  • gr_api_client_send_recv (41-52)
cli/table.c (1)
  • scols_line_sprintf (10-23)
modules/dhcp/control/dhcp_packet.c (4)
modules/dhcp/control/dhcp_options.c (2)
  • dhcp_parse_options (8-117)
  • dhcp_build_options_ex (123-171)
modules/infra/control/gr_iface.h (1)
  • iface (16-25)
modules/infra/control/iface.c (1)
  • iface_get_eth_addr (261-274)
modules/dhcp/control/dhcp_client.c (1)
  • dhcp_get_mempool (581-583)
modules/dhcp/control/dhcp_client.h (4)
modules/dhcp/control/dhcp_client.c (5)
  • dhcp_input_cb (278-440)
  • dhcp_start (456-514)
  • dhcp_stop (516-579)
  • dhcp_get_mempool (581-583)
  • dhcp_get_output (585-587)
modules/dhcp/datapath/dhcp_input.c (1)
  • dhcp_input_register_port (143-146)
modules/dhcp/control/dhcp_packet.c (3)
  • dhcp_parse_packet (23-91)
  • dhcp_build_discover (199-201)
  • dhcp_build_request (203-208)
modules/dhcp/control/dhcp_options.c (1)
  • dhcp_parse_options (8-117)
smoke/dhcp_test.sh (1)
smoke/_init.sh (2)
  • fail (104-107)
  • netns_add (113-121)
🪛 Shellcheck (0.11.0)
smoke/dhcp_test.sh

[warning] 5-5: Quote this to prevent word splitting.

(SC2046)


[warning] 18-18: tmp is referenced but not assigned.

(SC2154)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build-and-tests (clang-16, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (gcc-13, none, debugoptimized, ubuntu-24.04, -Dfrr=enabled, false)
  • GitHub Check: build-and-tests (gcc-14, address, debug, ubuntu-24.04, -Dfrr=enabled, true)
  • GitHub Check: build-and-tests (clang-15, none, debugoptimized, ubuntu-22.04, false)
  • GitHub Check: build-and-tests (clang-18, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: rpm
  • GitHub Check: build-cross-aarch64
  • GitHub Check: deb
🔇 Additional comments (38)
smoke/dhcp_test.sh (2)

1-9: LGTM on test setup.

Script structure follows the existing smoke test conventions. The $tmp variable is provided by the sourced _init.sh, and the coding guidelines indicate shell variable quoting concerns should be ignored.


65-68: Negation pattern works correctly here.

The ! cmd | grep -q pattern negates the entire pipeline's exit status (which is grep's exit code). This correctly fails the test if the pattern is found. Looks good.

modules/dhcp/cli/meson.build (1)

1-6: LGTM!

Follows the established pattern for CLI module contributions. Based on learnings, cli_src is collected by the top-level meson.build for the grcli executable.

modules/dhcp/datapath/meson.build (1)

1-7: LGTM!

Standard datapath module build configuration. Source file and include path properly registered.

modules/dhcp/control/meson.build (1)

1-9: LGTM!

Control module build configuration follows project conventions. Source files are logically separated by responsibility.

modules/dhcp/meson.build (1)

1-7: LGTM!

Standard Meson module structure with proper license header and all required subdirectories included.

modules/dhcp/api/meson.build (1)

1-5: LGTM!

Proper API header registration for the DHCP module.

modules/dhcp/datapath/dhcp_input.c (4)

20-29: LGTM!

Trace data structure and edge enum are well-defined.


31-88: LGTM!

Datapath processing correctly forwards to control output with appropriate trace data extraction. The options parsing is bounded by both options_len and a 64-byte limit.


90-125: LGTM!

Trace formatting handles all DHCP message types with clear output.


127-146: LGTM!

Node registration and UDP port 68 binding are correctly implemented.

modules/dhcp/cli/dhcp.c (4)

17-51: LGTM!

Enable/disable commands correctly resolve interfaces by name, free allocated memory, and handle errors appropriately.


53-70: LGTM!

State string conversion covers all defined states.


72-127: LGTM!

Show command correctly streams DHCP status, uses IP4_F for proper IP formatting, and handles table lifecycle properly.


129-177: LGTM!

CLI context and command registration follow established patterns. Using GR_IFACE_TYPE_UNDEF allows DHCP on any interface type.

modules/dhcp/control/dhcp_client.c (6)

32-115: LGTM!

Interface configuration correctly sets up the assigned IP and optional default gateway, with appropriate error handling that allows the lease to proceed even if gateway creation fails.


119-276: LGTM!

Timer management correctly implements RFC 2131 defaults (T1=50%, T2=87.5%) with proper cleanup and state validation in callbacks.


278-440: LGTM!

DHCP state machine correctly handles the full workflow per RFC 2131, including proper bounds validation and cleanup on NAK.


442-454: LGTM!

Module initialization properly registers handlers and acquires required resources.


456-579: LGTM!

Start/stop functions include proper bounds checking, resource cleanup, and error handling with appropriate errno values.


581-668: LGTM!

API handlers and module lifecycle are correctly implemented with proper resource management.

modules/dhcp/control/dhcp_packet.c (3)

23-91: LGTM!

The parsing logic correctly validates all header lengths before dereferencing, checks the DHCP magic cookie, verifies transaction ID, and extracts the offered IP and message type.


199-201: LGTM!

Clean delegation to the common builder.


203-208: LGTM!

Correct delegation with REQUEST parameters.

modules/dhcp/api/gr_dhcp.h (4)

17-24: LGTM!

Fixed-width enum syntax is valid with -std=gnu2x, and the states correctly model the RFC 2131 client state machine.


26-34: LGTM!

Field types are correct: ip4_addr_t for IPs and uint32_t for lease durations.


36-47: LGTM!

Structs correctly use flexible array member for list response and minimal request structures.


49-52: LGTM!

Module ID and request type macros follow project conventions.

modules/dhcp/control/dhcp_options.c (3)

8-117: LGTM!

Option parsing correctly validates bounds, uses direct memcpy for IP addresses (both options and ip4_addr_t are in network byte order), and properly converts time values from big-endian to host order.


119-121: LGTM!

Simple wrapper for the common case.


123-171: LGTM!

Buffer size validation matches worst-case option length (22 bytes), and direct memcpy for IP addresses is correct since ip4_addr_t is already in network byte order.

modules/dhcp/control/dhcp_client.h (7)

1-17: LGTM!

All necessary includes and forward declarations are present.


19-29: LGTM!

Message type values correctly match RFC 2132 option 53.


31-47: LGTM!

Option codes correctly match RFC 2132.


49-64: LGTM!

Field types are correct: ip4_addr_t for IPs, uint32_t for lease durations, time_t for absolute timestamps, and proper timer pointers.


66-83: LGTM!

Packet structure correctly matches RFC 2131 format with proper network byte order types and packed attribute.


85-87: LGTM!

DHCP magic cookie and BOOT opcodes match RFC 2131.


89-122: LGTM!

Function declarations use correct types and signatures.

Copy link
Collaborator

@christophefontaine christophefontaine left a comment

Choose a reason for hiding this comment

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

lgtm !

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: 1

♻️ Duplicate comments (1)
modules/dhcp/control/dhcp_packet.c (1)

164-187: Align mbuf length with IP/UDP lengths after building options.

You append a fixed 22‑byte options area but only account for opt_len in udp->dgram_len and ip->total_length. This leaves rte_pktmbuf_data_len(m) larger than the L3/L4 lengths when opt_len < 22, so the tail contains unused bytes.

Consider trimming the unused tail bytes after dhcp_build_options_ex succeeds, e.g.:

 opt_len = dhcp_build_options_ex(options, 22, msg_type, server_ip, requested_ip);
 if (opt_len < 0) {
   LOG(ERR, "%s: failed to build options", caller);
   rte_pktmbuf_free(m);
   return NULL;
 }
+
+ if (opt_len < 22 && rte_pktmbuf_trim(m, 22 - opt_len) < 0) {
+   LOG(ERR, "%s: failed to trim options tail", caller);
+   rte_pktmbuf_free(m);
+   return NULL;
+ }

This keeps the mbuf length consistent with the IP/UDP headers and avoids sending trailing garbage.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2357379 and 2a67af3.

⛔ Files ignored due to path filters (1)
  • docs/graph.svg is excluded by !**/*.svg
📒 Files selected for processing (15)
  • .github/workflows/check.yml (1 hunks)
  • modules/dhcp/api/gr_dhcp.h (1 hunks)
  • modules/dhcp/api/meson.build (1 hunks)
  • modules/dhcp/cli/dhcp.c (1 hunks)
  • modules/dhcp/cli/meson.build (1 hunks)
  • modules/dhcp/control/dhcp_client.c (1 hunks)
  • modules/dhcp/control/dhcp_client.h (1 hunks)
  • modules/dhcp/control/dhcp_options.c (1 hunks)
  • modules/dhcp/control/dhcp_packet.c (1 hunks)
  • modules/dhcp/control/meson.build (1 hunks)
  • modules/dhcp/datapath/dhcp_input.c (1 hunks)
  • modules/dhcp/datapath/meson.build (1 hunks)
  • modules/dhcp/meson.build (1 hunks)
  • modules/meson.build (1 hunks)
  • smoke/dhcp_test.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • modules/dhcp/meson.build
  • modules/dhcp/datapath/meson.build
  • .github/workflows/check.yml
  • modules/meson.build
  • modules/dhcp/control/meson.build
  • modules/dhcp/control/dhcp_options.c
  • modules/dhcp/api/meson.build
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{c,h}

⚙️ CodeRabbit configuration file

**/*.{c,h}: - gr_vec_*() functions cannot fail. No need to check their return value.

  • gr_vec_free(x) always sets x = NULL. There is no risk of double free.
  • ec_node_*() functions consume their ec_node arguments. No leaks on error.
  • rte_node->ctx is an uint8_t array of size 16, not a pointer.
  • Don't suggest to replace assert() with graceful error checking.
  • We compile with -std=gnu2x. Unnamed parameters in function definitions are valid.

Files:

  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/datapath/dhcp_input.c
  • modules/dhcp/control/dhcp_client.c
  • modules/dhcp/control/dhcp_client.h
  • modules/dhcp/api/gr_dhcp.h
  • modules/dhcp/control/dhcp_packet.c
**/*.sh

⚙️ CodeRabbit configuration file

**/*.sh: - Don't bother about unquoted shell variables.

Files:

  • smoke/dhcp_test.sh
🧠 Learnings (8)
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: In this project, cli_src is a global variable that gets populated by individual modules (like modules/policy/cli/meson.build) and is ultimately consumed by an executable() call in the top-level meson.build file. This creates a modular CLI build where each module contributes its CLI sources to the main CLI binary.

Applied to files:

  • modules/dhcp/cli/meson.build
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: The grout project uses a modular CLI build system where individual modules contribute their CLI sources to a global cli_src variable via `cli_src += files(...)`, and the top-level meson.build file builds the final grcli executable using all collected CLI sources. The grcli executable is defined in the top-level meson.build at lines 122-127.

Applied to files:

  • modules/dhcp/cli/meson.build
📚 Learning: 2025-11-24T12:50:09.310Z
Learnt from: rjarry
Repo: DPDK/grout PR: 408
File: api/gr_net_types.h:78-84
Timestamp: 2025-11-24T12:50:09.310Z
Learning: In api/gr_net_types.h, ip4_addr_t values are stored in network byte order (big-endian). When using a union to cast to a byte array (uint8_t u8[4]), accessing u8[0] correctly reads the first octet (most significant byte) of the IP address on all architectures, because it directly accesses the memory layout rather than performing integer operations. This pattern is valid for checking properties like multicast (first octet in range 224-239).

Applied to files:

  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/control/dhcp_client.h
  • modules/dhcp/api/gr_dhcp.h
📚 Learning: 2025-09-05T22:34:10.597Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/ip/datapath/ip_output.c:100-107
Timestamp: 2025-09-05T22:34:10.597Z
Learning: This codebase compiles with -Wswitch-enum and -Werror flags, which means all enum values must be explicitly handled in switch statements or compilation will fail. This provides compile-time safety against missing enum cases, making default cases in enum switches unnecessary and potentially harmful as they could mask new enum additions.

Applied to files:

  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/api/gr_dhcp.h
📚 Learning: 2025-09-21T10:48:42.539Z
Learnt from: rjarry
Repo: DPDK/grout PR: 312
File: modules/srv6/api/gr_srv6.h:18-21
Timestamp: 2025-09-21T10:48:42.539Z
Learning: In the Grout codebase, fixed-width enum syntax like `typedef enum : uint8_t { ... } type_t;` is valid and correct since the project uses `-std=gnu2x` which supports C23 features including fixed-width enums.

Applied to files:

  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/control/dhcp_client.h
📚 Learning: 2025-11-03T13:28:19.489Z
Learnt from: rjarry
Repo: DPDK/grout PR: 379
File: modules/infra/datapath/port_tx.c:36-46
Timestamp: 2025-11-03T13:28:19.489Z
Learning: In DPDK graph node process callbacks, the return value is only used for statistics and does not affect packet flow or scheduling through the graph. Nodes can return 0 when they haven't processed packets (e.g., when dropping or redirecting due to port state).

Applied to files:

  • modules/dhcp/datapath/dhcp_input.c
📚 Learning: 2025-11-05T14:28:28.817Z
Learnt from: rjarry
Repo: DPDK/grout PR: 372
File: modules/infra/datapath/loop_input.c:53-56
Timestamp: 2025-11-05T14:28:28.817Z
Learning: In modules/infra/datapath/loop_input.c, the l3_edges array is intentionally indexed using network byte order (rte_be16_t) for EtherType values. Both loopback_input_add_type() registration and loopback_input_process() lookup use network byte order consistently, so no byte order conversion is needed when accessing l3_edges.

Applied to files:

  • modules/dhcp/datapath/dhcp_input.c
📚 Learning: 2025-09-09T06:02:47.703Z
Learnt from: maxime-leroy
Repo: DPDK/grout PR: 309
File: modules/srv6/datapath/srv6_local.c:88-122
Timestamp: 2025-09-09T06:02:47.703Z
Learning: The DPDK function rte_ipv6_get_next_ext() only reads 2 bytes from the extension header, so a 2-byte precheck is sufficient before calling it.

Applied to files:

  • modules/dhcp/control/dhcp_packet.c
🧬 Code graph analysis (3)
modules/dhcp/cli/dhcp.c (5)
cli/ecoli.c (2)
  • arg_str (99-109)
  • with_help (15-24)
modules/infra/cli/iface.c (2)
  • iface_from_name (93-106)
  • complete_iface_names (71-91)
api/gr_api.h (1)
  • gr_api_client_send_recv (41-52)
cli/table.c (1)
  • scols_line_sprintf (10-23)
cli/exec.c (1)
  • cli_context_register (16-18)
modules/dhcp/control/dhcp_packet.c (4)
modules/dhcp/control/dhcp_options.c (2)
  • dhcp_parse_options (8-117)
  • dhcp_build_options_ex (123-171)
modules/infra/control/gr_iface.h (1)
  • iface (16-25)
modules/infra/control/iface.c (1)
  • iface_get_eth_addr (261-274)
modules/dhcp/control/dhcp_client.c (1)
  • dhcp_get_mempool (581-583)
smoke/dhcp_test.sh (1)
smoke/_init.sh (2)
  • fail (104-107)
  • netns_add (113-121)
🪛 Shellcheck (0.11.0)
smoke/dhcp_test.sh

[warning] 5-5: Quote this to prevent word splitting.

(SC2046)


[warning] 18-18: tmp is referenced but not assigned.

(SC2154)

🔇 Additional comments (10)
modules/dhcp/cli/meson.build (1)

1-6: LGTM!

Standard module pattern for contributing CLI sources. Follows existing conventions.

smoke/dhcp_test.sh (1)

1-70: LGTM!

Comprehensive end-to-end test covering DHCP enable, lease acquisition, route verification, and cleanup on disable. The cleanup mechanism for dnsmasq is properly handled.

modules/dhcp/control/dhcp_client.h (1)

1-124: LGTM!

Well-structured header. Fixed-width enums, forward declarations, and type usage all follow project conventions. Past review feedback has been addressed.

modules/dhcp/datapath/dhcp_input.c (1)

1-146: LGTM!

Clean datapath node implementation with proper trace support. Options parsing has adequate bounds checking, and the PAYLOAD macro is used as suggested.

modules/dhcp/cli/dhcp.c (1)

1-177: LGTM!

Clean CLI implementation. IP display uses IP4_F macro, API calls omit unused response parameter, and interface type is properly unrestricted to allow DHCP on various interface types including bonds.

modules/dhcp/control/dhcp_client.c (3)

442-668: LGTM!

Module initialization, API handlers, and registration are well-structured. Error handling throughout is comprehensive.


32-115: LGTM!

Interface configuration correctly creates nexthops with DHCP origin and handles optional router gateway. The separation of concerns between address assignment and default route is clean.


278-440: LGTM!

State machine handles all DHCP message types appropriately. Proper bounds checking on iface_id, packet parsing error handling, and state transitions. NAK handling during renewal correctly cleans up routes before restarting.

modules/dhcp/control/dhcp_packet.c (1)

23-91: Solid DHCP reply parsing and validation.

The parsing path does the right sanity checks (UDP/DHCP header sizes, magic cookie, xid match, BOOTREPLY op) before handing options to dhcp_parse_options, and correctly propagates msg_type and yiaddr into the client state. Looks good and matches how dhcp_build_options_ex is described.

modules/dhcp/api/gr_dhcp.h (1)

17-52: DHCP public API types look well-chosen and consistent.

Using enum dhcp_state : uint8_t plus ip4_addr_t for server_ip/assigned_ip and uint32_t for lease/renew/rebind durations gives a compact, clear wire/API surface. The flexible gr_dhcp_list_resp neatly matches list semantics, and GR_DHCP_* request IDs are straightforward.

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: 0

♻️ Duplicate comments (2)
modules/dhcp/control/client.c (1)

536-540: Memory leak when interface lookup fails.

If iface_from_id() returns NULL, the function returns without freeing client or clearing dhcp_clients[iface_id]. This orphans the allocated memory.

 	iface = iface_from_id(iface_id);
 	if (iface == NULL) {
+		dhcp_cancel_timers(client);
+		free(client);
+		dhcp_clients[iface_id] = NULL;
 		errno = ENODEV;
 		return;
 	}
modules/dhcp/control/dhcp_packet.c (1)

164-177: Trim mbuf to match actual options length.

The mbuf has 22 bytes appended but only opt_len bytes are used. While UDP/IP headers correctly reflect opt_len, the mbuf tail contains unused bytes. Consider trimming with rte_pktmbuf_trim(m, 22 - opt_len) after building options.

🧹 Nitpick comments (3)
smoke/dhcp_test.sh (1)

40-47: Consider polling instead of fixed sleeps for CI reliability.

Fixed sleep durations (lines 41, 47) could cause flakiness in slower CI environments. Consider polling with a timeout for more robust test behavior.

Example for the DHCP lease wait:

-# Wait for DHCP to acquire lease (adjust timeout as needed)
-sleep 5
+# Wait for DHCP to acquire lease
+for i in $(seq 1 30); do
+	grcli dhcp show | grep -q "BOUND" && break
+	sleep 1
+done
modules/dhcp/control/client.c (2)

225-276: Consider validating lease_time before scheduling timers.

If the server provides lease_time = 0 (or a very small value), timers will fire immediately or expire prematurely. Consider adding a sanity check or minimum lease duration.

 static void dhcp_schedule_timers(struct dhcp_client *client) {
 	struct timeval t1_tv, t2_tv, expire_tv;
 	uint32_t t1_secs, t2_secs;

+	if (client->lease_time == 0) {
+		LOG(WARNING, "dhcp: server provided zero lease time, using default");
+		client->lease_time = 3600; // 1 hour fallback
+	}
+
 	dhcp_cancel_timers(client);

633-636: Consider stopping all active clients in dhcp_fini.

The finalization function releases the mempool but doesn't stop active DHCP clients. This could leave dangling timer callbacks if the module is unloaded while clients are running.

 static void dhcp_fini(struct event_base *) {
+	for (uint16_t i = 0; i < MAX_IFACES; i++) {
+		if (dhcp_clients[i] != NULL)
+			dhcp_stop(i);
+	}
 	gr_pktmbuf_pool_release(dhcp_mp, 512);
 	LOG(INFO, "dhcp: module finalized");
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a67af3 and 010001f.

⛔ Files ignored due to path filters (1)
  • docs/graph.svg is excluded by !**/*.svg
📒 Files selected for processing (15)
  • .github/workflows/check.yml (1 hunks)
  • modules/dhcp/api/gr_dhcp.h (1 hunks)
  • modules/dhcp/api/meson.build (1 hunks)
  • modules/dhcp/cli/dhcp.c (1 hunks)
  • modules/dhcp/cli/meson.build (1 hunks)
  • modules/dhcp/control/client.c (1 hunks)
  • modules/dhcp/control/client.h (1 hunks)
  • modules/dhcp/control/dhcp_options.c (1 hunks)
  • modules/dhcp/control/dhcp_packet.c (1 hunks)
  • modules/dhcp/control/meson.build (1 hunks)
  • modules/dhcp/datapath/dhcp_input.c (1 hunks)
  • modules/dhcp/datapath/meson.build (1 hunks)
  • modules/dhcp/meson.build (1 hunks)
  • modules/meson.build (1 hunks)
  • smoke/dhcp_test.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • modules/dhcp/meson.build
  • modules/meson.build
  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/api/meson.build
  • modules/dhcp/datapath/dhcp_input.c
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{c,h}

⚙️ CodeRabbit configuration file

**/*.{c,h}: - gr_vec_*() functions cannot fail. No need to check their return value.

  • gr_vec_free(x) always sets x = NULL. There is no risk of double free.
  • ec_node_*() functions consume their ec_node arguments. No leaks on error.
  • rte_node->ctx is an uint8_t array of size 16, not a pointer.
  • Don't suggest to replace assert() with graceful error checking.
  • We compile with -std=gnu2x. Unnamed parameters in function definitions are valid.

Files:

  • modules/dhcp/api/gr_dhcp.h
  • modules/dhcp/control/client.c
  • modules/dhcp/control/dhcp_options.c
  • modules/dhcp/control/client.h
  • modules/dhcp/control/dhcp_packet.c
**/*.sh

⚙️ CodeRabbit configuration file

**/*.sh: - Don't bother about unquoted shell variables.

Files:

  • smoke/dhcp_test.sh
🧠 Learnings (7)
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: In this project, cli_src is a global variable that gets populated by individual modules (like modules/policy/cli/meson.build) and is ultimately consumed by an executable() call in the top-level meson.build file. This creates a modular CLI build where each module contributes its CLI sources to the main CLI binary.

Applied to files:

  • modules/dhcp/cli/meson.build
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: The grout project uses a modular CLI build system where individual modules contribute their CLI sources to a global cli_src variable via `cli_src += files(...)`, and the top-level meson.build file builds the final grcli executable using all collected CLI sources. The grcli executable is defined in the top-level meson.build at lines 122-127.

Applied to files:

  • modules/dhcp/cli/meson.build
📚 Learning: 2025-09-05T22:34:10.597Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/ip/datapath/ip_output.c:100-107
Timestamp: 2025-09-05T22:34:10.597Z
Learning: This codebase compiles with -Wswitch-enum and -Werror flags, which means all enum values must be explicitly handled in switch statements or compilation will fail. This provides compile-time safety against missing enum cases, making default cases in enum switches unnecessary and potentially harmful as they could mask new enum additions.

Applied to files:

  • modules/dhcp/api/gr_dhcp.h
📚 Learning: 2025-11-24T12:50:09.310Z
Learnt from: rjarry
Repo: DPDK/grout PR: 408
File: api/gr_net_types.h:78-84
Timestamp: 2025-11-24T12:50:09.310Z
Learning: In api/gr_net_types.h, ip4_addr_t values are stored in network byte order (big-endian). When using a union to cast to a byte array (uint8_t u8[4]), accessing u8[0] correctly reads the first octet (most significant byte) of the IP address on all architectures, because it directly accesses the memory layout rather than performing integer operations. This pattern is valid for checking properties like multicast (first octet in range 224-239).

Applied to files:

  • modules/dhcp/api/gr_dhcp.h
  • modules/dhcp/control/dhcp_options.c
📚 Learning: 2025-09-21T10:48:42.539Z
Learnt from: rjarry
Repo: DPDK/grout PR: 312
File: modules/srv6/api/gr_srv6.h:18-21
Timestamp: 2025-09-21T10:48:42.539Z
Learning: In the Grout codebase, fixed-width enum syntax like `typedef enum : uint8_t { ... } type_t;` is valid and correct since the project uses `-std=gnu2x` which supports C23 features including fixed-width enums.

Applied to files:

  • modules/dhcp/api/gr_dhcp.h
  • modules/dhcp/control/client.h
📚 Learning: 2025-09-09T06:02:47.703Z
Learnt from: maxime-leroy
Repo: DPDK/grout PR: 309
File: modules/srv6/datapath/srv6_local.c:88-122
Timestamp: 2025-09-09T06:02:47.703Z
Learning: The DPDK function rte_ipv6_get_next_ext() only reads 2 bytes from the extension header, so a 2-byte precheck is sufficient before calling it.

Applied to files:

  • modules/dhcp/control/dhcp_options.c
  • modules/dhcp/control/dhcp_packet.c
📚 Learning: 2025-11-05T14:28:28.817Z
Learnt from: rjarry
Repo: DPDK/grout PR: 372
File: modules/infra/datapath/loop_input.c:53-56
Timestamp: 2025-11-05T14:28:28.817Z
Learning: In modules/infra/datapath/loop_input.c, the l3_edges array is intentionally indexed using network byte order (rte_be16_t) for EtherType values. Both loopback_input_add_type() registration and loopback_input_process() lookup use network byte order consistently, so no byte order conversion is needed when accessing l3_edges.

Applied to files:

  • modules/dhcp/control/dhcp_options.c
🧬 Code graph analysis (3)
modules/dhcp/control/client.h (4)
modules/dhcp/control/client.c (5)
  • dhcp_input_cb (278-440)
  • dhcp_start (456-514)
  • dhcp_stop (516-579)
  • dhcp_get_mempool (581-583)
  • dhcp_get_output (585-587)
modules/dhcp/datapath/dhcp_input.c (1)
  • dhcp_input_register_port (143-146)
modules/dhcp/control/dhcp_packet.c (3)
  • dhcp_parse_packet (23-91)
  • dhcp_build_discover (199-201)
  • dhcp_build_request (203-208)
modules/dhcp/control/dhcp_options.c (3)
  • dhcp_parse_options (8-117)
  • dhcp_build_options (119-121)
  • dhcp_build_options_ex (123-171)
modules/dhcp/control/dhcp_packet.c (1)
modules/dhcp/control/dhcp_options.c (2)
  • dhcp_parse_options (8-117)
  • dhcp_build_options_ex (123-171)
smoke/dhcp_test.sh (1)
smoke/_init.sh (2)
  • fail (104-107)
  • netns_add (113-121)
🪛 Shellcheck (0.11.0)
smoke/dhcp_test.sh

[warning] 5-5: Quote this to prevent word splitting.

(SC2046)


[warning] 18-18: tmp is referenced but not assigned.

(SC2154)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: build-and-tests (clang-18, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (clang-16, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (clang-15, none, debugoptimized, ubuntu-22.04, false)
  • GitHub Check: build-and-tests (gcc-14, address, debug, ubuntu-24.04, -Dfrr=enabled, true)
  • GitHub Check: build-and-tests (gcc-13, none, debugoptimized, ubuntu-24.04, -Dfrr=enabled, false)
  • GitHub Check: deb
  • GitHub Check: rpm
🔇 Additional comments (18)
modules/dhcp/datapath/meson.build (1)

1-7: Meson snippet looks correct and consistent

License header, src += files('dhcp_input.c'), and inc += include_directories('.') are all straightforward and in line with typical Meson subdir patterns. No changes needed here.

.github/workflows/check.yml (1)

74-74: LGTM!

Adding dnsmasq for the DHCP smoke test is appropriate.

modules/dhcp/cli/meson.build (1)

1-6: LGTM!

Follows the established modular CLI build pattern.

modules/dhcp/control/meson.build (1)

1-9: LGTM!

Standard control module build configuration.

smoke/dhcp_test.sh (1)

1-70: Well-structured smoke test.

Good coverage of DHCP enable/disable workflow with proper namespace isolation and cleanup.

modules/dhcp/api/gr_dhcp.h (1)

1-59: LGTM!

Clean API design with proper use of ip4_addr_t and fixed-width enum. Duration fields correctly use uint32_t for DHCP timers.

modules/dhcp/control/client.c (3)

32-115: LGTM!

Interface configuration logic is sound. Continuing on gateway creation failure (line 103) is reasonable for degraded operation.


278-440: LGTM!

State machine implementation follows RFC 2131 correctly. Proper bounds checking and resource cleanup on all paths.


456-514: LGTM!

Proper bounds checking and cleanup on all error paths.

modules/dhcp/control/dhcp_packet.c (3)

23-91: LGTM!

Packet parsing logic is sound: validates sizes, checks magic/xid/op, and delegates option parsing correctly.


199-201: LGTM!

Clean delegation to the common builder.


203-208: LGTM!

Properly delegates to common builder with REQUEST parameters.

modules/dhcp/control/dhcp_options.c (3)

8-117: LGTM!

Parsing logic handles bounds correctly, converts time values from network order, and stores IPs in network byte order as expected.


119-121: LGTM!

Clean wrapper for basic option building.


123-171: LGTM!

Options built correctly with proper buffer size check and network byte order preservation.

modules/dhcp/control/client.h (3)

18-82: LGTM!

Well-structured type definitions. Fixed-width enums are valid per C23/gnu2x. Packed struct with flexible array for wire protocol is correct.


84-86: LGTM!

Constants correctly defined per RFC 2131.


88-121: LGTM!

API declarations are complete and consistent with implementations.

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: 0

♻️ Duplicate comments (1)
modules/dhcp/api/gr_dhcp.h (1)

35-40: Define request/streaming schema for GR_DHCP_LIST

GR_DHCP_LIST currently has its request struct and STREAM(struct gr_dhcp_status) commented out, so there’s no declared payload type for this opcode. To make the status query usable and consistent with other modules, please define gr_dhcp_list_req (even if empty or just an iface_id filter) and enable a streaming response of gr_dhcp_status. This mirrors the earlier “use a streaming response” feedback.

🧹 Nitpick comments (1)
modules/dhcp/api/gr_dhcp.h (1)

21-29: Clarify semantics of lease timing fields

It’s unclear whether lease_time, renewal_time, and rebind_time are absolute timestamps or durations, and what units they use. Adding a short comment (e.g., “seconds, durations since lease start” or “absolute seconds since epoch”) will prevent control‑plane consumers from misinterpreting these values, especially given the prior discussion about timestamps vs durations.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 010001f and 4f2b4b2.

⛔ Files ignored due to path filters (1)
  • docs/graph.svg is excluded by !**/*.svg
📒 Files selected for processing (15)
  • .github/workflows/check.yml (1 hunks)
  • modules/dhcp/api/gr_dhcp.h (1 hunks)
  • modules/dhcp/api/meson.build (1 hunks)
  • modules/dhcp/cli/dhcp.c (1 hunks)
  • modules/dhcp/cli/meson.build (1 hunks)
  • modules/dhcp/control/client.c (1 hunks)
  • modules/dhcp/control/client.h (1 hunks)
  • modules/dhcp/control/meson.build (1 hunks)
  • modules/dhcp/control/options.c (1 hunks)
  • modules/dhcp/control/packet.c (1 hunks)
  • modules/dhcp/datapath/dhcp_input.c (1 hunks)
  • modules/dhcp/datapath/meson.build (1 hunks)
  • modules/dhcp/meson.build (1 hunks)
  • modules/meson.build (1 hunks)
  • smoke/dhcp_test.sh (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • modules/dhcp/datapath/meson.build
🚧 Files skipped from review as they are similar to previous changes (5)
  • .github/workflows/check.yml
  • modules/dhcp/cli/meson.build
  • modules/dhcp/api/meson.build
  • modules/dhcp/control/client.h
  • modules/dhcp/cli/dhcp.c
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{c,h}

⚙️ CodeRabbit configuration file

**/*.{c,h}: - gr_vec_*() functions cannot fail. No need to check their return value.

  • gr_vec_free(x) always sets x = NULL. There is no risk of double free.
  • ec_node_*() functions consume their ec_node arguments. No leaks on error.
  • rte_node->ctx is an uint8_t array of size 16, not a pointer.
  • Don't suggest to replace assert() with graceful error checking.
  • We compile with -std=gnu2x. Unnamed parameters in function definitions are valid.

Files:

  • modules/dhcp/control/packet.c
  • modules/dhcp/datapath/dhcp_input.c
  • modules/dhcp/api/gr_dhcp.h
  • modules/dhcp/control/client.c
  • modules/dhcp/control/options.c
**/*.sh

⚙️ CodeRabbit configuration file

**/*.sh: - Don't bother about unquoted shell variables.

Files:

  • smoke/dhcp_test.sh
🧠 Learnings (7)
📚 Learning: 2025-09-09T06:02:47.703Z
Learnt from: maxime-leroy
Repo: DPDK/grout PR: 309
File: modules/srv6/datapath/srv6_local.c:88-122
Timestamp: 2025-09-09T06:02:47.703Z
Learning: The DPDK function rte_ipv6_get_next_ext() only reads 2 bytes from the extension header, so a 2-byte precheck is sufficient before calling it.

Applied to files:

  • modules/dhcp/control/packet.c
📚 Learning: 2025-11-03T13:28:19.489Z
Learnt from: rjarry
Repo: DPDK/grout PR: 379
File: modules/infra/datapath/port_tx.c:36-46
Timestamp: 2025-11-03T13:28:19.489Z
Learning: In DPDK graph node process callbacks, the return value is only used for statistics and does not affect packet flow or scheduling through the graph. Nodes can return 0 when they haven't processed packets (e.g., when dropping or redirecting due to port state).

Applied to files:

  • modules/dhcp/datapath/dhcp_input.c
📚 Learning: 2025-11-05T14:28:28.817Z
Learnt from: rjarry
Repo: DPDK/grout PR: 372
File: modules/infra/datapath/loop_input.c:53-56
Timestamp: 2025-11-05T14:28:28.817Z
Learning: In modules/infra/datapath/loop_input.c, the l3_edges array is intentionally indexed using network byte order (rte_be16_t) for EtherType values. Both loopback_input_add_type() registration and loopback_input_process() lookup use network byte order consistently, so no byte order conversion is needed when accessing l3_edges.

Applied to files:

  • modules/dhcp/datapath/dhcp_input.c
  • modules/dhcp/control/options.c
📚 Learning: 2025-09-05T22:34:10.597Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/ip/datapath/ip_output.c:100-107
Timestamp: 2025-09-05T22:34:10.597Z
Learning: This codebase compiles with -Wswitch-enum and -Werror flags, which means all enum values must be explicitly handled in switch statements or compilation will fail. This provides compile-time safety against missing enum cases, making default cases in enum switches unnecessary and potentially harmful as they could mask new enum additions.

Applied to files:

  • modules/dhcp/api/gr_dhcp.h
📚 Learning: 2025-11-24T12:50:09.310Z
Learnt from: rjarry
Repo: DPDK/grout PR: 408
File: api/gr_net_types.h:78-84
Timestamp: 2025-11-24T12:50:09.310Z
Learning: In api/gr_net_types.h, ip4_addr_t values are stored in network byte order (big-endian). When using a union to cast to a byte array (uint8_t u8[4]), accessing u8[0] correctly reads the first octet (most significant byte) of the IP address on all architectures, because it directly accesses the memory layout rather than performing integer operations. This pattern is valid for checking properties like multicast (first octet in range 224-239).

Applied to files:

  • modules/dhcp/api/gr_dhcp.h
  • modules/dhcp/control/options.c
📚 Learning: 2025-09-21T10:48:42.539Z
Learnt from: rjarry
Repo: DPDK/grout PR: 312
File: modules/srv6/api/gr_srv6.h:18-21
Timestamp: 2025-09-21T10:48:42.539Z
Learning: In the Grout codebase, fixed-width enum syntax like `typedef enum : uint8_t { ... } type_t;` is valid and correct since the project uses `-std=gnu2x` which supports C23 features including fixed-width enums.

Applied to files:

  • modules/dhcp/api/gr_dhcp.h
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: In this project, cli_src is a global variable that gets populated by individual modules (like modules/policy/cli/meson.build) and is ultimately consumed by an executable() call in the top-level meson.build file. This creates a modular CLI build where each module contributes its CLI sources to the main CLI binary.

Applied to files:

  • modules/dhcp/control/meson.build
  • modules/meson.build
🧬 Code graph analysis (2)
modules/dhcp/control/client.c (11)
modules/infra/control/gr_iface.h (1)
  • iface (16-25)
api/gr_errno.h (1)
  • errno_set (9-12)
modules/infra/control/iface.c (1)
  • iface_get_eth_addr (261-274)
modules/infra/control/nexthop.c (1)
  • nexthop_new (327-366)
modules/ip/control/route.c (2)
  • rib4_insert (206-214)
  • rib4_delete (216-260)
modules/dhcp/control/packet.c (3)
  • dhcp_build_discover (199-201)
  • dhcp_build_request (203-208)
  • dhcp_parse_packet (23-91)
modules/infra/datapath/control_input.c (2)
  • post_to_stack (45-54)
  • gr_control_input_register_handler (36-43)
modules/dhcp/datapath/dhcp_input.c (1)
  • dhcp_input_register_port (143-146)
modules/infra/control/mempool.c (1)
  • gr_pktmbuf_pool_get (39-103)
main/api.c (1)
  • api_send (242-258)
main/gr_module.h (1)
  • api_out (20-23)
smoke/dhcp_test.sh (1)
smoke/_init.sh (2)
  • fail (104-107)
  • netns_add (113-121)
🪛 Shellcheck (0.11.0)
smoke/dhcp_test.sh

[warning] 5-5: Quote this to prevent word splitting.

(SC2046)


[warning] 18-18: tmp is referenced but not assigned.

(SC2154)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: rpm
  • GitHub Check: deb
  • GitHub Check: build-and-tests (gcc-13, none, debugoptimized, ubuntu-24.04, -Dfrr=enabled, false)
  • GitHub Check: build-and-tests (clang-15, none, debugoptimized, ubuntu-22.04, false)
  • GitHub Check: build-and-tests (clang-16, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (clang-18, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (gcc-14, address, debug, ubuntu-24.04, -Dfrr=enabled, true)
🔇 Additional comments (20)
smoke/dhcp_test.sh (1)

1-70: LGTM!

The test script properly validates the DHCP workflow: setup, lease acquisition, route verification, status check, and cleanup. Static analysis warnings about unquoted variables are expected per coding guidelines.

modules/meson.build (1)

11-11: LGTM!

Properly integrates the DHCP module into the build system.

modules/dhcp/meson.build (1)

1-7: LGTM!

Clean module structure with proper separation of concerns across API, CLI, control, and datapath layers.

modules/dhcp/control/meson.build (1)

1-9: LGTM!

Properly integrates DHCP control sources into the build.

modules/dhcp/datapath/dhcp_input.c (3)

1-29: LGTM!

Headers and data structures follow project conventions. Local include path aligns with past review feedback.


32-88: LGTM!

The trace parsing correctly limits the search to 64 bytes and validates bounds before reading option values. Since this is trace-only code (not on the critical data path), the current validation is sufficient.


90-146: LGTM!

Helper functions and node registration follow standard DPDK graph patterns. Port registration properly logs the action.

modules/dhcp/control/options.c (2)

8-117: LGTM!

Option parsing is defensive with proper bounds validation. IP addresses correctly preserve network byte order via memcpy, and lease times properly convert from big-endian using shift operations.


119-171: LGTM!

Option building correctly validates buffer size and constructs well-formed DHCP options with proper conditional inclusion of server ID and requested IP.

modules/dhcp/control/packet.c (3)

23-91: LGTM!

Packet parsing is properly defensive with thorough validation of sizes, magic cookie, transaction ID, and operation code before processing options.


93-197: LGTM!

Packet construction properly validates each allocation step and fills all headers correctly. UDP and IP length fields accurately reflect the actual option length, ensuring correct packet parsing downstream.


199-208: LGTM!

Clean wrapper functions properly delegate to the common builder with appropriate parameters.

modules/dhcp/control/client.c (8)

27-115: LGTM!

Interface configuration properly validates inputs, calculates prefix length from subnet mask, and handles both local address and optional default route. Graceful degradation when gateway creation fails is appropriate.


117-276: LGTM!

Timer management correctly implements RFC 2131 lease renewal with proper defaults (T1 at 50%, T2 at 87.5% of lease time) and clean state transitions through RENEWING, REBINDING, and expiry paths.


278-440: LGTM!

Input callback implements a comprehensive DHCP state machine with proper validation and error handling. State transitions correctly handle OFFER, ACK, and NAK messages across all client states, including proper cleanup on renewal failures.


442-454: LGTM!

Initialization properly registers the datapath port and control output handler, and allocates a dedicated mempool for DHCP packets.


456-514: LGTM!

Start function properly validates inputs, allocates resources, and handles errors with appropriate cleanup. Each failure path correctly frees the client and clears the array slot before returning.


536-540: Verify handling when interface is deleted.

If iface_from_id() returns NULL, the function returns early without freeing client or clearing dhcp_clients[iface_id]. This leaves the client struct allocated and tracked even though its interface is gone.

Past review mentioned this as a potential memory leak, and was marked as addressed. However, the current code still exhibits this pattern. Please verify whether:

  1. Interfaces are guaranteed to never be deleted while DHCP clients are active, or
  2. There's cleanup elsewhere that handles orphaned clients, or
  3. The client should be freed even when the interface is missing

Apply this diff if cleanup should proceed despite missing interface:

 	iface = iface_from_id(iface_id);
 	if (iface == NULL) {
+		dhcp_cancel_timers(client);
+		free(client);
+		dhcp_clients[iface_id] = NULL;
+		LOG(WARNING, "dhcp: stopped client on deleted iface %u", iface_id);
 		errno = ENODEV;
 		return;
 	}

542-579: LGTM!

Route cleanup properly handles edge cases like missing subnet masks, and consistently removes both the address route and default route before freeing the client and clearing the array slot.


581-668: LGTM!

API handlers properly expose list, start, and stop operations with appropriate error handling. Module registration follows project conventions using RTE_INIT constructor.

@aharivel aharivel requested a review from rjarry November 27, 2025 15:59
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: 5

♻️ Duplicate comments (1)
modules/dhcp/control/client.c (1)

536-579: Fix leak when iface_from_id() fails in dhcp_stop().

If iface_from_id() returns NULL, the function returns with client still allocated and stored in dhcp_clients[iface_id]; timers and routes are also left untouched.

 	iface = iface_from_id(iface_id);
 	if (iface == NULL) {
-		errno = ENODEV;
-		return;
+		dhcp_cancel_timers(client);
+		free(client);
+		dhcp_clients[iface_id] = NULL;
+		errno = ENODEV;
+		return;
 	}
🧹 Nitpick comments (2)
smoke/dhcp_test.sh (1)

40-48: Replace fixed sleeps with a simple wait loop to reduce flakiness.

Using sleep 1 for dnsmasq startup and sleep 5 for lease acquisition can make this smoke test brittle on slower/faster environments; consider polling (with a bounded timeout) for dnsmasq PID/log readiness and for the expected route instead of fixed delays.

modules/dhcp/cli/dhcp.c (1)

53-70: Avoid default in enum switch to keep -Wswitch-enum effective.

dhcp_state_str() handles all current dhcp_state_t values but also has a default that will silently absorb future enum additions; drop the default and add a final return "UNKNOWN"; after the switch so new states trigger compiler warnings while still having a fallback.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4f2b4b2 and f67dea1.

⛔ Files ignored due to path filters (1)
  • docs/graph.svg is excluded by !**/*.svg
📒 Files selected for processing (15)
  • .github/workflows/check.yml (1 hunks)
  • modules/dhcp/api/gr_dhcp.h (1 hunks)
  • modules/dhcp/api/meson.build (1 hunks)
  • modules/dhcp/cli/dhcp.c (1 hunks)
  • modules/dhcp/cli/meson.build (1 hunks)
  • modules/dhcp/control/client.c (1 hunks)
  • modules/dhcp/control/client.h (1 hunks)
  • modules/dhcp/control/meson.build (1 hunks)
  • modules/dhcp/control/options.c (1 hunks)
  • modules/dhcp/control/packet.c (1 hunks)
  • modules/dhcp/datapath/dhcp_input.c (1 hunks)
  • modules/dhcp/datapath/meson.build (1 hunks)
  • modules/dhcp/meson.build (1 hunks)
  • modules/meson.build (1 hunks)
  • smoke/dhcp_test.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • .github/workflows/check.yml
  • modules/dhcp/api/gr_dhcp.h
  • modules/dhcp/datapath/meson.build
  • modules/dhcp/control/options.c
  • modules/meson.build
  • modules/dhcp/datapath/dhcp_input.c
  • modules/dhcp/control/packet.c
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{c,h}

⚙️ CodeRabbit configuration file

**/*.{c,h}: - gr_vec_*() functions cannot fail. No need to check their return value.

  • gr_vec_free(x) always sets x = NULL. There is no risk of double free.
  • ec_node_*() functions consume their ec_node arguments. No leaks on error.
  • rte_node->ctx is an uint8_t array of size 16, not a pointer.
  • Don't suggest to replace assert() with graceful error checking.
  • We compile with -std=gnu2x. Unnamed parameters in function definitions are valid.

Files:

  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/control/client.h
  • modules/dhcp/control/client.c
**/*.sh

⚙️ CodeRabbit configuration file

**/*.sh: - Don't bother about unquoted shell variables.

Files:

  • smoke/dhcp_test.sh
🧠 Learnings (5)
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: In this project, cli_src is a global variable that gets populated by individual modules (like modules/policy/cli/meson.build) and is ultimately consumed by an executable() call in the top-level meson.build file. This creates a modular CLI build where each module contributes its CLI sources to the main CLI binary.

Applied to files:

  • modules/dhcp/meson.build
  • modules/dhcp/control/meson.build
  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/cli/meson.build
📚 Learning: 2025-11-24T12:50:09.310Z
Learnt from: rjarry
Repo: DPDK/grout PR: 408
File: api/gr_net_types.h:78-84
Timestamp: 2025-11-24T12:50:09.310Z
Learning: In api/gr_net_types.h, ip4_addr_t values are stored in network byte order (big-endian). When using a union to cast to a byte array (uint8_t u8[4]), accessing u8[0] correctly reads the first octet (most significant byte) of the IP address on all architectures, because it directly accesses the memory layout rather than performing integer operations. This pattern is valid for checking properties like multicast (first octet in range 224-239).

Applied to files:

  • modules/dhcp/cli/dhcp.c
📚 Learning: 2025-09-05T22:34:10.597Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/ip/datapath/ip_output.c:100-107
Timestamp: 2025-09-05T22:34:10.597Z
Learning: This codebase compiles with -Wswitch-enum and -Werror flags, which means all enum values must be explicitly handled in switch statements or compilation will fail. This provides compile-time safety against missing enum cases, making default cases in enum switches unnecessary and potentially harmful as they could mask new enum additions.

Applied to files:

  • modules/dhcp/cli/dhcp.c
📚 Learning: 2025-09-21T10:48:42.539Z
Learnt from: rjarry
Repo: DPDK/grout PR: 312
File: modules/srv6/api/gr_srv6.h:18-21
Timestamp: 2025-09-21T10:48:42.539Z
Learning: In the Grout codebase, fixed-width enum syntax like `typedef enum : uint8_t { ... } type_t;` is valid and correct since the project uses `-std=gnu2x` which supports C23 features including fixed-width enums.

Applied to files:

  • modules/dhcp/cli/dhcp.c
  • modules/dhcp/control/client.h
📚 Learning: 2025-09-05T07:06:51.554Z
Learnt from: rjarry
Repo: DPDK/grout PR: 294
File: modules/policy/cli/meson.build:4-8
Timestamp: 2025-09-05T07:06:51.554Z
Learning: The grout project uses a modular CLI build system where individual modules contribute their CLI sources to a global cli_src variable via `cli_src += files(...)`, and the top-level meson.build file builds the final grcli executable using all collected CLI sources. The grcli executable is defined in the top-level meson.build at lines 122-127.

Applied to files:

  • modules/dhcp/cli/meson.build
🧬 Code graph analysis (2)
modules/dhcp/control/client.c (5)
api/gr_errno.h (1)
  • errno_set (9-12)
modules/ip/control/route.c (2)
  • rib4_insert (206-214)
  • rib4_delete (216-260)
modules/dhcp/control/packet.c (3)
  • dhcp_build_discover (199-201)
  • dhcp_build_request (203-208)
  • dhcp_parse_packet (23-91)
modules/dhcp/datapath/dhcp_input.c (1)
  • dhcp_input_register_port (143-146)
main/gr_module.h (1)
  • api_out (20-23)
smoke/dhcp_test.sh (1)
smoke/_init.sh (2)
  • fail (104-107)
  • netns_add (113-121)
🪛 Shellcheck (0.11.0)
smoke/dhcp_test.sh

[warning] 5-5: Quote this to prevent word splitting.

(SC2046)


[warning] 18-18: tmp is referenced but not assigned.

(SC2154)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: build-and-tests (clang-16, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (clang-18, none, debugoptimized, ubuntu-24.04, false)
  • GitHub Check: build-and-tests (clang-15, none, debugoptimized, ubuntu-22.04, false)
  • GitHub Check: build-and-tests (gcc-14, address, debug, ubuntu-24.04, -Dfrr=enabled, true)
  • GitHub Check: build-and-tests (gcc-13, none, debugoptimized, ubuntu-24.04, -Dfrr=enabled, false)
  • GitHub Check: deb
  • GitHub Check: rpm

Comment on lines +33 to +44
const struct iface *iface;
struct rte_ether_addr mac;
struct nexthop *nh;
uint8_t prefixlen;
int ret;

iface = iface_from_id(client->iface_id);
if (iface == NULL)
return errno_set(ENODEV);

if (iface_get_eth_addr(iface->id, &mac) < 0 && errno != EOPNOTSUPP)
return -errno;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Initialize mac to avoid using it uninitialized when EOPNOTSUPP.

If iface_get_eth_addr() fails with EOPNOTSUPP, mac is left uninitialized but still passed into the nexthop; zero-initialize it on declaration to avoid undefined contents.

-static int dhcp_configure_interface(struct dhcp_client *client) {
-	const struct iface *iface;
-	struct rte_ether_addr mac;
+static int dhcp_configure_interface(struct dhcp_client *client) {
+	const struct iface *iface;
+	struct rte_ether_addr mac = {0};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const struct iface *iface;
struct rte_ether_addr mac;
struct nexthop *nh;
uint8_t prefixlen;
int ret;
iface = iface_from_id(client->iface_id);
if (iface == NULL)
return errno_set(ENODEV);
if (iface_get_eth_addr(iface->id, &mac) < 0 && errno != EOPNOTSUPP)
return -errno;
static int dhcp_configure_interface(struct dhcp_client *client) {
const struct iface *iface;
struct rte_ether_addr mac = {0};
struct nexthop *nh;
uint8_t prefixlen;
int ret;
iface = iface_from_id(client->iface_id);
if (iface == NULL)
return errno_set(ENODEV);
if (iface_get_eth_addr(iface->id, &mac) < 0 && errno != EOPNOTSUPP)
return -errno;
🤖 Prompt for AI Agents
In modules/dhcp/control/client.c around lines 33 to 44, the local variable mac
may be left uninitialized if iface_get_eth_addr() returns EOPNOTSUPP; initialize
mac (zero-fill) at declaration so it has defined contents before any use (e.g.,
when passed into nexthop). Update the declaration to zero-initialize the struct
rte_ether_addr and keep the existing error handling logic.

Comment on lines +198 to +204
client->xid = rte_rand();
struct rte_mbuf *m = dhcp_build_discover(client->iface_id, client->xid);
if (m != NULL) {
post_to_stack(dhcp_output, m);
client->state = DHCP_STATE_SELECTING;
LOG(INFO, "dhcp: lease expired, sent new DISCOVER (iface=%u)", client->iface_id);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Also check post_to_stack() when retrying after lease expiry.

Same as other paths, ignoring post_to_stack() here can leak m and silently drop the new DISCOVER when the lease expires.

-	struct rte_mbuf *m = dhcp_build_discover(client->iface_id, client->xid);
-	if (m != NULL) {
-		post_to_stack(dhcp_output, m);
-		client->state = DHCP_STATE_SELECTING;
-		LOG(INFO, "dhcp: lease expired, sent new DISCOVER (iface=%u)", client->iface_id);
-	}
+	struct rte_mbuf *m = dhcp_build_discover(client->iface_id, client->xid);
+	if (m != NULL) {
+		if (post_to_stack(dhcp_output, m) < 0) {
+			LOG(ERR,
+			    "dhcp: failed to send DISCOVER after lease expiry (iface=%u)",
+			    client->iface_id);
+			rte_pktmbuf_free(m);
+		} else {
+			client->state = DHCP_STATE_SELECTING;
+			LOG(INFO,
+			    "dhcp: lease expired, sent new DISCOVER (iface=%u)",
+			    client->iface_id);
+		}
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
client->xid = rte_rand();
struct rte_mbuf *m = dhcp_build_discover(client->iface_id, client->xid);
if (m != NULL) {
post_to_stack(dhcp_output, m);
client->state = DHCP_STATE_SELECTING;
LOG(INFO, "dhcp: lease expired, sent new DISCOVER (iface=%u)", client->iface_id);
}
client->xid = rte_rand();
struct rte_mbuf *m = dhcp_build_discover(client->iface_id, client->xid);
if (m != NULL) {
if (post_to_stack(dhcp_output, m) < 0) {
LOG(ERR,
"dhcp: failed to send DISCOVER after lease expiry (iface=%u)",
client->iface_id);
rte_pktmbuf_free(m);
} else {
client->state = DHCP_STATE_SELECTING;
LOG(INFO,
"dhcp: lease expired, sent new DISCOVER (iface=%u)",
client->iface_id);
}
}
🤖 Prompt for AI Agents
In modules/dhcp/control/client.c around lines 198 to 204, the code ignores the
return value of post_to_stack() after building the DISCOVER and can leak the
mbuf m if post_to_stack() fails; modify this block to check
post_to_stack(dhcp_output, m)'s return, log an error when it fails, and free the
mbuf (or call the appropriate rte_pktmbuf_free) on failure so m is not leaked;
ensure client->state is only set to DHCP_STATE_SELECTING after a successful
post, and keep behavior consistent with other paths that handle post_to_stack()
failures.

Comment on lines +207 to +223
static void dhcp_send_request(struct dhcp_client *client) {
struct rte_mbuf *m;

m = dhcp_build_request(
client->iface_id, client->xid, client->server_ip, client->offered_ip
);
if (m == NULL) {
LOG(ERR, "dhcp: failed to build REQUEST for renewal");
return;
}

post_to_stack(dhcp_output, m);
LOG(INFO,
"dhcp: sent REQUEST for renewal (iface=%u, state=%s)",
client->iface_id,
client->state == DHCP_STATE_RENEWING ? "RENEWING" : "REBINDING");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Check post_to_stack() result and free m on failure.

Here post_to_stack() errors would leak m and silently drop the RENEW/REBIND request, unlike other call sites that handle failures; align this path with the others.

-	post_to_stack(dhcp_output, m);
-	LOG(INFO,
+	if (post_to_stack(dhcp_output, m) < 0) {
+		LOG(ERR, "dhcp: failed to send REQUEST for renewal");
+		rte_pktmbuf_free(m);
+		return;
+	}
+
+	LOG(INFO,
 	    "dhcp: sent REQUEST for renewal (iface=%u, state=%s)",
🤖 Prompt for AI Agents
In modules/dhcp/control/client.c around lines 207-223,
post_to_stack(dhcp_output, m) return value is not checked which can leak the
mbuf and silently drop the RENEW/REBIND request; update the code to capture
post_to_stack's result, and on failure call the appropriate free function (e.g.,
rte_pktmbuf_free(m)) to avoid leaking the mbuf, log an error with iface and
state context, and return; ensure the mbuf is only freed on failure and not
touched otherwise.

Comment on lines +425 to +430
client->xid = rte_rand();
response = dhcp_build_discover(client->iface_id, client->xid);
if (response != NULL) {
post_to_stack(dhcp_output, response);
client->state = DHCP_STATE_SELECTING;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle post_to_stack() failure when re-sending DISCOVER after NAK.

This resend path ignores post_to_stack()’s return code and never frees response on error, unlike other sites; same leak/routing-loss risk as in the renewal path.

-			response = dhcp_build_discover(client->iface_id, client->xid);
-			if (response != NULL) {
-				post_to_stack(dhcp_output, response);
-				client->state = DHCP_STATE_SELECTING;
-			}
+			response = dhcp_build_discover(client->iface_id, client->xid);
+			if (response != NULL) {
+				if (post_to_stack(dhcp_output, response) < 0) {
+					LOG(ERR, "dhcp: failed to send DISCOVER after NAK");
+					rte_pktmbuf_free(response);
+				} else {
+					client->state = DHCP_STATE_SELECTING;
+				}
+			}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
client->xid = rte_rand();
response = dhcp_build_discover(client->iface_id, client->xid);
if (response != NULL) {
post_to_stack(dhcp_output, response);
client->state = DHCP_STATE_SELECTING;
}
client->xid = rte_rand();
response = dhcp_build_discover(client->iface_id, client->xid);
if (response != NULL) {
if (post_to_stack(dhcp_output, response) < 0) {
LOG(ERR, "dhcp: failed to send DISCOVER after NAK");
rte_pktmbuf_free(response);
} else {
client->state = DHCP_STATE_SELECTING;
}
}
🤖 Prompt for AI Agents
In modules/dhcp/control/client.c around lines 425 to 430, the code resends a
DISCOVER after NAK but ignores post_to_stack()’s return value and never frees
response on failure; update the path to check the return code of
post_to_stack(), only set client->state = DHCP_STATE_SELECTING on success, and
on failure call the appropriate cleanup (free response) and optionally log the
error or handle retry without leaking memory or misrouting state.

Add a DHCP client that automatically acquires IP addresses and default
routes for interfaces. The client handles the complete DHCP workflow
including initial address assignment, lease renewal, and cleanup.

When enabled on an interface, the client sends DISCOVER messages and
processes server responses to configure the interface with an assigned
IP address, netmask, and default gateway. Leases are automatically
renewed before expiration using T1 and T2 timers. When disabled, all
DHCP-assigned configuration is removed.

CLI commands allow enabling and disabling DHCP per interface, and
viewing the current DHCP client status including assigned addresses,
lease times, and client state.

The implementation follows RFC 2131 with support for standard DHCP
options including subnet mask, router, lease time, and renewal timings.

Signed-off-by: Anthony Harivel <[email protected]>
Reviewed-by: Robin Jarry <[email protected]>
@rjarry rjarry merged commit 3bad4e2 into DPDK:main Nov 28, 2025
6 of 7 checks passed
@aharivel aharivel mentioned this pull request Nov 28, 2025
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.

3 participants