Skip to content

fix: correct IPv4 option wire format for SOME/IP-SD interop#239

Open
vtz wants to merge 2 commits intomainfrom
fix/sd-ipv4-option-interop-238
Open

fix: correct IPv4 option wire format for SOME/IP-SD interop#239
vtz wants to merge 2 commits intomainfrom
fix/sd-ipv4-option-interop-238

Conversation

@vtz
Copy link
Copy Markdown
Owner

@vtz vtz commented Apr 30, 2026

Summary

Fixes two spec violations in IPv4EndpointOption and IPv4MulticastOption that prevented interoperability with vsomeip (and any spec-compliant SOME/IP-SD implementation):

  • Length field: the open-someip-spec (feat_req_someipsd_129) mandates Length=0x0009 ("all bytes except length and type fields"). opensomeip wrote 8, counting only bytes after the full 4-byte header. The deserializer then rejected spec-compliant length=9 packets from vsomeip because the bounds check used the declared length after the base class had already consumed the Reserved byte it counts.
  • Byte order: ipv4_address_ stores addr.s_addr (NBO in memory). The shift-based serializer extracted integer MSB first, reversing IP bytes on the wire on little-endian hosts (e.g. 192.168.1.100 → wire bytes 64 01 A8 C0 instead of C0 A8 01 64). The same double-conversion bug affected port fields via redundant htons/ntohs calls around shifts.
  • IPv4MulticastOption: same byte-order and length bugs, plus a missing L4-Proto byte per spec and a pre-existing double-htons on multicast port in sd_server.cpp.

Both fixes are symmetric so opensomeip↔opensomeip traffic is unaffected. The deserializer accepts both length=8 (backward compat) and length=9 (spec-correct).

Closes #238

Test plan

  • 4 new interop tests added (TC_SD_INTEROP_001 through 004) that reproduce the exact failures from issue Interoperability with VSome/IP #238
  • Existing IPv4EndpointOptionSerialization test updated to verify spec-correct wire format
  • All 56 SD unit/integration tests pass
  • Full CTest suite (15 test suites) passes
  • Full project builds cleanly

Summary by CodeRabbit

Release Notes

  • New Features

    • Added protocol field support to IPv4 Multicast Options with getter and setter accessors.
  • Bug Fixes

    • Improved wire-format compliance for IPv4 endpoint and multicast option serialization/deserialization to align with specification requirements.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

IPv4 multicast and endpoint options in SD message serialization are restructured to include a Protocol field and adjust payload lengths to 9 bytes, aligning with AUTOSAR specifications and enabling interoperability with vsomeip. Public accessors and tests are updated accordingly.

Changes

IPv4 Option Wire-Format Update

Layer / File(s) Summary
Data Shape
include/sd/sd_message.h
IPv4MulticastOption gains public get_protocol() and set_protocol(uint8_t) methods, with private protocol_ field defaulting to 0x11.
Serialization / Deserialization
src/sd/sd_message.cpp
Both IPv4EndpointOption and IPv4MulticastOption serialize/deserialize with 9-byte payloads. IPv4 address now converted to host-order before wire transmission; Protocol field (0x11) and Reserved byte added; payload length enforced as 9 on deserialization.
Integration
src/sd/sd_server.cpp
Multicast option construction updated to use config_.multicast_address and config_.multicast_port directly without htons wrapper.
Tests / Validation
tests/test_sd.cpp
Expanded IPv4EndpointOptionSerialization to verify full 12-byte wire format (length, type, reserved, IPv4, protocol, port). Added comprehensive interop tests for wire-format compliance, round-trip verification, and vsomeip compatibility. Introduced integration test suite covering server/client lifecycle and service offer scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A protocol field hops into place,
Nine bytes now dance with proper grace,
Byte order fixed, no bytes reversed,
vsomeip's hand and ours, at last rehearsed!
The wire speaks truth in every byte. 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: fixing IPv4 option wire format for SOME/IP-SD interoperability.
Linked Issues check ✅ Passed All code requirements from issue #238 are addressed: bounds check fixed by correcting length field to 9, byte-order errors corrected in serialization/deserialization, both IPv4EndpointOption and IPv4MulticastOption updated, multicast port double-htons removed, and interop tests added.
Out of Scope Changes check ✅ Passed All changes directly address issue #238 objectives: header additions for protocol accessors, wire format serialization/deserialization fixes, multicast option configuration, and comprehensive interop testing with no extraneous modifications.
Docstring Coverage ✅ Passed Docstring coverage is 84.21% which is sufficient. The required threshold is 80.00%.

