Skip to content

zebra: Send enough NHG information to dplane to support RIB/FIB#21415

Open
GaladrielZhao wants to merge 2 commits into
FRRouting:masterfrom
GaladrielZhao:ribfib
Open

zebra: Send enough NHG information to dplane to support RIB/FIB#21415
GaladrielZhao wants to merge 2 commits into
FRRouting:masterfrom
GaladrielZhao:ribfib

Conversation

@GaladrielZhao

@GaladrielZhao GaladrielZhao commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

This change extends the zebra dataplane with richer nexthop group information and introduces a new --nhg-fib mode to enable RIB/FIB separation for SONiC FIBconstruction via FPM.

This commit makes the following changes:

1 Richer NHG context in dplane
The dplane NHG context is extended with a nh_grp_full array that captures the complete dependency tree including intermediate recursive nodes. Each entry carries a num_direct count so consumers can reconstruct the recursive hierarchy from the flat array. depends/dependents ID arrays and the NHG flag bitmask are also added so FPM can reconstruct the full forwarding topology.

2 Forwarding received NHGs to dplane
A new NEXTHOP_GROUP_RECEIVED flag is introduced to mark protocol-owned NHGs that should not have their addresses overwritten by zebra resolution. zebra_nhg_mark_received_flag() recursively sets this flag together with NEXTHOP_GROUP_VALID on the NHE and all its depends. The nhe_received field is now installed into the dplane alongside the resolved NHE so FPM providers can access both representations.

3 --nhg-fib command-line option
When --nhg-fib is set, recursive and received NHGs are sent to the dplane with skip_kernel, allowing FPM to perform backwalk and recursive nexthop resolution while keeping them out of the kernel. zebra_nhg_install_kernel() skips zebra_nhg_resolve() for these NHGs so they enter dplane in their original form.

4 FPM-only reinstall on dependent changes
A new NEXTHOP_GROUP_REINSTALL_FPM_ONLY flag is introduced to handle the case where an NHG's dependents list changes (add/remove) while the NHG itself is already installed in the kernel. When triggered, the NHG is re-enqueued to dplane with skip_kernel so FPM receives the updated dependents without redundant kernel programming.If the change occurs while an install is already in-flight (QUEUED), the flag is deferred and picked up in zebra_nhg_dplane_result() upon completion.

Topotests covering the above scenarios have been added.

@greptile-apps

greptile-apps Bot commented Mar 31, 2026

Copy link
Copy Markdown

Greptile Summary

This PR extends zebra's dataplane with richer nexthop-group context for FPM/SONiC consumers: a full recursive dependency tree (nh_grp_full), the raw received NHE alongside the resolved one, the NHG flag bitmask, and a new --nhg-fib mode that routes recursive NHGs to the dplane only (skipping the kernel). The architecture is sound, but two bugs in the changed code path will cause production crashes or silent data corruption.

Key issues found:

  • P0 – Crash on every normal route install (zebra_rib.c:655): zebra_nhg_install_kernel(re->nhe_received, re->type) is called unconditionally. re->nhe_received is NULL for all routes that do not carry a received NHE (i.e. the overwhelming majority). zebra_nhg_install_kernel() dereferences the pointer immediately — zebra will crash on the first normal route installation.
  • P1 – Corrupted nh_grp_full array (zebra_nhg.c): In zebra_nhg_nhe2grp_full_internal, the leaf-node branch writes a grp_full[i] entry but never increments i. Every subsequent leaf sibling overwrites the same slot, so the flat array passed to FPM is wrong for any NHG with two or more leaf-level members.
  • P2 – Weight truncation: nh_grp_full.weight is uint8_t while nh_grp.weight (and the --nexthop-weight-16-bit feature) uses uint16_t; weights above 255 will be silently truncated.
  • P2 – num_direct undercounting: The sub_depend_count filter inside the non-leaf branch checks NEXTHOP_GROUP_INSTALLED but not NEXTHOP_GROUP_QUEUED, while the outer inclusion logic accepts both.
  • P2 – Dead code: dep_scan_count is computed but never read.

Confidence Score: 1/5

Not safe to merge — the unconditional NULL dereference in rib_install_kernel will crash zebra on every normal route installation.

Two correctness bugs on the hot path: a guaranteed crash (NULL dereference for every route without nhe_received) and silent data corruption in the nh_grp_full builder (missing i++ for leaf nodes). Both must be fixed before merge.

zebra/zebra_rib.c (NULL dereference) and zebra/zebra_nhg.c (nh_grp_full leaf indexing bug) require fixes before this PR is mergeable.

Important Files Changed

