diff --git a/Cargo.toml b/Cargo.toml index 12bd574..28398d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,5 +30,17 @@ name = "routing_table" required-features = ["routing-table"] [[test]] -name = "routing_table" +name = "routing_table_insert" +required-features = ["routing-table"] + +[[test]] +name = "routing_table_len" +required-features = ["routing-table"] + +[[test]] +name = "routing_table_list_matched" +required-features = ["routing-table"] + +[[test]] +name = "routing_table_match_longest" required-features = ["routing-table"] diff --git a/src/cidr.rs b/src/cidr.rs index 9abffb0..8b68d3a 100644 --- a/src/cidr.rs +++ b/src/cidr.rs @@ -44,6 +44,9 @@ impl Ipv4Cidr { if bits == 0 { return 0; } + if bits > 32 { + panic!("bits must be <= 32"); + } u32::MAX << (32 - bits) } @@ -432,6 +435,9 @@ impl Ipv6Cidr { if bits == 0 { return 0; } + if bits > 128 { + panic!("bits must be <= 128"); + } u128::MAX << (128 - bits) } @@ -1140,137 +1146,3 @@ impl Iterator for Hosts { } } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_ipv4_mask() { - let tests = [ - (0, [0, 0, 0, 0]), - (1, [0x80, 0, 0, 0]), - (2, [0xc0, 0, 0, 0]), - (3, [0xe0, 0, 0, 0]), - (4, [0xf0, 0, 0, 0]), - (5, [0xf8, 0, 0, 0]), - (6, [0xfc, 0, 0, 0]), - (7, [0xfe, 0, 0, 0]), - (8, [0xff, 0, 0, 0]), - (16, [0xff, 0xff, 0, 0]), - (20, [0xff, 0xff, 0xf0, 0]), - (24, [0xff, 0xff, 0xff, 0]), - (32, [0xff, 0xff, 0xff, 0xff]), - ]; - - for (bit, expected) in tests { - let expected = u32::from_be_bytes(expected); - let actual = Ipv4Cidr::mask_of(bit); - assert_eq!(actual, expected); - } - } - - #[test] - #[should_panic] - fn test_ipv4_mask_overflow() { - Ipv4Cidr::mask_of(33); - } - - #[test] - fn test_ipv6_mask() { - let tests = [ - (0, [0x00; 16]), - ( - 1, - [ - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - ], - ), - ( - 2, - [ - 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - ], - ), - ( - 3, - [ - 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - ], - ), - (128, [0xff; 16]), - ]; - - for (bit, expected) in tests { - let expected = u128::from_be_bytes(expected); - let actual = Ipv6Cidr::mask_of(bit); - assert_eq!(actual, expected); - } - } - - #[test] - #[should_panic] - fn test_ipv6_mask_overflow() { - Ipv6Cidr::mask_of(129); - } - - #[test] - fn test_ipv4_cidr_contains() { - { - let ret = Ipv4Cidr::new([192, 168, 0, 1], 0); - assert!(ret.is_ok()); - let cidr = ret.unwrap(); - assert_eq!(cidr.bits(), 0); - assert!(cidr.contains(Ipv4Addr::new(192, 168, 0, 0))); - assert!(cidr.contains(Ipv4Addr::new(10, 10, 0, 0))); - assert!(cidr.contains(Ipv4Addr::new(172, 0, 0, 0))); - assert!(cidr.contains(Ipv4Addr::new(0, 0, 0, 0))); - } - { - let ret = Ipv4Cidr::new([192, 168, 0, 1], 24); - assert!(ret.is_ok()); - let cidr = ret.unwrap(); - assert_eq!(cidr.bits(), 24); - for i in 0..=255 { - assert!(cidr.contains(Ipv4Addr::new(192, 168, 0, i))); - } - assert!(!cidr.contains(Ipv4Addr::new(192, 168, 1, 0))); - assert!(!cidr.contains(Ipv4Addr::new(192, 168, 2, 0))); - assert!(!cidr.contains(Ipv4Addr::new(192, 168, 1, 255))); - } - - { - let ret = Ipv4Cidr::new([192, 168, 24, 1], 24); - assert!(ret.is_ok()); - let cidr = ret.unwrap(); - assert_eq!(cidr.bits(), 24); - for i in 0..=255 { - assert!(cidr.contains(Ipv4Addr::new(192, 168, 24, i))); - } - assert!(!cidr.contains(Ipv4Addr::new(192, 168, 23, 0))); - assert!(!cidr.contains(Ipv4Addr::new(192, 168, 25, 255))); - assert!(!cidr.contains(Ipv4Addr::new(192, 0, 0, 0))); - assert!(!cidr.contains(Ipv4Addr::new(192, 167, 255, 255))); - } - - { - let ret = Ipv4Cidr::new([192, 168, 24, 1], 16); - assert!(ret.is_ok()); - let cidr = ret.unwrap(); - assert_eq!(cidr.bits(), 16); - } - } - - #[test] - fn test_cidr_v6() { - { - let ret = Ipv6Cidr::from_str("::/0"); - assert!(ret.is_ok()); - let cidr = ret.unwrap(); - assert_eq!(cidr.bits(), 0); - } - } -} diff --git a/tests/cidr.rs b/tests/cidr.rs deleted file mode 100644 index c4c4ec0..0000000 --- a/tests/cidr.rs +++ /dev/null @@ -1,132 +0,0 @@ -use core::net::Ipv4Addr; - -use cidrs::Ipv4Cidr; - -#[test] -fn simple() { - let tests = [ - (([0, 0, 0, 0], 31), vec![[0, 0, 0, 0], [0, 0, 0, 1]]), - (([0, 0, 0, 0], 32), vec![[0, 0, 0, 0]]), - (([1, 0, 2, 0], 32), vec![[1, 0, 2, 0]]), - (([192, 168, 0, 1], 32), vec![[192, 168, 0, 1]]), - (([255, 255, 255, 255], 32), vec![[255, 255, 255, 255]]), - (([1, 1, 1, 0], 30), vec![[1, 1, 1, 1], [1, 1, 1, 2]]), - ( - ([255, 255, 255, 254], 31), - vec![[255, 255, 255, 254], [255, 255, 255, 255]], - ), - ( - ([0, 0, 0, 0], 24), - (1..=254).map(|x| [0, 0, 0, x]).collect(), - ), - ( - ([255, 255, 255, 0], 24), - (1..=254).map(|x| [255, 255, 255, x]).collect(), - ), - ( - ([10, 0, 0, 0], 24), - (1..=254).map(|x| [10, 0, 0, x]).collect(), - ), - ]; - - for ((octets, bits), expected) in tests { - let cidr = Ipv4Cidr::new(octets, bits).unwrap(); - let actual: Vec<_> = cidr.hosts().collect(); - let expected: Vec<_> = expected.into_iter().map(Ipv4Addr::from).collect(); - - assert_eq!(actual, expected, "Mismatch in hosts for CIDR {}", cidr); - assert_eq!( - actual.len(), - cidr.hosts().count(), - "Mismatch between collected and counted hosts for {}", - cidr - ); - assert!( - actual.first().unwrap() >= &cidr.network_addr(), - "First host address is before network address for {}", - cidr - ); - assert!( - actual.last().unwrap() <= &cidr.broadcast_addr(), - "Last host address is after broadcast address for {}", - cidr - ); - } -} - -mod ipv6 { - mod hosts { - use core::net::Ipv6Addr; - - use cidrs::Ipv6Cidr; - - #[test] - fn simple() { - let tests = [ - ( - ([0, 0, 0, 0, 0, 0, 0, 0], 128), - vec![[0, 0, 0, 0, 0, 0, 0, 0]], - ), - ( - ([0, 0, 0, 0, 0, 0, 0, 0], 127), - vec![[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1]], - ), - ( - ([0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0], 127), - vec![ - [0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0], - [0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 1], - ], - ), - ( - ([0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd], 128), - vec![[0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd]], - ), - ( - ([0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xff], 128), - vec![[0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xff]], - ), - ( - ([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0], 120), - (1..=254) - .map(|x| [0x2001, 0xdb8, 0, 0, 0, 0, 0, x]) - .collect(), - ), - ( - ([0, 0, 0, 0, 0, 0, 0, 0], 120), - (1..=254).map(|x| [0, 0, 0, 0, 0, 0, 0, x]).collect(), - ), - ( - ([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], 120), - (1..=254) - .map(|x| [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, x]) - .collect(), - ), - ]; - - for ((octets, bits), expected) in tests { - let cidr = Ipv6Cidr::new(octets, bits).unwrap(); - let actual: Vec<_> = cidr.hosts().collect(); - let expected: Vec<_> = expected.into_iter().map(Ipv6Addr::from).collect(); - - assert_eq!(actual, expected, "Mismatch in hosts for CIDR {}", cidr); - assert_eq!( - actual.len(), - cidr.hosts().count(), - "Mismatch between collected and counted hosts for {}", - cidr - ); - assert!( - actual.first().unwrap() >= &cidr.network_addr(), - "First host address is before network address for {}", - cidr - ); - assert!( - actual.last().unwrap() <= &cidr.broadcast_addr(), - "Last host address is after broadcast address for {}", - cidr - ); - } - } - } -} diff --git a/tests/cidr_contains.rs b/tests/cidr_contains.rs new file mode 100644 index 0000000..a53057e --- /dev/null +++ b/tests/cidr_contains.rs @@ -0,0 +1,39 @@ +use core::net::{Ipv4Addr, Ipv6Addr}; + +use cidrs::{Ipv4Cidr, Ipv6Cidr}; +#[test] +fn ipv4_basic() { + let cidr = Ipv4Cidr::new([192, 168, 0, 0], 24).unwrap(); + + // Test addresses within the CIDR block + assert!(cidr.contains(Ipv4Addr::new(192, 168, 0, 1))); + assert!(cidr.contains(Ipv4Addr::new(192, 168, 0, 254))); + + // Test the network address and broadcast address + assert!(cidr.contains(Ipv4Addr::new(192, 168, 0, 0))); + assert!(cidr.contains(Ipv4Addr::new(192, 168, 0, 255))); + + // Test addresses outside the CIDR block + assert!(!cidr.contains(Ipv4Addr::new(192, 168, 1, 0))); + assert!(!cidr.contains(Ipv4Addr::new(192, 167, 255, 255))); +} + +#[test] +fn ipv6_basic() { + let cidr = Ipv6Cidr::new([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0], 48).unwrap(); + + // Test addresses within the CIDR block + assert!(cidr.contains(Ipv6Addr::new(0x2001, 0xdb8, 0, 1, 0, 0, 0, 1))); + assert!(cidr.contains(Ipv6Addr::new( + 0x2001, 0xdb8, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff + ))); + + // Test the network address + assert!(cidr.contains(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0))); + + // Test addresses outside the CIDR block + assert!(!cidr.contains(Ipv6Addr::new(0x2001, 0xdb9, 0, 0, 0, 0, 0, 0))); + assert!(!cidr.contains(Ipv6Addr::new( + 0x2001, 0xdb7, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff + ))); +} diff --git a/tests/cidr_hosts.rs b/tests/cidr_hosts.rs new file mode 100644 index 0000000..df6211b --- /dev/null +++ b/tests/cidr_hosts.rs @@ -0,0 +1,124 @@ +use core::net::{Ipv4Addr, Ipv6Addr}; + +use cidrs::{Ipv4Cidr, Ipv6Cidr}; + +#[test] +fn ipv4_basic() { + let tests = [ + (([0, 0, 0, 0], 31), vec![[0, 0, 0, 0], [0, 0, 0, 1]]), + (([0, 0, 0, 0], 32), vec![[0, 0, 0, 0]]), + (([1, 0, 2, 0], 32), vec![[1, 0, 2, 0]]), + (([192, 168, 0, 1], 32), vec![[192, 168, 0, 1]]), + (([255, 255, 255, 255], 32), vec![[255, 255, 255, 255]]), + (([1, 1, 1, 0], 30), vec![[1, 1, 1, 1], [1, 1, 1, 2]]), + ( + ([255, 255, 255, 254], 31), + vec![[255, 255, 255, 254], [255, 255, 255, 255]], + ), + ( + ([0, 0, 0, 0], 24), + (1..=254).map(|x| [0, 0, 0, x]).collect(), + ), + ( + ([255, 255, 255, 0], 24), + (1..=254).map(|x| [255, 255, 255, x]).collect(), + ), + ( + ([10, 0, 0, 0], 24), + (1..=254).map(|x| [10, 0, 0, x]).collect(), + ), + ]; + + for ((octets, bits), expected) in tests { + let cidr = Ipv4Cidr::new(octets, bits).unwrap(); + let actual: Vec<_> = cidr.hosts().collect(); + let expected: Vec<_> = expected.into_iter().map(Ipv4Addr::from).collect(); + + assert_eq!(actual, expected, "Mismatch in hosts for CIDR {}", cidr); + assert_eq!( + actual.len(), + cidr.hosts().count(), + "Mismatch between collected and counted hosts for {}", + cidr + ); + assert!( + actual.first().unwrap() >= &cidr.network_addr(), + "First host address is before network address for {}", + cidr + ); + assert!( + actual.last().unwrap() <= &cidr.broadcast_addr(), + "Last host address is after broadcast address for {}", + cidr + ); + } +} + +#[test] +fn ipv6_basic() { + let tests = [ + ( + ([0, 0, 0, 0, 0, 0, 0, 0], 128), + vec![[0, 0, 0, 0, 0, 0, 0, 0]], + ), + ( + ([0, 0, 0, 0, 0, 0, 0, 0], 127), + vec![[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1]], + ), + ( + ([0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0], 127), + vec![ + [0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0], + [0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 1], + ], + ), + ( + ([0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd], 128), + vec![[0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd]], + ), + ( + ([0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xff], 128), + vec![[0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xff]], + ), + ( + ([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0], 120), + (1..=254) + .map(|x| [0x2001, 0xdb8, 0, 0, 0, 0, 0, x]) + .collect(), + ), + ( + ([0, 0, 0, 0, 0, 0, 0, 0], 120), + (1..=254).map(|x| [0, 0, 0, 0, 0, 0, 0, x]).collect(), + ), + ( + ([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], 120), + (1..=254) + .map(|x| [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, x]) + .collect(), + ), + ]; + + for ((octets, bits), expected) in tests { + let cidr = Ipv6Cidr::new(octets, bits).unwrap(); + let actual: Vec<_> = cidr.hosts().collect(); + let expected: Vec<_> = expected.into_iter().map(Ipv6Addr::from).collect(); + + assert_eq!(actual, expected, "Mismatch in hosts for CIDR {}", cidr); + assert_eq!( + actual.len(), + cidr.hosts().count(), + "Mismatch between collected and counted hosts for {}", + cidr + ); + assert!( + actual.first().unwrap() >= &cidr.network_addr(), + "First host address is before network address for {}", + cidr + ); + assert!( + actual.last().unwrap() <= &cidr.broadcast_addr(), + "Last host address is after broadcast address for {}", + cidr + ); + } +} diff --git a/tests/cidr_mask_of.rs b/tests/cidr_mask_of.rs new file mode 100644 index 0000000..618be34 --- /dev/null +++ b/tests/cidr_mask_of.rs @@ -0,0 +1,126 @@ +use cidrs::{Ipv4Cidr, Ipv6Cidr}; + +#[test] +fn ipv4_basic() { + let tests = [ + (0, [0, 0, 0, 0]), + (1, [0x80, 0, 0, 0]), + (2, [0xc0, 0, 0, 0]), + (3, [0xe0, 0, 0, 0]), + (4, [0xf0, 0, 0, 0]), + (5, [0xf8, 0, 0, 0]), + (6, [0xfc, 0, 0, 0]), + (7, [0xfe, 0, 0, 0]), + (8, [0xff, 0, 0, 0]), + (9, [0xff, 0x80, 0, 0]), + (10, [0xff, 0xc0, 0, 0]), + (11, [0xff, 0xe0, 0, 0]), + (12, [0xff, 0xf0, 0, 0]), + (13, [0xff, 0xf8, 0, 0]), + (14, [0xff, 0xfc, 0, 0]), + (15, [0xff, 0xfe, 0, 0]), + (16, [0xff, 0xff, 0, 0]), + (17, [0xff, 0xff, 0x80, 0]), + (18, [0xff, 0xff, 0xc0, 0]), + (19, [0xff, 0xff, 0xe0, 0]), + (20, [0xff, 0xff, 0xf0, 0]), + (21, [0xff, 0xff, 0xf8, 0]), + (22, [0xff, 0xff, 0xfc, 0]), + (23, [0xff, 0xff, 0xfe, 0]), + (24, [0xff, 0xff, 0xff, 0]), + (25, [0xff, 0xff, 0xff, 0x80]), + (26, [0xff, 0xff, 0xff, 0xc0]), + (27, [0xff, 0xff, 0xff, 0xe0]), + (28, [0xff, 0xff, 0xff, 0xf0]), + (29, [0xff, 0xff, 0xff, 0xf8]), + (30, [0xff, 0xff, 0xff, 0xfc]), + (31, [0xff, 0xff, 0xff, 0xfe]), + (32, [0xff, 0xff, 0xff, 0xff]), + ]; + + for (bits, expected) in tests { + let actual = Ipv4Cidr::mask_of(bits); + let expected = u32::from_be_bytes(expected); + assert_eq!(actual, expected, "bit: {bits}"); + } +} + +#[test] +#[should_panic(expected = "bits must be <= 32")] +fn ipv4_panic_33() { + Ipv4Cidr::mask_of(33); +} + +#[test] +#[should_panic(expected = "bits must be <= 32")] +fn ipv4_panic_255() { + Ipv4Cidr::mask_of(255); +} + +#[test] +fn ipv6_basic() { + let tests = [ + (0, [0; 16]), + (1, [0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + (8, [0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + (16, [0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ( + 23, + [0xff, 0xff, 0xfe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ), + ( + 32, + [0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ), + ( + 47, + [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + ), + ( + 64, + [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, + ], + ), + ( + 79, + [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0, 0, 0, 0, 0, 0, + ], + ), + ( + 96, + [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, + ], + ), + ( + 113, + [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x80, 0, + ], + ), + (128, [0xff; 16]), + ]; + + for (bits, expected) in tests { + let actual = Ipv6Cidr::mask_of(bits); + let expected = u128::from_be_bytes(expected); + assert_eq!(actual, expected, "bit: {bits}"); + } +} + +#[test] +#[should_panic(expected = "bits must be <= 128")] +fn ipv6_panic_129() { + Ipv6Cidr::mask_of(129); +} + +#[test] +#[should_panic(expected = "bits must be <= 128")] +fn ipv6_panic_255() { + Ipv6Cidr::mask_of(255); +} diff --git a/tests/cidr_overlaps.rs b/tests/cidr_overlaps.rs new file mode 100644 index 0000000..89ed93a --- /dev/null +++ b/tests/cidr_overlaps.rs @@ -0,0 +1,83 @@ +use cidrs::{Ipv4Cidr, Ipv6Cidr}; + +#[test] +fn ipv4_basic() { + let tests = [ + // Identical networks + ( + Ipv4Cidr::new([192, 168, 0, 0], 24).unwrap(), + Ipv4Cidr::new([192, 168, 0, 0], 24).unwrap(), + true, + ), + // One network contains the other + ( + Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap(), + Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap(), + true, + ), + // Overlapping networks + ( + Ipv4Cidr::new([192, 168, 0, 0], 23).unwrap(), + Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap(), + true, + ), + // Adjacent networks + ( + Ipv4Cidr::new([192, 168, 0, 0], 24).unwrap(), + Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap(), + false, + ), + // Completely separate networks + ( + Ipv4Cidr::new([192, 168, 0, 0], 24).unwrap(), + Ipv4Cidr::new([10, 0, 0, 0], 8).unwrap(), + false, + ), + ]; + + for (cidr1, cidr2, expected) in tests { + assert_eq!(cidr1.overlaps(&cidr2), expected); + assert_eq!(cidr2.overlaps(&cidr1), expected); // Test symmetry + } +} + +#[test] +fn ipv6_basic() { + let tests = [ + // Identical networks + ( + Ipv6Cidr::new([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0], 48).unwrap(), + Ipv6Cidr::new([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0], 48).unwrap(), + true, + ), + // One network contains the other + ( + Ipv6Cidr::new([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0], 32).unwrap(), + Ipv6Cidr::new([0x2001, 0xdb8, 1, 0, 0, 0, 0, 0], 48).unwrap(), + true, + ), + // Overlapping networks + ( + Ipv6Cidr::new([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0], 47).unwrap(), + Ipv6Cidr::new([0x2001, 0xdb8, 1, 0, 0, 0, 0, 0], 48).unwrap(), + true, + ), + // Adjacent networks + ( + Ipv6Cidr::new([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0], 48).unwrap(), + Ipv6Cidr::new([0x2001, 0xdb8, 1, 0, 0, 0, 0, 0], 48).unwrap(), + false, + ), + // Completely separate networks + ( + Ipv6Cidr::new([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0], 48).unwrap(), + Ipv6Cidr::new([0x2002, 0, 0, 0, 0, 0, 0, 0], 16).unwrap(), + false, + ), + ]; + + for (cidr1, cidr2, expected) in tests { + assert_eq!(cidr1.overlaps(&cidr2), expected); + assert_eq!(cidr2.overlaps(&cidr1), expected); // Test symmetry + } +} diff --git a/tests/routing_table.rs b/tests/routing_table.rs deleted file mode 100644 index 9f41ce7..0000000 --- a/tests/routing_table.rs +++ /dev/null @@ -1,252 +0,0 @@ -mod insert { - use cidrs::{CidrRoutingTable, Ipv4Cidr, Ipv4CidrRoutingTable, Ipv6Cidr, Ipv6CidrRoutingTable}; - - #[test] - fn ipv4() { - { - let mut table = Ipv4CidrRoutingTable::new(); - let cidr = "192.168.0.0/16".parse().unwrap(); - - assert_eq!(table.insert(cidr, 1), None); - assert_eq!(table.insert(cidr, 2), Some(1)); - } - - { - let mut table = Ipv4CidrRoutingTable::new(); - let cidr1 = "192.168.0.0/16".parse().unwrap(); - let cidr2 = "192.168.1.0/24".parse().unwrap(); - - assert_eq!(table.insert(cidr1, 1), None); - assert_eq!(table.insert(cidr1, 2), Some(1)); - - assert_eq!(table.insert(cidr2, 3), None); - assert_eq!(table.insert(cidr2, 4), Some(3)); - } - } - - #[test] - fn ipv6() { - { - let mut table = Ipv6CidrRoutingTable::new(); - let cidr = "2001:db8::/32".parse().unwrap(); - - assert_eq!(table.insert(cidr, 1), None); - assert_eq!(table.insert(cidr, 2), Some(1)); - } - { - let mut table = Ipv6CidrRoutingTable::new(); - let cidr1 = "2001:db8::/32".parse().unwrap(); - let cidr2 = "2001:db8::1/128".parse().unwrap(); - - assert_eq!(table.insert(cidr1, 1), None); - assert_eq!(table.insert(cidr1, 2), Some(1)); - - assert_eq!(table.insert(cidr2, 3), None); - assert_eq!(table.insert(cidr2, 4), Some(3)); - } - } - - #[test] - fn simple() { - let mut table = CidrRoutingTable::new(); - - let ipv4_cidr = "192.168.0.0/16".parse::().unwrap(); - assert_eq!(table.insert(ipv4_cidr, 1), None); - assert_eq!(table.insert(ipv4_cidr, 2), Some(1)); - - let ipv6_cidr = "2001:db8::/32".parse::().unwrap(); - assert_eq!(table.insert(ipv6_cidr, 3), None); - assert_eq!(table.insert(ipv6_cidr, 4), Some(3)); - } -} - -mod match_longest { - use cidrs::{Ipv4Cidr, Ipv4CidrRoutingTable}; - - #[test] - fn ipv4_simple() { - let mut table = Ipv4CidrRoutingTable::new(); - table.insert(Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap(), 1); - table.insert(Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap(), 2); - table.insert(Ipv4Cidr::new([192, 168, 1, 1], 32).unwrap(), 3); - - assert_eq!( - table.match_longest([192, 168, 1, 1]), - Some((Ipv4Cidr::new([192, 168, 1, 1], 32).unwrap(), &3)) - ); - - assert_eq!( - table.match_longest([192, 168, 1, 2]), - Some((Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap(), &2)) - ); - - assert_eq!( - table.match_longest([192, 168, 2, 1]), - Some((Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap(), &1)) - ); - - assert_eq!( - table.match_longest([192, 168, 3, 1]), - Some((Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap(), &1)) - ); - - assert_eq!( - table.match_longest([192, 168, 0, 0]), - Some((Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap(), &1)) - ); - - assert_eq!(table.match_longest([192, 167, 1, 1]), None); - } - - #[test] - fn ipv4_large_data_set() { - let table = { - let mut m = Ipv4CidrRoutingTable::new(); - - let cidr = Ipv4Cidr::new([0, 0, 0, 0], 0).unwrap(); - m.insert(cidr, cidr.to_string()); - - for i1 in 1..128 { - let cidr = Ipv4Cidr::new([i1, 0, 0, 0], 6).unwrap(); - m.insert(cidr, cidr.to_string()); - let cidr = Ipv4Cidr::new([i1, 0, 0, 0], 7).unwrap(); - m.insert(cidr, cidr.to_string()); - let cidr = Ipv4Cidr::new([i1, 0, 0, 0], 8).unwrap(); - m.insert(cidr, cidr.to_string()); - - for i2 in 0..128 { - let cidr = Ipv4Cidr::new([i1, i2, 0, 0], 9).unwrap(); - m.insert(cidr, cidr.to_string()); - let cidr = Ipv4Cidr::new([i1, i2, 0, 0], 11).unwrap(); - m.insert(cidr, cidr.to_string()); - let cidr = Ipv4Cidr::new([i1, i2, 0, 0], 13).unwrap(); - m.insert(cidr, cidr.to_string()); - for i3 in 0..128 { - let cidr = Ipv4Cidr::new([i1, i2, i3, 0], 24).unwrap(); - m.insert(cidr, cidr.to_string()); - } - } - } - - m - }; - - let tests = [ - ([1, 2, 3, 4], Some(([1, 2, 3, 0], 24))), - ([1, 1, 1, 1], Some(([1, 1, 1, 0], 24))), - ([1, 0, 129, 1], Some(([1, 0, 0, 0], 13))), - ]; - - for (input, expected) in tests { - let actual = table.match_longest(input).map(|(k, v)| (k, v.clone())); - - if let Some((octets, bits)) = expected { - let cidr = Ipv4Cidr::new(octets, bits).unwrap(); - - assert_eq!(actual, Some((cidr, cidr.to_string()))); - } else { - assert_eq!(actual, None); - } - } - } -} - -mod list_matched { - use cidrs::{Cidr, CidrRoutingTable, Ipv4Cidr}; - - #[test] - fn simple() { - let mut table = CidrRoutingTable::new(); - - let cidr1 = Cidr::V4(Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap()); - let cidr2 = Cidr::V4(Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap()); - let cidr3 = Cidr::V4(Ipv4Cidr::new([192, 168, 1, 1], 32).unwrap()); - - table.insert(cidr1, 1); - table.insert(cidr2, 2); - table.insert(cidr3, 3); - - let matched = table.list_matched([192, 168, 1, 1]); - assert_eq!(matched, vec![(cidr1, &1), (cidr2, &2), (cidr3, &3)]); - - let matched = table.list_matched([192, 168, 1, 2]); - assert_eq!(matched, vec![(cidr1, &1), (cidr2, &2)]); - - let matched = table.list_matched([192, 168, 2, 1]); - assert_eq!(matched, vec![(cidr1, &1)]); - - let matched = table.list_matched([192, 168, 3, 1]); - assert_eq!(matched, vec![(cidr1, &1)]); - - let matched = table.list_matched([192, 168, 0, 0]); - assert_eq!(matched, vec![(cidr1, &1)]); - - let matched = table.list_matched([192, 167, 1, 1]); - assert_eq!(matched, vec![]); - } -} - -mod len { - use cidrs::{Cidr, CidrRoutingTable, Ipv4Cidr}; - - #[test] - fn ipv4_simple_insert() { - let mut table = CidrRoutingTable::new(); - let cidr1 = Cidr::V4(Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap()); - let cidr2 = Cidr::V4(Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap()); - - assert_eq!(table.len(), 0); - table.insert(cidr1, 1); - assert_eq!(table.len(), 1); - table.insert(cidr2, 2); - assert_eq!(table.len(), 2); - } - - #[test] - fn ipv4_insert_existing() { - let mut table = CidrRoutingTable::new(); - let cidr = Cidr::V4(Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap()); - - assert_eq!(table.len(), 0); - table.insert(cidr, 1); - assert_eq!(table.len(), 1); - table.insert(cidr, 2); - assert_eq!(table.len(), 1); - } - - #[test] - fn ipv4_simple_remove() { - let mut table = CidrRoutingTable::new(); - let cidr1 = Cidr::V4(Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap()); - let cidr2 = Cidr::V4(Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap()); - - table.insert(cidr1, 1); - table.insert(cidr2, 2); - assert_eq!(table.len(), 2); - - table.remove(cidr1); - assert_eq!(table.len(), 1); - - table.remove(cidr2); - assert_eq!(table.len(), 0); - } - - #[test] - fn ipv4_remove_non_existing() { - let mut table = CidrRoutingTable::new(); - let cidr1 = Cidr::V4(Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap()); - let cidr2 = Cidr::V4(Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap()); - - table.insert(cidr1, 1); - assert_eq!(table.len(), 1); - - table.remove(cidr2); - assert_eq!(table.len(), 1); - - table.remove(cidr1); - assert_eq!(table.len(), 0); - - table.remove(cidr1); - assert_eq!(table.len(), 0); - } -} diff --git a/tests/routing_table_insert.rs b/tests/routing_table_insert.rs new file mode 100644 index 0000000..d9bb474 --- /dev/null +++ b/tests/routing_table_insert.rs @@ -0,0 +1,59 @@ +use cidrs::{CidrRoutingTable, Ipv4Cidr, Ipv4CidrRoutingTable, Ipv6Cidr, Ipv6CidrRoutingTable}; + +#[test] +fn ipv4_basic() { + { + let mut table = Ipv4CidrRoutingTable::new(); + let cidr = "192.168.0.0/16".parse().unwrap(); + + assert_eq!(table.insert(cidr, 1), None); + assert_eq!(table.insert(cidr, 2), Some(1)); + } + + { + let mut table = Ipv4CidrRoutingTable::new(); + let cidr1 = "192.168.0.0/16".parse().unwrap(); + let cidr2 = "192.168.1.0/24".parse().unwrap(); + + assert_eq!(table.insert(cidr1, 1), None); + assert_eq!(table.insert(cidr1, 2), Some(1)); + + assert_eq!(table.insert(cidr2, 3), None); + assert_eq!(table.insert(cidr2, 4), Some(3)); + } +} + +#[test] +fn ipv6_basic() { + { + let mut table = Ipv6CidrRoutingTable::new(); + let cidr = "2001:db8::/32".parse().unwrap(); + + assert_eq!(table.insert(cidr, 1), None); + assert_eq!(table.insert(cidr, 2), Some(1)); + } + { + let mut table = Ipv6CidrRoutingTable::new(); + let cidr1 = "2001:db8::/32".parse().unwrap(); + let cidr2 = "2001:db8::1/128".parse().unwrap(); + + assert_eq!(table.insert(cidr1, 1), None); + assert_eq!(table.insert(cidr1, 2), Some(1)); + + assert_eq!(table.insert(cidr2, 3), None); + assert_eq!(table.insert(cidr2, 4), Some(3)); + } +} + +#[test] +fn basic() { + let mut table = CidrRoutingTable::new(); + + let ipv4_cidr = "192.168.0.0/16".parse::().unwrap(); + assert_eq!(table.insert(ipv4_cidr, 1), None); + assert_eq!(table.insert(ipv4_cidr, 2), Some(1)); + + let ipv6_cidr = "2001:db8::/32".parse::().unwrap(); + assert_eq!(table.insert(ipv6_cidr, 3), None); + assert_eq!(table.insert(ipv6_cidr, 4), Some(3)); +} diff --git a/tests/routing_table_len.rs b/tests/routing_table_len.rs new file mode 100644 index 0000000..f7dc0fd --- /dev/null +++ b/tests/routing_table_len.rs @@ -0,0 +1,62 @@ +use cidrs::{Cidr, CidrRoutingTable, Ipv4Cidr}; + +#[test] +fn ipv4_basic_insert() { + let mut table = CidrRoutingTable::new(); + let cidr1 = Cidr::V4(Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap()); + let cidr2 = Cidr::V4(Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap()); + + assert_eq!(table.len(), 0); + table.insert(cidr1, 1); + assert_eq!(table.len(), 1); + table.insert(cidr2, 2); + assert_eq!(table.len(), 2); +} + +#[test] +fn ipv4_insert_existing() { + let mut table = CidrRoutingTable::new(); + let cidr = Cidr::V4(Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap()); + + assert_eq!(table.len(), 0); + table.insert(cidr, 1); + assert_eq!(table.len(), 1); + table.insert(cidr, 2); + assert_eq!(table.len(), 1); +} + +#[test] +fn ipv4_basic_remove() { + let mut table = CidrRoutingTable::new(); + let cidr1 = Cidr::V4(Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap()); + let cidr2 = Cidr::V4(Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap()); + + table.insert(cidr1, 1); + table.insert(cidr2, 2); + assert_eq!(table.len(), 2); + + table.remove(cidr1); + assert_eq!(table.len(), 1); + + table.remove(cidr2); + assert_eq!(table.len(), 0); +} + +#[test] +fn ipv4_remove_non_existing() { + let mut table = CidrRoutingTable::new(); + let cidr1 = Cidr::V4(Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap()); + let cidr2 = Cidr::V4(Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap()); + + table.insert(cidr1, 1); + assert_eq!(table.len(), 1); + + table.remove(cidr2); + assert_eq!(table.len(), 1); + + table.remove(cidr1); + assert_eq!(table.len(), 0); + + table.remove(cidr1); + assert_eq!(table.len(), 0); +} diff --git a/tests/routing_table_list_matched.rs b/tests/routing_table_list_matched.rs new file mode 100644 index 0000000..659fed8 --- /dev/null +++ b/tests/routing_table_list_matched.rs @@ -0,0 +1,32 @@ +use cidrs::{Cidr, CidrRoutingTable, Ipv4Cidr}; + +#[test] +fn basic() { + let mut table = CidrRoutingTable::new(); + + let cidr1 = Cidr::V4(Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap()); + let cidr2 = Cidr::V4(Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap()); + let cidr3 = Cidr::V4(Ipv4Cidr::new([192, 168, 1, 1], 32).unwrap()); + + table.insert(cidr1, 1); + table.insert(cidr2, 2); + table.insert(cidr3, 3); + + let matched = table.list_matched([192, 168, 1, 1]); + assert_eq!(matched, vec![(cidr1, &1), (cidr2, &2), (cidr3, &3)]); + + let matched = table.list_matched([192, 168, 1, 2]); + assert_eq!(matched, vec![(cidr1, &1), (cidr2, &2)]); + + let matched = table.list_matched([192, 168, 2, 1]); + assert_eq!(matched, vec![(cidr1, &1)]); + + let matched = table.list_matched([192, 168, 3, 1]); + assert_eq!(matched, vec![(cidr1, &1)]); + + let matched = table.list_matched([192, 168, 0, 0]); + assert_eq!(matched, vec![(cidr1, &1)]); + + let matched = table.list_matched([192, 167, 1, 1]); + assert_eq!(matched, vec![]); +} diff --git a/tests/routing_table_match_longest.rs b/tests/routing_table_match_longest.rs new file mode 100644 index 0000000..c96c596 --- /dev/null +++ b/tests/routing_table_match_longest.rs @@ -0,0 +1,88 @@ +use cidrs::{Ipv4Cidr, Ipv4CidrRoutingTable}; + +#[test] +fn ipv4_basic() { + let mut table = Ipv4CidrRoutingTable::new(); + table.insert(Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap(), 1); + table.insert(Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap(), 2); + table.insert(Ipv4Cidr::new([192, 168, 1, 1], 32).unwrap(), 3); + + assert_eq!( + table.match_longest([192, 168, 1, 1]), + Some((Ipv4Cidr::new([192, 168, 1, 1], 32).unwrap(), &3)) + ); + + assert_eq!( + table.match_longest([192, 168, 1, 2]), + Some((Ipv4Cidr::new([192, 168, 1, 0], 24).unwrap(), &2)) + ); + + assert_eq!( + table.match_longest([192, 168, 2, 1]), + Some((Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap(), &1)) + ); + + assert_eq!( + table.match_longest([192, 168, 3, 1]), + Some((Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap(), &1)) + ); + + assert_eq!( + table.match_longest([192, 168, 0, 0]), + Some((Ipv4Cidr::new([192, 168, 0, 0], 16).unwrap(), &1)) + ); + + assert_eq!(table.match_longest([192, 167, 1, 1]), None); +} + +#[test] +fn ipv4_large_data_set() { + let table = { + let mut m = Ipv4CidrRoutingTable::new(); + + let cidr = Ipv4Cidr::new([0, 0, 0, 0], 0).unwrap(); + m.insert(cidr, cidr.to_string()); + + for i1 in 1..128 { + let cidr = Ipv4Cidr::new([i1, 0, 0, 0], 6).unwrap(); + m.insert(cidr, cidr.to_string()); + let cidr = Ipv4Cidr::new([i1, 0, 0, 0], 7).unwrap(); + m.insert(cidr, cidr.to_string()); + let cidr = Ipv4Cidr::new([i1, 0, 0, 0], 8).unwrap(); + m.insert(cidr, cidr.to_string()); + + for i2 in 0..128 { + let cidr = Ipv4Cidr::new([i1, i2, 0, 0], 9).unwrap(); + m.insert(cidr, cidr.to_string()); + let cidr = Ipv4Cidr::new([i1, i2, 0, 0], 11).unwrap(); + m.insert(cidr, cidr.to_string()); + let cidr = Ipv4Cidr::new([i1, i2, 0, 0], 13).unwrap(); + m.insert(cidr, cidr.to_string()); + for i3 in 0..128 { + let cidr = Ipv4Cidr::new([i1, i2, i3, 0], 24).unwrap(); + m.insert(cidr, cidr.to_string()); + } + } + } + + m + }; + + let tests = [ + ([1, 2, 3, 4], Some(([1, 2, 3, 0], 24))), + ([1, 1, 1, 1], Some(([1, 1, 1, 0], 24))), + ([1, 0, 129, 1], Some(([1, 0, 0, 0], 13))), + ]; + + for (input, expected) in tests { + let actual = table.match_longest(input).map(|(k, v)| (k, v.clone())); + + if let Some((octets, bits)) = expected { + let cidr = Ipv4Cidr::new(octets, bits).unwrap(); + + assert_eq!(actual, Some((cidr, cidr.to_string()))); + } else { + assert_eq!(actual, None); + } + } +}