✏️ 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 fix/sd-ipv4-option-interop-238

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@vtz vtz force-pushed the fix/sd-ipv4-option-interop-238 branch 2 times, most recently from bf73352 to d1b7e99 Compare April 30, 2026 04:30
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3581bfa632

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/sd/sd_message.cpp Outdated
Comment on lines +372 to +373
constexpr size_t kPayloadBytes = 8;
if (offset + kPayloadBytes > data.size()) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Handle legacy IPv4Multicast length=7 payload correctly

The new backward-compatibility path for length_=7 is internally inconsistent: this function accepts 7-byte legacy multicast options, but immediately requires kPayloadBytes = 8 and later reads protocol+port as if the extra byte exists. For old opensomeip packets (which actually have 7 payload bytes), deserialization now either fails outright (offset + 8 > data.size()) or, when another option follows, can consume one byte from the next option and corrupt parsing. This breaks interoperability with the very legacy format the change says it supports.

Useful? React with 👍 / 👎.

Fix two spec violations in IPv4EndpointOption and IPv4MulticastOption
that prevented interoperability with vsomeip (and any spec-compliant
SOME/IP-SD implementation):

1. Length field: the spec (feat_req_someipsd_129) mandates Length=0x0009
   ("all bytes except length and type fields"). opensomeip wrote 8,
   counting only bytes after the full 4-byte header. The deserializer's
   bounds check then rejected spec-compliant length=9 packets because
   the base class had already consumed the Reserved byte.

2. Byte order: ipv4_address_ stores addr.s_addr (NBO in memory). The
   shift-based serializer extracted integer MSB first, reversing IP
   bytes on the wire on little-endian hosts. The same double-conversion
   bug affected port fields via redundant htons/ntohs calls.

Both fixes are symmetric so opensomeip-to-opensomeip traffic is
unaffected; the deserializer accepts both length=8 (backward compat)
and length=9 (spec-correct).

Also fixes IPv4MulticastOption: adds missing L4-Proto byte per spec,
corrects length (7→9) and byte order, and removes a pre-existing
double-htons on multicast port in sd_server.cpp.

Closes #238

Made-with: Cursor
@vtz vtz force-pushed the fix/sd-ipv4-option-interop-238 branch from d1b7e99 to b47a0f4 Compare April 30, 2026 04:40
@vtz
Copy link
Copy Markdown
Owner Author

vtz commented Apr 30, 2026

Re: review comment on legacy length=7 path — Good catch, but this is moot now. The backward-compat path has been removed entirely in the latest push: both options now strictly enforce length_ == 9 per spec. This is a breaking change for any pre-fix opensomeip↔opensomeip traffic, but that is intentional.

Simulates the exact wire bytes vsomeip sends for a server at
10.10.10.20:30510/UDP and verifies opensomeip deserializes the
address and port correctly. Also confirms opensomeip's serialized
output is byte-for-byte identical to vsomeip's wire format.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown
Contributor

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

Caution

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

⚠️ Outside diff range comments (3)
include/sd/sd_message.h (1)

168-172: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Stale/missing byte-order comments on port_ after the wire-format fix.

The pre-existing comment on line 171 still says // Port in network byte order, but after this PR the serialize/deserialize paths require port_ to be in host byte order (shifts produce NBO wire bytes directly from port_, and tests assert option.set_port(30509) followed by EXPECT_EQ(get_port(), 30509) round-trip without swapping). The new port_ on IPv4MulticastOption (line 196) follows the same convention but has no comment at all. Leaving these as-is invites a future contributor to "fix" callers by pre-swapping with htons and silently reintroduce the bug this PR removed from sd_server.cpp.

