Repository navigation
Add NetworkConfiguration::add_port_forwarding_rule #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -844,6 +844,33 @@ mod tests { | |
| iface.finalize().unwrap(); | ||
| } | ||
|
|
||
| #[test] | ||
| fn network_port_forwarding() { | ||
| let addr: IpAddr = Ipv4Addr::new(10, 88, 12, 2).into(); | ||
|
|
||
| // Configure port forwarding on the network | ||
| let mut configuration = NetworkConfiguration::new(NetworkMode::Shared).unwrap(); | ||
| configuration | ||
| .set_ipv4_subnet( | ||
| Ipv4Addr::new(10, 88, 12, 1), | ||
| Ipv4Addr::new(255, 255, 255, 248), | ||
| ) | ||
| .unwrap(); | ||
| configuration | ||
| .add_port_forwarding_rule(AddressFamily::Ipv4, Protocol::Tcp, 2222, addr, 22) | ||
| .unwrap(); | ||
| configuration | ||
| .add_port_forwarding_rule(AddressFamily::Ipv4, Protocol::Tcp, 8080, addr, 80) | ||
| .unwrap(); | ||
|
|
||
| // Create the network and attach an interface. The interface port | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test can't read the rules back, because The forwarding itself was verified end to end through softnet: an rdr rule appears for the port, and traffic reaches the guest. |
||
| // forwarding functions fail on it, so the rules can't be listed back | ||
| let network = Network::new(&configuration).unwrap(); | ||
| let mut iface = Interface::with_network(&network, Options::default()).unwrap(); | ||
|
|
||
| iface.finalize().unwrap(); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_batch() { | ||
| // Create two interfaces in the same broadcast domain | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,8 @@ | ||
| use crate::ffi::vmnet::{self, NetworkRef, Status}; | ||
| use crate::Result; | ||
| use crate::port_forwarding::{AddressFamily, Protocol}; | ||
| use crate::{Error, Result}; | ||
| use objc2_core_foundation::{CFRetained, CFType}; | ||
| use std::net::Ipv4Addr; | ||
| use std::net::{IpAddr, Ipv4Addr}; | ||
| use std::ptr::NonNull; | ||
|
|
||
| /// Modes supported by explicit networks. | ||
|
|
@@ -67,6 +68,46 @@ impl NetworkConfiguration { | |
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| /// Adds a port forwarding rule to the network. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The doc only covers what a caller needs: add rules before 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. |
||
| /// | ||
| /// Rules must be added before the network is created: | ||
| /// [`Interface::port_forwarding_rule_add`] can't add rules to an interface | ||
| /// attached to a network. `internal_addr` must be inside the network's | ||
| /// subnet; use [`set_ipv4_subnet`](Self::set_ipv4_subnet) and | ||
| /// [`add_dhcp_reservation`](Self::add_dhcp_reservation) to give a guest a | ||
| /// known address. | ||
| /// | ||
| /// [`Interface::port_forwarding_rule_add`]: crate::Interface::port_forwarding_rule_add | ||
| pub fn add_port_forwarding_rule( | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same arguments, order and |
||
| &mut self, | ||
| address_family: AddressFamily, | ||
| protocol: Protocol, | ||
| external_port: u16, | ||
| internal_addr: IpAddr, | ||
| internal_port: u16, | ||
| ) -> Result<()> { | ||
| let internal_addr_ffi = match (address_family, internal_addr) { | ||
| (AddressFamily::Ipv4, IpAddr::V4(addr)) => Vec::from(addr.octets()), | ||
| (AddressFamily::Ipv6, IpAddr::V6(addr)) => Vec::from(addr.octets()), | ||
| _ => return Err(Error::AddressFamilyMismatch), | ||
| }; | ||
|
|
||
| let status = unsafe { | ||
| vmnet::vmnet_network_configuration_add_port_forwarding_rule( | ||
| CFRetained::as_ptr(&self.handle).as_ptr().cast(), | ||
| protocol as u8, | ||
| address_family as u8, | ||
| internal_port, | ||
| external_port, | ||
| internal_addr_ffi.as_ptr().cast(), | ||
| ) | ||
| }; | ||
|
|
||
| Status::from_ffi(status)?; | ||
|
|
||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| /// A reserved network to which multiple interfaces can attach. | ||
|
|
@@ -128,4 +169,30 @@ mod tests { | |
|
|
||
| assert!(Network::new(&second_configuration).is_err()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn port_forwarding_rule_address_family_mismatch() { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Covers both mismatch directions. The check runs before any FFI call, so this test doesn't create a network. |
||
| let mut configuration = NetworkConfiguration::new(Mode::Shared).unwrap(); | ||
|
|
||
| assert!(matches!( | ||
| configuration.add_port_forwarding_rule( | ||
| AddressFamily::Ipv6, | ||
| Protocol::Tcp, | ||
| 2222, | ||
| Ipv4Addr::new(192, 168, 0, 2).into(), | ||
| 22, | ||
| ), | ||
| Err(Error::AddressFamilyMismatch) | ||
| )); | ||
| assert!(matches!( | ||
| configuration.add_port_forwarding_rule( | ||
| AddressFamily::Ipv4, | ||
| Protocol::Tcp, | ||
| 2222, | ||
| "fd00::2".parse::<std::net::Ipv6Addr>().unwrap().into(), | ||
| 22, | ||
| ), | ||
| Err(Error::AddressFamilyMismatch) | ||
| )); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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_tis__uint8_ton macOS, henceu8.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.