[nmh][network_manager] Adds NAT control
This patch adds support to control NAT from Network Manager. Actually
enabling and disabling NAT will be done in a future commit.
Bug: 35788
Tests: network_manager_{tests, integration, and cli} tests, all pass.
Change-Id: Ibde673ae87da46aec5f040843b9aebcb64daf78b
diff --git a/src/connectivity/management/network_manager/core/BUILD.gn b/src/connectivity/management/network_manager/core/BUILD.gn
index 3dea146..7c7be9f 100644
--- a/src/connectivity/management/network_manager/core/BUILD.gn
+++ b/src/connectivity/management/network_manager/core/BUILD.gn
@@ -11,9 +11,12 @@
with_unit_tests = true
deps = [
+ "//garnet/lib/rust/fidl_fuchsia_net_stack_ext",
+ "//garnet/public/lib/fidl/rust/fidl",
"//garnet/public/rust/fuchsia-async",
"//garnet/public/rust/fuchsia-component",
"//garnet/public/rust/fuchsia-syslog",
+ "//sdk/fidl/fuchsia.net.dhcp:fuchsia.net.dhcp-rustc",
"//sdk/fidl/fuchsia.net.filter:fuchsia.net.filter-rustc",
"//sdk/fidl/fuchsia.netstack:fuchsia.netstack-rustc",
"//sdk/fidl/fuchsia.router.config:fuchsia.router.config-rustc",
diff --git a/src/connectivity/management/network_manager/core/src/error.rs b/src/connectivity/management/network_manager/core/src/error.rs
index 2a09f50..5039d3e 100644
--- a/src/connectivity/management/network_manager/core/src/error.rs
+++ b/src/connectivity/management/network_manager/core/src/error.rs
@@ -4,7 +4,8 @@
//! Custom error types for the network manager.
-use failure::Fail;
+use core::fmt::{self, Display};
+use failure::{Context, Fail};
pub type Result<T> = std::result::Result<T, NetworkManager>;
@@ -23,6 +24,9 @@
/// Errors related to HAL layer.
#[fail(display = "{}", _0)]
HAL(#[cause] Hal),
+ /// Errors with a detail string attached.
+ #[fail(display = "An error occurred.")]
+ INTERNAL(ErrorWithDetail),
// Add error types here.
}
@@ -41,6 +45,23 @@
NetworkManager::HAL(e)
}
}
+impl From<Context<&'static str>> for NetworkManager {
+ fn from(inner: Context<&'static str>) -> Self {
+ NetworkManager::INTERNAL(ErrorWithDetail { inner: Context::new(inner.to_string()) })
+ }
+}
+
+#[derive(Fail, Debug)]
+pub struct ErrorWithDetail {
+ #[cause]
+ inner: Context<String>,
+}
+
+impl Display for ErrorWithDetail {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.inner, f)
+ }
+}
/// Error type for packet LIFManager.
#[derive(Fail, Debug, PartialEq)]
@@ -79,6 +100,12 @@
NotEnabled,
#[fail(display = "Could not disable service")]
NotDisabled,
+ #[fail(display = "Failed to add new packet filter rules")]
+ ErrorAddingPacketFilterRules,
+ #[fail(display = "Failed to get packet filter rules")]
+ ErrorGettingPacketFilterRules,
+ #[fail(display = "Failed to enable NAT")]
+ ErrorFailedEnableNat,
#[fail(display = "Service is not supported")]
NotSupported,
}
diff --git a/src/connectivity/management/network_manager/core/src/hal.rs b/src/connectivity/management/network_manager/core/src/hal.rs
index 4e840f11..04fc010 100644
--- a/src/connectivity/management/network_manager/core/src/hal.rs
+++ b/src/connectivity/management/network_manager/core/src/hal.rs
@@ -9,6 +9,7 @@
use failure::{Error, ResultExt};
use fidl_fuchsia_net;
use fidl_fuchsia_net_stack::{InterfaceInfo, StackMarker, StackProxy};
+use fidl_fuchsia_net_stack_ext::FidlReturn;
use fidl_fuchsia_netstack::{NetstackMarker, NetstackProxy};
use fuchsia_component::client::connect_to_service;
use std::collections::HashSet;
@@ -159,7 +160,7 @@
pub async fn get_interface(&mut self, port: u64) -> Option<Interface> {
match self.stack.get_interface_info(port).await {
- Ok((Some(info), _)) => Some((&(*info)).into()),
+ Ok(Ok(info)) => Some((&info).into()),
_ => None,
}
}
@@ -222,16 +223,9 @@
&mut addr.to_fidl_interface_address(),
)
.await;
- match r {
- Err(_) => Err(error::NetworkManager::HAL(error::Hal::OperationFailed)),
- Ok(r) => match r {
- None => Ok(()),
- Some(e) => {
- println!("could not set interface address: ${:?}", e);
- Err(error::NetworkManager::HAL(error::Hal::OperationFailed))
- }
- },
- }
+ r.squash_result()
+ .map_err(|_| error::NetworkManager::HAL(error::Hal::OperationFailed))
+ .map(|_| ())
}
/// `unset_ip_address` removes an IP address from the interface configuration.
@@ -273,14 +267,24 @@
}
pub async fn set_dhcp_client_state(&mut self, pid: PortId, enable: bool) -> error::Result<()> {
- let r = self.netstack.set_dhcp_client_status(StackPortId::from(pid).to_u32(), enable).await;
- match r {
- Ok(fidl_fuchsia_netstack::NetErr {
- status: fidl_fuchsia_netstack::Status::Ok,
- message: _,
- }) => Ok(()),
- _ => Err(error::NetworkManager::HAL(error::Hal::OperationFailed)),
+ let (dhcp_client, server_end) =
+ fidl::endpoints::create_proxy::<fidl_fuchsia_net_dhcp::ClientMarker>()
+ .context("dhcp client: failed to create fidl endpoints")?;
+ if let Err(e) = self.netstack.get_dhcp_client(pid.to_u32(), server_end).await {
+ warn!("failed to create fidl endpoint for dhch client: {:?}", e);
+ return Err(error::NetworkManager::HAL(error::Hal::OperationFailed));
}
+ let r = if enable {
+ dhcp_client.start().await.context("failed to start dhcp client")?
+ } else {
+ dhcp_client.stop().await.context("failed to stop dhcp client")?
+ };
+ if let Err(e) = r {
+ warn!("failed to start dhcp client: {:?}", e);
+ return Err(error::NetworkManager::HAL(error::Hal::OperationFailed));
+ }
+ info!("DHCP client on nicid: {}, enabled: {}", pid.to_u32(), enable);
+ Ok(())
}
// apply_manual_address updates the configured IP address.
diff --git a/src/connectivity/management/network_manager/core/src/lib.rs b/src/connectivity/management/network_manager/core/src/lib.rs
index 927a6e7..5ed7d4f 100644
--- a/src/connectivity/management/network_manager/core/src/lib.rs
+++ b/src/connectivity/management/network_manager/core/src/lib.rs
@@ -32,18 +32,20 @@
port_manager: portmgr::PortManager,
lif_manager: lifmgr::LIFManager,
service_manager: servicemgr::Manager,
+ packet_filter: packet_filter::PacketFilter,
hal: hal::NetCfg,
}
impl DeviceState {
//! Create an empty DeviceState.
- pub fn new(hal: hal::NetCfg) -> Self {
+ pub fn new(hal: hal::NetCfg, packet_filter: packet_filter::PacketFilter) -> Self {
let v = 0;
DeviceState {
version: v,
port_manager: portmgr::PortManager::new(),
lif_manager: lifmgr::LIFManager::new(),
service_manager: servicemgr::Manager::new(),
+ packet_filter: packet_filter,
hal,
}
}
@@ -285,6 +287,7 @@
address: Some(address),
prefix_length: Some(prefix_length),
}) => {
+ // The WAN IPv4 address is changing.
if lp.dhcp {
warn!(
"Setting a static ip is not allowed when \
@@ -293,8 +296,9 @@
return Err(error::NetworkManager::LIF(error::Lif::NotSupported));
}
info!("setting ip to {:?} {:?}", address, prefix_length);
- let a = LifIpAddr::from(p.address_v4.as_ref().unwrap());
- lp.address = Some(a);
+ let v4addr = LifIpAddr::from(p.address_v4.as_ref().unwrap());
+ self.service_manager.set_global_ip_nat(v4addr.clone(), lif.pid());
+ lp.address = Some(v4addr);
}
_ => {
warn!("invalid address {:?}", p.address_v4);
@@ -321,6 +325,7 @@
address: Some(address),
prefix_length: Some(prefix_length),
}) => {
+ // WAN IPv6 address is changing.
if lp.dhcp {
warn!(
"Setting a static ip is not allowed when \
@@ -376,9 +381,11 @@
address: Some(address),
prefix_length: Some(prefix_length),
}) => {
+ // LAN IPv4 address is changing.
info!("setting ip to {:?} {:?}", address, prefix_length);
- let a = LifIpAddr::from(p.address_v4.as_ref().unwrap());
- lp.address = Some(a);
+ let v4addr = LifIpAddr::from(p.address_v4.as_ref().unwrap());
+ self.service_manager.set_local_subnet_nat(v4addr.clone());
+ lp.address = Some(v4addr);
}
_ => {
warn!("invalid address {:?}", p.address_v4);
@@ -391,6 +398,7 @@
address: Some(address),
prefix_length: Some(prefix_length),
}) => {
+ // LAN IPv6 address is changing.
info!("setting ip to {:?} {:?}", address, prefix_length);
let a = LifIpAddr::from(p.address_v6.as_ref().unwrap());
lp.address = Some(a);
@@ -407,8 +415,19 @@
lp.enabled = *enable
}
};
+
self.hal.apply_properties(lif.pid(), &old, &lp).await?;
lif.set_properties(self.version, lp)?;
+ if self.service_manager.is_nat_enabled() {
+ if let Err(e) =
+ self.packet_filter.update_nat(self.service_manager.get_nat_config()).await
+ {
+ warn!("Failed to enable nat: {:?}", e);
+ return Err(error::NetworkManager::SERVICE(
+ error::Service::ErrorFailedEnableNat,
+ ));
+ }
+ }
self.version += 1;
Ok(())
}
@@ -434,6 +453,28 @@
pub fn version(&self) -> Version {
self.version
}
+
+ /// `set_filter` installs a new packet filter rule.
+ pub async fn set_filter(
+ &self,
+ rule: fidl_fuchsia_router_config::FilterRule,
+ ) -> error::Result<()> {
+ match self.packet_filter.set_filter(rule).await {
+ Ok(_) => Ok(()),
+ Err(e) => {
+ warn!("Failed to set new filter rules: {:?}", e);
+ Err(error::NetworkManager::SERVICE(error::Service::ErrorAddingPacketFilterRules))
+ }
+ }
+ }
+
+ /// `get_filters` returns the currently installed packet filter rules.
+ pub async fn get_filters(&self) -> error::Result<Vec<fidl_fuchsia_router_config::FilterRule>> {
+ self.packet_filter.get_filters().await.map_err(|e| {
+ warn!("Failed to get filter rules: {:?}", e);
+ error::NetworkManager::SERVICE(error::Service::ErrorGettingPacketFilterRules)
+ })
+ }
}
type Version = u64;
@@ -473,7 +514,10 @@
#[fasync::run_singlethreaded(test)]
async fn test_new_device_state() {
- let d = DeviceState::new(hal::NetCfg::new().unwrap());
+ let d = DeviceState::new(
+ hal::NetCfg::new().unwrap(),
+ packet_filter::PacketFilter::start().unwrap(),
+ );
assert_eq!(d.version, 0);
assert_eq!(d.port_manager.ports().count(), 0);
diff --git a/src/connectivity/management/network_manager/core/src/lifmgr.rs b/src/connectivity/management/network_manager/core/src/lifmgr.rs
index 1cb86df..f80d508 100644
--- a/src/connectivity/management/network_manager/core/src/lifmgr.rs
+++ b/src/connectivity/management/network_manager/core/src/lifmgr.rs
@@ -231,6 +231,26 @@
},
}
}
+ pub fn to_fidl_subnet(&self) -> fidl_fuchsia_net::Subnet {
+ match self.address {
+ IpAddr::V4(a) => fidl_fuchsia_net::Subnet {
+ addr: fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address {
+ addr: (u32::from_be_bytes(a.octets()) >> (32 - self.prefix)
+ << (32 - self.prefix))
+ .to_be_bytes(),
+ }),
+ prefix_len: self.prefix,
+ },
+ IpAddr::V6(a) => fidl_fuchsia_net::Subnet {
+ addr: fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
+ addr: (u128::from_be_bytes(a.octets()) >> (128 - self.prefix)
+ << (128 - self.prefix))
+ .to_be_bytes(),
+ }),
+ prefix_len: self.prefix,
+ },
+ }
+ }
pub fn to_fidl_interface_address(&self) -> fidl_fuchsia_net_stack::InterfaceAddress {
match self.address {
IpAddr::V4(a) => fidl_fuchsia_net_stack::InterfaceAddress {
@@ -367,6 +387,7 @@
#![allow(unused)]
use super::*;
use crate::portmgr::{Port, PortManager};
+ use fidl_fuchsia_net::Ipv4Address;
fn create_ports() -> PortManager {
let mut pm = PortManager::new();
@@ -740,4 +761,45 @@
}
);
}
+
+ fn build_lif_subnet(
+ lifip_addr: &str,
+ expected_addr: &str,
+ prefix_len: u8,
+ ) -> (LifIpAddr, fidl_fuchsia_net::Subnet) {
+ let lifip = LifIpAddr { address: lifip_addr.parse().unwrap(), prefix: prefix_len };
+
+ let ip: IpAddr = expected_addr.parse().unwrap();
+ let expected_subnet = fidl_fuchsia_net::Subnet {
+ addr: fidl_fuchsia_net::IpAddress::Ipv4(Ipv4Address {
+ addr: match ip {
+ std::net::IpAddr::V4(v4addr) => v4addr.octets(),
+ std::net::IpAddr::V6(_) => panic!("unexpected ipv6 address"),
+ },
+ }),
+ prefix_len,
+ };
+ (lifip, expected_subnet)
+ }
+
+ #[test]
+ fn test_fidl_subnet_math() {
+ let (lifip, expected_subnet) = build_lif_subnet("169.254.10.10", "169.254.10.10", 32);
+ assert_eq!(lifip.to_fidl_subnet(), expected_subnet);
+
+ let (lifip, expected_subnet) = build_lif_subnet("169.254.10.10", "169.254.10.0", 24);
+ assert_eq!(lifip.to_fidl_subnet(), expected_subnet);
+
+ let (lifip, expected_subnet) = build_lif_subnet("169.254.10.10", "169.254.0.0", 16);
+ assert_eq!(lifip.to_fidl_subnet(), expected_subnet);
+
+ let (lifip, expected_subnet) = build_lif_subnet("169.254.10.10", "169.0.0.0", 8);
+ assert_eq!(lifip.to_fidl_subnet(), expected_subnet);
+
+ let (lifip, expected_subnet) = build_lif_subnet("169.254.127.254", "169.254.124.0", 22);
+ assert_eq!(lifip.to_fidl_subnet(), expected_subnet);
+
+ let (lifip, expected_subnet) = build_lif_subnet("16.25.12.25", "16.16.0.0", 12);
+ assert_eq!(lifip.to_fidl_subnet(), expected_subnet);
+ }
}
diff --git a/src/connectivity/management/network_manager/core/src/packet_filter.rs b/src/connectivity/management/network_manager/core/src/packet_filter.rs
index 78c3fe5..67cb6cc 100644
--- a/src/connectivity/management/network_manager/core/src/packet_filter.rs
+++ b/src/connectivity/management/network_manager/core/src/packet_filter.rs
@@ -4,10 +4,12 @@
//! Handles packet filtering requests for Network Manager.
+use crate::servicemgr::NatConfig;
use failure::{format_err, Error};
use fidl_fuchsia_net_filter::{self as netfilter, Direction, FilterMarker, FilterProxy, Status};
use fidl_fuchsia_router_config as router_config;
use fuchsia_component::client::connect_to_service;
+use std::net::IpAddr;
/// Storage for this PacketFilter's attributes.
pub struct PacketFilter {
@@ -183,6 +185,30 @@
Ok(Some(Box::new(fidl_fuchsia_net::Subnet { addr, prefix_len })))
}
+/// Parses a [`servicemgr::NatConfig`] and extracts the required fields.
+fn from_nat_config(
+ nat_config: &NatConfig,
+) -> Result<(fidl_fuchsia_net::Subnet, fidl_fuchsia_net::IpAddress, u32), Error> {
+ let src_subnet = match &nat_config.local_subnet {
+ Some(subnet) => subnet.clone().to_fidl_subnet(),
+ None => return Err(format_err!("NatConfig must have a local_subnet set")),
+ };
+ let wan_ip = match nat_config.global_ip.clone() {
+ Some(lif) => match lif.address {
+ IpAddr::V4(a) => fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address {
+ addr: a.octets(),
+ }),
+ IpAddr::V6(_) => return Err(format_err!("IPv6 is not supported")),
+ },
+ None => return Err(format_err!("NatConfig must have a global_ip set")),
+ };
+ let nicid = match nat_config.pid {
+ Some(pid) => pid.to_u32(),
+ None => return Err(format_err!("NatConfig must have a pid set")),
+ };
+ Ok((src_subnet, wan_ip, nicid))
+}
+
/// Manages a Packet Filter connection to netstack filter service (netfilter).
///
/// Mainly serves as a wrapper around the netfilter service. Converts Network Manager FIDL APIs into
@@ -232,6 +258,7 @@
/// include in the request.
///
/// # Error
+ ///
/// If we fail to get the generation number from the netfilter service, or the result of the
/// request to netfilter is anything other than [`netfilter::Status::Ok`] then produce an error
/// result. Failure to convert the [`router_config::FilterRule`] to a [`netfilter::Rule`] will
@@ -255,12 +282,79 @@
Err(e) => Err(format_err!("fidl error: {:?}", e)),
}
}
+
+ /// Installs a Network Address Translation (NAT) rule.
+ ///
+ /// This method calls the netfilter API to install a rule that rewrites source addresses on the
+ /// LAN side with a publicly routable IP address (SNAT). Reverse translation (DNAT) is handled
+ /// by netstack's connection state tracker.
+ ///
+ /// # Error
+ ///
+ /// If we fail to get the generation number from the netfilter service, or fail to update the
+ /// the NAT ruleset, then we'll return an error result to the caller.
+ ///
+ /// A complete [`servicemgr::NatConfig`] is required at this point. If any fields are missing,
+ /// then an appropriate error will be returned to the caller.
+ // TODO(cgibson): Currently there is no way for this to be called. Add a method to the CLI
+ // to actually enable NAT, which would allow this method to run: fxb/35788.
+ pub async fn update_nat(&self, nat_config: &mut NatConfig) -> Result<(), Error> {
+ info!("Received request to enable NAT");
+ let (src_subnet, wan_ip, nicid) = from_nat_config(nat_config)
+ .map_err(|e| format_err!("Failed to parse NAT config: {:?}", e))?;
+
+ // TODO(cgibson): NAT should work on IP packets, we shouldn't need to provide a proto field
+ // here. This is a bug: fxb/35950.
+ //
+ // Ultimately this should all collapse into a single rule.
+ let mut nat_rules = vec![
+ netfilter::Nat {
+ proto: netfilter::SocketProtocol::Tcp,
+ src_subnet: src_subnet.clone(),
+ new_src_addr: wan_ip.clone(),
+ nic: nicid,
+ },
+ netfilter::Nat {
+ proto: netfilter::SocketProtocol::Udp,
+ src_subnet: src_subnet.clone(),
+ new_src_addr: wan_ip.clone(),
+ nic: nicid,
+ },
+ netfilter::Nat {
+ proto: netfilter::SocketProtocol::Icmp,
+ src_subnet: src_subnet.clone(),
+ new_src_addr: wan_ip.clone(),
+ nic: nicid,
+ },
+ ];
+
+ // Since we discard any existing NAT rules here, this is a pure "update-only" situation.
+ let generation: u32 = match self.filter_svc.get_nat_rules().await {
+ Ok((_, generation, Status::Ok)) => generation,
+ Ok((_, _, status)) => {
+ return Err(format_err!(
+ "Failed to get generation number! Status was: {:?}",
+ status
+ ))
+ }
+ Err(e) => return Err(format_err!("fidl error: {:?}", e)),
+ };
+ // TODO(cgibson): When we add the CLI command to enable/disable NAT, we will be able to use
+ // our CLI integration framework to test this.
+ match self.filter_svc.update_nat_rules(&mut nat_rules.iter_mut(), generation).await {
+ Ok(Status::Ok) => Ok(()),
+ Ok(status) => Err(format_err!("failed to set NAT state: {:?}", status)),
+ Err(e) => Err(format_err!("fidl error: {:?}", e)),
+ }
+ }
}
#[cfg(test)]
mod tests {
use super::*;
+ use crate::hal;
+ use crate::lifmgr::LifIpAddr;
use fidl_fuchsia_net::IpAddress::Ipv4;
use fidl_fuchsia_net::Ipv4Address;
use fidl_fuchsia_router_config::{
@@ -430,4 +524,85 @@
let both = from_protocol(Some(router_config::Protocol::Both));
assert_eq!(both.is_none(), true);
}
+
+ #[test]
+ fn test_from_nat_config() {
+ let expected_subnet_lifip =
+ LifIpAddr { address: "192.168.0.0".parse().unwrap(), prefix: 24 };
+ let expected_lifip = LifIpAddr { address: "17.0.0.1".parse().unwrap(), prefix: 32 };
+ let ip: IpAddr = "192.168.0.0".parse().unwrap();
+ let expected_subnet = fidl_fuchsia_net::Subnet {
+ addr: fidl_fuchsia_net::IpAddress::Ipv4(Ipv4Address {
+ addr: match ip {
+ std::net::IpAddr::V4(v4addr) => v4addr.octets(),
+ std::net::IpAddr::V6(_) => panic!("unexpected ipv6 address"),
+ },
+ }),
+ prefix_len: 24,
+ };
+ let expected_pid = hal::PortId::from(1);
+
+ // should fail if all fields are set to None.
+ let mut nat_config = NatConfig {
+ enable: true, // not being validated, so we don't care what this is set to.
+ local_subnet: None,
+ global_ip: None,
+ pid: None,
+ };
+ let result = from_nat_config(&mut nat_config);
+ assert_eq!(result.is_err(), true);
+
+ // should fail if local_subnet is missing.
+ let mut nat_config = NatConfig {
+ enable: true, // not being validated, so we don't care what this is set to.
+ local_subnet: None,
+ global_ip: Some(expected_lifip.clone()),
+ pid: Some(expected_pid),
+ };
+ let result = from_nat_config(&mut nat_config);
+ assert_eq!(result.is_err(), true);
+
+ // should fail if global_ip is missing.
+ let mut nat_config = NatConfig {
+ enable: true, // not being validated, so we don't care what this is set to.
+ local_subnet: Some(expected_subnet_lifip.clone()),
+ global_ip: None,
+ pid: Some(expected_pid),
+ };
+ let result = from_nat_config(&mut nat_config);
+ assert_eq!(result.is_err(), true);
+
+ // should fail if pid is missing.
+ let mut nat_config = NatConfig {
+ enable: true, // not being validated, so we don't care what this is set to.
+ local_subnet: Some(expected_subnet_lifip.clone()),
+ global_ip: Some(expected_lifip.clone()),
+ pid: None,
+ };
+ let result = from_nat_config(&mut nat_config);
+ assert_eq!(result.is_err(), true);
+
+ // should pass when all fields are present.
+ let expected_wan_ip = fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address {
+ addr: match expected_lifip.clone().address {
+ IpAddr::V4(v4addr) => v4addr.octets(),
+ IpAddr::V6(_) => panic!("unexpected ipv6 address"),
+ },
+ });
+ let mut nat_config = NatConfig {
+ enable: true, // not being validated, so we don't care what this is set to.
+ local_subnet: Some(expected_subnet_lifip.clone()),
+ global_ip: Some(expected_lifip.clone()),
+ pid: Some(expected_pid),
+ };
+ let result = from_nat_config(&mut nat_config);
+ assert_eq!(result.is_ok(), true);
+ let (actual_subnet, actual_wan_ip, actual_nicid) = match result {
+ Ok((s, w, n)) => (s, w, n),
+ Err(e) => panic!("NatConfig test failed: {:?}", e),
+ };
+ assert_eq!(expected_subnet, actual_subnet);
+ assert_eq!(expected_wan_ip, actual_wan_ip);
+ assert_eq!(expected_pid.to_u32(), actual_nicid);
+ }
}
diff --git a/src/connectivity/management/network_manager/core/src/servicemgr.rs b/src/connectivity/management/network_manager/core/src/servicemgr.rs
index 70133f1..c06701f 100644
--- a/src/connectivity/management/network_manager/core/src/servicemgr.rs
+++ b/src/connectivity/management/network_manager/core/src/servicemgr.rs
@@ -13,10 +13,22 @@
// TODO(dpradilla): remove allow
#![allow(dead_code)]
-use crate::lifmgr::LIF;
+use crate::hal::PortId;
+use crate::lifmgr::{LifIpAddr, LIF};
use crate::{error, UUID};
use std::collections::HashSet;
+pub struct NatConfig {
+ pub enable: bool,
+ pub local_subnet: Option<LifIpAddr>,
+ pub global_ip: Option<LifIpAddr>,
+ pub pid: Option<PortId>,
+}
+
+struct SecurityFeatures {
+ nat: NatConfig,
+}
+
/// `Manager` keeps track of interfaces where a service is enabled
/// and verifies conflicting services are not enabled.
pub struct Manager {
@@ -24,12 +36,20 @@
dhcp_server: std::collections::HashSet<UUID>,
// dhcp_client has collection of interfaces DHCP dhcp_client is enabled.
dhcp_client: std::collections::HashSet<UUID>,
+ // security features.
+ security: SecurityFeatures,
}
impl Manager {
//! Creates a new Manager.
pub fn new() -> Self {
- Manager { dhcp_server: HashSet::new(), dhcp_client: HashSet::new() }
+ Manager {
+ dhcp_server: HashSet::new(),
+ dhcp_client: HashSet::new(),
+ security: SecurityFeatures {
+ nat: NatConfig { enable: false, local_subnet: None, global_ip: None, pid: None },
+ },
+ }
}
/// `enable_server` sets dhcp dhcp_server as enabled on indicated interface.
pub fn enable_server(&mut self, lif: &LIF) -> error::Result<bool> {
@@ -61,12 +81,37 @@
pub fn is_client_enabled(&mut self, lif: &LIF) -> bool {
self.dhcp_client.contains(&lif.id().uuid)
}
+ /// `is_nat_enabled` returns true if NAT is enabled.
+ pub fn is_nat_enabled(&mut self) -> bool {
+ self.security.nat.enable
+ }
+ /// `enable_nat` method indicates that NAT is now enabled.
+ pub fn enable_nat(&mut self) {
+ self.security.nat.enable = true;
+ }
+ /// `disable_nat` method indicates that NAT is now disabled.
+ pub fn disable_nat(&mut self) {
+ self.security.nat.enable = false;
+ }
+ /// `get_nat_config` returns the current NAT configuration.
+ pub fn get_nat_config(&mut self) -> &mut NatConfig {
+ &mut self.security.nat
+ }
+ /// `set_local_subnet_nat` sets the local subnet to be NATed.
+ pub fn set_local_subnet_nat(&mut self, s: LifIpAddr) {
+ self.security.nat.local_subnet = Some(s);
+ }
+ /// `set_global_ip_nat` sets the global IP to be NATed.
+ pub fn set_global_ip_nat(&mut self, g: LifIpAddr, p: PortId) {
+ self.security.nat.global_ip = Some(g);
+ self.security.nat.pid = Some(p);
+ }
}
#[cfg(test)]
mod tests {
use super::*;
- use crate::lifmgr::{LIFType, LIF};
+ use crate::lifmgr::{LIFType, LifIpAddr, LIF};
use crate::portmgr::PortId;
use crate::portmgr::{Port, PortManager};
@@ -191,4 +236,26 @@
assert!(!m.enable_client(&l).unwrap_or(false));
assert_eq!(m.dhcp_client.len(), 0);
}
+
+ #[test]
+ fn test_nat_config() {
+ let mut m = Manager::new();
+
+ assert_eq!(m.get_nat_config().enable, false);
+ assert_eq!(m.is_nat_enabled(), false);
+
+ m.enable_nat();
+ assert_eq!(m.is_nat_enabled(), true);
+
+ m.disable_nat();
+ assert_eq!(m.is_nat_enabled(), false);
+
+ let lifip = LifIpAddr { address: "169.254.0.1".parse().unwrap(), prefix: 32 };
+ m.set_local_subnet_nat(lifip.clone());
+ assert_eq!(m.get_nat_config().local_subnet.as_ref().unwrap(), &lifip);
+
+ let pid = PortId::from(1);
+ m.set_global_ip_nat(lifip.clone(), pid);
+ assert_eq!(m.get_nat_config().pid.unwrap(), pid);
+ }
}
diff --git a/src/connectivity/management/network_manager/src/eventloop.rs b/src/connectivity/management/network_manager/src/eventloop.rs
index ae773d4..58af16a 100644
--- a/src/connectivity/management/network_manager/src/eventloop.rs
+++ b/src/connectivity/management/network_manager/src/eventloop.rs
@@ -82,7 +82,6 @@
pub struct EventLoop {
event_recv: Option<mpsc::UnboundedReceiver<Event>>,
device: DeviceState,
- packet_filter: PacketFilter,
}
impl EventLoop {
@@ -96,10 +95,10 @@
let packet_filter = PacketFilter::start()
.context("network_manager failed to start packet filter!")
.unwrap();
+
Ok(EventLoop {
event_recv: Some(event_recv),
- device: DeviceState::new(netcfg),
- packet_filter,
+ device: DeviceState::new(netcfg, packet_filter),
})
}
@@ -109,6 +108,7 @@
self.event_recv.take().unwrap(),
self.device.take_event_stream().map(|e| Event::StackObservable(e)),
);
+
loop {
match select_stream.next().await {
Some(Event::FidlRouterAdminEvent(req)) => {
@@ -277,7 +277,7 @@
}
RouterAdminRequest::SetFilter { rule, responder } => {
let r = self
- .packet_filter
+ .device
.set_filter(rule)
.await
.context("Error installing new packet filter rule");
@@ -553,8 +553,7 @@
responder.send(None, not_supported!())
}
RouterStateRequest::GetFilters { responder } => {
- let result =
- self.packet_filter.get_filters().await.context("Error getting filters");
+ let result = self.device.get_filters().await.context("Error getting filters");
let mut filter_rules = Vec::new();
match result {
Ok(f) => {