Skip to content

route: Rework rt structure so sockaddrs are pointers#621

Merged
rsmarples merged 1 commit into
masterfrom
rt
Jun 3, 2026
Merged

route: Rework rt structure so sockaddrs are pointers#621
rsmarples merged 1 commit into
masterfrom
rt

Conversation

@rsmarples
Copy link
Copy Markdown
Member

For each sockaddr, put a sockaddr_storage in the rt and reference it.
This removes the need of a union and the macro dance around it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

Walkthrough

This PR refactors the struct rt to use explicit pointer fields to per-field sockaddr_storage, adds rt_init/rt_copy helpers, renames the route-table init API to rt_init_routes(), and updates all route construction, parsing, and option-decoding sites to use the new pointer-based access.

Changes

Route Structure and Initialization Refactoring

Layer / File(s) Summary
Core struct layout and initialization infrastructure
src/route.h, src/route.c, src/sa.h, src/sa.c, src/dhcpcd.c
struct rt now uses explicit struct sockaddr * pointers (rt_dest, rt_netmask, rt_gateway, rt_ifa) pointing to corresponding struct sockaddr_storage backing members. union sa_ss is removed from public headers. New helpers rt_init(rt) and rt_copy(dst, src) initialize route pointers via internal rt_setup_sa() wiring. Public API rt_init(ctx) is renamed to rt_init_routes(ctx). Debug validation in sa_toprefix() updated to use sockaddr_storage instead of union. Main startup calls new rt_init_routes(&ctx).
Route table operations with pointer-based fields
src/route.c
Route comparison, logging, insertion, and filtering functions updated to dereference pointer-based socket fields. Changes in rt_cmp_netmask, rt_cmp_dest, rt_is_default, rt_desc, rt_headclear0, rt_recvrt, rt_add, rt_cmp, rt_doroute, and rt_build now use sa_cmp(), sa_is_unspecified(), sa_addrtop() directly on rt->rt_dest, rt->rt_gateway, rt->rt_ifa pointers instead of taking addresses.
BSD route parsing and message construction
src/if-bsd.c
Routes are initialized via rt_init(rt) in if_copyrt() and copied via rt_copy(rtn, &rt) in if_initrt(). Route message assembly in if_route() uses pointer-based rt->rt_* fields. IPv6 neighbor notifications in if_rtm() access destination family and address via pointers.
Linux netlink route parsing and attributes
src/if-linux.c
if_copyrt() initializes routes via rt_init(rt) and uses struct sockaddr_storage for RTA_SRC temporary storage. Netlink attribute processing sets gateway/ifa pointers directly to backing storage. if_route() derives message family from rt->rt_dest->sa_family and builds attributes using pointer-based fields. if_initrt() copies routes via rt_copy().
Solaris route parsing and walkers
src/if-sun.c
if_copyrt() initializes via rt_init(rt) and copies addresses using pointer-based targets. if_route0() computes gateway flags and assembles messages using pointer-based rt->rt_* fields. if_rtm() accesses destination family via pointer for IPv6 neighbor updates. Route walkers if_walkrt()/if_walkrt6() initialize routes via rt_init() and duplicate via rt_copy().
IPv4 route construction and host routes
src/ipv4.c
inet_dhcproutes() calls sa_in_init() with pointer-based destination/netmask/gateway/ifa arguments and sets gateway family via pointer. inet_routerhostroute() updates address comparisons and unspecified checks to use pointers, and initializes generated host routes via sa_in_init() with pointer arguments.
IPv4LL and IPv6 route construction
src/ipv4ll.c, src/ipv6.c
ipv4ll_subnetroute() and ipv4ll_defaultroute() call sa_in_init() with pointer-based rt->rt_* arguments. inet6_makeprefix(), inet6_makerouter(), and inet6_raroutes() update sa_in6_init() calls to use pointer-based fields. ipv4ll_recvrt() checks unspecified status using pointer-based destination.
Static route option parsing and DHCP decoding
src/if-options.c, src/dhcp.c
parse_option() for -S routes= and -S routers= calls sa_in_init() with pointer-based route fields. decode_rfc3442_rt() and get_option_routes() initialize routes using pointer-based socket fields instead of taking addresses.

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main refactoring: converting socket address fields in the rt structure from inline values to pointers backed by sockaddr_storage.
Description check ✅ Passed The description directly relates to the changeset, explaining the core motivation: storing sockaddr_storage in rt and referencing it via pointers, eliminating the union and associated macro complexity.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rt

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Infer (1.2.0)
src/dhcpcd.c

