meta: switch from ipnetwork to ipnet (#193)

pull/195/head
Jake McGinty 2022-02-01 14:01:21 +09:00 committed by GitHub
parent b6ce16bc00
commit a77cbb4f49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 116 additions and 77 deletions

13
Cargo.lock generated
View File

@ -137,7 +137,7 @@ dependencies = [
"dialoguer",
"hostsfile",
"indoc",
"ipnetwork",
"ipnet",
"lazy_static",
"log",
"regex",
@ -470,9 +470,10 @@ dependencies = [
]
[[package]]
name = "ipnetwork"
version = "0.17.0"
source = "git+https://github.com/mcginty/ipnetwork?rev=393f2d89e41ac6c1c0d80a31fc0997c387a7f7ba#393f2d89e41ac6c1c0d80a31fc0997c387a7f7ba"
name = "ipnet"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
dependencies = [
"serde",
]
@ -933,7 +934,7 @@ dependencies = [
"dialoguer",
"hyper",
"indoc",
"ipnetwork",
"ipnet",
"lazy_static",
"libc",
"libsqlite3-sys",
@ -966,7 +967,7 @@ dependencies = [
"colored",
"dialoguer",
"indoc",
"ipnetwork",
"ipnet",
"lazy_static",
"libc",
"log",

View File

@ -21,7 +21,7 @@ clap_complete = "3"
dialoguer = { version = "0.9", default-features = false }
hostsfile = { path = "../hostsfile" }
indoc = "1"
ipnetwork = { git = "https://github.com/mcginty/ipnetwork", rev = "393f2d89e41ac6c1c0d80a31fc0997c387a7f7ba" }
ipnet = { version = "2", features = ["serde"] }
lazy_static = "1"
log = "0.4"
regex = { version = "1", default-features = false, features = ["std"] }

View File

@ -24,7 +24,7 @@ colored = "2"
dialoguer = { version = "0.9", default-features = false }
hyper = { version = "0.14", default-features = false, features = ["http1", "server", "runtime", "stream"] }
indoc = "1"
ipnetwork = { git = "https://github.com/mcginty/ipnetwork", rev = "393f2d89e41ac6c1c0d80a31fc0997c387a7f7ba" }
ipnet = { version = "2", features = ["serde"] }
lazy_static = "1"
libc = "0.2"
libsqlite3-sys = "0.23"

View File

@ -1,7 +1,7 @@
use crate::ServerError;
use ipnetwork::IpNetwork;
use ipnet::IpNet;
use rusqlite::{params, Connection};
use shared::{Cidr, CidrContents};
use shared::{Cidr, CidrContents, IpNetExt};
use std::ops::Deref;
pub static CREATE_TABLE_SQL: &str = "CREATE TABLE cidrs (
@ -56,8 +56,8 @@ impl DatabaseCidr {
let closest_parent = cidrs
.iter()
.filter(|current| cidr.is_subnet_of(current.cidr))
.max_by_key(|current| current.cidr.prefix());
.filter(|current| current.cidr.contains(cidr))
.max_by_key(|current| current.cidr.prefix_len());
if let Some(closest_parent) = closest_parent {
if closest_parent.id != *parent_id {
@ -70,7 +70,7 @@ impl DatabaseCidr {
}
let parent_cidr = Self::get(conn, *parent_id)?.cidr;
if !parent_cidr.contains(cidr.network()) || !parent_cidr.contains(cidr.broadcast()) {
if !parent_cidr.contains(&cidr.network()) || !parent_cidr.contains(&cidr.broadcast()) {
log::warn!("tried to add a CIDR with a network range outside of its parent.");
return Err(ServerError::InvalidQuery);
}
@ -81,10 +81,10 @@ impl DatabaseCidr {
.filter(|current| current.parent == *parent)
.map(|sibling| sibling.cidr)
.any(|sibling| {
cidr.contains(sibling.network())
|| cidr.contains(sibling.broadcast())
|| sibling.contains(cidr.network())
|| sibling.contains(cidr.broadcast())
cidr.contains(&sibling.network())
|| cidr.contains(&sibling.broadcast())
|| sibling.contains(&cidr.network())
|| sibling.contains(&cidr.broadcast())
});
if overlapping_sibling {
@ -95,7 +95,12 @@ impl DatabaseCidr {
conn.execute(
"INSERT INTO cidrs (name, ip, prefix, parent)
VALUES (?1, ?2, ?3, ?4)",
params![name, cidr.ip().to_string(), cidr.prefix() as i32, parent],
params![
name,
cidr.addr().to_string(),
cidr.prefix_len() as i32,
parent
],
)?;
let id = conn.last_insert_rowid();
Ok(Cidr { id, contents })
@ -109,14 +114,12 @@ impl DatabaseCidr {
fn from_row(row: &rusqlite::Row) -> Result<Cidr, rusqlite::Error> {
let id = row.get(0)?;
let name = row.get(1)?;
let ip: String = row.get(2)?;
let ip_str: String = row.get(2)?;
let prefix = row.get(3)?;
let cidr = IpNetwork::new(
ip.parse()
.map_err(|_| rusqlite::Error::ExecuteReturnedResults)?,
prefix,
)
let ip = ip_str
.parse()
.map_err(|_| rusqlite::Error::ExecuteReturnedResults)?;
let cidr = IpNet::new(ip, prefix).map_err(|_| rusqlite::Error::ExecuteReturnedResults)?;
let parent = row.get(4)?;
Ok(Cidr {
id,

View File

@ -3,7 +3,7 @@ use crate::ServerError;
use lazy_static::lazy_static;
use regex::Regex;
use rusqlite::{params, types::Type, Connection};
use shared::{Peer, PeerContents, PERSISTENT_KEEPALIVE_INTERVAL_SECS};
use shared::{IpNetExt, Peer, PeerContents, PERSISTENT_KEEPALIVE_INTERVAL_SECS};
use std::{
net::IpAddr,
ops::{Deref, DerefMut},
@ -96,12 +96,12 @@ impl DatabasePeer {
}
let cidr = DatabaseCidr::get(conn, *cidr_id)?;
if !cidr.cidr.contains(*ip) {
if !cidr.cidr.contains(ip) {
log::warn!("tried to add peer with IP outside of parent CIDR range.");
return Err(ServerError::InvalidQuery);
}
if !cidr.cidr.is_assignable(*ip) {
if !cidr.cidr.is_assignable(ip) {
println!(
"Peer IP {} is not unicast assignable in CIDR {}",
ip, cidr.cidr

View File

@ -4,9 +4,12 @@ use clap::Parser;
use db::DatabaseCidr;
use dialoguer::{theme::ColorfulTheme, Input};
use indoc::printdoc;
use ipnet::IpNet;
use publicip::Preference;
use rusqlite::{params, Connection};
use shared::{prompts, CidrContents, Endpoint, PeerContents, PERSISTENT_KEEPALIVE_INTERVAL_SECS};
use shared::{
prompts, CidrContents, Endpoint, IpNetExt, PeerContents, PERSISTENT_KEEPALIVE_INTERVAL_SECS,
};
use wireguard_control::KeyPair;
fn create_database<P: AsRef<Path>>(
@ -31,7 +34,7 @@ pub struct InitializeOpts {
/// The network CIDR (ex: 10.42.0.0/16)
#[clap(long)]
pub network_cidr: Option<IpNetwork>,
pub network_cidr: Option<IpNet>,
/// This server's external endpoint (ex: 100.100.100.100:51820)
#[clap(long, conflicts_with = "auto-external-endpoint")]
@ -48,8 +51,8 @@ pub struct InitializeOpts {
struct DbInitData {
network_name: String,
network_cidr: IpNetwork,
server_cidr: IpNetwork,
network_cidr: IpNet,
server_cidr: IpNet,
our_ip: IpAddr,
public_key_base64: String,
endpoint: Endpoint,
@ -131,7 +134,7 @@ pub fn init_wizard(conf: &ServerConfig, opts: InitializeOpts) -> Result<(), Erro
.interact()?
};
let root_cidr: IpNetwork = if let Some(cidr) = opts.network_cidr {
let root_cidr: IpNet = if let Some(cidr) = opts.network_cidr {
cidr
} else {
Input::with_theme(&theme)
@ -163,10 +166,9 @@ pub fn init_wizard(conf: &ServerConfig, opts: InitializeOpts) -> Result<(), Erro
};
let our_ip = root_cidr
.iter()
.find(|ip| root_cidr.is_assignable(*ip))
.hosts()
.find(|ip| root_cidr.is_assignable(ip))
.unwrap();
let server_cidr = IpNetwork::new(our_ip, root_cidr.max_prefix())?;
let config_path = conf.config_path(&name);
let our_keypair = KeyPair::generate();
@ -174,14 +176,14 @@ pub fn init_wizard(conf: &ServerConfig, opts: InitializeOpts) -> Result<(), Erro
private_key: our_keypair.private.to_base64(),
listen_port,
address: our_ip,
network_cidr_prefix: root_cidr.prefix(),
network_cidr_prefix: root_cidr.prefix_len(),
};
config.write_to_path(&config_path)?;
let db_init_data = DbInitData {
network_name: name.to_string(),
network_cidr: root_cidr,
server_cidr,
server_cidr: IpNet::new(our_ip, root_cidr.max_prefix_len())?,
our_ip,
public_key_base64: our_keypair.public.to_base64(),
endpoint,

View File

@ -4,12 +4,12 @@ use colored::*;
use dialoguer::Confirm;
use hyper::{http, server::conn::AddrStream, Body, Request, Response};
use indoc::printdoc;
use ipnetwork::IpNetwork;
use ipnet::IpNet;
use parking_lot::{Mutex, RwLock};
use rusqlite::Connection;
use serde::{Deserialize, Serialize};
use shared::{
get_local_addrs, AddCidrOpts, AddPeerOpts, DeleteCidrOpts, Endpoint, IoErrorContext,
get_local_addrs, AddCidrOpts, AddPeerOpts, DeleteCidrOpts, Endpoint, IoErrorContext, IpNetExt,
NetworkOpts, PeerContents, RenamePeerOpts, INNERNET_PUBKEY_HEADER,
};
use std::{
@ -502,7 +502,7 @@ async fn serve(
wg::up(
&interface,
&config.private_key,
IpNetwork::new(config.address, config.network_cidr_prefix)?,
IpNet::new(config.address, config.network_cidr_prefix)?,
Some(config.listen_port),
None,
network,

View File

@ -13,7 +13,7 @@ clap = { version = "3", features = ["derive"] }
colored = "2.0"
dialoguer = { version = "0.9", default-features = false }
indoc = "1"
ipnetwork = { git = "https://github.com/mcginty/ipnetwork", rev ="393f2d89e41ac6c1c0d80a31fc0997c387a7f7ba" }
ipnet = { version = "2", features = ["serde"] }
lazy_static = "1"
libc = "0.2"
log = "0.4"

View File

@ -1,6 +1,6 @@
use crate::{chmod, ensure_dirs_exist, Endpoint, Error, IoErrorContext, WrappedIoError};
use indoc::writedoc;
use ipnetwork::IpNetwork;
use ipnet::IpNet;
use serde::{Deserialize, Serialize};
use std::{
fs::{File, OpenOptions},
@ -28,7 +28,7 @@ pub struct InterfaceInfo {
/// The invited peer's internal IP address that's been allocated to it, inside
/// the entire network's CIDR prefix.
pub address: IpNetwork,
pub address: IpNet,
/// WireGuard private key (base64)
pub private_key: String,

View File

@ -1,4 +1,5 @@
pub use anyhow::Error;
use ipnet::{IpNet, Ipv4Net, Ipv6Net, PrefixLenError};
use std::{
fs::{self, File, Permissions},
io,
@ -116,3 +117,29 @@ pub fn get_local_addrs() -> Result<impl Iterator<Item = std::net::IpAddr>, io::E
})
.take(10))
}
pub trait IpNetExt {
fn is_assignable(&self, ip: &IpAddr) -> bool;
#[allow(clippy::new_ret_no_self)]
fn new(ip: IpAddr, prefix_len: u8) -> Result<IpNet, PrefixLenError>;
}
impl IpNetExt for IpNet {
fn new(ip: IpAddr, prefix_len: u8) -> Result<IpNet, PrefixLenError> {
Ok(match ip {
IpAddr::V4(a) => Ipv4Net::new(a, prefix_len)?.into(),
IpAddr::V6(a) => Ipv6Net::new(a, prefix_len)?.into(),
})
}
fn is_assignable(&self, ip: &IpAddr) -> bool {
self.contains(ip)
&& match self {
IpNet::V4(_) => {
self.prefix_len() >= 31 || (ip != &self.network() && ip != &self.broadcast())
},
IpNet::V6(_) => self.prefix_len() >= 127 || ip != &self.network(),
}
}
}

View File

@ -1,4 +1,4 @@
use ipnetwork::IpNetwork;
use ipnet::IpNet;
use netlink_packet_core::{NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_CREATE, NLM_F_REQUEST};
use netlink_packet_route::{
address,
@ -36,11 +36,11 @@ pub fn set_up(interface: &InterfaceName, mtu: u32) -> Result<(), io::Error> {
Ok(())
}
pub fn set_addr(interface: &InterfaceName, addr: IpNetwork) -> Result<(), io::Error> {
pub fn set_addr(interface: &InterfaceName, addr: IpNet) -> Result<(), io::Error> {
let index = if_nametoindex(interface)?;
let (family, nlas) = match addr {
IpNetwork::V4(network) => {
let addr_bytes = network.ip().octets().to_vec();
IpNet::V4(network) => {
let addr_bytes = network.addr().octets().to_vec();
(
AF_INET as u8,
vec![
@ -49,16 +49,16 @@ pub fn set_addr(interface: &InterfaceName, addr: IpNetwork) -> Result<(), io::Er
],
)
},
IpNetwork::V6(network) => (
IpNet::V6(network) => (
AF_INET6 as u8,
vec![address::Nla::Address(network.ip().octets().to_vec())],
vec![address::Nla::Address(network.addr().octets().to_vec())],
),
};
let message = AddressMessage {
header: AddressHeader {
index,
family,
prefix_len: addr.prefix(),
prefix_len: addr.prefix_len(),
scope: RT_SCOPE_UNIVERSE,
..Default::default()
},
@ -72,11 +72,11 @@ pub fn set_addr(interface: &InterfaceName, addr: IpNetwork) -> Result<(), io::Er
Ok(())
}
pub fn add_route(interface: &InterfaceName, cidr: IpNetwork) -> Result<bool, io::Error> {
pub fn add_route(interface: &InterfaceName, cidr: IpNet) -> Result<bool, io::Error> {
let if_index = if_nametoindex(interface)?;
let (address_family, dst) = match cidr {
IpNetwork::V4(network) => (AF_INET as u8, network.network().octets().to_vec()),
IpNetwork::V6(network) => (AF_INET6 as u8, network.network().octets().to_vec()),
IpNet::V4(network) => (AF_INET as u8, network.network().octets().to_vec()),
IpNet::V6(network) => (AF_INET6 as u8, network.network().octets().to_vec()),
};
let message = RouteMessage {
header: RouteHeader {
@ -84,7 +84,7 @@ pub fn add_route(interface: &InterfaceName, cidr: IpNetwork) -> Result<bool, io:
protocol: RTPROT_BOOT,
scope: RT_SCOPE_LINK,
kind: RTN_UNICAST,
destination_prefix_length: cidr.prefix(),
destination_prefix_length: cidr.prefix_len(),
address_family,
..Default::default()
},

View File

@ -1,13 +1,13 @@
use crate::{
interface_config::{InterfaceConfig, InterfaceInfo, ServerInfo},
AddCidrOpts, AddDeleteAssociationOpts, AddPeerOpts, Association, Cidr, CidrContents, CidrTree,
DeleteCidrOpts, Endpoint, Error, Hostname, ListenPortOpts, OverrideEndpointOpts, Peer,
PeerContents, RenamePeerOpts, PERSISTENT_KEEPALIVE_INTERVAL_SECS,
DeleteCidrOpts, Endpoint, Error, Hostname, IpNetExt, ListenPortOpts, OverrideEndpointOpts,
Peer, PeerContents, RenamePeerOpts, PERSISTENT_KEEPALIVE_INTERVAL_SECS,
};
use anyhow::anyhow;
use colored::*;
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Select};
use ipnetwork::IpNetwork;
use ipnet::IpNet;
use lazy_static::lazy_static;
use publicip::Preference;
use std::{
@ -267,7 +267,7 @@ pub fn add_peer(
};
let mut available_ip = None;
let candidate_ips = cidr.iter().filter(|ip| cidr.is_assignable(*ip));
let candidate_ips = cidr.hosts().filter(|ip| cidr.is_assignable(ip));
for ip in candidate_ips {
if !peers.iter().any(|peer| peer.ip == ip) {
available_ip = Some(ip);
@ -439,7 +439,7 @@ pub fn write_peer_invitation(
interface: InterfaceInfo {
network_name: network_name.to_string(),
private_key: keypair.private.to_base64(),
address: IpNetwork::new(peer.ip, root_cidr.prefix())?,
address: IpNet::new(peer.ip, root_cidr.prefix_len())?,
listen_port: None,
},
server: ServerInfo {

View File

@ -1,6 +1,6 @@
use anyhow::{anyhow, Error};
use clap::Args;
use ipnetwork::IpNetwork;
use ipnet::IpNet;
use lazy_static::lazy_static;
use regex::Regex;
use serde::{Deserialize, Serialize};
@ -195,12 +195,12 @@ impl Deref for Association {
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, PartialOrd, Eq, Ord)]
pub struct CidrContents {
pub name: String,
pub cidr: IpNetwork,
pub cidr: IpNet,
pub parent: Option<i64>,
}
impl Deref for CidrContents {
type Target = IpNetwork;
type Target = IpNet;
fn deref(&self) -> &Self::Target {
&self.cidr
@ -247,7 +247,7 @@ impl<'a> CidrTree<'a> {
pub fn new(cidrs: &'a [Cidr]) -> Self {
let root = cidrs
.iter()
.min_by_key(|c| c.cidr.prefix())
.min_by_key(|c| c.cidr.prefix_len())
.expect("failed to find root CIDR");
Self::with_root(cidrs, root)
}
@ -356,7 +356,7 @@ pub struct AddCidrOpts {
/// The CIDR network (eg. '10.42.5.0/24')
#[clap(long)]
pub cidr: Option<IpNetwork>,
pub cidr: Option<IpNet>,
/// The CIDR parent name
#[clap(long)]
@ -431,7 +431,7 @@ pub struct NatOpts {
#[clap(long)]
/// Exclude one or more CIDRs from NAT candidate reporting.
/// ex. --exclude-nat-candidates '0.0.0.0/0' would report no candidates.
pub exclude_nat_candidates: Vec<IpNetwork>,
pub exclude_nat_candidates: Vec<IpNet>,
#[clap(long, conflicts_with = "exclude-nat-candidates")]
/// Don't report any candidates to coordinating server.
@ -454,7 +454,7 @@ impl NatOpts {
|| self
.exclude_nat_candidates
.iter()
.any(|network| network.contains(ip))
.any(|network| network.contains(&ip))
}
}

View File

@ -1,5 +1,5 @@
use crate::{Error, IoErrorContext, NetworkOpts, Peer, PeerDiff};
use ipnetwork::IpNetwork;
use ipnet::IpNet;
use std::{
io,
net::{IpAddr, SocketAddr},
@ -32,17 +32,17 @@ fn cmd(bin: &str, args: &[&str]) -> Result<std::process::Output, io::Error> {
}
#[cfg(target_os = "macos")]
pub fn set_addr(interface: &InterfaceName, addr: IpNetwork) -> Result<(), io::Error> {
pub fn set_addr(interface: &InterfaceName, addr: IpNet) -> Result<(), io::Error> {
let real_interface = wireguard_control::backends::userspace::resolve_tun(interface)?;
if addr.is_ipv4() {
if matches!(addr, IpNet::V4(_)) {
cmd(
"ifconfig",
&[
&real_interface,
"inet",
&addr.to_string(),
&addr.ip().to_string(),
&addr.addr().to_string(),
"alias",
],
)
@ -72,7 +72,7 @@ pub use super::netlink::set_up;
pub fn up(
interface: &InterfaceName,
private_key: &str,
address: IpNetwork,
address: IpNet,
listen_port: Option<u16>,
peer: Option<(&str, IpAddr, SocketAddr)>,
network: NetworkOpts,
@ -102,9 +102,11 @@ pub fn up(
set_addr(interface, address)?;
set_up(
interface,
network
.mtu
.unwrap_or_else(|| if address.is_ipv4() { 1420 } else { 1400 }),
network.mtu.unwrap_or(if matches!(address, IpNet::V4(_)) {
1420
} else {
1400
}),
)?;
if !network.no_routing {
add_route(interface, address)?;
@ -139,14 +141,18 @@ pub fn down(interface: &InterfaceName, backend: Backend) -> Result<(), Error> {
/// Returns an error if the process doesn't exit successfully, otherwise returns
/// true if the route was changed, false if the route already exists.
#[cfg(target_os = "macos")]
pub fn add_route(interface: &InterfaceName, cidr: IpNetwork) -> Result<bool, io::Error> {
pub fn add_route(interface: &InterfaceName, cidr: IpNet) -> Result<bool, io::Error> {
let real_interface = wireguard_control::backends::userspace::resolve_tun(interface)?;
let output = cmd(
"route",
&[
"-n",
"add",
if cidr.is_ipv4() { "-inet" } else { "-inet6" },
if matches!(cidr, IpNet::V4(_)) {
"-inet"
} else {
"-inet6"
},
&cidr.to_string(),
"-interface",
&real_interface,