diff --git a/easytier/src/common/config.rs b/easytier/src/common/config.rs index 4887904..e834da0 100644 --- a/easytier/src/common/config.rs +++ b/easytier/src/common/config.rs @@ -23,8 +23,8 @@ pub trait ConfigLoader: Send + Sync { fn get_netns(&self) -> Option; fn set_netns(&self, ns: Option); - fn get_ipv4(&self) -> Option; - fn set_ipv4(&self, addr: Option); + fn get_ipv4(&self) -> Option; + fn set_ipv4(&self, addr: Option); fn get_dhcp(&self) -> bool; fn set_dhcp(&self, dhcp: bool); @@ -324,16 +324,23 @@ impl ConfigLoader for TomlConfigLoader { self.config.lock().unwrap().netns = ns; } - fn get_ipv4(&self) -> Option { + fn get_ipv4(&self) -> Option { let locked_config = self.config.lock().unwrap(); locked_config .ipv4 .as_ref() .map(|s| s.parse().ok()) .flatten() + .map(|c: cidr::Ipv4Inet| { + if c.network_length() == 32 { + cidr::Ipv4Inet::new(c.address(), 24).unwrap() + } else { + c + } + }) } - fn set_ipv4(&self, addr: Option) { + fn set_ipv4(&self, addr: Option) { self.config.lock().unwrap().ipv4 = if let Some(addr) = addr { Some(addr.to_string()) } else { @@ -590,7 +597,7 @@ level = "warn" assert!(ret.is_ok()); let ret = ret.unwrap(); - assert_eq!("10.144.144.10", ret.get_ipv4().unwrap().to_string()); + assert_eq!("10.144.144.10/24", ret.get_ipv4().unwrap().to_string()); assert_eq!( vec!["tcp://0.0.0.0:11010", "udp://0.0.0.0:11010"], diff --git a/easytier/src/common/global_ctx.rs b/easytier/src/common/global_ctx.rs index 693322b..3d481b1 100644 --- a/easytier/src/common/global_ctx.rs +++ b/easytier/src/common/global_ctx.rs @@ -40,8 +40,8 @@ pub enum GlobalCtxEvent { VpnPortalClientConnected(String, String), // (portal, client ip) VpnPortalClientDisconnected(String, String), // (portal, client ip) - DhcpIpv4Changed(Option, Option), // (old, new) - DhcpIpv4Conflicted(Option), + DhcpIpv4Changed(Option, Option), // (old, new) + DhcpIpv4Conflicted(Option), } type EventBus = tokio::sync::broadcast::Sender; @@ -56,7 +56,7 @@ pub struct GlobalCtx { event_bus: EventBus, - cached_ipv4: AtomicCell>, + cached_ipv4: AtomicCell>, cached_proxy_cidrs: AtomicCell>>, ip_collector: Arc, @@ -139,7 +139,7 @@ impl GlobalCtx { } } - pub fn get_ipv4(&self) -> Option { + pub fn get_ipv4(&self) -> Option { if let Some(ret) = self.cached_ipv4.load() { return Some(ret); } @@ -148,7 +148,7 @@ impl GlobalCtx { return addr; } - pub fn set_ipv4(&self, addr: Option) { + pub fn set_ipv4(&self, addr: Option) { self.config.set_ipv4(addr); self.cached_ipv4.store(None); } diff --git a/easytier/src/easytier-cli.rs b/easytier/src/easytier-cli.rs index c51529d..783b57c 100644 --- a/easytier/src/easytier-cli.rs +++ b/easytier/src/easytier-cli.rs @@ -227,7 +227,12 @@ impl CommandHandler { impl From for PeerTableItem { fn from(p: PeerRoutePair) -> Self { PeerTableItem { - ipv4: p.route.ipv4_addr.clone(), + ipv4: p + .route + .ipv4_addr + .clone() + .map(|ip| ip.to_string()) + .unwrap_or_default(), hostname: p.route.hostname.clone(), cost: cost_to_str(p.route.cost), lat_ms: float_to_str(p.get_latency_ms().unwrap_or(0.0), 3), @@ -413,7 +418,12 @@ impl CommandHandler { if p.route.cost == 1 { items.push(RouteTableItem { - ipv4: p.route.ipv4_addr.clone(), + ipv4: p + .route + .ipv4_addr + .clone() + .map(|ip| ip.to_string()) + .unwrap_or_default(), hostname: p.route.hostname.clone(), proxy_cidrs: p.route.proxy_cidrs.clone().join(",").to_string(), next_hop_ipv4: "DIRECT".to_string(), @@ -428,10 +438,20 @@ impl CommandHandler { }); } else { items.push(RouteTableItem { - ipv4: p.route.ipv4_addr.clone(), + ipv4: p + .route + .ipv4_addr + .clone() + .map(|ip| ip.to_string()) + .unwrap_or_default(), hostname: p.route.hostname.clone(), proxy_cidrs: p.route.proxy_cidrs.clone().join(",").to_string(), - next_hop_ipv4: next_hop_pair.route.ipv4_addr.clone(), + next_hop_ipv4: next_hop_pair + .route + .ipv4_addr + .clone() + .map(|ip| ip.to_string()) + .unwrap_or_default(), next_hop_hostname: next_hop_pair.route.hostname.clone(), next_hop_lat: next_hop_pair.get_latency_ms().unwrap_or(0.0), cost: p.route.cost, diff --git a/easytier/src/gateway/icmp_proxy.rs b/easytier/src/gateway/icmp_proxy.rs index 8d35131..86e12b8 100644 --- a/easytier/src/gateway/icmp_proxy.rs +++ b/easytier/src/gateway/icmp_proxy.rs @@ -358,7 +358,12 @@ impl IcmpProxy { if !self.cidr_set.contains_v4(ipv4.get_destination()) && !is_exit_node && !(self.global_ctx.no_tun() - && Some(ipv4.get_destination()) == self.global_ctx.get_ipv4()) + && Some(ipv4.get_destination()) + == self + .global_ctx + .get_ipv4() + .as_ref() + .map(cidr::Ipv4Inet::address)) { return None; } @@ -382,7 +387,14 @@ impl IcmpProxy { return None; } - if self.global_ctx.no_tun() && Some(ipv4.get_destination()) == self.global_ctx.get_ipv4() { + if self.global_ctx.no_tun() + && Some(ipv4.get_destination()) + == self + .global_ctx + .get_ipv4() + .as_ref() + .map(cidr::Ipv4Inet::address) + { self.send_icmp_reply_to_peer( &ipv4.get_destination(), &ipv4.get_source(), diff --git a/easytier/src/gateway/socks5.rs b/easytier/src/gateway/socks5.rs index 5d7c2c1..c3635f4 100644 --- a/easytier/src/gateway/socks5.rs +++ b/easytier/src/gateway/socks5.rs @@ -111,7 +111,7 @@ struct Socks5Entry { type Socks5EntrySet = Arc>; struct Socks5ServerNet { - ipv4_addr: Ipv4Addr, + ipv4_addr: cidr::Ipv4Inet, auth: Option, smoltcp_net: Arc, @@ -122,7 +122,7 @@ struct Socks5ServerNet { impl Socks5ServerNet { pub fn new( - ipv4_addr: Ipv4Addr, + ipv4_addr: cidr::Ipv4Inet, auth: Option, peer_manager: Arc, packet_recv: Arc>>, @@ -173,8 +173,10 @@ impl Socks5ServerNet { dev, NetConfig::new( interface_config, - format!("{}/24", ipv4_addr).parse().unwrap(), - vec![format!("{}", ipv4_addr).parse().unwrap()], + format!("{}/{}", ipv4_addr.address(), ipv4_addr.network_length()) + .parse() + .unwrap(), + vec![format!("{}", ipv4_addr.address()).parse().unwrap()], ), ); diff --git a/easytier/src/gateway/tcp_proxy.rs b/easytier/src/gateway/tcp_proxy.rs index ed5e273..a5987ae 100644 --- a/easytier/src/gateway/tcp_proxy.rs +++ b/easytier/src/gateway/tcp_proxy.rs @@ -1,3 +1,4 @@ +use cidr::Ipv4Inet; use core::panic; use crossbeam::atomic::AtomicCell; use dashmap::DashMap; @@ -526,7 +527,8 @@ impl TcpProxy { tracing::warn!("set_nodelay failed, ignore it: {:?}", e); } - let nat_dst = if Some(nat_entry.dst.ip()) == global_ctx.get_ipv4().map(|ip| IpAddr::V4(ip)) + let nat_dst = if Some(nat_entry.dst.ip()) + == global_ctx.get_ipv4().map(|ip| IpAddr::V4(ip.address())) { format!("127.0.0.1:{}", nat_entry.dst.port()) .parse() @@ -591,7 +593,10 @@ impl TcpProxy { { Some(Ipv4Addr::new(192, 88, 99, 254)) } else { - self.global_ctx.get_ipv4() + self.global_ctx + .get_ipv4() + .as_ref() + .map(cidr::Ipv4Inet::address) } } @@ -621,7 +626,8 @@ impl TcpProxy { if !self.cidr_set.contains_v4(ipv4.get_destination()) && !is_exit_node && !(self.global_ctx.no_tun() - && Some(ipv4.get_destination()) == self.global_ctx.get_ipv4()) + && Some(ipv4.get_destination()) + == self.global_ctx.get_ipv4().as_ref().map(Ipv4Inet::address)) { return None; } diff --git a/easytier/src/gateway/udp_proxy.rs b/easytier/src/gateway/udp_proxy.rs index 18e382e..c074704 100644 --- a/easytier/src/gateway/udp_proxy.rs +++ b/easytier/src/gateway/udp_proxy.rs @@ -4,6 +4,7 @@ use std::{ time::Duration, }; +use cidr::Ipv4Inet; use crossbeam::atomic::AtomicCell; use dashmap::DashMap; use pnet::packet::{ @@ -245,7 +246,8 @@ impl UdpProxy { if !self.cidr_set.contains_v4(ipv4.get_destination()) && !is_exit_node && !(self.global_ctx.no_tun() - && Some(ipv4.get_destination()) == self.global_ctx.get_ipv4()) + && Some(ipv4.get_destination()) + == self.global_ctx.get_ipv4().as_ref().map(Ipv4Inet::address)) { return None; } @@ -296,14 +298,16 @@ impl UdpProxy { .replace(tokio::spawn(UdpNatEntry::forward_task( nat_entry.clone(), self.sender.clone(), - self.global_ctx.get_ipv4()?, + self.global_ctx.get_ipv4().map(|x| x.address())?, ))); } nat_entry.mark_active(); // TODO: should it be async. - let dst_socket = if Some(ipv4.get_destination()) == self.global_ctx.get_ipv4() { + let dst_socket = if Some(ipv4.get_destination()) + == self.global_ctx.get_ipv4().as_ref().map(Ipv4Inet::address) + { format!("127.0.0.1:{}", udp_packet.get_destination()) .parse() .unwrap() diff --git a/easytier/src/instance/instance.rs b/easytier/src/instance/instance.rs index e973e8a..d347026 100644 --- a/easytier/src/instance/instance.rs +++ b/easytier/src/instance/instance.rs @@ -270,19 +270,11 @@ impl Instance { let mut used_ipv4 = HashSet::new(); for route in routes { - if route.ipv4_addr.is_empty() { - continue; - } - - let Ok(peer_ipv4_addr) = route.ipv4_addr.parse::() else { + let Some(peer_ipv4_addr) = route.ipv4_addr else { continue; }; - let Ok(peer_ipv4_addr) = Ipv4Inet::new(peer_ipv4_addr, 24) else { - continue; - }; - - used_ipv4.insert(peer_ipv4_addr); + used_ipv4.insert(peer_ipv4_addr.into()); } let dhcp_inet = used_ipv4.iter().next().unwrap_or(&default_ipv4_addr); @@ -304,7 +296,7 @@ impl Instance { continue; } - let last_ip = current_dhcp_ip.as_ref().map(Ipv4Inet::address); + let last_ip = current_dhcp_ip.clone(); tracing::debug!( ?current_dhcp_ip, ?candidate_ipv4_addr, @@ -316,11 +308,9 @@ impl Instance { if let Some(ip) = candidate_ipv4_addr { if global_ctx_c.no_tun() { current_dhcp_ip = Some(ip); - global_ctx_c.set_ipv4(Some(ip.address())); - global_ctx_c.issue_event(GlobalCtxEvent::DhcpIpv4Changed( - last_ip, - Some(ip.address()), - )); + global_ctx_c.set_ipv4(Some(ip)); + global_ctx_c + .issue_event(GlobalCtxEvent::DhcpIpv4Changed(last_ip, Some(ip))); continue; } @@ -331,7 +321,7 @@ impl Instance { &peer_manager_c, _peer_packet_receiver.clone(), ); - if let Err(e) = new_nic_ctx.run(ip.address()).await { + if let Err(e) = new_nic_ctx.run(ip).await { tracing::error!( ?current_dhcp_ip, ?candidate_ipv4_addr, @@ -345,9 +335,8 @@ impl Instance { } current_dhcp_ip = Some(ip); - global_ctx_c.set_ipv4(Some(ip.address())); - global_ctx_c - .issue_event(GlobalCtxEvent::DhcpIpv4Changed(last_ip, Some(ip.address()))); + global_ctx_c.set_ipv4(Some(ip)); + global_ctx_c.issue_event(GlobalCtxEvent::DhcpIpv4Changed(last_ip, Some(ip))); } else { current_dhcp_ip = None; global_ctx_c.set_ipv4(None); diff --git a/easytier/src/instance/virtual_nic.rs b/easytier/src/instance/virtual_nic.rs index 128cd53..c45108b 100644 --- a/easytier/src/instance/virtual_nic.rs +++ b/easytier/src/instance/virtual_nic.rs @@ -504,8 +504,7 @@ pub fn reg_change_catrgory_in_profile(dev_name: &str) -> io::Result<()> { let subkey = profiles_key.open_subkey_with_flags(&subkey_name, KEY_ALL_ACCESS)?; match subkey.get_value::("ProfileName") { Ok(profile_name) => { - if !dev_name.is_empty() && dev_name == profile_name - { + if !dev_name.is_empty() && dev_name == profile_name { match subkey.set_value("Category", &1u32) { Ok(_) => tracing::trace!("Successfully set Category in registry"), Err(e) => tracing::error!("Failed to set Category in registry: {}", e), @@ -548,14 +547,16 @@ impl NicCtx { } } - async fn assign_ipv4_to_tun_device(&self, ipv4_addr: Ipv4Addr) -> Result<(), Error> { + async fn assign_ipv4_to_tun_device(&self, ipv4_addr: cidr::Ipv4Inet) -> Result<(), Error> { let nic = self.nic.lock().await; nic.link_up().await?; nic.remove_ip(None).await?; - nic.add_ip(ipv4_addr, 24).await?; + nic.add_ip(ipv4_addr.address(), ipv4_addr.network_length() as i32) + .await?; #[cfg(any(target_os = "macos", target_os = "freebsd"))] { - nic.add_route(ipv4_addr, 24).await?; + nic.add_route(ipv4_addr.first_address(), ipv4_addr.network_length()) + .await?; } Ok(()) } @@ -710,18 +711,17 @@ impl NicCtx { Ok(()) } - pub async fn run(&mut self, ipv4_addr: Ipv4Addr) -> Result<(), Error> { + pub async fn run(&mut self, ipv4_addr: cidr::Ipv4Inet) -> Result<(), Error> { let tunnel = { let mut nic = self.nic.lock().await; match nic.create_dev().await { Ok(ret) => { - - #[cfg(target_os = "windows")] + #[cfg(target_os = "windows")] { let dev_name = self.global_ctx.get_flags().dev_name; let _ = reg_change_catrgory_in_profile(&dev_name); } - + self.global_ctx .issue_event(GlobalCtxEvent::TunDeviceReady(nic.ifname().to_string())); ret diff --git a/easytier/src/peers/peer_manager.rs b/easytier/src/peers/peer_manager.rs index 0a7ac78..9183f6e 100644 --- a/easytier/src/peers/peer_manager.rs +++ b/easytier/src/peers/peer_manager.rs @@ -718,8 +718,16 @@ impl PeerManager { let mut is_exit_node = false; let mut dst_peers = vec![]; - // NOTE: currently we only support ipv4 and cidr is 24 - if ipv4_addr.is_broadcast() || ipv4_addr.is_multicast() || ipv4_addr.octets()[3] == 255 { + let network_length = self + .global_ctx + .get_ipv4() + .map(|x| x.network_length()) + .unwrap_or(24); + let ipv4_inet = cidr::Ipv4Inet::new(ipv4_addr, network_length).unwrap(); + if ipv4_addr.is_broadcast() + || ipv4_addr.is_multicast() + || ipv4_addr == ipv4_inet.last_address() + { dst_peers.extend( self.peers .list_routes() diff --git a/easytier/src/peers/peer_ospf_route.rs b/easytier/src/peers/peer_ospf_route.rs index 39d60ba..f9cb74c 100644 --- a/easytier/src/peers/peer_ospf_route.rs +++ b/easytier/src/peers/peer_ospf_route.rs @@ -30,7 +30,7 @@ use crate::{ }, peers::route_trait::{Route, RouteInterfaceBox}, proto::{ - common::{NatType, StunInfo}, + common::{Ipv4Inet, NatType, StunInfo}, peer_rpc::{ route_foreign_network_infos, ForeignNetworkRouteInfoEntry, ForeignNetworkRouteInfoKey, OspfRouteRpc, OspfRouteRpcClientFactory, OspfRouteRpcServer, PeerIdVersion, @@ -118,6 +118,7 @@ impl RoutePeerInfo { easytier_version: EASYTIER_VERSION.to_string(), feature_flag: None, peer_route_id: 0, + network_length: 24, } } @@ -131,7 +132,7 @@ impl RoutePeerInfo { peer_id: my_peer_id, inst_id: Some(global_ctx.get_id().into()), cost: 0, - ipv4_addr: global_ctx.get_ipv4().map(|x| x.into()), + ipv4_addr: global_ctx.get_ipv4().map(|x| x.address().into()), proxy_cidrs: global_ctx .get_proxy_cidrs() .iter() @@ -150,6 +151,10 @@ impl RoutePeerInfo { easytier_version: EASYTIER_VERSION.to_string(), feature_flag: Some(global_ctx.get_feature_flags()), peer_route_id, + network_length: global_ctx + .get_ipv4() + .map(|x| x.network_length() as u32) + .unwrap_or(24), }; let need_update_periodically = if let Ok(Ok(d)) = @@ -171,12 +176,21 @@ impl RoutePeerInfo { impl Into for RoutePeerInfo { fn into(self) -> crate::proto::cli::Route { + let network_length = if self.network_length == 0 { + 24 + } else { + self.network_length + }; + crate::proto::cli::Route { peer_id: self.peer_id, ipv4_addr: if let Some(ipv4_addr) = self.ipv4_addr { - ipv4_addr.to_string() + Some(Ipv4Inet { + address: Some(ipv4_addr.into()), + network_length, + }) } else { - "".to_string() + None }, next_hop_peer_id: 0, cost: self.cost as i32, diff --git a/easytier/src/proto/cli.proto b/easytier/src/proto/cli.proto index 1b2dd1f..74321ca 100644 --- a/easytier/src/proto/cli.proto +++ b/easytier/src/proto/cli.proto @@ -45,7 +45,7 @@ message ListPeerResponse { message Route { uint32 peer_id = 1; - string ipv4_addr = 2; + common.Ipv4Inet ipv4_addr = 2; uint32 next_hop_peer_id = 3; int32 cost = 4; repeated string proxy_cidrs = 5; diff --git a/easytier/src/proto/common.proto b/easytier/src/proto/common.proto index bd9901d..6eaad97 100644 --- a/easytier/src/proto/common.proto +++ b/easytier/src/proto/common.proto @@ -72,6 +72,11 @@ message Ipv6Addr { uint32 part4 = 4; } +message Ipv4Inet { + Ipv4Addr address = 1; + uint32 network_length = 2; +} + message Url { string url = 1; } message SocketAddr { diff --git a/easytier/src/proto/common.rs b/easytier/src/proto/common.rs index 7b87667..d389ed5 100644 --- a/easytier/src/proto/common.rs +++ b/easytier/src/proto/common.rs @@ -1,5 +1,7 @@ use std::{fmt::Display, str::FromStr}; +use anyhow::Context; + include!(concat!(env!("OUT_DIR"), "/common.rs")); impl From for Uuid { @@ -60,10 +62,8 @@ impl From for std::net::Ipv6Addr { let part3 = value.part3.to_be_bytes(); let part4 = value.part4.to_be_bytes(); std::net::Ipv6Addr::from([ - part1[0], part1[1], part1[2], part1[3], - part2[0], part2[1], part2[2], part2[3], - part3[0], part3[1], part3[2], part3[3], - part4[0], part4[1], part4[2], part4[3] + part1[0], part1[1], part1[2], part1[3], part2[0], part2[1], part2[2], part2[3], + part3[0], part3[1], part3[2], part3[3], part4[0], part4[1], part4[2], part4[3], ]) } } @@ -74,6 +74,37 @@ impl ToString for Ipv6Addr { } } +impl From for Ipv4Inet { + fn from(value: cidr::Ipv4Inet) -> Self { + Ipv4Inet { + address: Some(value.address().into()), + network_length: value.network_length() as u32, + } + } +} + +impl From for cidr::Ipv4Inet { + fn from(value: Ipv4Inet) -> Self { + cidr::Ipv4Inet::new(value.address.unwrap().into(), value.network_length as u8).unwrap() + } +} + +impl std::fmt::Display for Ipv4Inet { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", cidr::Ipv4Inet::from(self.clone())) + } +} + +impl FromStr for Ipv4Inet { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + Ok(Ipv4Inet::from( + cidr::Ipv4Inet::from_str(s).with_context(|| "Failed to parse Ipv4Inet")?, + )) + } +} + impl From for Url { fn from(value: url::Url) -> Self { Url { diff --git a/easytier/src/proto/peer_rpc.proto b/easytier/src/proto/peer_rpc.proto index 8fffa37..f493e36 100644 --- a/easytier/src/proto/peer_rpc.proto +++ b/easytier/src/proto/peer_rpc.proto @@ -20,6 +20,8 @@ message RoutePeerInfo { string easytier_version = 10; common.PeerFeatureFlag feature_flag = 11; uint64 peer_route_id = 12; + + uint32 network_length = 13; } message PeerIdVersion { diff --git a/easytier/src/tests/mod.rs b/easytier/src/tests/mod.rs index 03399da..cd6b26c 100644 --- a/easytier/src/tests/mod.rs +++ b/easytier/src/tests/mod.rs @@ -130,7 +130,7 @@ pub fn enable_log() { fn check_route(ipv4: &str, dst_peer_id: PeerId, routes: Vec) { let mut found = false; for r in routes.iter() { - if r.ipv4_addr == ipv4.to_string() { + if r.ipv4_addr == Some(ipv4.parse().unwrap()) { found = true; assert_eq!(r.peer_id, dst_peer_id, "{:?}", routes); } @@ -154,7 +154,7 @@ async fn wait_proxy_route_appear( let r = r; if r.proxy_cidrs.contains(&proxy_cidr.to_owned()) { assert_eq!(r.peer_id, dst_peer_id); - assert_eq!(r.ipv4_addr, ipv4); + assert_eq!(r.ipv4_addr, Some(ipv4.parse().unwrap())); return; } } diff --git a/easytier/src/tests/three_node.rs b/easytier/src/tests/three_node.rs index ce2718e..4be6972 100644 --- a/easytier/src/tests/three_node.rs +++ b/easytier/src/tests/three_node.rs @@ -184,13 +184,13 @@ pub async fn basic_three_node_test(#[values("tcp", "udp", "wg", "ws", "wss")] pr let insts = init_three_node(proto).await; check_route( - "10.144.144.2", + "10.144.144.2/24", insts[1].peer_id(), insts[0].get_peer_manager().list_routes().await, ); check_route( - "10.144.144.3", + "10.144.144.3/24", insts[2].peer_id(), insts[0].get_peer_manager().list_routes().await, ); @@ -357,7 +357,7 @@ pub async fn subnet_proxy_three_node_test( wait_proxy_route_appear( &insts[0].get_peer_manager(), - "10.144.144.3", + "10.144.144.3/24", insts[2].peer_id(), "10.1.2.0/24", ) diff --git a/easytier/src/vpn_portal/wireguard.rs b/easytier/src/vpn_portal/wireguard.rs index 738cbd3..d62ea4f 100644 --- a/easytier/src/vpn_portal/wireguard.rs +++ b/easytier/src/vpn_portal/wireguard.rs @@ -284,13 +284,11 @@ impl VpnPortal for WireGuard { .collect::>(); for ipv4 in routes .iter() - .map(|x| x.ipv4_addr.clone()) - .chain(global_ctx.get_ipv4().iter().map(|x| x.to_string())) + .filter(|x| x.ipv4_addr.is_some()) + .map(|x| x.ipv4_addr.unwrap()) + .chain(global_ctx.get_ipv4().into_iter().map(Into::into)) { - let Ok(ipv4) = ipv4.parse() else { - continue; - }; - let inet = Ipv4Inet::new(ipv4, 24).unwrap(); + let inet = Ipv4Inet::from(ipv4); allow_ips.push(inet.network().to_string()); break; }