Skip to content

Socket: send pktinfo with the source address in the right field - #243

Open
lhoward wants to merge 1 commit into
swhitty:mainfrom
PADL:pktinfo-source-address
Open

lhoward wants to merge 1 commit into
swhitty:mainfrom
PADL:pktinfo-source-address

Conversation

@lhoward

@lhoward lhoward commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Socket.withPacketInfoControl sends IPv4 datagrams with an uninitialised source address. On Linux, sendMessage_WithPacketInfo_RoundTripsDatagram() then fails with SendMsg errno 101 (Network is unreachable). Whether it fails depends on what happens to be in memory, so it passes on some builds and fails on others:

Cause

The in_pktinfo and in6_pktinfo are built in the element storage of a ManagedBuffer, which starts out uninitialised.

  1. IPv4 source in the wrong field. The local address is written to ipi_addr, and ipi_spec_dst is never set. ip(7): "If IP_PKTINFO is passed to sendmsg(2) and ipi_spec_dst is not zero, then it is used as the local source address for the routing table lookup". ipi_addr is the destination of a received packet. So the source used is whatever was in memory.
  2. The address is read from the wrong bytes. It's read by reinterpreting the bytes of the any SocketAddress existential as a sockaddr_in or sockaddr_in6. Those bytes are the address only when the value is stored inline in the existential. A sockaddr_in6 (28 bytes) or a sockaddr_storage is boxed, so what gets read is the box. This affects every IPv6 send with a local address.

Changes

  • Zero-initialise the in_pktinfo or in6_pktinfo.
  • Read the address through makeStorage(), checking its family.
  • Put an IPv4 source in ipi_spec_dst. For IPv6, ipi6_addr was already the right field (RFC 3542 §6.1).

Tests

Two new unit tests read back the control message that's built, with the address passed both as a plain value and as a boxed sockaddr_storage:

  • withPacketInfoControl_IP4_SetsSourceAddressInSpecDst: checks ipi_spec_dst is the source address, ipi_addr is 0, and ipi_ifindex is set. Without this change it fails: ipi_spec_dst is 0, and ipi_addr is 127.0.0.1 for the plain value but garbage for the boxed one.
  • withPacketInfoControl_IP6_SetsSourceAddress: checks ipi6_addr is the source address. Without this change it fails: ipi6_addr holds the existential's box pointer rather than ::1.

Test plan:

  • macOS (Swift 6.3.3): full suite, 474 tests pass
  • Linux (x86_64, Swift 6.3.3): the pktinfo unit tests and both sendMessage_WithPacketInfo… round trips pass in 10 of 10 runs. On the same machine and toolchain, sendMessage_WithPacketInfo_RoundTripsDatagram failed 10 of 10 without this change.

🤖 Generated with Claude Code

https://claude.ai/code/session_012Fq7ie1LzJERrh9uRuZZWE

withPacketInfoControl built the in_pktinfo or in6_pktinfo in the element
storage of a ManagedBuffer, which starts out uninitialised, and:

- For IPv4 it wrote the local address to ipi_addr and never set
  ipi_spec_dst, the field ip(7) says sendmsg(2) takes the source address
  from. So the source was whatever was in memory, and sendmsg could fail
  with ENETUNREACH, as sendMessage_WithPacketInfo_RoundTripsDatagram does
  on some Linux builds.
- It read the address by reinterpreting the bytes of the any SocketAddress
  existential. Those bytes are the address only when the value is held
  inline: a sockaddr_in6 or sockaddr_storage is boxed, so what was read
  was the box.

Zero the structure, read the address from makeStorage(), and put an IPv4
source in ipi_spec_dst.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Fq7ie1LzJERrh9uRuZZWE
@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.73%. Comparing base (bd32d43) to head (49356ee).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #243      +/-   ##
==========================================
+ Coverage   93.72%   93.73%   +0.01%     
==========================================
  Files          72       72              
  Lines        3777     3785       +8     
==========================================
+ Hits         3540     3548       +8     
  Misses        237      237              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

swhitty pushed a commit that referenced this pull request Oct 3, 2026
Apply the proposed fix from #243 (49356ee) for Linux CI verification.
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