In file included from src/dhcpcd.c:53:
In file included from src/arp.h:44:
In file included from src/bpf.h:55:
src/dhcpcd.h:38:10: fatal error: 'config.h' file not found
38 | #include "config.h"
| ^~~~~~~~~~
1 error generated.
Error: the following clang command did not run successfully:
/opt/infer-linux-x86_64-v1.2.0/lib/infer/facebook-clang-plugins/clang/install/bin/clang-18
@/tmp/coderabbit-infer/d3147d3d00bfd98ec1d9ccf543aab3b8da08b9ad-5ba67626cc214a1d/tmp/clang_command_.tmp.0d212e.txt
++Contents of '/tmp/coderabbit-infer/d3147d3d00bfd98ec1d9ccf543aab3b8da08b9ad-5ba67626cc214a1d/tmp/clang_command_.tmp.0d212e.txt':
"-cc1" "-load"
"/opt/infer-linux-x86_64-v1.2.0/lib/infer/infer/bin/../../facebook-clang-plugins/libtooling/build/FacebookClangPlugin.dylib"
"-add-plugin" "BiniouASTExporter" "-plugin-arg-BiniouASTExporter" "-"
"-plugin-arg-BiniouASTExporter" "PREPEND_CURRENT_DIR=1"
"-plugin-arg-BiniouASTExporter" "MAX_STRING_SIZE=65535" "-cc1

... [truncated 769 characters] ...

"/opt/infer-linux-x86_64-v1.2.0/lib/infer/facebook-clang-plugins/clang/install/lib/clang/18/include"
"-internal-isystem" "/usr/local/include" "-internal-isystem"
"/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include"
"-internal-externc-isystem" "/usr/include/x86_64-linux-gnu"
"-internal-externc-isystem" "/include" "-internal-externc-isystem"
"/usr/include" "-Wno-ignored-optimization-argument" "-Wno-everything"
"-ferror-limit" "19" "-fgnuc-version=4.2.1" "-fskip-odr-check-in-gmf"
"-D__GCC_HAVE_DWARF2_CFI_ASM=1" "-o"
"/tmp/coderabbit-infer/5ba67626cc214a1d/file.o" "-x" "c" "src/dhcpcd.c"
"-O0" "-fno-builtin" "-include"
"/opt/infer-linux-x86_64-v1.2.0/lib/infer/infer/bin/../lib/clang_wrappers/global_defines.h"
"-Wno-everything"
src/dhcp.c

In file included from src/dhcp.c:63:
In file included from src/arp.h:44:
In file included from src/bpf.h:55:
src/dhcpcd.h:38:10: fatal error: 'config.h' file not found
38 | #include "config.h"
| ^~~~~~~~~~
1 error generated.
Error: the following clang command did not run successfully:
/opt/infer-linux-x86_64-v1.2.0/lib/infer/facebook-clang-plugins/clang/install/bin/clang-18
@/tmp/coderabbit-infer/d3147d3d00bfd98ec1d9ccf543aab3b8da08b9ad-563d56800f880f7c/tmp/clang_command_.tmp.b0b670.txt
++Contents of '/tmp/coderabbit-infer/d3147d3d00bfd98ec1d9ccf543aab3b8da08b9ad-563d56800f880f7c/tmp/clang_command_.tmp.b0b670.txt':
"-cc1" "-load"
"/opt/infer-linux-x86_64-v1.2.0/lib/infer/infer/bin/../../facebook-clang-plugins/libtooling/build/FacebookClangPlugin.dylib"
"-add-plugin" "BiniouASTExporter" "-plugin-arg-BiniouASTExporter" "-"
"-plugin-arg-BiniouASTExporter" "PREPEND_CURRENT_DIR=1"
"-plugin-arg-BiniouASTExporter" "MAX_STRING_SIZE=65535" "-cc1"