Filename Overview
zebra/zebra_rib.c Adds nhe_received install call to rib_install_kernel without a NULL guard — causes a crash for every normal route; also reorders route_entry_update_original_nhe/route_entry_update_nhe and removes stale nhe_received sync from route_entry_update_nhe.
zebra/zebra_nhg.c Adds zebra_nhg_nhe2grp_full_internal with a missing i++ in the leaf-node branch (corrupts the flat array), zebra_nhg_mark_received_flag, and modifies zebra_nhg_install_kernel to skip resolve for RECEIVED/RECURSIVE NHGs when --nhg-fib is active.
zebra/zebra_nhg.h Introduces nh_grp_full struct with a uint8_t weight field (should be uint16_t to match nh_grp and the 16-bit weight option) and adds NEXTHOP_GROUP_RECEIVED flag.
zebra/zebra_dplane.c Extends dplane_nexthop_info with nh_grp_full, depends, dependents arrays and adds accessors; populates nhg_flags and calls zebra_nhg_nhe2grp_full; sets skip_kernel for RECEIVED/RECURSIVE NHGs under --nhg-fib.
zebra/zebra_dplane.h Declares new accessor functions for nh_grp_full, depends, dependents, nhg_flags, and nhe_received_id; straightforward header additions.
zebra/rt_netlink.c Adds a skip_kernel early-return guard in netlink_put_nexthop_update_msg to prevent kernel programming of RECEIVED/RECURSIVE NHGs — logic is clean and correct.
zebra/main.c Adds --nhg-fib CLI flag and zebra_nhg_fib_enabled global; follows the existing pattern for netlink-specific options.

Sequence Diagram

sequenceDiagram
    participant Proto as Protocol Client
    participant RIB as zebra_rib
    participant NHG as zebra_nhg
    participant DPlane as zebra_dplane
    participant NetLink as rt_netlink
    participant FPM as FPM/SONiC

    Proto->>RIB: route add (with NHE)
    RIB->>NHG: route_entry_update_original_nhe(nhe)
    NHG-->>NHG: zebra_nhg_mark_received_flag(nhe) sets NEXTHOP_GROUP_RECEIVED + VALID
    RIB->>NHG: route_entry_update_nhe(nhe)

    RIB->>NHG: rib_install_kernel(re)
    NHG->>NHG: zebra_nhg_install_kernel(re->nhe)
    NHG->>NHG: zebra_nhg_install_kernel(re->nhe_received)

    NHG->>DPlane: dplane_nexthop_add(nhe)
    DPlane->>DPlane: dplane_ctx_nexthop_init() builds nh_grp_full tree
    DPlane->>DPlane: sets skip_kernel if RECEIVED or nhg_fib+RECURSIVE

    DPlane->>NetLink: netlink_put_nexthop_update_msg()
    alt skip_kernel set
        NetLink-->>DPlane: FRR_NETLINK_SUCCESS (no kernel write)
    else normal NHG
        NetLink->>NetLink: netlink_batch_add_msg() to kernel
    end

    DPlane->>FPM: ctx with full NHG tree (nh_grp_full, nhg_flags, nhe_received_id)
    FPM->>FPM: reconstruct forwarding topology for SONiC FIB
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: zebra/zebra_rib.c
Line: 655

Comment:
**Unconditional NULL dereference on `re->nhe_received`**

`re->nhe_received` is only set when `route_entry_update_original_nhe()` is called (i.e. routes submitted with a received NHE). For the vast majority of routes it remains `NULL` (its default zero-initialised value in `route_entry`). `zebra_nhg_install_kernel()` immediately dereferences the pointer via `CHECK_FLAG(nhe->flags, ...)` at the very start of the function — no NULL guard exists there.

Every call to `rib_install_kernel()` for a normal route will crash the zebra process. A guard is required:

```suggestion
	zebra_nhg_install_kernel(re->nhe, re->type);
	if (re->nhe_received)
		zebra_nhg_install_kernel(re->nhe_received, re->type);
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: zebra/zebra_nhg.c
Line: 3592-3595

Comment:
**Missing `i++` for leaf nodes — array slots overwritten**

In the `else` branch (leaf NHG, i.e. `zebra_nhg_depends_is_empty(curr_node)` is true), after writing to `grp_full[i]` the index `i` is never incremented. Every subsequent leaf-level dependency will overwrite the same slot. For any NHG with two or more leaf siblings only the last one survives in the array, and `nh_grp_full_count` (the return value) will also be wrong.

Compare the non-leaf branch on line 3556 which correctly does `i++`. The fix:

```suggestion
			grp_full[i].id = curr_node->id;
			grp_full[i].weight = found ? nexthop->weight : 0;
			grp_full[i].num_direct = 0;
			i++;
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: zebra/zebra_nhg.h
Line: 28-32

Comment:
**`weight` field truncated from 16-bit to 8-bit**

`nh_grp` (the struct this mirrors) declares `weight` as `uint16_t`, and the existing `--nexthop-weight-16-bit` option allows weights up to 65535. Using `uint8_t` here silently truncates any weight above 255 when the FPM consumer reads `nh_grp_full`. Consider aligning with `nh_grp`:

