Skip to content

fix(netwatch): set SO_RECV_ANYIF on Apple UDP sockets - #248

Merged
matheus23 merged 2 commits into
n0-computer:mainfrom
structuresound:apple-so-recv-anyif
Oct 8, 2026
Merged

matheus23 merged 2 commits into
n0-computer:mainfrom
structuresound:apple-so-recv-anyif

Conversation

@1e1f

@1e1f 1e1f commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Closes #246.

On macOS and iOS, traffic arriving on a restricted interface such as AWDL (awdl0) is delivered only to sockets that set SO_RECV_ANYIF. This sets it in SocketState::bind on Apple targets, before the configure_socket hook runs. If setsockopt fails it's logged at debug and the bind carries on as before.

HUMAN NOTE:
multi-decade programmer, writing rust since 2022 -- but this is AI assisted work. I AM NOT a sockets guru -- so this COULD break something somewhere for anybody using #[cfg(target_vendor = "apple")]. it is a NOOP on all other platforms. probably the most controversial thing is do you want that "debug!("failed to set SO_RECV_ANYIF: {:?}", err);" line or not ;)

API Changes

None.

Notes & open questions

  • This adds unsafe to udp.rs: one libc::setsockopt call, plus a getsockopt in the test. As far as I can find, no safe API can set this option. libc, socket2 and nix don't define SO_RECV_ANYIF, so it's a local const (0x1104, from <sys/socket.h>). socket2's generic setsockopt is pub(crate), and nix's sockopt_impl! expands to the same unsafe block inside netwatch. Each block has a SAFETY comment. If you'd rather, I can upstream the constant to libc and a setter to socket2, so this becomes a safe call.
  • test_bind_sets_recv_anyif reads the option back through the configure-socket hook, for v4 and v6. Whether AWDL then delivers can't be checked on one machine. The manual check and before/after numbers are in Apple: sockets need SO_RECV_ANYIF to receive over AWDL (and link-local addresses are never advertised) #246.
  • End-to-end AWDL in iroh also needs the scope-id fix from IPv6 link-local addresses lose their scope id, so connecting to fe80::…%iface never completes iroh#4584. This PR doesn't depend on it and can merge first.
  • cargo make on macOS: fmt-check, check and test (30/30) pass. clippy and doc fail on warnings in interfaces/bsd.rs, a file this PR doesn't touch: unused_async on default_route (:40) and unclosed <must …> tags in a doc comment (:519–525). clippy fails the same way on main. I've left them alone here; happy to send a separate fix if you'd like.

Change checklist

  • Self-review.
  • Documentation updates following the style guide, if relevant.
  • Tests if relevant.
  • All API changes documented.
  • This PR was created by a human that thought critically about the
    proposed change and wrote an as clear and concise description as
    they could.
  • This PR isn't slop, and is carefully crafted to do have the
    intented effect.
  • cargo make passes locally.

@n0bot n0bot Bot added this to iroh Oct 7, 2026
Comment thread netwatch/src/udp.rs Outdated
@1e1f
1e1f force-pushed the apple-so-recv-anyif branch from cf28e57 to 9eefd3f Compare October 7, 2026 20:38

@matheus23 matheus23 left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The only thing that was concerning me about this PR was whether enabling this unconditionally could have bad side-effects, but it turns out there's lots of precedence in enabling this in widely used apple tools:

So that looks fine.
(The reason I looked into this was because in theory you could also implement this using the configure socket hook. But obviously that then would be opt-in.)

@matheus23

Copy link
Copy Markdown
Member

the most controversial thing is do you want that "debug!("failed to set SO_RECV_ANYIF: {:?}", err);" line or not ;)

This is the right pattern. It's also what a bunch of MacOS tools are doing, and debug is the correct level IMO.

@matheus23

Copy link
Copy Markdown
Member

If you'd rather, I can upstream the constant to libc and a setter to socket2, so this becomes a safe call.

I think we can merge as-is, but upstreaming could be worth either way? Seems like socket2 accepts PRs for options like these.

@matheus23
matheus23 added this pull request to the merge queue Oct 8, 2026
Merged via the queue into n0-computer:main with commit 87ad510 Oct 8, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

Apple: sockets need SO_RECV_ANYIF to receive over AWDL (and link-local addresses are never advertised)

3 participants