... [truncated 763 characters] ...

m"
"/opt/infer-linux-x86_64-v1.2.0/lib/infer/facebook-clang-plugins/clang/install/lib/clang/18/include"
"-internal-isystem" "/usr/local/include" "-internal-isystem"
"/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include"
"-internal-externc-isystem" "/usr/include/x86_64-linux-gnu"
"-internal-externc-isystem" "/include" "-internal-externc-isystem"
"/usr/include" "-Wno-ignored-optimization-argument" "-Wno-everything"
"-ferror-limit" "19" "-fgnuc-version=4.2.1" "-fskip-odr-check-in-gmf"
"-D__GCC_HAVE_DWARF2_CFI_ASM=1" "-o"
"/tmp/coderabbit-infer/563d56800f880f7c/file.o" "-x" "c" "src/dhcp.c"
"-O0" "-fno-builtin" "-include"
"/opt/infer-linux-x86_64-v1.2.0/lib/infer/infer/bin/../lib/clang_wrappers/global_defines.h"
"-Wno-everything"

src/if-bsd.c

src/if-bsd.c:34:10: fatal error: 'sys/sysctl.h' file not found
34 | #include <sys/sysctl.h>
| ^~~~~~~~~~~~~~
1 error generated.
Error: the following clang command did not run successfully:
/opt/infer-linux-x86_64-v1.2.0/lib/infer/facebook-clang-plugins/clang/install/bin/clang-18
@/tmp/coderabbit-infer/d3147d3d00bfd98ec1d9ccf543aab3b8da08b9ad-eaf3836ffed8dbda/tmp/clang_command_.tmp.c2b4bf.txt
++Contents of '/tmp/coderabbit-infer/d3147d3d00bfd98ec1d9ccf543aab3b8da08b9ad-eaf3836ffed8dbda/tmp/clang_command_.tmp.c2b4bf.txt':
"-cc1" "-load"
"/opt/infer-linux-x86_64-v1.2.0/lib/infer/infer/bin/../../facebook-clang-plugins/libtooling/build/FacebookClangPlugin.dylib"
"-add-plugin" "BiniouASTExporter" "-plugin-arg-BiniouASTExporter" "-"
"-plugin-arg-BiniouASTExporter" "PREPEND_CURRENT_DIR=1"
"-plugin-arg-BiniouASTExporter" "MAX_STRING_SIZE=65535" "-cc1" "-triple"
"x86_64-unknown-linux-gnu" "-emit-obj" "-mrelax-all" "-disable-free"
"-clear-as

... [truncated 670 characters] ...

"/opt/infer-linux-x86_64-v1.2.0/lib/infer/facebook-clang-plugins/clang/install/lib/clang/18/include"
"-internal-isystem" "/usr/local/include" "-internal-isystem"
"/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include"
"-internal-externc-isystem" "/usr/include/x86_64-linux-gnu"
"-internal-externc-isystem" "/include" "-internal-externc-isystem"
"/usr/include" "-Wno-ignored-optimization-argument" "-Wno-everything"
"-ferror-limit" "19" "-fgnuc-version=4.2.1" "-fskip-odr-check-in-gmf"
"-D__GCC_HAVE_DWARF2_CFI_ASM=1" "-o"
"/tmp/coderabbit-infer/eaf3836ffed8dbda/file.o" "-x" "c" "src/if-bsd.c"
"-O0" "-fno-builtin" "-include"
"/opt/infer-linux-x86_64-v1.2.0/lib/infer/infer/bin/../lib/clang_wrappers/global_defines.h"
"-Wno-everything"
  • 8 others

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
Copy Markdown

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/ipv4.c (1)

