Repository navigation
Add NetworkConfiguration::add_port_forwarding_rule - #12
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| ) -> VmnetReturnT; | ||
| // internal_port comes before external_port here, unlike in | ||
| // vmnet_interface_add_ip_port_forwarding_rule | ||
| pub fn vmnet_network_configuration_add_port_forwarding_rule( |
There was a problem hiding this comment.
Declared exactly as in the macOS 27 SDK's vmnet.h (available since macOS 26): (config, uint8_t protocol, sa_family_t address_family, uint16_t internal_port, uint16_t external_port, void const *internal_address). sa_family_t is __uint8_t on macOS, hence u8.
The port order is internal-then-external, the reverse of vmnet_interface_add_ip_port_forwarding_rule. That's what the comment above is for.
| Ok(()) | ||
| } | ||
|
|
||
| /// Adds a port forwarding rule to the network. |
There was a problem hiding this comment.
The doc only covers what a caller needs: add rules before Network::new, and target an address inside the network's subnet.
The macOS 27.0.1 observation that listing and removing through the interface also fail is in the PR description rather than here, because Apple documents those calls as working.
| /// known address. | ||
| /// | ||
| /// [`Interface::port_forwarding_rule_add`]: crate::Interface::port_forwarding_rule_add | ||
| pub fn add_port_forwarding_rule( |
There was a problem hiding this comment.
Same arguments, order and AddressFamilyMismatch check as Interface::port_forwarding_rule_add, so the two read the same to callers. The call below maps them onto the C function's internal-before-external order.
| } | ||
|
|
||
| #[test] | ||
| fn port_forwarding_rule_address_family_mismatch() { |
There was a problem hiding this comment.
Covers both mismatch directions. The check runs before any FFI call, so this test doesn't create a network.
| .add_port_forwarding_rule(AddressFamily::Ipv4, Protocol::Tcp, 8080, addr, 80) | ||
| .unwrap(); | ||
|
|
||
| // Create the network and attach an interface. The interface port |
There was a problem hiding this comment.
This test can't read the rules back, because port_forwarding_rules returns Failure on an interface attached to a network. So it checks that configure → create network → attach interface succeeds with rules present.
The forwarding itself was verified end to end through softnet: an rdr rule appears for the port, and traffic reaches the guest.
TL;DR
Adds
NetworkConfiguration::add_port_forwarding_rule, a binding for macOS 26'svmnet_network_configuration_add_port_forwarding_rule. On a network created withNetwork::new, it's the only way I found to forward a host port to a guest: the interface-level forwarding calls fail there. softnet needs it to fix--expose(openai/softnet#222).Details
The problem. For an interface attached to a network (
Interface::with_network),Interface::port_forwarding_rule_addreturnsVmnetErrorStatus(Failure). On macOS 27.0.1 the same happens forport_forwarding_rulesandport_forwarding_rule_remove, in bothSharedandHostmode. That includes rules added through the configuration, even though Apple's header documents list and remove for that case. Forwards for these networks therefore have to be declared on theNetworkConfigurationbeforeNetwork::new.The change.
src/ffi/vmnet.rs: declaresvmnet_network_configuration_add_port_forwarding_ruleexactly as in the macOS 27 SDK'svmnet.h. This function takesinternal_portbeforeexternal_port, the opposite of the interface variant, so the declaration has a comment saying so.src/network.rs: addsNetworkConfiguration::add_port_forwarding_rule. Its arguments and itsAddressFamilyMismatchcheck matchInterface::port_forwarding_rule_add, so it looks the same to callers. The doc covers what callers need: add rules before creating the network, and use an address inside the network's subnet.network::tests::port_forwarding_rule_address_family_mismatchchecks both mismatch directions.interface::tests::network_port_forwardingconfigures two rules, creates the network and attaches an interface. Because of the problem above, it can't read the rules back, so the forwarding itself was checked end to end (below).Open question. The address-family match now appears in both
Interface::port_forwarding_rule_addand here. I'm happy to move it into a shared helper, in this PR or a follow-up, if you'd prefer.Verification
On an Apple silicon Mac mini running macOS 27.0.1 (Xcode 27 SDK):
cargo fmt --checkandcargo clippy --all-targets -- -D warningsare clean, andcargo docbuilds withRUSTDOCFLAGS="-D warnings".cargo test -- --test-threads 1passes (all 14 tests, including the two new ones), on top of 0.9.0.--expose 2222:22producesrdr on en0 inet proto tcp from any to (en0:0) port = 2222 -> <guest> port 22incom.apple.internet-sharing/port-mapping.Happy to adjust naming or shape. Questions are welcome here.
🤖 Generated with Claude Code