Skip to content

Conversation

@rjarry
Copy link
Collaborator

@rjarry rjarry commented Nov 6, 2025

IPv6 requires receiving multicast traffic for neighbor discovery and other essential protocols. Additionally, we will soon need to receive all multicast ethernet packets to allow Linux daemons running in network namespaces to subscribe to multicast groups independently.

Rather than track multicast addresses in hardware filters (which are subject to the same capacity limits as unicast filtering), unconditionally enable allmulti mode on all ports.

Simplify the MAC filter structure to only handle unicast addresses. Merge the separate ucast_filter and mcast_filter into a single filter array. Combine each MAC address with its reference count in a port_mac structure rather than maintaining parallel arrays.

Remove the MAC_FILTER_F_ALL flag since only promiscuous mode (for unicast overflow) needs to be tracked. Replace GR_IFACE_S_ALLMULTI_FIXED with GR_IFACE_S_ALLMULTI as it is no longer a temporary state that can be disabled.

Remove allmulti from user-configurable interface flags since it is now always enabled for all ports.

Update IPv6 neighbor discovery to no longer install multicast MAC filters when joining solicited-node multicast groups, as allmulti mode already ensures their reception.

Summary by CodeRabbit

  • Refactor

    • Restructured MAC address filtering to use a unified model for improved maintainability.
    • Automated all-multicast enabling during port reconfiguration.
  • Bug Fixes

    • Improved MAC address filtering logic and error handling during filter operations.

IPv6 requires receiving multicast traffic for neighbor discovery and
other essential protocols. Additionally, we will soon need to receive
all multicast ethernet packets to allow Linux daemons running in network
namespaces to subscribe to multicast groups independently.

Rather than track multicast addresses in hardware filters (which are
subject to the same capacity limits as unicast filtering), unconditionally
enable allmulti mode on all ports.

Simplify the MAC filter structure to only handle unicast addresses.
Merge the separate ucast_filter and mcast_filter into a single filter
array. Combine each MAC address with its reference count in a port_mac
structure rather than maintaining parallel arrays.

Remove the MAC_FILTER_F_ALL flag since only promiscuous mode (for
unicast overflow) needs to be tracked. Replace GR_IFACE_S_ALLMULTI_FIXED
with GR_IFACE_S_ALLMULTI as it is no longer a temporary state that can
be disabled.

Remove allmulti from user-configurable interface flags since it is now
always enabled for all ports.

Update IPv6 neighbor discovery to no longer install multicast MAC
filters when joining solicited-node multicast groups, as allmulti mode
already ensures their reception.

Signed-off-by: Robin Jarry <[email protected]>
When typing interface set port foo promisc <tab> there is no completion
because the node is a regular expression.

Change it to an or node with deterministic values.

Signed-off-by: Robin Jarry <[email protected]>
@coderabbitai
Copy link

coderabbitai bot commented Nov 6, 2025

📝 Walkthrough

Walkthrough

This pull request migrates allmulticast (ALLMULTI) handling from a configurable flag to an internal state-only mechanism and consolidates MAC filtering infrastructure. ALLMULTI is removed from public APIs, CLI configuration, and enums—no longer user-controllable. MAC filtering is refactored by merging separate unicast and multicast filter structures into a unified filter model with a port_mac array. Port reconfig now automatically enables allmulticast state. IPv6 multicast address management no longer manipulates ethernet filters directly.

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 accurately captures the main changes: unconditionally enabling allmulti on ports and refactoring MAC filtering logic from separate unicast/multicast structures to a unified approach.

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

📜 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 a7ede13 and 6d1a01a.

📒 Files selected for processing (10)
  • frr/if_grout.c (1 hunks)
  • modules/infra/api/gr_infra.h (1 hunks)
  • modules/infra/cli/gr_cli_iface.h (1 hunks)
  • modules/infra/cli/iface.c (2 hunks)
  • modules/infra/control/gr_iface.h (0 hunks)
  • modules/infra/control/gr_port.h (2 hunks)
  • modules/infra/control/iface.c (0 hunks)
  • modules/infra/control/port.c (3 hunks)
  • modules/infra/control/port_test.c (2 hunks)
  • modules/ip6/control/address.c (1 hunks)