324-329: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use rt_copy instead of raw memcpy.

Line 328 is no longer safe after the struct rt refactor. memcpy(rt, r, sizeof(*rt)) copies rt_dest/rt_netmask/rt_gateway/rt_ifa as-is, so this clone keeps pointing at r's rt_ss_* storage instead of its own. The later sa_in_init(rt->rt_ifa, ...) in this function then mutates the source route, and the copied route will dangle once the source tree is freed. Use the new route copy helper here.

Suggested fix
-			memcpy(rt, r, sizeof(*rt));
+			rt_copy(rt, r);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ipv4.c` around lines 324 - 329, Replace the raw memcpy(rt, r,
sizeof(*rt)) with the route copy helper: allocate the new route via
rt_new0(ifp->ctx) as you do, then call rt_copy(rt, r) (checking its return for
error if rt_copy returns non-zero/NULL) to duplicate all internal sa storage
safely, and then call rt_setif(rt, ifp); remove the memcpy line and any code
that assumes the memcpy behavior (e.g., do not rely on shared rt_ss_* storage);
keep the surrounding checks (sa_is_unspecified, rt_new0) as-is.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/ipv4ll.c`:
- Around line 512-513: The callback currently returns early when
sa_is_unspecified(rt->rt_dest) is true, which incorrectly skips handling the
default-route case; invert the check so the function returns early only for
non-default routes by negating the sa_is_unspecified(rt->rt_dest) condition
(i.e., return 0 when rt->rt_dest is NOT unspecified) so the default-route
updates are processed while unrelated route updates are ignored.
- Line 120: Replace the invalid designated initializer for struct in_addr by
using the correct syntax (use .s_addr = INADDR_ANY) where the variable `in` is
declared; in `ipv4ll_recvrt()` invert the default-route early-return so the
function does not skip processing when `rt_dest` is unspecified, and
additionally gate this check by testing that `rt_netmask` is also unspecified
(or call/check `rt_is_default()`), ensuring default-route entries created by
`ipv4ll_defaultroute()` cause the route table to be rebuilt rather than being
ignored.

---

Outside diff comments:
In `@src/ipv4.c`:
- Around line 324-329: Replace the raw memcpy(rt, r, sizeof(*rt)) with the route
copy helper: allocate the new route via rt_new0(ifp->ctx) as you do, then call
rt_copy(rt, r) (checking its return for error if rt_copy returns non-zero/NULL)
to duplicate all internal sa storage safely, and then call rt_setif(rt, ifp);
remove the memcpy line and any code that assumes the memcpy behavior (e.g., do
not rely on shared rt_ss_* storage); keep the surrounding checks
(sa_is_unspecified, rt_new0) as-is.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b70e511b-c24c-4860-abd6-e0be11b3f0ef

📥 Commits

Reviewing files that changed from the base of the PR and between 4bbe25e and 5d1e774.

📒 Files selected for processing (13)
  • src/dhcp.c
  • src/dhcpcd.c
  • src/if-bsd.c
  • src/if-linux.c
  • src/if-options.c
  • src/if-sun.c
  • src/ipv4.c
  • src/ipv4ll.c
  • src/ipv6.c
  • src/route.c
  • src/route.h
  • src/sa.c
  • src/sa.h
💤 Files with no reviewable changes (1)
  • src/sa.h

Comment thread src/ipv4ll.c Outdated
Comment thread src/ipv4ll.c Outdated
For each sockaddr, put a sockaddr_storage in the rt and
reference it.
This removes the need of a union and the macro dance
around it.
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
src/sa.c (1)

