From 1ad0a6829aae93d759054bbcf0db3b800d0db317 Mon Sep 17 00:00:00 2001 From: Rob Walker Date: Wed, 19 Sep 2018 14:34:21 -0700 Subject: [PATCH 1/2] sort local interfaces before selecting one --- src/bin/fullnode-config.rs | 2 ++ src/netutil.rs | 47 +++++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/bin/fullnode-config.rs b/src/bin/fullnode-config.rs index 5427a3860fc65c..343d1d5a5ba3e7 100644 --- a/src/bin/fullnode-config.rs +++ b/src/bin/fullnode-config.rs @@ -7,12 +7,14 @@ extern crate solana; use clap::{App, Arg}; use solana::crdt::FULLNODE_PORT_RANGE; use solana::fullnode::Config; +use solana::logger; use solana::netutil::{get_ip_addr, get_public_ip_addr, parse_port_or_addr}; use solana::signature::read_pkcs8; use std::io; use std::net::SocketAddr; fn main() { + logger::setup(); let matches = App::new("fullnode-config") .version(crate_version!()) .arg( diff --git a/src/netutil.rs b/src/netutil.rs index aba76a242bd19d..67f10c107e966c 100644 --- a/src/netutil.rs +++ b/src/netutil.rs @@ -49,21 +49,42 @@ pub fn parse_port_or_addr(optstr: Option<&str>, default_port: u16) -> SocketAddr } pub fn get_ip_addr() -> Option { - for iface in datalink::interfaces() { + let mut ifaces = datalink::interfaces(); + + // put eth0 and wifi0, etc. up front of our list of candidates + ifaces.sort_by(|a, b| { + a.name + .chars() + .last() + .unwrap() + .cmp(&b.name.chars().last().unwrap()) + }); + + for iface in ifaces { for p in iface.ips { - if !p.ip().is_loopback() && !p.ip().is_multicast() { - match p.ip() { - IpAddr::V4(addr) => { - if !addr.is_link_local() { - return Some(p.ip()); - } + trace!("get_ip_addr considering iface {} {}", iface.name, p); + if p.ip().is_loopback() { + trace!(" loopback"); + continue; + } + if p.ip().is_multicast() { + trace!(" multicast"); + continue; + } + match p.ip() { + IpAddr::V4(addr) => { + if addr.is_link_local() { + trace!(" link local"); + continue; } - IpAddr::V6(_addr) => { - // Select an ipv6 address if the config is selected - #[cfg(feature = "ipv6")] - { - return Some(p.ip()); - } + trace!(" using ==> {}", p.ip()); + return Some(p.ip()); + } + IpAddr::V6(_addr) => { + // Select an ipv6 address if the config is selected + #[cfg(feature = "ipv6")] + { + return Some(p.ip()); } } } From 4c5edbf029d8859108d7e0850383119895ffb361 Mon Sep 17 00:00:00 2001 From: Rob Walker Date: Wed, 19 Sep 2018 18:33:40 -0700 Subject: [PATCH 2/2] tests for my IP picker --- Cargo.toml | 1 + src/lib.rs | 1 + src/netutil.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 80 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dc54e77c24df71..839eb815200f74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,6 +83,7 @@ influx_db_client = "0.3.4" jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc", rev = "4b6060b" } jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc", rev = "4b6060b" } jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc", rev = "4b6060b" } +ipnetwork = "0.12.7" itertools = "0.7.8" log = "0.4.2" matches = "0.1.6" diff --git a/src/lib.rs b/src/lib.rs index d8baa52e99f93e..c9eecb89599e7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,6 +72,7 @@ extern crate chrono; extern crate clap; extern crate dirs; extern crate generic_array; +extern crate ipnetwork; extern crate itertools; extern crate jsonrpc_core; #[macro_use] diff --git a/src/netutil.rs b/src/netutil.rs index 67f10c107e966c..05e1ef2931bb62 100644 --- a/src/netutil.rs +++ b/src/netutil.rs @@ -48,9 +48,7 @@ pub fn parse_port_or_addr(optstr: Option<&str>, default_port: u16) -> SocketAddr } } -pub fn get_ip_addr() -> Option { - let mut ifaces = datalink::interfaces(); - +fn find_eth0ish_ip_addr(ifaces: &mut Vec) -> Option { // put eth0 and wifi0, etc. up front of our list of candidates ifaces.sort_by(|a, b| { a.name @@ -60,9 +58,10 @@ pub fn get_ip_addr() -> Option { .cmp(&b.name.chars().last().unwrap()) }); - for iface in ifaces { + for iface in ifaces.clone() { + trace!("get_ip_addr considering iface {}", iface.name); for p in iface.ips { - trace!("get_ip_addr considering iface {} {}", iface.name, p); + trace!(" ip {}", p); if p.ip().is_loopback() { trace!(" loopback"); continue; @@ -77,7 +76,7 @@ pub fn get_ip_addr() -> Option { trace!(" link local"); continue; } - trace!(" using ==> {}", p.ip()); + trace!(" picked {}", p.ip()); return Some(p.ip()); } IpAddr::V6(_addr) => { @@ -93,6 +92,12 @@ pub fn get_ip_addr() -> Option { None } +pub fn get_ip_addr() -> Option { + let mut ifaces = datalink::interfaces(); + + find_eth0ish_ip_addr(&mut ifaces) +} + fn udp_socket(reuseaddr: bool) -> io::Result { let sock = Socket::new(Domain::ipv4(), Type::dgram(), None)?; let sock_fd = sock.as_raw_fd(); @@ -156,7 +161,74 @@ pub fn bind_to(port: u16, reuseaddr: bool) -> io::Result { #[cfg(test)] mod tests { + use ipnetwork::IpNetwork; + use logger; use netutil::*; + use pnet_datalink as datalink; + + #[test] + fn test_find_eth0ish_ip_addr() { + logger::setup(); + + macro_rules! mock_interface { + ($name:ident, $ip_mask:expr) => { + datalink::NetworkInterface { + name: stringify!($name).to_string(), + index: 0, + mac: None, + ips: vec![IpNetwork::V4($ip_mask.parse().unwrap())], + flags: 0, + } + }; + } + + // loopback bad + assert_eq!( + find_eth0ish_ip_addr(&mut vec![mock_interface!(lo, "127.0.0.1/24")]), + None + ); + // multicast bad + assert_eq!( + find_eth0ish_ip_addr(&mut vec![mock_interface!(eth0, "224.0.1.5/24")]), + None + ); + + // finds "wifi0" + assert_eq!( + find_eth0ish_ip_addr(&mut vec![ + mock_interface!(eth0, "224.0.1.5/24"), + mock_interface!(eth2, "192.168.137.1/8"), + mock_interface!(eth3, "10.0.75.1/8"), + mock_interface!(eth4, "172.22.140.113/4"), + mock_interface!(lo, "127.0.0.1/24"), + mock_interface!(wifi0, "192.168.1.184/8"), + ]), + Some(mock_interface!(wifi0, "192.168.1.184/8").ips[0].ip()) + ); + // finds "wifi0" in the middle + assert_eq!( + find_eth0ish_ip_addr(&mut vec![ + mock_interface!(eth0, "224.0.1.5/24"), + mock_interface!(eth2, "192.168.137.1/8"), + mock_interface!(eth3, "10.0.75.1/8"), + mock_interface!(wifi0, "192.168.1.184/8"), + mock_interface!(eth4, "172.22.140.113/4"), + mock_interface!(lo, "127.0.0.1/24"), + ]), + Some(mock_interface!(wifi0, "192.168.1.184/8").ips[0].ip()) + ); + // picks "eth2", is lowest valid "eth" + assert_eq!( + find_eth0ish_ip_addr(&mut vec![ + mock_interface!(eth0, "224.0.1.5/24"), + mock_interface!(eth2, "192.168.137.1/8"), + mock_interface!(eth3, "10.0.75.1/8"), + mock_interface!(eth4, "172.22.140.113/4"), + mock_interface!(lo, "127.0.0.1/24"), + ]), + Some(mock_interface!(eth2, "192.168.137.1/8").ips[0].ip()) + ); + } #[test] fn test_parse_port_or_addr() {