💤 Files with no reviewable changes (2)
  • modules/infra/control/iface.c
  • modules/infra/control/gr_iface.h
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{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/ip6/control/address.c
  • modules/infra/control/gr_port.h
  • frr/if_grout.c
  • modules/infra/cli/gr_cli_iface.h
  • modules/infra/control/port_test.c
  • modules/infra/control/port.c
  • modules/infra/api/gr_infra.h
  • modules/infra/cli/iface.c
🧠 Learnings (4)
📚 Learning: 2025-09-22T09:21:51.749Z
Learnt from: rjarry
Repo: DPDK/grout PR: 312
File: modules/ip/control/address.c:102-110
Timestamp: 2025-09-22T09:21:51.749Z
Learning: The rib4_insert() function in the IP module takes ownership of the nexthop reference and automatically calls nexthop_decref(nh) on failure paths, so callers don't need to manually decrement the reference count when rib4_insert fails.

Applied to files:

  • modules/ip6/control/address.c
📚 Learning: 2025-09-22T09:21:51.749Z
Learnt from: rjarry
Repo: DPDK/grout PR: 312
File: modules/ip/control/address.c:102-110
Timestamp: 2025-09-22T09:21:51.749Z
Learning: The rib4_insert() function in modules/ip/control/route.c takes ownership of the nexthop reference by calling nexthop_incref(nh) at the beginning and properly calls nexthop_decref(nh) on any failure paths via the fail: label, so callers don't need to manually decrement the reference count when rib4_insert fails.

Applied to files:

  • modules/ip6/control/address.c
📚 Learning: 2025-11-05T13:55:26.169Z
Learnt from: maxime-leroy
Repo: DPDK/grout PR: 372
File: smoke/cross_vrf_forward_test.sh:18-18
Timestamp: 2025-11-05T13:55:26.169Z
Learning: In the DPDK/grout codebase, VRF interfaces (named gr-vrf<id>) are automatically created when an interface is added to a non-existing VRF using port_add. The VRF creation is handled automatically by the event system in vrf_netlink.c, so no explicit VRF interface creation commands are needed in test scripts.

Applied to files:

  • modules/infra/api/gr_infra.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/infra/api/gr_infra.h
🧬 Code graph analysis (3)
modules/infra/control/port_test.c (1)
modules/infra/control/port.c (1)
  • port_mac_add (482-551)
modules/infra/control/port.c (2)
modules/infra/control/gr_iface.h (1)
  • iface (16-22)
api/gr_errno.h (1)
  • errno_set (9-12)
modules/infra/cli/iface.c (1)
modules/infra/control/gr_iface.h (1)
  • iface (16-22)

Comment on lines +350 to +354
if ((ret = rte_eth_allmulticast_enable(p->port_id)) < 0)
return errno_log(-ret, "rte_eth_allmulticast_enable");

iface->state |= GR_IFACE_S_ALLMULTI;

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

Handle unsupported allmulti fallback.
Ports on NICs that return -EOPNOTSUPP/-ENOSYS from rte_eth_allmulticast_enable() will hit this return and fail to come up. We still need multicast delivery on such hardware, so we should fall back to forcing promisc instead of aborting the reconfig.

-		if ((ret = rte_eth_allmulticast_enable(p->port_id)) < 0)
-			return errno_log(-ret, "rte_eth_allmulticast_enable");
-
-		iface->state |= GR_IFACE_S_ALLMULTI;
+		ret = rte_eth_allmulticast_enable(p->port_id);
+		if (ret < 0) {
+			if (ret == -ENOSYS || ret == -EOPNOTSUPP) {
+				LOG(NOTICE,
+				    "%s: %s, forcing promisc",
+				    iface->name,
+				    rte_strerror(-ret));
+				ret = rte_eth_promiscuous_enable(p->port_id);
+				if (ret < 0)
+					return errno_log(-ret, "rte_eth_promiscuous_enable");
+				iface->state |= GR_IFACE_S_PROMISC_FIXED | GR_IFACE_S_ALLMULTI;
+			} else {
+				return errno_log(-ret, "rte_eth_allmulticast_enable");
+			}
+		} else {
+			iface->state |= GR_IFACE_S_ALLMULTI;
+		}
📝 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
if ((ret = rte_eth_allmulticast_enable(p->port_id)) < 0)
return errno_log(-ret, "rte_eth_allmulticast_enable");
iface->state |= GR_IFACE_S_ALLMULTI;
ret = rte_eth_allmulticast_enable(p->port_id);
if (ret < 0) {
if (ret == -ENOSYS || ret == -EOPNOTSUPP) {
LOG(NOTICE,
"%s: %s, forcing promisc",
iface->name,
rte_strerror(-ret));
ret = rte_eth_promiscuous_enable(p->port_id);
if (ret < 0)
return errno_log(-ret, "rte_eth_promiscuous_enable");
iface->state |= GR_IFACE_S_PROMISC_FIXED | GR_IFACE_S_ALLMULTI;
} else {
return errno_log(-ret, "rte_eth_allmulticast_enable");
}
} else {
iface->state |= GR_IFACE_S_ALLMULTI;
}
🤖 Prompt for AI Agents
In modules/infra/control/port.c around lines 350-354, handle -EOPNOTSUPP/-ENOSYS
from rte_eth_allmulticast_enable by falling back to enabling promiscuous mode
instead of returning: call rte_eth_allmulticast_enable and if it returns < 0
check if ret is -EOPNOTSUPP or -ENOSYS; in that case log a warning and call
rte_eth_promiscuous_enable(p->port_id), set iface->state |= GR_IFACE_S_PROMISC
(and still set GR_IFACE_S_ALLMULTI semantics as needed), and only return an
error if the promiscuous enable call fails; for any other negative return from
rte_eth_allmulticast_enable keep existing error handling and return.

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.

2 participants