284-290: 💤 Low value

Static analyzer false positive on sockaddr_storage initialization.

Cppcheck flags line 288 for an uninitialized variable, but the code is correct: C99 designated initializers zero-fill all unspecified members, so the entire ss structure except ss_family is zeroed. The subsequent sa_fromprefix call will populate the address bytes and (if HAVE_SA_LEN is defined) set sa_len.

If you want to silence the static analyzer warning explicitly, consider:

📝 Optional: more explicit initialization
-	struct sockaddr_storage ss = { .ss_family = sa->sa_family };
-	struct sockaddr *ss_sa = (struct sockaddr *)&ss;
+	struct sockaddr_storage ss;
+	struct sockaddr *ss_sa = (struct sockaddr *)&ss;
 
+	memset(&ss, 0, sizeof(ss));
+	ss.ss_family = sa->sa_family;
 	sa_inprefix = true;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/sa.c` around lines 284 - 290, Cppcheck reports a false positive for
uninitialized bytes in struct sockaddr_storage `ss` despite using a designated
initializer; to silence this, explicitly zero the structure before setting the
family: replace the `struct sockaddr_storage ss = { .ss_family = sa->sa_family
};` pattern with an explicit zero-init (e.g., `struct sockaddr_storage ss;
memset(&ss, 0, sizeof ss); ss.ss_family = sa->sa_family;`) so `sa_fromprefix`,
`sa_cmp`, and the `ss` usage are unambiguously initialized; alternatively, if
you prefer suppressing the analyzer, add a localized cppcheck suppression
comment near the `ss` declaration (e.g., `/* cppcheck-suppress uninitvar */`)
referencing `ss`, but do not change the subsequent calls to `sa_fromprefix`,
`sa_cmp`, or the `sa_inprefix` toggling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/sa.c`:
- Around line 284-290: Cppcheck reports a false positive for uninitialized bytes
in struct sockaddr_storage `ss` despite using a designated initializer; to
silence this, explicitly zero the structure before setting the family: replace
the `struct sockaddr_storage ss = { .ss_family = sa->sa_family };` pattern with
an explicit zero-init (e.g., `struct sockaddr_storage ss; memset(&ss, 0, sizeof
ss); ss.ss_family = sa->sa_family;`) so `sa_fromprefix`, `sa_cmp`, and the `ss`
usage are unambiguously initialized; alternatively, if you prefer suppressing
the analyzer, add a localized cppcheck suppression comment near the `ss`
declaration (e.g., `/* cppcheck-suppress uninitvar */`) referencing `ss`, but do
not change the subsequent calls to `sa_fromprefix`, `sa_cmp`, or the
`sa_inprefix` toggling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2cd21565-ae64-4cbb-852e-e44a315bf1a2

📥 Commits

Reviewing files that changed from the base of the PR and between 5d1e774 and d3147d3.

📒 Files selected for processing (13)
  • src/dhcp.c
  • src/dhcpcd.c
  • src/if-bsd.c
  • src/if-linux.c
  • src/if-options.c
  • src/if-sun.c
  • src/ipv4.c
  • src/ipv4ll.c
  • src/ipv6.c
  • src/route.c
  • src/route.h
  • src/sa.c
  • src/sa.h
💤 Files with no reviewable changes (1)
  • src/sa.h
🚧 Files skipped from review as they are similar to previous changes (11)
  • src/dhcpcd.c
  • src/ipv6.c
  • src/dhcp.c
  • src/if-linux.c
  • src/ipv4ll.c
  • src/if-options.c
  • src/route.h
  • src/ipv4.c
  • src/if-bsd.c
  • src/if-sun.c
  • src/route.c

@rsmarples rsmarples merged commit 7cb7fe5 into master Jun 3, 2026
6 checks passed
@rsmarples rsmarples deleted the rt branch June 3, 2026 10:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant