Skip to content

Commit

Permalink
Merge pull request imsnif#23 from ebroto/feature/listen-on-all-interf…
Browse files Browse the repository at this point in the history
…aces

Listen on all interfaces
  • Loading branch information
ebroto authored Dec 20, 2019
2 parents 408ec39 + b44b646 commit b150bf3
Show file tree
Hide file tree
Showing 44 changed files with 320 additions and 285 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Windows is not supported at the moment - if you'd like to contribute a windows p
### Usage
```
USAGE:
what [FLAGS] --interface <interface>
what [FLAGS] [OPTIONS]
FLAGS:
-h, --help Prints help information
Expand All @@ -46,14 +46,14 @@ Note that since `what` sniffs network packets, it requires root privileges - so
### raw_mode
`what` also supports an easier-to-parse mode that can be piped or redirected to a file. For example, try:
```
what -i eth0 --raw | grep firefox
what --raw | grep firefox
```
### Contributing
Contributions of any kind are very welcome. If you'd like a new feature (or found a bug), please open an issue or a PR.

To set up your development environment:
1. Clone the project
2. `cargo run -- -i <network interface name>` (you can often find out the name with `ifconfig` or `iwconfig`). You might need root privileges to run this application, so be sure to use (for example) sudo.
2. `cargo run`, or if you prefer `cargo run -- -i <network interface name>` (you can often find out the name with `ifconfig` or `iwconfig`). You might need root privileges to run this application, so be sure to use (for example) sudo.

To run tests: `cargo test`

Expand Down
6 changes: 5 additions & 1 deletion src/display/components/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ impl<'a> Table<'a> {
.iter()
.map(|(connection, connection_data)| {
vec![
display_connection_string(&connection, &ip_to_host),
display_connection_string(
&connection,
&ip_to_host,
&connection_data.interface_name,
),
connection_data.process_name.to_string(),
display_upload_and_download(*connection_data),
]
Expand Down
6 changes: 5 additions & 1 deletion src/display/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ where
write_to_stdout(format!(
"connection: <{}> {} up/down Bps: {}/{} process: \"{}\"",
timestamp,
display_connection_string(connection, ip_to_host),
display_connection_string(
connection,
ip_to_host,
&connection_network_data.interface_name
),
connection_network_data.total_bytes_uploaded,
connection_network_data.total_bytes_downloaded,
connection_network_data.process_name
Expand Down
28 changes: 12 additions & 16 deletions src/display/ui_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct ConnectionData {
pub total_bytes_downloaded: u128,
pub total_bytes_uploaded: u128,
pub process_name: String,
pub interface_name: String,
}

impl Bandwidth for ConnectionData {
Expand Down Expand Up @@ -52,40 +53,35 @@ pub struct UIState {
impl UIState {
pub fn new(
connections_to_procs: HashMap<Connection, String>,
network_utilization: Utilization,
mut network_utilization: Utilization,
) -> Self {
let mut processes: BTreeMap<String, NetworkData> = BTreeMap::new();
let mut remote_addresses: BTreeMap<Ipv4Addr, 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;
for (connection, process_name) in connections_to_procs {
if let Some(connection_bandwidth_utilization) =
network_utilization.connections.get(&connection)
{
if let Some(connection_info) = network_utilization.connections.remove(&connection) {
let data_for_remote_address = remote_addresses
.entry(connection.remote_socket.ip)
.or_default();
let connection_data = connections.entry(connection).or_default();
let data_for_process = processes.entry(process_name.clone()).or_default();

data_for_process.total_bytes_downloaded +=
&connection_bandwidth_utilization.total_bytes_downloaded;
data_for_process.total_bytes_uploaded +=
&connection_bandwidth_utilization.total_bytes_uploaded;
data_for_process.total_bytes_downloaded += connection_info.total_bytes_downloaded;
data_for_process.total_bytes_uploaded += connection_info.total_bytes_uploaded;
data_for_process.connection_count += 1;
connection_data.total_bytes_downloaded +=
&connection_bandwidth_utilization.total_bytes_downloaded;
connection_data.total_bytes_uploaded +=
&connection_bandwidth_utilization.total_bytes_uploaded;
connection_data.total_bytes_downloaded += connection_info.total_bytes_downloaded;
connection_data.total_bytes_uploaded += connection_info.total_bytes_uploaded;
connection_data.process_name = process_name;
connection_data.interface_name = connection_info.interface_name;
data_for_remote_address.total_bytes_downloaded +=
connection_bandwidth_utilization.total_bytes_downloaded;
connection_info.total_bytes_downloaded;
data_for_remote_address.total_bytes_uploaded +=
connection_bandwidth_utilization.total_bytes_uploaded;
connection_info.total_bytes_uploaded;
data_for_remote_address.connection_count += 1;
total_bytes_downloaded += connection_bandwidth_utilization.total_bytes_downloaded;
total_bytes_uploaded += connection_bandwidth_utilization.total_bytes_uploaded;
total_bytes_downloaded += connection_info.total_bytes_downloaded;
total_bytes_uploaded += connection_info.total_bytes_uploaded;
}
}
UIState {
Expand Down
24 changes: 17 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use structopt::StructOpt;
pub struct Opt {
#[structopt(short, long)]
/// The network interface to listen on, eg. eth0
interface: String,
interface: Option<String>,
#[structopt(short, long)]
/// Machine friendlier output
raw: bool,
Expand Down Expand Up @@ -78,8 +78,8 @@ fn try_main() -> Result<(), failure::Error> {
}

pub struct OsInputOutput {
pub network_interface: NetworkInterface,
pub network_frames: Box<dyn DataLinkReceiver>,
pub network_interfaces: Vec<NetworkInterface>,
pub network_frames: Vec<Box<dyn DataLinkReceiver>>,
pub get_open_sockets: fn() -> HashMap<Connection, String>,
pub keyboard_events: Box<dyn Iterator<Item = Event> + Send>,
pub dns_client: Option<dns::Client>,
Expand All @@ -105,7 +105,13 @@ where

let raw_mode = opts.raw;

let mut sniffer = Sniffer::new(os_input.network_interface, os_input.network_frames);
let mut sniffers = os_input
.network_interfaces
.into_iter()
.zip(os_input.network_frames.into_iter())
.map(|(iface, frames)| Sniffer::new(iface, frames))
.collect::<Vec<Sniffer>>();

let network_utilization = Arc::new(Mutex::new(Utilization::new()));
let ui = Arc::new(Mutex::new(Ui::new(terminal_backend)));

Expand Down Expand Up @@ -196,10 +202,14 @@ where
active_threads.push(
thread::Builder::new()
.name("sniffing_handler".to_string())
.spawn(move || {
while running.load(Ordering::Acquire) {
.spawn(move || 'sniffing: loop {
for sniffer in sniffers.iter_mut() {
if let Some(segment) = sniffer.next() {
network_utilization.lock().unwrap().update(&segment)
network_utilization.lock().unwrap().update(segment);
}
if !running.load(Ordering::Acquire) {
// Break from the outer loop and finish the thread
break 'sniffing;
}
}
})
Expand Down
4 changes: 3 additions & 1 deletion src/network/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ 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>,
interface_name: &str,
) -> String {
format!(
":{} => {}:{} ({})",
"<{}>:{} => {}:{} ({})",
interface_name,
connection.local_port,
display_ip_or_host(connection.remote_socket.ip, ip_to_host),
connection.remote_socket.port,
Expand Down
3 changes: 3 additions & 0 deletions src/network/sniffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use ::std::net::{IpAddr, SocketAddr};
use crate::network::{Connection, Protocol};

pub struct Segment {
pub interface_name: String,
pub connection: Connection,
pub direction: Direction,
pub data_length: u128,
Expand Down Expand Up @@ -81,6 +82,7 @@ impl Sniffer {
}
_ => return None,
};
let interface_name = self.network_interface.name.clone();
let direction = Direction::new(&self.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);
Expand All @@ -90,6 +92,7 @@ impl Sniffer {
Direction::Upload => Connection::new(to, source_port, protocol)?,
};
Some(Segment {
interface_name,
connection,
data_length,
direction,
Expand Down
22 changes: 12 additions & 10 deletions src/network/utilization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ use crate::network::{Connection, Direction, Segment};
use ::std::collections::HashMap;

#[derive(Clone)]
pub struct TotalBandwidth {
pub struct ConnectionInfo {
pub interface_name: String,
pub total_bytes_downloaded: u128,
pub total_bytes_uploaded: u128,
}

#[derive(Clone)]
pub struct Utilization {
pub connections: HashMap<Connection, TotalBandwidth>,
pub connections: HashMap<Connection, ConnectionInfo>,
}

impl Utilization {
Expand All @@ -23,14 +24,15 @@ impl Utilization {
self.connections.clear();
clone
}
pub fn update(&mut self, seg: &Segment) {
let total_bandwidth =
self.connections
.entry(seg.connection.clone())
.or_insert(TotalBandwidth {
total_bytes_downloaded: 0,
total_bytes_uploaded: 0,
});
pub fn update(&mut self, seg: Segment) {
let total_bandwidth = self
.connections
.entry(seg.connection)
.or_insert(ConnectionInfo {
interface_name: seg.interface_name,
total_bytes_downloaded: 0,
total_bytes_uploaded: 0,
});
match seg.direction {
Direction::Download => {
total_bandwidth.total_bytes_downloaded += seg.data_length;
Expand Down
32 changes: 22 additions & 10 deletions src/os/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ fn get_datalink_channel(
interface: &NetworkInterface,
) -> Result<Box<dyn DataLinkReceiver>, failure::Error> {
let mut config = Config::default();
config.read_timeout = Some(time::Duration::new(0, 1));
config.read_timeout = Some(time::Duration::new(0, 2_000_000));
match datalink::channel(interface, config) {
Ok(Ethernet(_tx, rx)) => Ok(rx),
Ok(_) => failure::bail!("Unknown interface type"),
Err(e) => failure::bail!("Failed to listen to network interface: {}", e),
Err(e) => failure::bail!("Failed to listen on network interface: {}", e),
}
}

Expand Down Expand Up @@ -76,15 +76,27 @@ fn create_write_to_stdout() -> Box<dyn FnMut(String) + Send> {
})
}

pub fn get_input(interface_name: &str, resolve: bool) -> Result<OsInputOutput, failure::Error> {
let keyboard_events = Box::new(KeyboardEvents);
let network_interface = match get_interface(interface_name) {
Some(interface) => interface,
None => {
failure::bail!("Cannot find interface {}", interface_name);
pub fn get_input(
interface_name: &Option<String>,
resolve: bool,
) -> Result<OsInputOutput, failure::Error> {
let network_interfaces = if let Some(name) = interface_name {
match get_interface(&name) {
Some(interface) => vec![interface],
None => {
failure::bail!("Cannot find interface {}", name);
}
}
} else {
datalink::interfaces()
};
let network_frames = get_datalink_channel(&network_interface)?;

let network_frames = network_interfaces
.iter()
.map(|iface| get_datalink_channel(iface))
.collect::<Result<Vec<_>, _>>()?;

let keyboard_events = Box::new(KeyboardEvents);
let write_to_stdout = create_write_to_stdout();
let (on_winch, cleanup) = sigwinch();
let dns_client = if resolve {
Expand All @@ -96,7 +108,7 @@ pub fn get_input(interface_name: &str, resolve: bool) -> Result<OsInputOutput, f
};

Ok(OsInputOutput {
network_interface,
network_interfaces,
network_frames,
get_open_sockets,
keyboard_events,
Expand Down
Loading

0 comments on commit b150bf3

Please sign in to comment.