Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Ipv6 #70

Merged
merged 6 commits into from
Jan 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions src/display/components/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use ::tui::widgets::{Block, Borders, Row, Widget};
use crate::display::{Bandwidth, DisplayBandwidth, UIState};
use crate::network::{display_connection_string, display_ip_or_host};

use ::std::net::Ipv4Addr;
use ::std::net::IpAddr;
use std::iter::FromIterator;

const FIRST_WIDTH_BREAKPOINT: u16 = 50;
Expand Down Expand Up @@ -54,10 +54,7 @@ pub struct Table<'a> {
}

impl<'a> Table<'a> {
pub fn create_connections_table(
state: &UIState,
ip_to_host: &HashMap<Ipv4Addr, String>,
) -> Self {
pub fn create_connections_table(state: &UIState, ip_to_host: &HashMap<IpAddr, String>) -> Self {
let mut connections_list = Vec::from_iter(&state.connections);
sort_by_bandwidth(&mut connections_list);
let connections_rows = connections_list
Expand Down Expand Up @@ -105,7 +102,7 @@ impl<'a> Table<'a> {
}
pub fn create_remote_addresses_table(
state: &UIState,
ip_to_host: &HashMap<Ipv4Addr, String>,
ip_to_host: &HashMap<IpAddr, String>,
) -> Self {
let mut remote_addresses_list = Vec::from_iter(&state.remote_addresses);
sort_by_bandwidth(&mut remote_addresses_list);
Expand Down
6 changes: 3 additions & 3 deletions src/display/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::display::components::{HelpText, Layout, Table, TotalBandwidth};
use crate::display::UIState;
use crate::network::{display_connection_string, display_ip_or_host, LocalSocket, Utilization};

use ::std::net::Ipv4Addr;
use ::std::net::IpAddr;

use chrono::prelude::*;

Expand All @@ -17,7 +17,7 @@ where
{
terminal: Terminal<B>,
state: UIState,
ip_to_host: HashMap<Ipv4Addr, String>,
ip_to_host: HashMap<IpAddr, String>,
}

impl<B> Ui<B>
Expand Down Expand Up @@ -101,7 +101,7 @@ where
&mut self,
connections_to_procs: HashMap<LocalSocket, String>,
utilization: Utilization,
ip_to_host: HashMap<Ipv4Addr, String>,
ip_to_host: HashMap<IpAddr, String>,
) {
self.state.update(connections_to_procs, utilization);
self.ip_to_host.extend(ip_to_host);
Expand Down
4 changes: 2 additions & 2 deletions src/display/ui_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub struct UtilizationData {
#[derive(Default)]
pub struct UIState {
pub processes: BTreeMap<String, NetworkData>,
pub remote_addresses: BTreeMap<Ipv4Addr, NetworkData>,
pub remote_addresses: BTreeMap<IpAddr, NetworkData>,
pub connections: BTreeMap<Connection, ConnectionData>,
pub total_bytes_downloaded: u128,
pub total_bytes_uploaded: u128,
Expand Down Expand Up @@ -106,7 +106,7 @@ impl UIState {
self.utilization_data.pop_front();
}
let mut processes: BTreeMap<String, NetworkData> = BTreeMap::new();
let mut remote_addresses: BTreeMap<Ipv4Addr, NetworkData> = BTreeMap::new();
let mut remote_addresses: BTreeMap<IpAddr, NetworkData> = BTreeMap::new();
let mut connections: BTreeMap<Connection, ConnectionData> = BTreeMap::new();
let mut total_bytes_downloaded: u128 = 0;
let mut total_bytes_uploaded: u128 = 0;
Expand Down
33 changes: 15 additions & 18 deletions src/network/connection.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use ::std::collections::HashMap;
use ::std::fmt;
use ::std::net::{IpAddr, Ipv4Addr};
use ::std::net::IpAddr;

use ::std::net::SocketAddr;

Expand Down Expand Up @@ -35,7 +35,7 @@ impl fmt::Display for Protocol {

#[derive(Clone, Ord, PartialOrd, PartialEq, Eq, Hash, Debug, Copy)]
pub struct Socket {
pub ip: Ipv4Addr,
pub ip: IpAddr,
pub port: u16,
}

Expand All @@ -52,7 +52,7 @@ pub struct Connection {
pub local_socket: LocalSocket,
}

pub fn display_ip_or_host(ip: Ipv4Addr, ip_to_host: &HashMap<Ipv4Addr, String>) -> String {
pub fn display_ip_or_host(ip: IpAddr, ip_to_host: &HashMap<IpAddr, String>) -> String {
match ip_to_host.get(&ip) {
Some(host) => host.clone(),
None => ip.to_string(),
Expand All @@ -61,7 +61,7 @@ pub fn display_ip_or_host(ip: Ipv4Addr, ip_to_host: &HashMap<Ipv4Addr, String>)

pub fn display_connection_string(
connection: &Connection,
ip_to_host: &HashMap<Ipv4Addr, String>,
ip_to_host: &HashMap<IpAddr, String>,
interface_name: &str,
) -> String {
format!(
Expand All @@ -80,20 +80,17 @@ impl Connection {
local_ip: IpAddr,
local_port: u16,
protocol: Protocol,
) -> Option<Self> {
match remote_socket {
SocketAddr::V4(remote_socket) => Some(Connection {
remote_socket: Socket {
ip: *remote_socket.ip(),
port: remote_socket.port(),
},
local_socket: LocalSocket {
ip: local_ip,
port: local_port,
protocol,
},
}),
_ => None,
) -> Self {
Connection {
remote_socket: Socket {
ip: remote_socket.ip(),
port: remote_socket.port(),
},
local_socket: LocalSocket {
ip: local_ip,
port: local_port,
protocol,
},
}
}
}
10 changes: 5 additions & 5 deletions src/network/dns/client.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::network::dns::{resolver::Lookup, IpTable};
use std::{
collections::HashSet,
net::Ipv4Addr,
net::IpAddr,
sync::{Arc, Mutex},
thread::{Builder, JoinHandle},
};
Expand All @@ -10,14 +10,14 @@ use tokio::{
sync::mpsc::{self, Sender},
};

type PendingAddrs = HashSet<Ipv4Addr>;
type PendingAddrs = HashSet<IpAddr>;

const CHANNEL_SIZE: usize = 1_000;

pub struct Client {
cache: Arc<Mutex<IpTable>>,
pending: Arc<Mutex<PendingAddrs>>,
tx: Option<Sender<Vec<Ipv4Addr>>>,
tx: Option<Sender<Vec<IpAddr>>>,
handle: Option<JoinHandle<()>>,
}

Expand All @@ -28,7 +28,7 @@ impl Client {
{
let cache = Arc::new(Mutex::new(IpTable::new()));
let pending = Arc::new(Mutex::new(PendingAddrs::new()));
let (tx, mut rx) = mpsc::channel::<Vec<Ipv4Addr>>(CHANNEL_SIZE);
let (tx, mut rx) = mpsc::channel::<Vec<IpAddr>>(CHANNEL_SIZE);

let handle = Builder::new().name("resolver".into()).spawn({
let cache = cache.clone();
Expand Down Expand Up @@ -65,7 +65,7 @@ impl Client {
})
}

pub fn resolve(&mut self, ips: Vec<Ipv4Addr>) {
pub fn resolve(&mut self, ips: Vec<IpAddr>) {
// Remove ips that are already being resolved
let ips = ips
.into_iter()
Expand Down
4 changes: 2 additions & 2 deletions src/network/dns/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::{collections::HashMap, net::Ipv4Addr};
use std::{collections::HashMap, net::IpAddr};

mod client;
mod resolver;

pub use client::*;
pub use resolver::*;

pub type IpTable = HashMap<Ipv4Addr, String>;
pub type IpTable = HashMap<IpAddr, String>;
8 changes: 4 additions & 4 deletions src/network/dns/resolver.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use async_trait::async_trait;
use std::net::Ipv4Addr;
use std::net::IpAddr;
use tokio::runtime::Handle;
use trust_dns_resolver::{error::ResolveErrorKind, TokioAsyncResolver};

#[async_trait]
pub trait Lookup {
async fn lookup(&self, ip: Ipv4Addr) -> Option<String>;
async fn lookup(&self, ip: IpAddr) -> Option<String>;
}

pub struct Resolver(TokioAsyncResolver);
Expand All @@ -19,8 +19,8 @@ impl Resolver {

#[async_trait]
impl Lookup for Resolver {
async fn lookup(&self, ip: Ipv4Addr) -> Option<String> {
let lookup_future = self.0.reverse_lookup(ip.into());
async fn lookup(&self, ip: IpAddr) -> Option<String> {
let lookup_future = self.0.reverse_lookup(ip);
match lookup_future.await {
Ok(names) => {
// Take the first result and convert it to a string
Expand Down
113 changes: 82 additions & 31 deletions src/network/sniffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use ::std::boxed::Box;

use ::pnet_bandwhich_fork::datalink::{DataLinkReceiver, NetworkInterface};
use ::pnet_bandwhich_fork::packet::ethernet::{EtherTypes, EthernetPacket};
use ::pnet_bandwhich_fork::packet::ip::IpNextHeaderProtocols;
use ::pnet_bandwhich_fork::packet::ip::{IpNextHeaderProtocol, IpNextHeaderProtocols};
use ::pnet_bandwhich_fork::packet::ipv4::Ipv4Packet;
use ::pnet_bandwhich_fork::packet::ipv6::Ipv6Packet;
use ::pnet_bandwhich_fork::packet::tcp::TcpPacket;
use ::pnet_bandwhich_fork::packet::udp::UdpPacket;
use ::pnet_bandwhich_fork::packet::Packet;
Expand All @@ -13,6 +14,7 @@ use ::std::net::{IpAddr, SocketAddr};

use crate::network::{Connection, Protocol};

#[derive(Debug)]
pub struct Segment {
pub interface_name: String,
pub connection: Connection,
Expand All @@ -27,10 +29,10 @@ pub enum Direction {
}

impl Direction {
pub fn new(network_interface_ips: &[IpNetwork], ip_packet: &Ipv4Packet) -> Self {
pub fn new(network_interface_ips: &[IpNetwork], source: IpAddr) -> Self {
if network_interface_ips
.iter()
.any(|ip_network| ip_network.ip() == ip_packet.get_source())
.any(|ip_network| ip_network.ip() == source)
{
Direction::Upload
} else {
Expand All @@ -39,6 +41,42 @@ impl Direction {
}
}

trait NextLevelProtocol {
fn get_next_level_protocol(&self) -> IpNextHeaderProtocol;
}

impl NextLevelProtocol for Ipv6Packet<'_> {
fn get_next_level_protocol(&self) -> IpNextHeaderProtocol {
self.get_next_header()
}
}

macro_rules! extract_transport_protocol {
( $ip_packet: ident ) => {{
match $ip_packet.get_next_level_protocol() {
IpNextHeaderProtocols::Tcp => {
let message = TcpPacket::new($ip_packet.payload())?;
(
Protocol::Tcp,
message.get_source(),
message.get_destination(),
$ip_packet.payload().len() as u128,
)
}
IpNextHeaderProtocols::Udp => {
let datagram = UdpPacket::new($ip_packet.payload())?;
(
Protocol::Udp,
datagram.get_source(),
datagram.get_destination(),
$ip_packet.payload().len() as u128,
)
}
_ => return None,
}
}};
}

pub struct Sniffer {
network_interface: NetworkInterface,
network_frames: Box<dyn DataLinkReceiver>,
Expand All @@ -56,55 +94,68 @@ impl Sniffer {
}
pub fn next(&mut self) -> Option<Segment> {
let bytes = self.network_frames.next().ok()?;
let ip_packet = Ipv4Packet::new(&bytes)?;
// See https://github.com/libpnet/libpnet/blob/master/examples/packetdump.rs
let payload_offset = if self.network_interface.is_loopback() && cfg!(target_os = "macos") {
// The pnet code for BPF loopback adds a zero'd out Ethernet header
14
} else {
0
};
let ip_packet = Ipv4Packet::new(&bytes[payload_offset..])?;
let version = ip_packet.get_version();

match version {
4 => Self::handle_v4(ip_packet, &self.network_interface),
6 => None, // FIXME v6 support!
6 => Self::handle_v6(
Ipv6Packet::new(&bytes[payload_offset..])?,
&self.network_interface,
),
_ => {
let pkg = EthernetPacket::new(bytes)?;
match pkg.get_ethertype() {
EtherTypes::Ipv4 => {
Self::handle_v4(Ipv4Packet::new(pkg.payload())?, &self.network_interface)
}
EtherTypes::Ipv6 => {
Self::handle_v6(Ipv6Packet::new(pkg.payload())?, &self.network_interface)
}
_ => None,
}
}
}
}
fn handle_v6(ip_packet: Ipv6Packet, network_interface: &NetworkInterface) -> Option<Segment> {
let (protocol, source_port, destination_port, data_length) =
extract_transport_protocol!(ip_packet);

let interface_name = network_interface.name.clone();
let direction = Direction::new(&network_interface.ips, ip_packet.get_source().into());
let from = SocketAddr::new(ip_packet.get_source().into(), source_port);
let to = SocketAddr::new(ip_packet.get_destination().into(), destination_port);

let connection = match direction {
Direction::Download => Connection::new(from, to.ip(), destination_port, protocol),
Direction::Upload => Connection::new(to, from.ip(), source_port, protocol),
};
Some(Segment {
interface_name,
connection,
data_length,
direction,
})
}
fn handle_v4(ip_packet: Ipv4Packet, network_interface: &NetworkInterface) -> Option<Segment> {
let (protocol, source_port, destination_port, data_length) =
match ip_packet.get_next_level_protocol() {
IpNextHeaderProtocols::Tcp => {
let message = TcpPacket::new(ip_packet.payload())?;
(
Protocol::Tcp,
message.get_source(),
message.get_destination(),
ip_packet.payload().len() as u128,
)
}
IpNextHeaderProtocols::Udp => {
let datagram = UdpPacket::new(ip_packet.payload())?;
(
Protocol::Udp,
datagram.get_source(),
datagram.get_destination(),
ip_packet.payload().len() as u128,
)
}
_ => return None,
};
extract_transport_protocol!(ip_packet);

let interface_name = network_interface.name.clone();
let direction = Direction::new(&network_interface.ips, &ip_packet);
let from = SocketAddr::new(IpAddr::V4(ip_packet.get_source()), source_port);
let to = SocketAddr::new(IpAddr::V4(ip_packet.get_destination()), destination_port);
let direction = Direction::new(&network_interface.ips, ip_packet.get_source().into());
let from = SocketAddr::new(ip_packet.get_source().into(), source_port);
let to = SocketAddr::new(ip_packet.get_destination().into(), destination_port);

let connection = match direction {
Direction::Download => Connection::new(from, to.ip(), destination_port, protocol)?,
Direction::Upload => Connection::new(to, from.ip(), source_port, protocol)?,
Direction::Download => Connection::new(from, to.ip(), destination_port, protocol),
Direction::Upload => Connection::new(to, from.ip(), source_port, protocol),
};
Some(Segment {
interface_name,
Expand Down
Loading