From 4a6d87b7247554ccf2feccad798ed47844fa3a64 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Wed, 6 Aug 2025 09:55:44 +0200 Subject: [PATCH 1/5] Merge adjacent IP subnets into ranges for nftables (#182) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * restore merging of ips * merge adjacent subnets for nft (#185) * merge adjacent elements * update new tests * linter fixes * linter fix * review fixes --------- Co-authored-by: Maciek <19913370+wojcik91@users.noreply.github.com> Co-authored-by: Maciej Wójcik Co-authored-by: Maciej Wójcik --- src/enterprise/firewall/iprange.rs | 17 + src/enterprise/firewall/mod.rs | 99 +++++ src/enterprise/firewall/nftables/mod.rs | 408 ++++++++++++++++-- src/enterprise/firewall/nftables/netfilter.rs | 83 +--- 4 files changed, 489 insertions(+), 118 deletions(-) diff --git a/src/enterprise/firewall/iprange.rs b/src/enterprise/firewall/iprange.rs index 33eab39d..b1b4acd6 100644 --- a/src/enterprise/firewall/iprange.rs +++ b/src/enterprise/firewall/iprange.rs @@ -76,6 +76,23 @@ impl IpAddrRange { Self::V6(_) => true, } } + + /// Returns the start of the range. + pub fn start(&self) -> IpAddr { + match self { + Self::V4(range) => IpAddr::V4(*range.start()), + Self::V6(range) => IpAddr::V6(*range.start()), + } + } + + /// Returns the end of the range. + /// If the range is empty, returns the start of the range. + pub fn end(&self) -> IpAddr { + match self { + Self::V4(range) => IpAddr::V4(*range.end()), + Self::V6(range) => IpAddr::V6(*range.end()), + } + } } impl Iterator for IpAddrRange { diff --git a/src/enterprise/firewall/mod.rs b/src/enterprise/firewall/mod.rs index 5f564697..ef4304d2 100644 --- a/src/enterprise/firewall/mod.rs +++ b/src/enterprise/firewall/mod.rs @@ -26,6 +26,24 @@ pub(crate) enum Address { } impl Address { + // FIXME: remove after merging nft hotfix into dev + #[allow(dead_code)] + pub fn first(&self) -> IpAddr { + match self { + Address::Network(network) => network.ip(), + Address::Range(range) => range.start(), + } + } + + // FIXME: remove after merging nft hotfix into dev + #[allow(dead_code)] + pub fn last(&self) -> IpAddr { + match self { + Address::Network(network) => max_address(network), + Address::Range(range) => range.end(), + } + } + pub fn from_proto(ip: &proto::enterprise::firewall::IpAddress) -> Result { match &ip.address { Some(proto::enterprise::firewall::ip_address::Address::Ip(ip)) => { @@ -322,3 +340,84 @@ pub enum FirewallError { #[error("Firewall transaction failed: {0}")] TransactionFailed(String), } + +/// Get the max address in a network. +/// +/// - In IPv4 this is the broadcast address. +/// - In IPv6 this is just the last address in the network. +pub fn max_address(network: &IpNetwork) -> IpAddr { + match network { + IpNetwork::V4(network) => { + let addr = network.ip().to_bits(); + let mask = network.mask().to_bits(); + + IpAddr::V4(Ipv4Addr::from(addr | !mask)) + } + IpNetwork::V6(network) => { + let addr = network.ip().to_bits(); + let mask = network.mask().to_bits(); + + IpAddr::V6(Ipv6Addr::from(addr | !mask)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_max_address_ipv4_24() { + let network = IpNetwork::V4(Ipv4Network::from_str("192.168.1.0/24").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 255))); + } + + #[test] + fn test_max_address_ipv4_16() { + let network = IpNetwork::V4(Ipv4Network::from_str("10.1.0.0/16").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V4(Ipv4Addr::new(10, 1, 255, 255))); + } + + #[test] + fn test_max_address_ipv4_8() { + let network = IpNetwork::V4(Ipv4Network::from_str("172.16.0.0/8").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V4(Ipv4Addr::new(172, 255, 255, 255))); + } + + #[test] + fn test_max_address_ipv4_32() { + let network = IpNetwork::V4(Ipv4Network::from_str("192.168.1.1/32").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))); + } + + #[test] + fn test_max_address_ipv6_64() { + let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8::/64").unwrap()); + let max = max_address(&network); + assert_eq!( + max, + IpAddr::V6(Ipv6Addr::from_str("2001:db8::ffff:ffff:ffff:ffff").unwrap()) + ); + } + + #[test] + fn test_max_address_ipv6_128() { + let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8::1/128").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V6(Ipv6Addr::from_str("2001:db8::1").unwrap())); + } + + #[test] + fn test_max_address_ipv6_48() { + let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8:1234::/48").unwrap()); + let max = max_address(&network); + assert_eq!( + max, + IpAddr::V6(Ipv6Addr::from_str("2001:db8:1234:ffff:ffff:ffff:ffff:ffff").unwrap()) + ); + } +} diff --git a/src/enterprise/firewall/nftables/mod.rs b/src/enterprise/firewall/nftables/mod.rs index d5e5574b..614b9831 100644 --- a/src/enterprise/firewall/nftables/mod.rs +++ b/src/enterprise/firewall/nftables/mod.rs @@ -1,6 +1,9 @@ pub mod netfilter; -use std::sync::atomic::{AtomicU32, Ordering}; +use std::{ + net::{IpAddr, Ipv4Addr, Ipv6Addr}, + sync::atomic::{AtomicU32, Ordering}, +}; use netfilter::{ allow_established_traffic, apply_filter_rules, drop_table, ignore_unrelated_traffic, @@ -10,8 +13,10 @@ use nftnl::Batch; use super::{ api::{FirewallApi, FirewallManagementApi}, + iprange::IpAddrRangeError, Address, FirewallError, FirewallRule, Policy, Port, Protocol, }; +use crate::enterprise::firewall::iprange::IpAddrRange; static SET_ID_COUNTER: AtomicU32 = AtomicU32::new(0); @@ -51,29 +56,110 @@ struct FilterRule<'a> { negated_iifname: bool, } +/// Merges any contiguous subets or addres ranges into an address range. +/// +/// This reflects the way `nft` CLI handles such cases. +/// Otherwise first address in any subnet after the first is not matched. +/// For example if we use `172.30.0.2/31, 172.30.0.4/31` as `saddr` in a rule, +/// then 172.30.0.4 will not be matched. +fn merge_addrs(addrs: Vec
) -> Result, IpAddrRangeError> { + debug!("Merging any contiguous subnets and ranges found within address list: {addrs:?}"); + + if addrs.is_empty() { + debug!("No addresses provided, returning empty vector."); + return Ok(Vec::new()); + } + + let mut merged_addrs = Vec::new(); + let mut current_address = None; + + // we can assume addresses coming from the core + // are already sorted and non-overlapping + for next_address in addrs { + match ¤t_address { + None => { + debug!("Initializing current address with: {next_address:?}"); + current_address = Some(next_address); + } + Some(previous_address) => { + let previous_range_start = previous_address.first(); + let previous_range_end = previous_address.last(); + let next_ip = next_ip(previous_range_end); + + let next_range_start = next_address.first(); + let next_range_end = next_address.last(); + + // check if range is adjacent to current address + if next_range_start == next_ip { + // replace current address with a combined range + debug!("Merging {next_address:?} with {current_address:?}"); + current_address = Some(Address::Range(IpAddrRange::new( + previous_range_start, + next_range_end, + )?)); + } else { + // push previous address to result and replace with next address + merged_addrs.push(previous_address.clone()); + current_address = Some(next_address); + }; + } + } + } + + // push last remaining address to results + if let Some(address) = current_address { + debug!("Pushing last remaining address into results: {address:?}"); + merged_addrs.push(address) + } + + debug!("Prepared addresses: {merged_addrs:?}"); + + Ok(merged_addrs) +} + +/// Returns the next IP address in sequence, handling overflow via wrapping +fn next_ip(ip: IpAddr) -> IpAddr { + match ip { + IpAddr::V4(ipv4) => { + let ip_u32 = ipv4.to_bits(); + let next_ip_u32 = ip_u32.wrapping_add(1); + IpAddr::V4(Ipv4Addr::from(next_ip_u32)) + } + IpAddr::V6(ipv6) => { + let ip_u128 = ipv6.to_bits(); + let next_ip_u128 = ip_u128.wrapping_add(1); + IpAddr::V6(Ipv6Addr::from(next_ip_u128)) + } + } +} + impl FirewallApi { fn add_rule(&mut self, rule: FirewallRule) -> Result<(), FirewallError> { debug!("Applying the following Defguard ACL rule: {rule:?}"); - let mut rules = Vec::new(); let batch = if let Some(ref mut batch) = self.batch { batch } else { return Err(FirewallError::TransactionNotStarted); }; + let mut filter_rules = Vec::new(); debug!( "The rule will be split into multiple nftables rules based on the specified \ destination ports and protocols." ); + + let source_addrs = merge_addrs(rule.source_addrs)?; + let dest_addrs = merge_addrs(rule.destination_addrs)?; + if rule.destination_ports.is_empty() { debug!( "No destination ports specified, applying single aggregate nftables rule for \ every protocol." ); let rule = FilterRule { - src_ips: &rule.source_addrs, - dest_ips: &rule.destination_addrs, - protocols: rule.protocols, + src_ips: &source_addrs, + dest_ips: &dest_addrs, + protocols: rule.protocols.clone(), action: rule.verdict, counter: true, defguard_rule_id: rule.id, @@ -81,47 +167,35 @@ impl FirewallApi { comment: rule.comment.clone(), ..Default::default() }; - rules.push(rule); + filter_rules.push(rule); } else if !rule.protocols.is_empty() { debug!( "Destination ports and protocols specified, applying individual nftables rules \ for each protocol." ); - for protocol in rule.protocols { + for protocol in rule.protocols.clone() { debug!("Applying rule for protocol: {protocol:?}"); + let mut filter_rule = FilterRule { + src_ips: &source_addrs, + dest_ips: &dest_addrs, + protocols: vec![protocol], + action: rule.verdict, + counter: true, + defguard_rule_id: rule.id, + v4: rule.ipv4, + comment: rule.comment.clone(), + ..Default::default() + }; if protocol.supports_ports() { debug!("Protocol supports ports, rule."); - let rule = FilterRule { - src_ips: &rule.source_addrs, - dest_ips: &rule.destination_addrs, - dest_ports: &rule.destination_ports, - protocols: vec![protocol], - action: rule.verdict, - counter: true, - defguard_rule_id: rule.id, - v4: rule.ipv4, - comment: rule.comment.clone(), - ..Default::default() - }; - rules.push(rule); + filter_rule.dest_ports = &rule.destination_ports; } else { debug!( "Protocol does not support ports, applying nftables rule and ignoring \ destination ports." ); - let rule = FilterRule { - src_ips: &rule.source_addrs, - dest_ips: &rule.destination_addrs, - protocols: vec![protocol], - action: rule.verdict, - counter: true, - defguard_rule_id: rule.id, - v4: rule.ipv4, - comment: rule.comment.clone(), - ..Default::default() - }; - rules.push(rule); } + filter_rules.push(filter_rule); } } else { debug!( @@ -131,8 +205,8 @@ impl FirewallApi { for protocol in [Protocol::Tcp, Protocol::Udp] { debug!("Applying nftables rule for protocol: {protocol:?}"); let rule = FilterRule { - src_ips: &rule.source_addrs, - dest_ips: &rule.destination_addrs, + src_ips: &source_addrs, + dest_ips: &dest_addrs, dest_ports: &rule.destination_ports, protocols: vec![protocol], action: rule.verdict, @@ -142,11 +216,11 @@ impl FirewallApi { comment: rule.comment.clone(), ..Default::default() }; - rules.push(rule); + filter_rules.push(rule); } } - apply_filter_rules(rules, batch, &self.ifname)?; + apply_filter_rules(filter_rules, batch, &self.ifname)?; debug!( "Applied firewall rules for Defguard ACL rule ID: {}", @@ -257,3 +331,265 @@ impl FirewallManagementApi for FirewallApi { } } } + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use ipnetwork::IpNetwork; + + use super::*; + + #[test] + fn test_sorting() { + let mut addrs = vec![ + Address::Network(IpNetwork::from_str("10.10.10.11/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.10.12/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.10/32").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.11/32").unwrap()), + Address::Network(IpNetwork::from_str("10.10.10.10/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.12/32").unwrap()), + ]; + + addrs.sort_by(|a, b| { + a.first() + .partial_cmp(&b.first()) + .unwrap_or(std::cmp::Ordering::Equal) + }); + + assert_eq!( + addrs, + vec![ + Address::Network(IpNetwork::from_str("10.10.10.10/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.10.11/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.10.12/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.10/32").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.11/32").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.12/32").unwrap()), + ] + ); + + let _prepared_addrs = merge_addrs(addrs).unwrap(); + } + + #[test] + fn test_merge_addrs_empty() { + let addrs: Vec
= vec![]; + let result = merge_addrs(addrs).unwrap(); + assert!(result.is_empty()); + } + + #[test] + fn test_merge_addrs_single_address() { + let addrs = vec![Address::Network( + IpNetwork::from_str("192.168.1.10/32").unwrap(), + )]; + let result = merge_addrs(addrs.clone()).unwrap(); + + assert_eq!(result, addrs); + } + + #[test] + fn test_merge_addrs_adjacent_ranges() { + let addrs = vec![ + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.10").unwrap(), + IpAddr::from_str("192.168.1.20").unwrap(), + ) + .unwrap(), + ), + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.21").unwrap(), + IpAddr::from_str("192.168.1.30").unwrap(), + ) + .unwrap(), + ), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 1); + if let Address::Range(range) = &result[0] { + assert_eq!(range.start(), IpAddr::from_str("192.168.1.10").unwrap()); + assert_eq!(range.end(), IpAddr::from_str("192.168.1.30").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_merge_addrs_adjacent_single_addresses() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("192.168.1.10/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.11/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.12/32").unwrap()), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 1); + if let Address::Range(range) = &result[0] { + assert_eq!(range.start(), IpAddr::from_str("192.168.1.10").unwrap()); + assert_eq!(range.end(), IpAddr::from_str("192.168.1.12").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_merge_addrs_non_adjacent_ranges() { + let addrs = vec![ + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.10").unwrap(), + IpAddr::from_str("192.168.1.20").unwrap(), + ) + .unwrap(), + ), + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.30").unwrap(), + IpAddr::from_str("192.168.1.40").unwrap(), + ) + .unwrap(), + ), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 2); + if let Address::Range(range1) = &result[0] { + assert_eq!(range1.start(), IpAddr::from_str("192.168.1.10").unwrap()); + assert_eq!(range1.end(), IpAddr::from_str("192.168.1.20").unwrap()); + } else { + panic!("Expected Address::Range"); + } + if let Address::Range(range2) = &result[1] { + assert_eq!(range2.start(), IpAddr::from_str("192.168.1.30").unwrap()); + assert_eq!(range2.end(), IpAddr::from_str("192.168.1.40").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_merge_addrs_mixed_networks_and_ranges() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("192.168.1.10/32").unwrap()), + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.11").unwrap(), + IpAddr::from_str("192.168.1.15").unwrap(), + ) + .unwrap(), + ), + Address::Network(IpNetwork::from_str("192.168.1.16/32").unwrap()), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 1); + if let Address::Range(range) = &result[0] { + assert_eq!(range.start(), IpAddr::from_str("192.168.1.10").unwrap()); + assert_eq!(range.end(), IpAddr::from_str("192.168.1.16").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_merge_addrs_non_adjacent_singles() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("192.168.1.10/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.11/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.15/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.20/32").unwrap()), + ]; + let result = merge_addrs(addrs).unwrap(); + + // These should result in 3 separate ranges: 10-11, 15, 20 + let expected_addrs = vec![ + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.10").unwrap(), + IpAddr::from_str("192.168.1.11").unwrap(), + ) + .unwrap(), + ), + Address::Network(IpNetwork::from_str("192.168.1.15/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.20/32").unwrap()), + ]; + assert_eq!(result, expected_addrs); + } + + #[test] + fn test_merge_addrs_ipv6() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("2001:db8::1/128").unwrap()), + Address::Network(IpNetwork::from_str("2001:db8::2/128").unwrap()), + Address::Network(IpNetwork::from_str("2001:db8::3/128").unwrap()), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 1); + if let Address::Range(range) = &result[0] { + assert_eq!(range.start(), IpAddr::from_str("2001:db8::1").unwrap()); + assert_eq!(range.end(), IpAddr::from_str("2001:db8::3").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_next_ip_ipv4() { + assert_eq!( + next_ip(IpAddr::from_str("192.168.1.10").unwrap()), + IpAddr::from_str("192.168.1.11").unwrap() + ); + + // Test overflow + assert_eq!( + next_ip(IpAddr::from_str("255.255.255.255").unwrap()), + IpAddr::from_str("0.0.0.0").unwrap() + ); + } + + #[test] + fn test_next_ip_ipv6() { + assert_eq!( + next_ip(IpAddr::from_str("2001:db8::1").unwrap()), + IpAddr::from_str("2001:db8::2").unwrap() + ); + + // Test overflow + assert_eq!( + next_ip(IpAddr::from_str("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap()), + IpAddr::from_str("::").unwrap() + ); + } + + #[test] + fn test_merge_addrs_gap() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("192.168.1.1/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.100/32").unwrap()), + ]; + let result = merge_addrs(addrs.clone()).unwrap(); + + // Should not merge since there's a gap + assert_eq!(result, addrs); + + let addrs = vec![ + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.10").unwrap(), + IpAddr::from_str("192.168.1.20").unwrap(), + ) + .unwrap(), + ), + Address::Network(IpNetwork::from_str("192.168.1.100/32").unwrap()), + ]; + let result = merge_addrs(addrs.clone()).unwrap(); + + // Should not merge since there's a gap + assert_eq!(result, addrs); + } +} diff --git a/src/enterprise/firewall/nftables/netfilter.rs b/src/enterprise/firewall/nftables/netfilter.rs index 7174d9b0..b5d8505c 100644 --- a/src/enterprise/firewall/nftables/netfilter.rs +++ b/src/enterprise/firewall/nftables/netfilter.rs @@ -1,13 +1,8 @@ -#[cfg(test)] -use std::str::FromStr; use std::{ ffi::{CStr, CString}, net::{IpAddr, Ipv4Addr, Ipv6Addr}, }; -use ipnetwork::IpNetwork; -#[cfg(test)] -use ipnetwork::{Ipv4Network, Ipv6Network}; use nftnl::{ expr::{Expression, InterfaceName}, nft_expr, nftnl_sys, @@ -16,7 +11,7 @@ use nftnl::{ }; use super::{get_set_id, Address, FilterRule, Policy, Port, Protocol, State}; -use crate::enterprise::firewall::{iprange::IpAddrRange, FirewallError}; +use crate::enterprise::firewall::{iprange::IpAddrRange, max_address, FirewallError}; const FILTER_TABLE: &str = "filter"; const NAT_TABLE: &str = "nat"; @@ -809,27 +804,6 @@ fn socket_recv<'a>( } } -/// Get the max address in a network. -/// -/// - In IPv4 this is the broadcast address. -/// - In IPv6 this is just the last address in the network. -fn max_address(network: &IpNetwork) -> IpAddr { - match network { - IpNetwork::V4(network) => { - let addr = network.ip().to_bits(); - let mask = network.mask().to_bits(); - - IpAddr::V4(Ipv4Addr::from(addr | !mask)) - } - IpNetwork::V6(network) => { - let addr = network.ip().to_bits(); - let mask = network.mask().to_bits(); - - IpAddr::V6(Ipv6Addr::from(addr | !mask)) - } - } -} - fn new_anon_set( table: &Table, family: ProtoFamily, @@ -991,59 +965,4 @@ mod tests { increment_bytes(&mut ip); assert_eq!(ip, [0, 0, 0, 0, 0, 0, 0, 0]); } - - #[test] - fn test_max_address_ipv4_24() { - let network = IpNetwork::V4(Ipv4Network::from_str("192.168.1.0/24").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 255))); - } - - #[test] - fn test_max_address_ipv4_16() { - let network = IpNetwork::V4(Ipv4Network::from_str("10.1.0.0/16").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V4(Ipv4Addr::new(10, 1, 255, 255))); - } - - #[test] - fn test_max_address_ipv4_8() { - let network = IpNetwork::V4(Ipv4Network::from_str("172.16.0.0/8").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V4(Ipv4Addr::new(172, 255, 255, 255))); - } - - #[test] - fn test_max_address_ipv4_32() { - let network = IpNetwork::V4(Ipv4Network::from_str("192.168.1.1/32").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))); - } - - #[test] - fn test_max_address_ipv6_64() { - let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8::/64").unwrap()); - let max = max_address(&network); - assert_eq!( - max, - IpAddr::V6(Ipv6Addr::from_str("2001:db8::ffff:ffff:ffff:ffff").unwrap()) - ); - } - - #[test] - fn test_max_address_ipv6_128() { - let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8::1/128").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V6(Ipv6Addr::from_str("2001:db8::1").unwrap())); - } - - #[test] - fn test_max_address_ipv6_48() { - let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8:1234::/48").unwrap()); - let max = max_address(&network); - assert_eq!( - max, - IpAddr::V6(Ipv6Addr::from_str("2001:db8:1234:ffff:ffff:ffff:ffff:ffff").unwrap()) - ); - } } From e41a55a7c94b087cc567fee5d225b543e6f3f3b8 Mon Sep 17 00:00:00 2001 From: Maciek <19913370+wojcik91@users.noreply.github.com> Date: Wed, 6 Aug 2025 17:36:32 +0200 Subject: [PATCH 2/5] bump version (#188) --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c65dda20..2d7c659a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -445,7 +445,7 @@ dependencies = [ [[package]] name = "defguard-gateway" -version = "1.4.0" +version = "1.4.1" dependencies = [ "axum 0.8.4", "base64", diff --git a/Cargo.toml b/Cargo.toml index 8f7e50b6..6ab6afd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "defguard-gateway" -version = "1.4.0" +version = "1.4.1" edition = "2021" [dependencies] From 0895cd28f1e6808810a3273140c9053ea7c699bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Wed, 10 Sep 2025 09:38:07 +0200 Subject: [PATCH 3/5] update dependencies --- Cargo.lock | 90 ++++++++++++++++++++++++++++++++++-------------------- flake.lock | 12 ++++---- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f41b4430..ff4c9d42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -200,9 +200,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.35" +version = "1.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3" +checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" dependencies = [ "find-msvc-tools", "jobserver", @@ -522,12 +522,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -544,9 +544,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" [[package]] name = "fixedbitset" @@ -648,7 +648,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.3+wasi-0.2.4", + "wasi 0.14.5+wasi-0.2.4", ] [[package]] @@ -709,7 +709,7 @@ checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" dependencies = [ "cfg-if", "libc", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -930,9 +930,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921" dependencies = [ "equivalent", "hashbrown", @@ -1048,9 +1048,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -1596,15 +1596,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -1668,18 +1668,18 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] name = "security-framework" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" +checksum = "60b369d18893388b345804dc0007963c99b7d665ae71d275812d828c6f089640" dependencies = [ "bitflags", "core-foundation", @@ -1690,9 +1690,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -1876,15 +1876,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.21.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -2231,9 +2231,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "untrusted" @@ -2333,9 +2333,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.3+wasi-0.2.4" +version = "0.14.5+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4494f6290a82f5fe584817a676a34b9d6763e8d9d18204009fb31dceca98fd4" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.0+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" +checksum = "03fa2761397e5bd52002cd7e73110c71af2109aca4e521a9f40473fe685b0a24" dependencies = [ "wit-bindgen", ] @@ -2346,6 +2355,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + [[package]] name = "windows-sys" version = "0.52.0" @@ -2373,6 +2388,15 @@ dependencies = [ "windows-targets 0.53.3", ] +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -2395,7 +2419,7 @@ version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -2510,9 +2534,9 @@ checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" [[package]] name = "wit-bindgen" -version = "0.45.0" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" +checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36" [[package]] name = "writeable" diff --git a/flake.lock b/flake.lock index 4c226f53..abf59ab7 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1754725699, - "narHash": "sha256-iAcj9T/Y+3DBy2J0N+yF9XQQQ8IEb5swLFzs23CdP88=", + "lastModified": 1757347588, + "narHash": "sha256-tLdkkC6XnsY9EOZW9TlpesTclELy8W7lL2ClL+nma8o=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "85dbfc7aaf52ecb755f87e577ddbe6dbbdbc1054", + "rev": "b599843bad24621dcaa5ab60dac98f9b0eb1cabe", "type": "github" }, "original": { @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1754880555, - "narHash": "sha256-tG6l0wiX8V8IvG4HFYY8IYN5vpNAxQ+UWunjjpE6SqU=", + "lastModified": 1757471515, + "narHash": "sha256-0+rSzNsYindDWjO9VVULKGjXlPsQV6IDjRU5G3SwI9U=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "17c591a44e4eb77f05f27cd37e1cfc3f219c7fc4", + "rev": "aecf31120156fe47a7d1992aa814052910178fca", "type": "github" }, "original": { From b360ab99a052e3f9e16a625f54fff873b02c0e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Wed, 10 Sep 2025 09:47:21 +0200 Subject: [PATCH 4/5] update protos --- proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto b/proto index c0aef683..883487df 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit c0aef68395720f46a7f038b6766de3bb30e02930 +Subproject commit 883487df67d90fd14fae900737cd8b5ea6c10de3 From 947b84c91f7bcd75ce371da788938e9215310455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Wed, 10 Sep 2025 10:29:32 +0200 Subject: [PATCH 5/5] update core dependency --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff4c9d42..210b9d7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,7 +399,7 @@ dependencies = [ [[package]] name = "defguard_version" version = "0.0.0" -source = "git+https://github.com/DefGuard/defguard.git?rev=9aaa99f38eb9e4a07685d47779a8a43d0047b7b9#9aaa99f38eb9e4a07685d47779a8a43d0047b7b9" +source = "git+https://github.com/DefGuard/defguard.git?rev=c8e1bc91e0410a91af0b124d2d2b57c5a7226517#c8e1bc91e0410a91af0b124d2d2b57c5a7226517" dependencies = [ "axum", "http", diff --git a/Cargo.toml b/Cargo.toml index 958a3326..20fbf4e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "1.5.0" edition = "2021" [dependencies] -defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "9aaa99f38eb9e4a07685d47779a8a43d0047b7b9" } +defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "c8e1bc91e0410a91af0b124d2d2b57c5a7226517" } axum = "0.8" base64 = "0.22" clap = { version = "4.5", features = ["derive", "env"] }