```suggestion
struct nh_grp_full {
	uint32_t id;
	uint16_t weight;
	uint32_t num_direct;
};
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: zebra/zebra_nhg.c
Line: 3540-3550

Comment:
**`num_direct` undercounts queued sub-dependencies**

When computing `sub_depend_count` for `grp_full[i].num_direct`, the filter only accepts `NEXTHOP_GROUP_INSTALLED` nodes (line 3546). However, the outer loop (lines 3513-3515) deliberately includes `NEXTHOP_GROUP_QUEUED` nodes too. Any sub-dependency that is queued-but-not-yet-installed will be excluded from `num_direct` but present in the flat array, causing the count to mismatch the actual number of children written. The QUEUED check should be added for consistency:

```suggestion
				frr_each(nhg_connected_tree, &curr_node->nhg_depends, sub_rb_node) {
					/* Apply same filters as main loop */
					if (!is_srv6_nhg(sub_rb_node->nhe)
					    && !CHECK_FLAG(sub_rb_node->nhe->flags, NEXTHOP_GROUP_VALID))
						continue;
					if (!is_srv6_nhg(sub_rb_node->nhe)
					    && !(CHECK_FLAG(sub_rb_node->nhe->flags, NEXTHOP_GROUP_INSTALLED) ||
					         CHECK_FLAG(sub_rb_node->nhe->flags, NEXTHOP_GROUP_QUEUED)))
						continue;

					sub_depend_count++;
				}
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: zebra/zebra_nhg.c
Line: 3481-3483

Comment:
**`dep_scan_count` is computed but never used**

The variable `dep_scan_count` is populated by iterating through `nhe->nhg_depends` but is never referenced after that. This is dead code that adds an unnecessary full traversal of the tree on every call. It should be removed.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "zebra: Send enough NHG information to dp..." | Re-trigger Greptile

Comment thread zebra/zebra_rib.c Outdated
Comment thread zebra/zebra_nhg.c Outdated
Comment thread zebra/zebra_nhg.h
Comment thread zebra/zebra_nhg.c Outdated
Comment thread zebra/zebra_nhg.c Outdated
@github-actions

Copy link
Copy Markdown

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions Bot removed the conflicts label May 7, 2026
@GaladrielZhao GaladrielZhao force-pushed the ribfib branch 2 times, most recently from 56f9be2 to feb57f3 Compare May 7, 2026 08:11
@frrbot frrbot Bot added the bugfix label May 8, 2026
@GaladrielZhao GaladrielZhao force-pushed the ribfib branch 5 times, most recently from 495a37f to f090395 Compare May 12, 2026 10:49
@frrbot frrbot Bot added the tests Topotests, make check, etc label May 12, 2026
@github-actions github-actions Bot added size/XXL and removed size/L labels May 12, 2026
@GaladrielZhao GaladrielZhao force-pushed the ribfib branch 2 times, most recently from d1269f9 to 4dc1310 Compare May 12, 2026 15:01
@GaladrielZhao

This comment was marked as duplicate.

@GaladrielZhao GaladrielZhao force-pushed the ribfib branch 3 times, most recently from 20e9673 to 79535b7 Compare May 21, 2026 04:24
@GaladrielZhao

This comment was marked as duplicate.

@GaladrielZhao

Copy link
Copy Markdown
Contributor Author

ci:rerun

@GaladrielZhao GaladrielZhao force-pushed the ribfib branch 5 times, most recently from 2fab30c to 81e2e75 Compare May 27, 2026 08:22
@GaladrielZhao

Copy link
Copy Markdown
Contributor Author

ci:rerun

2 similar comments
@GaladrielZhao

Copy link
Copy Markdown
Contributor Author

ci:rerun

@eddieruan-alibaba

Copy link
Copy Markdown

ci:rerun

@GaladrielZhao

Copy link
Copy Markdown
Contributor Author

ci:rerun

2 similar comments
@GaladrielZhao

Copy link
Copy Markdown
Contributor Author

ci:rerun

@GaladrielZhao

Copy link
Copy Markdown
Contributor Author

ci:rerun

@GaladrielZhao GaladrielZhao force-pushed the ribfib branch 2 times, most recently from 70fc7f3 to 1c2e685 Compare June 1, 2026 12:05
@GaladrielZhao

Copy link
Copy Markdown
Contributor Author

Hi @mjstapp , the PR is ready for review. I’ve addressed all the comments from greptile and the CI sanity has passed.
Could you please help review it? Thanks!

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@mergify

mergify Bot commented Jun 19, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@GaladrielZhao GaladrielZhao force-pushed the ribfib branch 4 times, most recently from d02d4fe to 34cf5da Compare June 23, 2026 14:32
To support RIB/FIB use cases (e.g. SONiC FIB construction via FPM),
zebra needs to pass richer nexthop group information through the
dataplane and send additional NHG types that were previously skipped.

This commit makes the following changes:
  1 Extend dplane NHG context with full dependency information
  2 Introduce NEXTHOP_GROUP_RECEIVED flag and zebra_nhg_mark_received_flag()
  3 Add --nhg-fib command-line option to enable RIB/FIB mode

Signed-off-by: Yuqing Zhao <galadriel.zyq@alibaba-inc.com>
Signed-off-by: Yuqing Zhao <galadriel.zyq@alibaba-inc.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants