99 Commits

Author SHA1 Message Date
7af6e16f98 Merge pull request #98 from achanda/release-0.15
Uprev for a new release
2019-09-12 20:34:06 +01:00
9e42aaeb3e Uprev for a new release 2019-09-12 20:25:48 +01:00
2b34ba991a Merge pull request #96 from achanda/dependabot/cargo/criterion-0.3.0
Update criterion requirement from 0.2.9 to 0.3.0
2019-08-26 08:25:21 +01:00
2dff6a0863 Update criterion requirement from 0.2.9 to 0.3.0
Updates the requirements on [criterion](https://github.com/bheisler/criterion.rs) to permit the latest version.
- [Release notes](https://github.com/bheisler/criterion.rs/releases)
- [Changelog](https://github.com/bheisler/criterion.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bheisler/criterion.rs/compare/0.2.9...0.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-08-26 05:35:47 +00:00
a45e76c92f Merge pull request #95 from faern/upgrade-to-rust2018
Upgrade to Rust 2018 edition
2019-03-25 16:57:22 +00:00
a6dfadc21d Manual import cleanup 2019-03-25 15:24:17 +01:00
61eeb5843f Run cargo fix --edition-idioms 2019-03-25 15:15:41 +01:00
d8f3cc1992 Upgrade to edition 2018 2019-03-25 15:15:21 +01:00
c2d4b9aeae Run cargo fix --edition 2019-03-25 15:13:50 +01:00
3479b6f272 Merge pull request #94 from mullvad/fix-deserialization
Deserialize from String rather &str for Ipv4Network and Ipv6Network
2019-02-20 16:49:28 +01:00
10d12cd945 Deserialize from String rather &str for ipnetwork:Ipv{4,6}Network 2019-02-20 11:07:29 +00:00
47a539f2f1 Merge pull request #93 from achanda/new-methods
Implement is_subnet_of, is_supernet_of and overlaps
2019-02-07 18:43:47 +01:00
e7c85b5b81 Implement overlaps for both types 2019-02-06 23:29:46 +00:00
439a0deeb7 Implement is_subnet_of and is_supernet_of 2019-02-06 23:14:07 +00:00
b2c458e6f1 Merge pull request #91 from achanda/contains-bench
Track benchmark for contains
2019-02-03 01:10:09 +01:00
1b06452e88 Optimise contains for IPv4 2019-02-02 23:57:25 +00:00
2b9936fa8e Track benchmark for contains 2019-02-02 23:36:08 +00:00
553fa0ba1c Merge pull request #90 from achanda/parse-bench
Add a benchmark for CIDR parsing
2019-02-03 00:22:15 +01:00
a19f24a9c6 Add a benchmark for CIDR parsing
Also optimise the cidr_parts implementation
2019-02-02 21:54:16 +00:00
aff0419a75 Merge pull request #89 from achanda/release-0.14.0
Prepare new release
2019-02-01 23:22:41 +01:00
1f96439a87 Prepare new release 2019-02-01 22:16:38 +00:00
25d7dc19d1 Merge pull request #88 from mullvad/fix-deserialization
Added custom deserialization and serialization for ipnetwork::IpNetwork
2019-02-01 23:11:31 +01:00
d8ce2e4dbc Added custom deserialization and serialization for ipnetwork::IpNetwork 2019-02-01 12:01:33 +00:00
16c4af9823 Merge pull request #86 from achanda/dependabot/cargo/clippy-0.0.302
Update clippy requirement from 0.0.104 to 0.0.302
2018-12-08 20:39:54 +00:00
72677fbe8f Update clippy requirement from 0.0.104 to 0.0.302
Updates the requirements on [clippy](https://github.com/rust-lang-nursery/rust-clippy) to permit the latest version.
- [Release notes](https://github.com/rust-lang-nursery/rust-clippy/releases)
- [Changelog](https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang-nursery/rust-clippy/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-12-08 20:29:20 +00:00
35331cf7ea Merge pull request #85 from achanda/from-casts
Use from casts in some more places
2018-11-26 11:28:55 +00:00
1d57287c77 Use from casts in some more places 2018-11-26 16:50:33 +05:30
ddec283819 Merge pull request #84 from achanda/change-type
Change return type of Ipv4Network::size to u32
2018-11-24 04:31:13 +00:00
2b21f38171 Change return type of Ipv4Network::size to u32
Also, generalize size over IpvNetwork
NOTE: this is a breaking change
2018-11-21 15:48:29 +00:00
2bd3db84a8 Merge pull request #83 from achanda/network-toplevel
Export network and broadcast for IpNetwork
2018-11-10 21:10:48 +00:00
a8edccafa1 Export network and broadcast for IpNetwork
Adopted from https://github.com/achanda/ipnetwork/pull/78
2018-11-10 21:04:41 +00:00
9ab988715e Merge pull request #81 from achanda/release-0.13.1
Release version 0.13.1
2018-08-31 23:56:27 +01:00
1f3d42d89f Release 0.13.1 2018-08-31 23:27:53 +01:00
e0a9afcb36 Merge pull request #79 from sharksforarms/issue-#62
Parse Ipv4Addr using Ipv4Addr::from_str
2018-05-30 19:01:38 +02:00
5e7eb0bd1b Parse IpAddr using Ipv4Addr::from_str 2018-05-27 19:26:11 -05:00
d5e6b11170 Merge pull request #77 from achanda/cleanup
Remove features from cargo manifest
2018-05-12 15:23:29 +02:00
a406474bea Remove features from cargo manifest 2018-05-12 14:12:42 +01:00
b762304ee4 Merge pull request #76 from achanda/release_0.13
Uprev for release
2018-05-12 15:11:41 +02:00
9ac9df11e2 Uprev for release 2018-05-12 14:04:16 +01:00
98946a8fa9 Merge pull request #75 from achanda/stable-128
Remove the feature flag for 128 bit integers
2018-05-12 15:01:31 +02:00
bbcc53cd94 Remove the feature flag for 128 bit integers
Since it is stable now
2018-05-12 13:53:59 +01:00
36ebedf346 Merge pull request #74 from sharksforarms/serde-issue-73
Serde serialize/deserialize
2018-04-18 23:53:24 +02:00
2286d58728 cargo fmt and clippy 2018-04-17 19:28:45 -05:00
c6ead9d654 remove serde feature flag 2018-04-17 19:28:38 -05:00
de707e35ae Remove with-serde in favor of just 'serde'
https://rust-lang-nursery.github.io/api-guidelines/naming.html#feature-names-are-free-of-placeholder-words-c-feature
2018-04-17 17:03:42 -05:00
4e97146605 No need to implement serialize/deserialize for enum, use serde(untagged) 2018-04-17 06:30:44 -05:00
4414ae4f0f direct serialize/deserialize from string/to string 2018-04-16 19:25:22 -05:00
ac3b11b1d7 ignore .idea/ folder 2018-04-16 19:22:54 -05:00
6b4dc9762b pin version 2018-04-16 19:22:33 -05:00
15fa093618 cargo fmt 2018-04-15 21:15:36 -05:00
811cab642e vector of ip networks 2018-04-15 21:11:01 -05:00
ce26663b1f Add Ipv6 test case 2018-04-15 21:04:13 -05:00
7f684cf06e WARNING: src/lib.rs - IpNetwork::mask (line 83) Code block is not currently run as a test, but will in future versions of rustdoc. Please ensure this code block is a runnable test, or use the ignore directive. 2018-04-15 21:04:00 -05:00
e075fb80f8 Add IPv4 test case 2018-04-15 20:58:52 -05:00
47d8f0d89e Merge pull request #72 from sharksforarms/master
* Default to /32 for an IP
2018-04-16 00:40:14 +02:00
72326bd96f use an option 2018-03-27 21:58:10 -05:00
411c71e900 cargo fmt 2018-03-27 21:48:34 -05:00
cc24cceb1a add test cases 2018-03-27 21:48:29 -05:00
fcdb0c6b87 simpler way of doing it 2018-03-27 21:39:15 -05:00
5fabbc91d5 If there is no prefix, default to number of bits ipv4/32, ipv6/128 2018-03-27 21:36:39 -05:00
82fef85a02 Doc tests 2018-03-27 20:34:12 -05:00
746342c0af Default to /32 for an IP 2018-03-27 20:34:07 -05:00
c0143a2362 Merge pull request #70 from achanda/release_0.12.8
Uprev for release
2018-03-15 08:51:54 +00:00
eddcc026cb Uprev for release 2018-03-15 08:45:16 +00:00
78dc628196 Merge pull request #69 from admwrd/add_serde_feature
Add Serde Feature
2018-03-15 08:41:11 +00:00
c9e25e15fa Add Serde Feature for IpNetwork 2018-03-14 19:00:38 -07:00
3703488473 Merge pull request #68 from tailhook/generic_contains
Add `IpNetwork::contains`
2018-03-05 14:59:27 +00:00
42eed8dbb5 Add IpNetwork::contains 2018-03-05 16:49:32 +02:00
dc0cc2d5a1 Merge pull request #65 from achanda/fmt
Another round of formatting
2017-12-21 17:48:48 +00:00
a3528a8f43 Another round of formatting 2017-12-21 17:45:45 +00:00
226bd7ec00 Merge pull request #64 from achanda/clippy_run
Readability improvements as suggested by clippy
2017-11-08 19:17:17 +00:00
023406419b Readability improvements as suggested by clippy 2017-11-08 16:59:56 +00:00
bafba6ed93 Merge pull request #63 from tshepang/multiple-slashes
avoid misleading message
2017-10-09 11:03:26 +01:00
124330be88 avoid misleading message
Closes #61
2017-10-09 11:59:48 +02:00
5f5d985e7d Merge pull request #60 from achanda/send_sync
Add two tests for send and sync
2017-09-22 18:02:19 +01:00
8ee7f2d1ed Add two tests for send and sync
To prevent future regressions as the library evolves
2017-09-22 17:56:43 +01:00
384f86d590 Merge pull request #58 from achanda/release_0.12.7
Uprev for new release
2017-09-07 18:34:25 +01:00
bfafdc2c2f Uprev for new release 2017-09-07 18:29:18 +01:00
14e28232d7 Merge pull request #57 from achanda/badge
Add travis badge
2017-09-07 18:27:50 +01:00
6e64322655 Add travis badge 2017-09-07 18:24:34 +01:00
d876a9d58a Merge pull request #56 from nrdmn/master
add From<IpAddr> for IpNetwork
2017-08-22 22:27:46 +01:00
9db3d5c30d add From<IpAddr> for IpNetwork 2017-08-22 15:15:18 +00:00
0a99bf969c Merge pull request #55 from bgermann/master
Fix annoying typo
2017-08-14 17:07:41 +01:00
a28098444e Fix annoying typo
prifex -> prefix
2017-08-14 18:05:22 +02:00
f7c963258b Merge pull request #54 from achanda/docs
Add a html_root_url attribute
2017-07-30 12:45:01 +01:00
482aed66e7 Add a html_root_url attribute 2017-07-30 12:38:13 +01:00
d56ab96c53 Merge pull request #53 from achanda/vscode
Ignore vscode
2017-07-30 12:36:18 +01:00
2d9b2d27cb Ignore vscode 2017-07-30 12:30:14 +01:00
1abf10d52b Merge pull request #51 from achanda/release_0.12.6
Uprev for release
2017-07-11 21:59:28 +01:00
f075e0de38 Remove the push to rust-ci
docs.rs takes care of this now
2017-07-11 21:54:41 +01:00
3d020d8b2a Test ipv6 methods in CI 2017-07-11 21:50:25 +01:00
4d2d9a2dd6 Uprev for release 2017-07-11 21:49:06 +01:00
f8aec99e90 Merge pull request #50 from achanda/v6_methods
Add a bunch of new methods for Ipv6Network
2017-07-11 21:46:45 +01:00
910a52b64b Add a size method for Ipv6Network 2017-07-11 21:21:00 +01:00
2ee819a438 Add a broadcast method for Ipv6Network 2017-07-11 21:11:39 +01:00
1171e5ae94 Add a network method for Ipv6Network 2017-07-11 21:01:41 +01:00
99203160b9 Add an iterator for ipv6 network. (#49)
This is feature gated since i128 is not stable yet
2017-07-08 20:28:00 +01:00
fb8f735741 Merge pull request #48 from achanda/categories
Add categories
2017-06-07 11:23:07 +01:00
d7b2a14caa Add categories 2017-06-07 11:21:47 +01:00
9 changed files with 745 additions and 129 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
.gitignore
Cargo.lock
target
.vscode
.idea/

View File

@ -6,12 +6,10 @@ rust:
env:
global:
- secure: gokQ7xIWwmAuEUW3IyS5B/pbZxdFSSDBto5beJ+4ACGcRMDqJ/eCPf1ekSVXME4TWM46uUCjxcdUjYhIhQ6sG4zfWck4u45qRJ5JbIoTvR+ykxhN1j3Zi5x9ptP3ALDbHn2i3v6t9xohORfQpz3dVND5c7thbYDyKP2ZR1sez5c=
- FEATURES: default
script:
- cargo build --verbose
- cargo test --verbose
- cargo build --release --verbose
- cargo doc --verbose
after_script:
- curl http://www.rust-ci.org/artifacts/put?t=$RUSTCI_TOKEN | sh

View File

@ -1,6 +1,6 @@
[package]
name = "ipnetwork"
version = "0.12.4"
version = "0.15.0" # When updating version, also modify html_root_url in the lib.rs
authors = ["Abhishek Chanda <abhishek.becs@gmail.com>", "Linus Färnstrand <faern@faern.net>"]
description = "A library to work with IP CIDRs in Rust, heavily WIP"
license = "Apache-2.0"
@ -8,10 +8,25 @@ repository = "https://github.com/achanda/ipnetwork"
keywords = ["network", "ip", "address"]
readme = "README.md"
documentation = "https://docs.rs/ipnetwork/"
categories = ["network-programming", "os"]
edition = "2018"
[dependencies]
clippy = {version = "0.0.104", optional = true}
clippy = {version = "0.0.302", optional = true}
serde = ">=0.8.0, <2.0"
[dev-dependencies]
serde_json = "1.0"
serde_derive = ">=0.8.0, <2.0"
criterion = "0.3.0"
[badges]
travis-ci = { repository = "achanda/ipnetwork" }
[features]
default = []
dev = ["clippy"]
[[bench]]
name = "parse_bench"
harness = false

32
benches/parse_bench.rs Normal file
View File

@ -0,0 +1,32 @@
use criterion::{criterion_group, criterion_main, Criterion};
use ipnetwork::{Ipv4Network, Ipv6Network};
use std::net::{Ipv4Addr, Ipv6Addr};
fn parse_ipv4_benchmark(c: &mut Criterion) {
c.bench_function("parse ipv4", |b| b.iter(|| {
"127.1.0.0/24".parse::<Ipv4Network>().unwrap()
}));
}
fn parse_ipv6_benchmark(c: &mut Criterion) {
c.bench_function("parse ipv6", |b| b.iter(|| {
"FF01:0:0:17:0:0:0:2/64".parse::<Ipv6Network>().unwrap()
}));
}
fn contains_ipv4_benchmark(c: &mut Criterion) {
c.bench_function("contains ipv4", |b| b.iter(|| {
let cidr = "74.125.227.0/25".parse::<Ipv4Network>().unwrap();
cidr.contains(Ipv4Addr::new(74, 125, 227, 4))
}));
}
fn contains_ipv6_benchmark(c: &mut Criterion) {
c.bench_function("contains ipv6", |b| b.iter(|| {
let cidr = "FF01:0:0:17:0:0:0:2/65".parse::<Ipv6Network>().unwrap();
cidr.contains(Ipv6Addr::new(0xff01, 0, 0, 0x17, 0x7fff, 0, 0, 0x2))
}));
}
criterion_group!(benches, parse_ipv4_benchmark, parse_ipv6_benchmark, contains_ipv4_benchmark, contains_ipv6_benchmark);
criterion_main!(benches);

View File

@ -1,9 +1,7 @@
use std::net::Ipv4Addr;
use std::fmt;
use std::error::Error;
use std::{error::Error, fmt};
/// Represents a bunch of errors that can occur while working with a `IpNetwork`
#[derive(Debug,Clone,PartialEq,Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IpNetworkError {
InvalidAddr(String),
InvalidPrefix,
@ -11,11 +9,11 @@ pub enum IpNetworkError {
}
impl fmt::Display for IpNetworkError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use IpNetworkError::*;
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use crate::IpNetworkError::*;
match *self {
InvalidAddr(ref s) => write!(f, "invalid address: {}", s),
InvalidPrefix => write!(f, "invalid prifex"),
InvalidPrefix => write!(f, "invalid prefix"),
InvalidCidrFormat(ref s) => write!(f, "invalid cidr format: {}", s),
}
}
@ -23,44 +21,43 @@ impl fmt::Display for IpNetworkError {
impl Error for IpNetworkError {
fn description(&self) -> &str {
use IpNetworkError::*;
use crate::IpNetworkError::*;
match *self {
InvalidAddr(_) => "address is invalid",
InvalidPrefix => "prefix is invalid",
InvalidCidrFormat(_) => "cidr is invalid",
}
}
}
pub fn cidr_parts(cidr: &str) -> Result<(&str, &str), IpNetworkError> {
let parts = cidr.split('/').collect::<Vec<&str>>();
if parts.len() == 2 {
Ok((parts[0], parts[1]))
pub fn cidr_parts(cidr: &str) -> Result<(&str, Option<&str>), IpNetworkError> {
// Try to find a single slash
if let Some(sep) = cidr.find('/') {
let (ip, prefix) = cidr.split_at(sep);
// Error if cidr has multiple slashes
if prefix[1..].find('/').is_some() {
return Err(IpNetworkError::InvalidCidrFormat(format!(
"CIDR must contain a single '/': {}",
cidr
)));
}
else {
// Handle the case when cidr has exactly one slash
return Ok((ip, Some(&prefix[1..])));
}
} else {
Err(IpNetworkError::InvalidCidrFormat(format!("CIDR must contain '/': {}", cidr)))
// Handle the case when cidr does not have a slash
return Ok((cidr, None));
}
}
pub fn parse_prefix(prefix: &str, max: u8) -> Result<u8, IpNetworkError> {
let mask = prefix.parse::<u8>().map_err(|_| IpNetworkError::InvalidPrefix)?;
let mask = prefix
.parse::<u8>()
.map_err(|_| IpNetworkError::InvalidPrefix)?;
if mask > max {
Err(IpNetworkError::InvalidPrefix)
} else {
Ok(mask)
}
}
pub fn parse_addr(addr: &str) -> Result<Ipv4Addr, IpNetworkError> {
let addr_parts = addr.split('.').map(|b| b.parse::<u8>());
let mut bytes = [0; 4];
for (i, byte) in addr_parts.enumerate() {
if i >= 4 {
return Err(IpNetworkError::InvalidAddr(format!("More than 4 bytes: {}", addr)));
}
bytes[i] = byte.map_err(|_| {
IpNetworkError::InvalidAddr(format!("All bytes not 0-255: {}", addr))
})?;
}
Ok(Ipv4Addr::new(bytes[0], bytes[1], bytes[2], bytes[3]))
}

View File

@ -1,18 +1,35 @@
use std::fmt;
use std::net::Ipv4Addr;
use std::str::FromStr;
use common::{IpNetworkError, cidr_parts, parse_prefix, parse_addr};
use crate::common::{cidr_parts, parse_prefix, IpNetworkError};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::{fmt, net::Ipv4Addr, str::FromStr};
const IPV4_BITS: u8 = 32;
/// Represents a network range where the IP addresses are of v4
#[derive(Debug,Clone,Copy,Hash,PartialEq,Eq,PartialOrd,Ord)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Ipv4Network {
addr: Ipv4Addr,
prefix: u8,
}
impl<'de> Deserialize<'de> for Ipv4Network {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = <String>::deserialize(deserializer)?;
Ipv4Network::from_str(&s).map_err(de::Error::custom)
}
}
impl Serialize for Ipv4Network {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl Ipv4Network {
/// Constructs a new `Ipv4Network` from any `Ipv4Addr` and a prefix denoting the network size.
/// If the prefix is larger than 32 this will return an `IpNetworkError::InvalidPrefix`.
@ -20,10 +37,7 @@ impl Ipv4Network {
if prefix > IPV4_BITS {
Err(IpNetworkError::InvalidPrefix)
} else {
Ok(Ipv4Network {
addr: addr,
prefix: prefix,
})
Ok(Ipv4Network { addr, prefix })
}
}
@ -31,12 +45,9 @@ impl Ipv4Network {
/// `Ipv4Addr` in the given network. `None` will be returned when there are no more
/// addresses.
pub fn iter(&self) -> Ipv4NetworkIterator {
let start = u32::from(self.network()) as u64;
let start = u32::from(self.network());
let end = start + self.size();
Ipv4NetworkIterator {
next: start,
end: end,
}
Ipv4NetworkIterator { next: start, end }
}
pub fn ip(&self) -> Ipv4Addr {
@ -47,6 +58,27 @@ impl Ipv4Network {
self.prefix
}
/// Checks if the given `Ipv4Network` is a subnet of the other.
pub fn is_subnet_of(self, other: Ipv4Network) -> bool {
other.ip() <= self.ip() && other.broadcast() >= self.broadcast()
}
/// Checks if the given `Ipv4Network` is a supernet of the other.
pub fn is_supernet_of(self, other: Ipv4Network) -> bool {
other.is_subnet_of(self)
}
/// Checks if the given `Ipv4Network` is partly contained in other.
pub fn overlaps(self, other: Ipv4Network) -> bool {
other.contains(self.ip()) || (
other.contains(self.broadcast()) || (
self.contains(other.ip()) || (
self.contains(other.broadcast())
)
)
)
}
/// Returns the mask for this `Ipv4Network`.
/// That means the `prefix` most significant bits will be 1 and the rest 0
///
@ -56,12 +88,14 @@ impl Ipv4Network {
/// use std::net::Ipv4Addr;
/// use ipnetwork::Ipv4Network;
///
/// let net: Ipv4Network = "127.0.0.0".parse().unwrap();
/// assert_eq!(net.mask(), Ipv4Addr::new(255, 255, 255, 255));
/// let net: Ipv4Network = "127.0.0.0/16".parse().unwrap();
/// assert_eq!(net.mask(), Ipv4Addr::new(255, 255, 0, 0));
/// ```
pub fn mask(&self) -> Ipv4Addr {
let prefix = self.prefix;
let mask = !(0xffffffff as u64 >> prefix) as u32;
let mask = !(0xffff_ffff as u64 >> prefix) as u32;
Ipv4Addr::from(mask)
}
@ -114,8 +148,8 @@ impl Ipv4Network {
/// assert!(!net.contains(Ipv4Addr::new(127, 0, 1, 70)));
/// ```
pub fn contains(&self, ip: Ipv4Addr) -> bool {
let net = u32::from(self.network());
let mask = u32::from(self.mask());
let mask = !(0xffff_ffff as u64 >> self.prefix) as u32;
let net = u32::from(self.addr) & mask;
(u32::from(ip) & mask) == net
}
@ -133,9 +167,9 @@ impl Ipv4Network {
/// let tinynet: Ipv4Network = "0.0.0.0/32".parse().unwrap();
/// assert_eq!(tinynet.size(), 1);
/// ```
pub fn size(&self) -> u64 {
let host_bits = (IPV4_BITS - self.prefix) as u32;
(2 as u64).pow(host_bits)
pub fn size(&self) -> u32 {
let host_bits = u32::from(IPV4_BITS - self.prefix);
(2 as u32).pow(host_bits)
}
/// Returns the `n`:th address within this network.
@ -156,7 +190,7 @@ impl Ipv4Network {
/// assert_eq!(net2.nth(256).unwrap(), Ipv4Addr::new(10, 0, 1, 0));
/// ```
pub fn nth(&self, n: u32) -> Option<Ipv4Addr> {
if (n as u64) < self.size() {
if n < self.size() {
let net = u32::from(self.network());
Some(Ipv4Addr::from(net + n))
} else {
@ -166,12 +200,11 @@ impl Ipv4Network {
}
impl fmt::Display for Ipv4Network {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{}/{}", self.ip(), self.prefix())
}
}
/// Creates an `Ipv4Network` from parsing a string in CIDR notation.
///
/// # Examples
@ -189,15 +222,28 @@ impl FromStr for Ipv4Network {
type Err = IpNetworkError;
fn from_str(s: &str) -> Result<Ipv4Network, IpNetworkError> {
let (addr_str, prefix_str) = cidr_parts(s)?;
let addr = parse_addr(addr_str)?;
let prefix = parse_prefix(prefix_str, IPV4_BITS)?;
let addr = Ipv4Addr::from_str(addr_str)
.map_err(|_| IpNetworkError::InvalidAddr(addr_str.to_string()))?;
let prefix = match prefix_str {
Some(v) => parse_prefix(v, IPV4_BITS)?,
None => IPV4_BITS,
};
Ipv4Network::new(addr, prefix)
}
}
impl From<Ipv4Addr> for Ipv4Network {
fn from(a: Ipv4Addr) -> Ipv4Network {
Ipv4Network {
addr: a,
prefix: 32,
}
}
}
pub struct Ipv4NetworkIterator {
next: u64,
end: u64,
next: u32,
end: u32,
}
impl Iterator for Ipv4NetworkIterator {
@ -220,7 +266,7 @@ pub fn ipv4_mask_to_prefix(mask: Ipv4Addr) -> Result<u8, IpNetworkError> {
let mask = u32::from(mask);
let prefix = (!mask).leading_zeros() as u8;
if ((mask as u64) << prefix) & 0xffffffff != 0 {
if (u64::from(mask) << prefix) & 0xffff_ffff != 0 {
Err(IpNetworkError::InvalidPrefix)
} else {
Ok(prefix)
@ -229,10 +275,10 @@ pub fn ipv4_mask_to_prefix(mask: Ipv4Addr) -> Result<u8, IpNetworkError> {
#[cfg(test)]
mod test {
use std::mem;
use std::collections::HashMap;
use std::net::Ipv4Addr;
use super::*;
use std::collections::HashMap;
use std::mem;
use std::net::Ipv4Addr;
#[test]
fn create_v4() {
@ -246,13 +292,6 @@ mod test {
assert!(net.is_err());
}
#[test]
fn parse_v4_0bit() {
let cidr: Ipv4Network = "0/0".parse().unwrap();
assert_eq!(cidr.ip(), Ipv4Addr::new(0, 0, 0, 0));
assert_eq!(cidr.prefix(), 0);
}
#[test]
fn parse_v4_24bit() {
let cidr: Ipv4Network = "127.1.0.0/24".parse().unwrap();
@ -267,6 +306,13 @@ mod test {
assert_eq!(cidr.prefix(), 32);
}
#[test]
fn parse_v4_noprefix() {
let cidr: Ipv4Network = "127.0.0.0".parse().unwrap();
assert_eq!(cidr.ip(), Ipv4Addr::new(127, 0, 0, 0));
assert_eq!(cidr.prefix(), 32);
}
#[test]
fn parse_v4_fail_addr() {
let cidr: Option<Ipv4Network> = "10.a.b/8".parse().ok();
@ -299,27 +345,9 @@ mod test {
}
#[test]
fn size_v4_24bit() {
let net: Ipv4Network = "0/24".parse().unwrap();
assert_eq!(net.size(), 256);
}
#[test]
fn size_v4_1bit() {
let net: Ipv4Network = "0/31".parse().unwrap();
assert_eq!(net.size(), 2);
}
#[test]
fn size_v4_max() {
let net: Ipv4Network = "0/0".parse().unwrap();
assert_eq!(net.size(), 4_294_967_296);
}
#[test]
fn size_v4_min() {
let net: Ipv4Network = "0/32".parse().unwrap();
assert_eq!(net.size(), 1);
fn parse_v4_fail_two_slashes() {
let cidr: Option<Ipv4Network> = "10.1.1.1/24/".parse().ok();
assert_eq!(None, cidr);
}
#[test]
@ -398,14 +426,6 @@ mod test {
assert_eq!(None, iter.next());
}
#[test]
fn iterator_v4_tiny() {
let cidr: Ipv4Network = "10/32".parse().unwrap();
let mut iter = cidr.iter();
assert_eq!(Ipv4Addr::new(10, 0, 0, 0), iter.next().unwrap());
assert_eq!(None, iter.next());
}
// Tests the entire IPv4 space to see if the iterator will stop at the correct place
// and not overflow or wrap around. Ignored since it takes a long time to run.
#[test]
@ -432,4 +452,66 @@ mod test {
let prefix = ipv4_mask_to_prefix(mask);
assert!(prefix.is_err());
}
#[test]
fn ipv4network_from_ipv4addr() {
let net = Ipv4Network::from(Ipv4Addr::new(127, 0, 0, 1));
let expected = Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap();
assert_eq!(net, expected);
}
#[test]
fn test_send() {
fn assert_send<T: Send>() {}
assert_send::<Ipv4Network>();
}
#[test]
fn test_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<Ipv4Network>();
}
// Tests from cpython https://github.com/python/cpython/blob/e9bc4172d18db9c182d8e04dd7b033097a994c06/Lib/test/test_ipaddress.py
#[test]
fn test_is_subnet_of() {
let mut test_cases: HashMap<(Ipv4Network, Ipv4Network), bool> = HashMap::new();
test_cases.insert(("10.0.0.0/30".parse().unwrap(), "10.0.1.0/24".parse().unwrap()), false);
test_cases.insert(("10.0.0.0/30".parse().unwrap(), "10.0.0.0/24".parse().unwrap()), true);
test_cases.insert(("10.0.0.0/30".parse().unwrap(), "10.0.1.0/24".parse().unwrap()), false);
test_cases.insert(("10.0.1.0/24".parse().unwrap(), "10.0.0.0/30".parse().unwrap()), false);
for (key, val) in test_cases.iter() {
let (src, dest) = (key.0, key.1);
assert_eq!(src.is_subnet_of(dest), *val, "testing with {} and {}", src, dest);
}
}
#[test]
fn test_is_supernet_of() {
let mut test_cases: HashMap<(Ipv4Network, Ipv4Network), bool> = HashMap::new();
test_cases.insert(("10.0.0.0/30".parse().unwrap(), "10.0.1.0/24".parse().unwrap()), false);
test_cases.insert(("10.0.0.0/30".parse().unwrap(), "10.0.0.0/24".parse().unwrap()), false);
test_cases.insert(("10.0.0.0/30".parse().unwrap(), "10.0.1.0/24".parse().unwrap()), false);
test_cases.insert(("10.0.0.0/24".parse().unwrap(), "10.0.0.0/30".parse().unwrap()), true);
for (key, val) in test_cases.iter() {
let (src, dest) = (key.0, key.1);
assert_eq!(src.is_supernet_of(dest), *val, "testing with {} and {}", src, dest);
}
}
#[test]
fn test_overlaps() {
let other: Ipv4Network = "1.2.3.0/30".parse().unwrap();
let other2: Ipv4Network = "1.2.2.0/24".parse().unwrap();
let other3: Ipv4Network = "1.2.2.64/26".parse().unwrap();
let skynet: Ipv4Network = "1.2.3.0/24".parse().unwrap();
assert_eq!(skynet.overlaps(other), true);
assert_eq!(skynet.overlaps(other2), false);
assert_eq!(other2.overlaps(other3), true);
}
}

View File

@ -1,20 +1,36 @@
use std::cmp;
use std::fmt;
use std::net::Ipv6Addr;
use std::str::FromStr;
use common::{IpNetworkError, cidr_parts, parse_prefix};
use crate::common::{cidr_parts, parse_prefix, IpNetworkError};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::{cmp, fmt, net::Ipv6Addr, str::FromStr};
const IPV6_BITS: u8 = 128;
const IPV6_SEGMENT_BITS: u8 = 16;
/// Represents a network range where the IP addresses are of v6
#[derive(Debug,Clone,Copy,Hash,PartialEq,Eq,PartialOrd,Ord)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Ipv6Network {
addr: Ipv6Addr,
prefix: u8,
}
impl<'de> Deserialize<'de> for Ipv6Network {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = <String>::deserialize(deserializer)?;
Ipv6Network::from_str(&s).map_err(de::Error::custom)
}
}
impl Serialize for Ipv6Network {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl Ipv6Network {
/// Constructs a new `Ipv6Network` from any `Ipv6Addr` and a prefix denoting the network size.
/// If the prefix is larger than 128 this will return an `IpNetworkError::InvalidPrefix`.
@ -22,13 +38,66 @@ impl Ipv6Network {
if prefix > IPV6_BITS {
Err(IpNetworkError::InvalidPrefix)
} else {
Ok(Ipv6Network {
addr: addr,
prefix: prefix,
})
Ok(Ipv6Network { addr, prefix })
}
}
/// Returns an iterator over `Ipv6Network`. Each call to `next` will return the next
/// `Ipv6Addr` in the given network. `None` will be returned when there are no more
/// addresses.
pub fn iter(&self) -> Ipv6NetworkIterator {
let dec = u128::from(self.addr);
let max = u128::max_value();
let prefix = self.prefix;
let mask = max.checked_shl(u32::from(IPV6_BITS - prefix)).unwrap_or(0);
let start: u128 = dec & mask;
let mask = max.checked_shr(u32::from(prefix)).unwrap_or(0);
let end: u128 = dec | mask;
Ipv6NetworkIterator {
next: start,
end: end,
}
}
/// Returns the address of the network denoted by this `Ipv6Network`.
/// This means the lowest possible IPv6 address inside of the network.
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use ipnetwork::Ipv6Network;
///
/// let net: Ipv6Network = "2001:db8::/96".parse().unwrap();
/// assert_eq!(net.network(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
/// ```
pub fn network(&self) -> Ipv6Addr {
let mask = u128::from(self.mask());
let ip = u128::from(self.addr) & mask;
Ipv6Addr::from(ip)
}
/// Returns the broadcast address of this `Ipv6Network`.
/// This means the highest possible IPv4 address inside of the network.
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use ipnetwork::Ipv6Network;
///
/// let net: Ipv6Network = "2001:db8::/96".parse().unwrap();
/// assert_eq!(net.broadcast(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xffff, 0xffff));
/// ```
pub fn broadcast(&self) -> Ipv6Addr {
let mask = u128::from(self.mask());
let broadcast = u128::from(self.addr) | !mask;
Ipv6Addr::from(broadcast)
}
pub fn ip(&self) -> Ipv6Addr {
self.addr
}
@ -37,6 +106,27 @@ impl Ipv6Network {
self.prefix
}
/// Checks if the given `Ipv6Network` is a subnet of the other.
pub fn is_subnet_of(self, other: Ipv6Network) -> bool {
other.ip() <= self.ip() && other.broadcast() >= self.broadcast()
}
/// Checks if the given `Ipv6Network` is a supernet of the other.
pub fn is_supernet_of(self, other: Ipv6Network) -> bool {
other.is_subnet_of(self)
}
/// Checks if the given `Ipv6Network` is partly contained in other.
pub fn overlaps(self, other: Ipv6Network) -> bool {
other.contains(self.ip()) || (
other.contains(self.broadcast()) || (
self.contains(other.ip()) || (
self.contains(other.broadcast())
)
)
)
}
/// Returns the mask for this `Ipv6Network`.
/// That means the `prefix` most significant bits will be 1 and the rest 0
///
@ -46,6 +136,8 @@ impl Ipv6Network {
/// use std::net::Ipv6Addr;
/// use ipnetwork::Ipv6Network;
///
/// let net: Ipv6Network = "ff01::0".parse().unwrap();
/// assert_eq!(net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff));
/// let net: Ipv6Network = "ff01::0/32".parse().unwrap();
/// assert_eq!(net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0));
/// ```
@ -76,7 +168,30 @@ impl Ipv6Network {
let a = self.addr.segments();
let b = ip.segments();
let addrs = Iterator::zip(a.iter(), b.iter());
self.mask().segments().iter().zip(addrs).all(|(mask, (a, b))| a & mask == b & mask)
self.mask()
.segments()
.iter()
.zip(addrs)
.all(|(mask, (a, b))| a & mask == b & mask)
}
/// Returns number of possible host addresses in this `Ipv6Network`.
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use ipnetwork::Ipv6Network;
///
/// let net: Ipv6Network = "ff01::0/32".parse().unwrap();
/// assert_eq!(net.size(), 79228162514264337593543950336);
///
/// let tinynet: Ipv6Network = "ff01::0/128".parse().unwrap();
/// assert_eq!(tinynet.size(), 1);
/// ```
pub fn size(&self) -> u128 {
let host_bits = u32::from(IPV6_BITS - self.prefix);
(2 as u128).pow(host_bits)
}
}
@ -84,14 +199,46 @@ impl FromStr for Ipv6Network {
type Err = IpNetworkError;
fn from_str(s: &str) -> Result<Ipv6Network, IpNetworkError> {
let (addr_str, prefix_str) = cidr_parts(s)?;
let addr = Ipv6Addr::from_str(addr_str).map_err(|_| IpNetworkError::InvalidAddr(addr_str.to_string()))?;
let prefix = parse_prefix(prefix_str, IPV6_BITS)?;
let addr = Ipv6Addr::from_str(addr_str)
.map_err(|_| IpNetworkError::InvalidAddr(addr_str.to_string()))?;
let prefix = match prefix_str {
Some(v) => parse_prefix(v, IPV6_BITS)?,
None => IPV6_BITS,
};
Ipv6Network::new(addr, prefix)
}
}
impl From<Ipv6Addr> for Ipv6Network {
fn from(a: Ipv6Addr) -> Ipv6Network {
Ipv6Network {
addr: a,
prefix: 128,
}
}
}
pub struct Ipv6NetworkIterator {
next: u128,
end: u128,
}
impl Iterator for Ipv6NetworkIterator {
type Item = Ipv6Addr;
fn next(&mut self) -> Option<Ipv6Addr> {
if self.next <= self.end {
let next = Ipv6Addr::from(self.next);
self.next += 1;
Some(next)
} else {
None
}
}
}
impl fmt::Display for Ipv6Network {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{}/{}", self.ip(), self.prefix())
}
}
@ -133,8 +280,9 @@ pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, IpNetworkError> {
#[cfg(test)]
mod test {
use std::net::Ipv6Addr;
use super::*;
use std::collections::HashMap;
use std::net::Ipv6Addr;
#[test]
fn create_v6() {
@ -162,6 +310,13 @@ mod test {
assert_eq!(cidr.prefix(), 64);
}
#[test]
fn parse_v6_noprefix() {
let cidr: Ipv6Network = "::1".parse().unwrap();
assert_eq!(cidr.ip(), Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
assert_eq!(cidr.prefix(), 128);
}
#[test]
fn parse_v6_fail_addr() {
let cidr: Option<Ipv6Network> = "2001::1::/8".parse().ok();
@ -174,6 +329,12 @@ mod test {
assert_eq!(None, cidr);
}
#[test]
fn parse_v6_fail_two_slashes() {
let cidr: Option<Ipv6Network> = "::1/24/".parse().ok();
assert_eq!(None, cidr);
}
#[test]
fn mask_v6() {
let cidr = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 40).unwrap();
@ -208,4 +369,127 @@ mod test {
let prefix = ipv6_mask_to_prefix(mask);
assert!(prefix.is_err());
}
#[test]
fn iterator_v6() {
let cidr: Ipv6Network = "2001:db8::/126".parse().unwrap();
let mut iter = cidr.iter();
assert_eq!(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
iter.next().unwrap()
);
assert_eq!(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
iter.next().unwrap()
);
assert_eq!(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2),
iter.next().unwrap()
);
assert_eq!(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 3),
iter.next().unwrap()
);
assert_eq!(None, iter.next());
}
#[test]
fn iterator_v6_tiny() {
let cidr: Ipv6Network = "2001:db8::/128".parse().unwrap();
let mut iter = cidr.iter();
assert_eq!(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
iter.next().unwrap()
);
assert_eq!(None, iter.next());
}
#[test]
fn iterator_v6_huge() {
let cidr: Ipv6Network = "2001:db8::/0".parse().unwrap();
let mut iter = cidr.iter();
assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), iter.next().unwrap());
assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), iter.next().unwrap());
assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2), iter.next().unwrap());
}
#[test]
fn network_v6() {
let cidr: Ipv6Network = "2001:db8::0/96".parse().unwrap();
let net = cidr.network();
let expected: Ipv6Addr = "2001:db8::".parse().unwrap();
assert_eq!(net, expected);
}
#[test]
fn broadcast_v6() {
let cidr: Ipv6Network = "2001:db8::0/96".parse().unwrap();
let net = cidr.broadcast();
let expected: Ipv6Addr = "2001:db8::ffff:ffff".parse().unwrap();
assert_eq!(net, expected);
}
#[test]
fn size_v6() {
let cidr: Ipv6Network = "2001:db8::0/96".parse().unwrap();
assert_eq!(cidr.size(), 4294967296);
}
#[test]
fn ipv6network_from_ipv6addr() {
let net = Ipv6Network::from(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
let expected = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 128).unwrap();
assert_eq!(net, expected);
}
#[test]
fn test_send() {
fn assert_send<T: Send>() {}
assert_send::<Ipv6Network>();
}
#[test]
fn test_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<Ipv6Network>();
}
// Tests from cpython https://github.com/python/cpython/blob/e9bc4172d18db9c182d8e04dd7b033097a994c06/Lib/test/test_ipaddress.py
#[test]
fn test_is_subnet_of() {
let mut test_cases: HashMap<(Ipv6Network, Ipv6Network), bool> = HashMap::new();
test_cases.insert(("2000:999::/56".parse().unwrap(), "2000:aaa::/48".parse().unwrap()), false);
test_cases.insert(("2000:aaa::/56".parse().unwrap(), "2000:aaa::/48".parse().unwrap()), true);
test_cases.insert(("2000:bbb::/56".parse().unwrap(), "2000:aaa::/48".parse().unwrap()), false);
test_cases.insert(("2000:aaa::/48".parse().unwrap(), "2000:aaa::/56".parse().unwrap()), false);
for (key, val) in test_cases.iter() {
let (src, dest) = (key.0, key.1);
assert_eq!(src.is_subnet_of(dest), *val, "testing with {} and {}", src, dest);
}
}
#[test]
fn test_is_supernet_of() {
let mut test_cases: HashMap<(Ipv6Network, Ipv6Network), bool> = HashMap::new();
test_cases.insert(("2000:999::/56".parse().unwrap(), "2000:aaa::/48".parse().unwrap()), false);
test_cases.insert(("2000:aaa::/56".parse().unwrap(), "2000:aaa::/48".parse().unwrap()), false);
test_cases.insert(("2000:bbb::/56".parse().unwrap(), "2000:aaa::/48".parse().unwrap()), false);
test_cases.insert(("2000:aaa::/48".parse().unwrap(), "2000:aaa::/56".parse().unwrap()), true);
for (key, val) in test_cases.iter() {
let (src, dest) = (key.0, key.1);
assert_eq!(src.is_supernet_of(dest), *val, "testing with {} and {}", src, dest);
}
}
#[test]
fn test_overlaps() {
let other: Ipv6Network = "2001:DB8:ACAD::1/64".parse().unwrap();
let other2: Ipv6Network = "2001:DB8:ACAD::20:2/64".parse().unwrap();
assert_eq!(other2.overlaps(other), true);
}
}

View File

@ -4,28 +4,53 @@
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]
#![crate_type = "lib"]
#![doc(html_root_url = "https://docs.rs/ipnetwork/0.15.0")]
use std::fmt;
use std::net::IpAddr;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::{fmt, net::IpAddr, str::FromStr};
mod common;
mod ipv4;
mod ipv6;
mod common;
use std::str::FromStr;
pub use ipv4::{Ipv4Network, ipv4_mask_to_prefix};
pub use ipv6::{Ipv6Network, ipv6_mask_to_prefix};
pub use common::IpNetworkError;
pub use crate::common::IpNetworkError;
pub use crate::ipv4::{ipv4_mask_to_prefix, Ipv4Network};
pub use crate::ipv6::{ipv6_mask_to_prefix, Ipv6Network};
/// Represents a generic network range. This type can have two variants:
/// the v4 and the v6 case.
#[derive(Debug,Clone,Copy,Hash,PartialEq,Eq,PartialOrd,Ord)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum IpNetwork {
V4(Ipv4Network),
V6(Ipv6Network),
}
impl<'de> Deserialize<'de> for IpNetwork {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = <String>::deserialize(deserializer)?;
IpNetwork::from_str(&s).map_err(de::Error::custom)
}
}
impl Serialize for IpNetwork {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
/// Represents a generic network size. For IPv4, the max size is a u32 and for IPv6, it is a u128
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum NetworkSize {
V4(u32),
V6(u128),
}
impl IpNetwork {
/// Constructs a new `IpNetwork` from a given `IpAddr` and a prefix denoting the
/// network size. If the prefix is larger than 32 (for IPv4) or 128 (for IPv6), this
@ -52,7 +77,10 @@ impl IpNetwork {
/// ```
/// use ipnetwork::IpNetwork;
///
/// assert_eq!(IpNetwork::V4("10.9.0.1".parse().unwrap()).prefix(), 32u8);
/// assert_eq!(IpNetwork::V4("10.9.0.32/16".parse().unwrap()).prefix(), 16u8);
///
/// assert_eq!(IpNetwork::V6("ff01::0".parse().unwrap()).prefix(), 128u8);
/// assert_eq!(IpNetwork::V6("ff01::0/32".parse().unwrap()).prefix(), 32u8);
/// ```
pub fn prefix(&self) -> u8 {
@ -62,19 +90,65 @@ impl IpNetwork {
}
}
/// Returns the address of the network denoted by this `IpNetwork`.
/// This means the lowest possible IP address inside of the network.
///
/// # Examples
///
/// ```
/// use std::net::{Ipv4Addr, Ipv6Addr};
/// use ipnetwork::IpNetwork;
///
/// let net: IpNetwork = "10.1.9.32/16".parse().unwrap();
/// assert_eq!(net.network(), Ipv4Addr::new(10, 1, 0, 0));
/// let net: IpNetwork = "2001:db8::/96".parse().unwrap();
/// assert_eq!(net.network(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
/// ```
pub fn network(&self) -> IpAddr {
match *self {
IpNetwork::V4(ref a) => IpAddr::V4(a.network()),
IpNetwork::V6(ref a) => IpAddr::V6(a.network()),
}
}
/// Returns the broadcasting address of this `IpNetwork`.
/// This means the highest possible IP address inside of the network.
///
/// # Examples
///
/// ```
/// use std::net::Ipv4Addr;
/// use ipnetwork::{IpNetwork, Ipv4Network};
///
/// let net: Ipv4Network = "10.9.0.32/16".parse().unwrap();
/// assert_eq!(net.broadcast(), Ipv4Addr::new(10, 9, 255, 255));
/// ```
pub fn broadcast(&self) -> IpAddr {
match *self {
IpNetwork::V4(ref a) => IpAddr::V4(a.broadcast()),
IpNetwork::V6(ref a) => IpAddr::V6(a.broadcast()),
}
}
/// Returns the mask for this `IpNetwork`.
/// That means the `prefix` most significant bits will be 1 and the rest 0
///
/// # Example
///
/// ```
/// use ipnetwork::IpNetwork;
/// use std::net::{Ipv4Addr, Ipv6Addr};
///
/// let v4_net: IpNetwork = "10.9.0.1".parse().unwrap();
/// assert_eq!(v4_net.mask(), Ipv4Addr::new(255, 255, 255, 255));
/// let v4_net: IpNetwork = "10.9.0.32/16".parse().unwrap();
/// assert_eq!(v4_net.mask(), Ipv4Addr::new(255, 255, 0, 0));
///
/// let v6_net: IpNetwork = "ff01::0".parse().unwrap();
/// assert_eq!(v6_net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff));
/// let v6_net: IpNetwork = "ff01::0/32".parse().unwrap();
/// assert_eq!(v6_net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0));
///```
/// ```
pub fn mask(&self) -> IpAddr {
match *self {
IpNetwork::V4(ref a) => IpAddr::V4(a.mask()),
@ -119,6 +193,52 @@ impl IpNetwork {
IpNetwork::V6(_) => true,
}
}
// TODO(abhishek) when TryFrom is stable, implement it for IpNetwork to
// variant conversions. Then use that to implement a generic is_subnet_of
// is_supernet_of, overlaps
/// Checks if a given `IpAddr` is in this `IpNetwork`
///
/// # Examples
///
/// ```
/// use std::net::IpAddr;
/// use ipnetwork::IpNetwork;
///
/// let net: IpNetwork = "127.0.0.0/24".parse().unwrap();
/// let ip1: IpAddr = "127.0.0.1".parse().unwrap();
/// let ip2: IpAddr = "172.0.0.1".parse().unwrap();
/// let ip4: IpAddr = "::1".parse().unwrap();
/// assert!(net.contains(ip1));
/// assert!(!net.contains(ip2));
/// assert!(!net.contains(ip4));
/// ```
pub fn contains(&self, ip: IpAddr) -> bool {
match (*self, ip) {
(IpNetwork::V4(net), IpAddr::V4(ip)) => net.contains(ip),
(IpNetwork::V6(net), IpAddr::V6(ip)) => net.contains(ip),
_ => false,
}
}
/// Returns the number of possible host addresses in this `IpAddr`
///
/// # Examples
///
/// ```
/// use ipnetwork::{IpNetwork, NetworkSize};
///
///
/// let net: IpNetwork = "127.0.0.0/24".parse().unwrap();
/// assert_eq!(net.size(), NetworkSize::V4(256))
/// ```
pub fn size(&self) -> NetworkSize {
match *self {
IpNetwork::V4(ref ip) => NetworkSize::V4(ip.size()),
IpNetwork::V6(ref ip) => NetworkSize::V6(ip.size()),
}
}
}
/// Tries to parse the given string into a `IpNetwork`. Will first try to parse
@ -160,8 +280,17 @@ impl From<Ipv6Network> for IpNetwork {
}
}
impl From<IpAddr> for IpNetwork {
fn from(addr: IpAddr) -> IpNetwork {
match addr {
IpAddr::V4(a) => IpNetwork::V4(Ipv4Network::from(a)),
IpAddr::V6(a) => IpNetwork::V6(Ipv6Network::from(a)),
}
}
}
impl fmt::Display for IpNetwork {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
IpNetwork::V4(net) => net.fmt(f),
IpNetwork::V6(net) => net.fmt(f),
@ -177,3 +306,15 @@ pub fn ip_mask_to_prefix(mask: IpAddr) -> Result<u8, IpNetworkError> {
IpAddr::V6(mask) => ipv6_mask_to_prefix(mask),
}
}
#[cfg(test)]
mod test {
#[test]
fn deserialize_from_serde_json_value() {
use super::*;
let network = IpNetwork::from_str("0.0.0.0/0").unwrap();
let val: serde_json::value::Value = serde_json::from_str(&serde_json::to_string(&network).unwrap()).unwrap();
let _deser: IpNetwork =
serde_json::from_value(val).expect("Fails to deserialize from json_value::value::Value");
}
}

65
tests/test_json.rs Normal file
View File

@ -0,0 +1,65 @@
#[cfg(test)]
mod tests {
use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
use serde_derive::{Deserialize, Serialize};
use std::net::{Ipv4Addr, Ipv6Addr};
#[test]
fn test_ipv4_json() {
let json_string = r#"{"ipnetwork":"127.1.0.0/24"}"#;
#[derive(Serialize, Deserialize)]
struct MyStruct {
ipnetwork: Ipv4Network,
}
let mystruct: MyStruct = ::serde_json::from_str(json_string).unwrap();
assert_eq!(mystruct.ipnetwork.ip(), Ipv4Addr::new(127, 1, 0, 0));
assert_eq!(mystruct.ipnetwork.prefix(), 24);
assert_eq!(::serde_json::to_string(&mystruct).unwrap(), json_string);
}
#[test]
fn test_ipv6_json() {
let json_string = r#"{"ipnetwork":"::1/0"}"#;
#[derive(Serialize, Deserialize)]
struct MyStruct {
ipnetwork: Ipv6Network,
}
let mystruct: MyStruct = ::serde_json::from_str(json_string).unwrap();
assert_eq!(
mystruct.ipnetwork.ip(),
Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)
);
assert_eq!(mystruct.ipnetwork.prefix(), 0);
assert_eq!(::serde_json::to_string(&mystruct).unwrap(), json_string);
}
#[test]
fn test_ipnetwork_json() {
let json_string = r#"{"ipnetwork":["127.1.0.0/24","::1/0"]}"#;
#[derive(Serialize, Deserialize)]
struct MyStruct {
ipnetwork: Vec<IpNetwork>,
}
let mystruct: MyStruct = ::serde_json::from_str(json_string).unwrap();
assert_eq!(mystruct.ipnetwork[0].ip(), Ipv4Addr::new(127, 1, 0, 0));
assert_eq!(mystruct.ipnetwork[0].prefix(), 24);
assert_eq!(
mystruct.ipnetwork[1].ip(),
Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)
);
assert_eq!(mystruct.ipnetwork[1].prefix(), 0);
assert_eq!(::serde_json::to_string(&mystruct).unwrap(), json_string);
}
}