Repository navigation
Put --expose port forwards on the vmnet network configuration #223
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,10 @@ | ||
| use crate::network::Network; | ||
| use anyhow::{Context, Result, anyhow}; | ||
| use log::info; | ||
| use std::net::Ipv4Addr; | ||
| use std::sync::Arc; | ||
| use std::sync::mpsc::{SyncSender, sync_channel}; | ||
| use tokio::sync::Notify; | ||
| use vmnet::parameters::{Parameter, ParameterKind}; | ||
| use vmnet::port_forwarding::{AddressFamily, Protocol}; | ||
| use vmnet::{Batch, Events, Options}; | ||
|
|
||
| pub struct Host { | ||
|
|
@@ -84,28 +82,6 @@ impl Host { | |
| } | ||
|
|
||
| impl Host { | ||
| pub fn port_forwarding_add_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. Removed. This was the interface-level call (
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. Removed. This was the interface-level call ( |
||
| &mut self, | ||
| external_port: u16, | ||
| internal_addr: Ipv4Addr, | ||
| internal_port: u16, | ||
| ) -> Result<()> { | ||
| let details = format!( | ||
| "external_port={external_port}, internal_addr={internal_addr}, internal_port={internal_port}" | ||
| ); | ||
|
|
||
| self.interface | ||
| .port_forwarding_rule_add( | ||
| AddressFamily::Ipv4, | ||
| Protocol::Tcp, | ||
| external_port, | ||
| internal_addr.into(), | ||
| internal_port, | ||
| ) | ||
| .map(|_| info!("added port forwarding rule {details}")) | ||
| .map_err(|err| anyhow!("failed to add port forwarding rule {details}: {err}")) | ||
| } | ||
|
|
||
| pub async fn read(&mut self, batch: &mut Batch, bufs: &mut [Vec<u8>]) -> vmnet::Result<usize> { | ||
| loop { | ||
| // One callback can cover several batches; stay ready until an empty read | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -295,7 +295,13 @@ impl Manager { | |
| .iter() | ||
| .map(|(_, config, vm_ip, _)| (config.vm_mac_address, *vm_ip)) | ||
| .collect(); | ||
| let network = Network::new(vm_net_type, subnet, &reservations)?; | ||
| let port_forwardings: Vec<_> = configured | ||
|
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. Built from the same |
||
| .iter() | ||
| .flat_map(|(_, config, vm_ip, _)| { | ||
| config.exposed_ports.iter().map(|port| (*vm_ip, *port)) | ||
| }) | ||
|
Comment on lines
+299
to
+302
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.
When two entries in one Useful? React with 👍 / 👎.
Contributor
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. There was no explicit duplicate-port check before. Let's just keep relying on |
||
| .collect(); | ||
| let network = Network::new(vm_net_type, subnet, &reservations, &port_forwardings)?; | ||
| let mut proxies = Vec::with_capacity(configured.len()); | ||
|
|
||
| // Finish the entire batch before giving a proxy to any VM task | ||
|
|
@@ -307,7 +313,6 @@ impl Manager { | |
| &network, | ||
| config.allow, | ||
| config.block, | ||
| config.exposed_ports, | ||
| ) | ||
| .with_context(|| format!("failed to initialize VM at index {index} ({vm_id})"))?; | ||
| proxies.push(proxy); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -766,6 +766,29 @@ async fn native_vm_disconnect_ends_run() { | |
| timeout(TIMEOUT, manager.shutdown()).await.unwrap().unwrap(); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| #[serial_test::serial] | ||
| async fn native_batch_exposes_ports_for_every_vm() { | ||
|
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. Regression test for #222. On |
||
| let mut manager = native_manager(None); | ||
| let (socket, _peer) = UnixDatagram::pair().unwrap(); | ||
| let mut first = vm_config(&socket, "02:00:00:00:00:02"); | ||
| first.exposed_ports = vec!["52222:22".parse().unwrap()]; | ||
| let mut second = vm_config(&socket, "02:00:00:00:00:03"); | ||
| second.exposed_ports = vec!["52223:22".parse().unwrap(), "58080:80".parse().unwrap()]; | ||
| drop(socket); | ||
|
|
||
| // Every VM's forwards are added to the shared network before it starts | ||
| timeout( | ||
| TIMEOUT, | ||
| manager.initialize(NetType::Nat, vec![first, second]), | ||
| ) | ||
| .await | ||
| .unwrap() | ||
| .unwrap(); | ||
|
|
||
| timeout(TIMEOUT, manager.shutdown()).await.unwrap().unwrap(); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| #[serial_test::serial] | ||
| async fn native_control_disconnect_keeps_vms_running() { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,13 @@ | ||
| use crate::proxy::ExposedPort; | ||
| use anyhow::{Context, Result}; | ||
| use clap::ValueEnum; | ||
| use ipnet::Ipv4Net; | ||
| use log::info; | ||
| use mac_address::MacAddress; | ||
| use std::net::Ipv4Addr; | ||
| use vmnet::NetworkConfiguration; | ||
| use vmnet::network::Mode; | ||
| use vmnet::port_forwarding::{AddressFamily, Protocol}; | ||
|
|
||
| // A /29 has six usable addresses, including the gateway | ||
| pub(crate) const MAX_VMS: usize = 5; | ||
|
|
@@ -35,6 +38,7 @@ impl Network { | |
| net_type: NetType, | ||
| subnet: Ipv4Net, | ||
| reservations: &[(MacAddress, Ipv4Addr)], | ||
| port_forwardings: &[(Ipv4Addr, ExposedPort)], | ||
|
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. One entry per forward, each paired with its VM's reserved address and flattened across the batch, so |
||
| ) -> Result<Self> { | ||
| // Create vmnet's network configuration | ||
| let mut configuration = NetworkConfiguration::new(match net_type { | ||
|
|
@@ -62,10 +66,38 @@ impl Network { | |
| })?; | ||
| } | ||
|
|
||
| // Forward exposed ports to the reserved VM addresses, which vmnet only | ||
|
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 has to happen before |
||
| // accepts on the configuration, before the network is created | ||
| let port_forwardings = port_forwardings | ||
| .iter() | ||
| .map(|(vm_ip, port)| { | ||
| let details = format!( | ||
| "external_port={}, internal_addr={vm_ip}, internal_port={}", | ||
| port.external_port, port.internal_port | ||
| ); | ||
|
|
||
| configuration | ||
| .add_port_forwarding_rule( | ||
| AddressFamily::Ipv4, | ||
| Protocol::Tcp, | ||
| port.external_port, | ||
| (*vm_ip).into(), | ||
| port.internal_port, | ||
| ) | ||
| .with_context(|| format!("failed to add port forwarding rule {details}"))?; | ||
|
|
||
| Ok(details) | ||
| }) | ||
| .collect::<Result<Vec<_>>>()?; | ||
|
|
||
| // Create vmnet's network | ||
| let handle = vmnet::Network::new(&configuration) | ||
| .with_context(|| format!("failed to create vmnet network {subnet}"))?; | ||
|
|
||
| for details in port_forwardings { | ||
|
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. Logged only after the network exists, so the line only appears when the forward is actually in place. |
||
| info!("added port forwarding rule {details}"); | ||
| } | ||
|
|
||
| Ok(Self { | ||
| handle, | ||
| subnet, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,7 @@ use crate::dhcp_server::DhcpServer; | |
| use crate::host::Host; | ||
| use crate::network::Network; | ||
| use crate::vm::VM; | ||
| use anyhow::{Context, Result}; | ||
| use anyhow::Result; | ||
| pub use exposed_port::ExposedPort; | ||
| use flows::{FlowTable, PendingFlow}; | ||
| use ipnet::Ipv4Net; | ||
|
|
@@ -62,14 +62,13 @@ impl Proxy { | |
| network: &Network, | ||
| allow: Vec<Rule>, | ||
| block: Vec<Rule>, | ||
| exposed_ports: Vec<ExposedPort>, | ||
| ) -> Result<Proxy> { | ||
| let vm_mac_address = EthernetAddress(vm_mac_address.bytes()); | ||
| let allow = normalize_rules(allow); | ||
| let block = normalize_rules(block); | ||
|
|
||
| // Create this VM's vmnet interface on the shared network | ||
| let mut host = Host::new( | ||
| let host = Host::new( | ||
| network, | ||
| vm_ip, | ||
| !allow.contains(&Rule::Stateless(Target::Prefix(Ipv4Net::default()))), | ||
|
|
@@ -89,12 +88,6 @@ impl Proxy { | |
| host.gateway_ip, | ||
| ); | ||
|
|
||
| // Install port forwardings for the VM's reserved IP address | ||
|
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 failing path: this loop ran after |
||
| for port in exposed_ports { | ||
| host.port_forwarding_add_rule(port.external_port, host.vm_ip, port.internal_port) | ||
| .context("failed to configure port forwarding")?; | ||
| } | ||
|
|
||
| Ok(Proxy { | ||
| vm, | ||
| host, | ||
|
|
@@ -443,7 +436,8 @@ mod tests { | |
| let (_, vm_ip, subnet) = crate::subnet_finder::find_available_subnet(29).unwrap(); | ||
| let vm_mac_address = MacAddress::from_str("02:00:00:00:00:02").unwrap(); | ||
| let network = | ||
| crate::network::Network::new(NetType::Nat, subnet, &[(vm_mac_address, vm_ip)]).unwrap(); | ||
| crate::network::Network::new(NetType::Nat, subnet, &[(vm_mac_address, vm_ip)], &[]) | ||
| .unwrap(); | ||
| let proxy = Proxy::new( | ||
| VM::new(vm_fd.as_raw_fd()).unwrap(), | ||
| vm_mac_address, | ||
|
|
@@ -457,7 +451,6 @@ mod tests { | |
| .into_iter() | ||
| .map(|value| value.parse().unwrap()) | ||
| .collect(), | ||
| Vec::new(), | ||
| ) | ||
| .unwrap(); | ||
|
|
||
|
|
||
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.
vmnet 0.9 → 0.10, which adds
NetworkConfiguration::add_port_forwarding_rule. No other changes were needed for 0.10.