📝 Proposed comment fix
 private:
     uint8_t protocol_{0};      // 0x06 = TCP, 0x11 = UDP
     uint32_t ipv4_address_{0}; // IPv4 address in network byte order
-    uint16_t port_{0};         // Port in network byte order
+    uint16_t port_{0};         // Port in host byte order (serializer writes NBO on the wire)
 };
 private:
     uint32_t ipv4_address_{0}; // IPv4 address in network byte order
     uint8_t protocol_{0x11};   // L4 protocol (default UDP per spec)
-    uint16_t port_{0};
+    uint16_t port_{0};         // Port in host byte order (serializer writes NBO on the wire)
 };

Also applies to: 193-197

🤖 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 `@include/sd/sd_message.h` around lines 168 - 172, The comments on the member
port_ in the IPv4Option (port_) and IPv4MulticastOption classes are stale: port_
is stored in host byte order (serialize/deserialize perform shifts to produce
network bytes) so callers must not pre-swap with htons/ntohs; update the inline
comments for port_ (and add one for IPv4MulticastOption::port_) to state "Port
in host byte order (serialized as network byte order)" and mention that
set_port/get_port use/return host-order values to prevent future callers from
applying htons/ntohs erroneously. Ensure references to set_port/get_port are
noted in the comment so reviewers immediately see the intended API contract.
src/sd/sd_message.cpp (1)

288-297: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Warning prints octets from the NBO value instead of host order — inconsistent with the multicast path.

Inside the validation block, the shifts operate on ipv4_address_, which after this PR holds the NBO (s_addr) value. On a little-endian host, that prints octets in reversed order. The bug is currently latent only because the predicate fires solely on 0 and 0xFFFFFFFF (both palindromic), but the multicast counterpart at lines 386-389 already does the right thing using the local host_addr — these two near-identical blocks should match so a future loosening of the predicate (e.g. to log loopback or non-routable addresses) doesn't suddenly emit reversed dotted-quads.

🔧 Proposed fix — use the already-computed `host_addr`
     // Validate IP address (REQ_SD_064_E01)
     if (ipv4_address_ == 0 || ipv4_address_ == 0xFFFFFFFFU) {
         std::cout << "Warning: Invalid IP address in endpoint option: "
-                  << ((ipv4_address_ >> 24U) & 0xFFU) << "."
-                  << ((ipv4_address_ >> 16U) & 0xFFU) << "."
-                  << ((ipv4_address_ >> 8U) & 0xFFU) << "."
-                  << (ipv4_address_ & 0xFFU) << '\n';
+                  << ((host_addr >> 24U) & 0xFFU) << "."
+                  << ((host_addr >> 16U) & 0xFFU) << "."
+                  << ((host_addr >> 8U) & 0xFFU) << "."
+                  << (host_addr & 0xFFU) << '\n';
         // Continue processing despite invalid address
     }
🤖 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/sd/sd_message.cpp` around lines 288 - 297, The warning prints IP octets
from the NBO-stored ipv4_address_ (reversed on little-endian hosts); update the
validation block that checks ipv4_address_ (the "Validate IP address
(REQ_SD_064_E01)" section) to print the already-computed host-order value
host_addr instead of ipv4_address_ so it matches the multicast block
behavior—locate the block referencing ipv4_address_ and replace the octet
extraction to use host_addr when formatting the dotted-quad.
tests/test_sd.cpp (1)

636-643: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Multicast wire-format test is too weak to lock in this PR's fixes.

IPv4MulticastOptionSerialization only asserts serialized.size() > 0u. The PR changes the multicast option in three observable ways — payload length 7→9, address now written in NBO, new Protocol byte (default 0x11), plus the dependent sd_server.cpp removal of someip_htons on the multicast port — none of which are exercised here. A regression on any of them (e.g. someone re-introducing htons on the port, or a builder forgetting to set protocol) would still pass this test.

Recommend adding a byte-for-byte assertion mirroring IPv4EndpointOptionSerialization so the multicast path has the same regression guard as the endpoint path.

🧪 Proposed test expansion
 TEST_F(SdTest, IPv4MulticastOptionSerialization) {
     IPv4MulticastOption original;
     original.set_ipv4_address(0xEFFFFFFB);  // 239.255.255.251 (host-order int)
     original.set_port(30490);

     auto serialized = original.serialize();
-    EXPECT_GT(serialized.size(), 0u);
+    // Length(2) + Type(1) + Reserved(1) + IPv4(4) + Reserved(1) + Proto(1) + Port(2) = 12
+    ASSERT_EQ(serialized.size(), 12u);
+
+    // Length = 0x0009 per spec
+    EXPECT_EQ(serialized[0], 0x00);
+    EXPECT_EQ(serialized[1], 0x09);
+    // Type = 0x14 (IPv4 multicast)
+    EXPECT_EQ(serialized[2], 0x14);
+    EXPECT_EQ(serialized[3], 0x00);  // Reserved
+    // 239.255.255.251 in NBO
+    EXPECT_EQ(serialized[4], 0xEF);
+    EXPECT_EQ(serialized[5], 0xFF);
+    EXPECT_EQ(serialized[6], 0xFF);
+    EXPECT_EQ(serialized[7], 0xFB);
+    EXPECT_EQ(serialized[8], 0x00);  // Reserved
+    EXPECT_EQ(serialized[9], 0x11);  // Default protocol = UDP
+    // Port 30490 in NBO = 0x771A
+    EXPECT_EQ(serialized[10], 0x77);
+    EXPECT_EQ(serialized[11], 0x1A);
 }
🤖 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 `@tests/test_sd.cpp` around lines 636 - 643, Update the TEST_F named
IPv4MulticastOptionSerialization to assert the full serialized byte sequence
instead of only size: build an expected byte vector for IPv4MulticastOption that
reflects the new wire format (payload length 9, IPv4 address in NBO, protocol
byte 0x11, and port in network byte order) and compare it against
original.serialize(); use the IPv4MulticastOption methods (set_ipv4_address,
set_port and any protocol setter if present) and the serialize() result to
perform a byte-for-byte equality check to guard against regressions like
reintroducing htons or omitting the protocol byte.
🤖 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.

Outside diff comments:
In `@include/sd/sd_message.h`:
- Around line 168-172: The comments on the member port_ in the IPv4Option
(port_) and IPv4MulticastOption classes are stale: port_ is stored in host byte
order (serialize/deserialize perform shifts to produce network bytes) so callers
must not pre-swap with htons/ntohs; update the inline comments for port_ (and
add one for IPv4MulticastOption::port_) to state "Port in host byte order
(serialized as network byte order)" and mention that set_port/get_port
use/return host-order values to prevent future callers from applying htons/ntohs
erroneously. Ensure references to set_port/get_port are noted in the comment so
reviewers immediately see the intended API contract.

In `@src/sd/sd_message.cpp`:
- Around line 288-297: The warning prints IP octets from the NBO-stored
ipv4_address_ (reversed on little-endian hosts); update the validation block
that checks ipv4_address_ (the "Validate IP address (REQ_SD_064_E01)" section)
to print the already-computed host-order value host_addr instead of
ipv4_address_ so it matches the multicast block behavior—locate the block
referencing ipv4_address_ and replace the octet extraction to use host_addr when
formatting the dotted-quad.

In `@tests/test_sd.cpp`:
- Around line 636-643: Update the TEST_F named IPv4MulticastOptionSerialization
to assert the full serialized byte sequence instead of only size: build an
expected byte vector for IPv4MulticastOption that reflects the new wire format
(payload length 9, IPv4 address in NBO, protocol byte 0x11, and port in network
byte order) and compare it against original.serialize(); use the
IPv4MulticastOption methods (set_ipv4_address, set_port and any protocol setter
if present) and the serialize() result to perform a byte-for-byte equality check
to guard against regressions like reintroducing htons or omitting the protocol
byte.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 668e0dec-60eb-481b-afdb-c989504deae9

📥 Commits

Reviewing files that changed from the base of the PR and between 85b4c91 and 9cca58c.

📒 Files selected for processing (4)
  • include/sd/sd_message.h
  • src/sd/sd_message.cpp
  • src/sd/sd_server.cpp
  • tests/test_sd.cpp

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.

Interoperability with VSome/IP

1 participant