Skip to content

Commit bf6b10b

Browse files
committed
New bandwidth limit feature
1 parent c796009 commit bf6b10b

File tree

4 files changed

+68
-23
lines changed

4 files changed

+68
-23
lines changed

src/args.rs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ pub fn build_args<'a, 'b>() -> App<'a, 'b> {
109109
.takes_value(true).value_name("INTERVAL_DURATION")
110110
.help("Milliseconds between ARP requests")
111111
)
112+
.arg(
113+
Arg::with_name("bandwidth").short("B").long("bandwidth")
114+
.takes_value(true).value_name("BITS")
115+
.conflicts_with("interval")
116+
.help("Limit scan bandwidth (bits/second)")
117+
)
112118
.arg(
113119
Arg::with_name("oui-file").long("oui-file")
114120
.takes_value(true).value_name("FILE_PATH")
@@ -166,6 +172,11 @@ pub enum ProfileType {
166172
Chaos
167173
}
168174

175+
pub enum ScanTiming {
176+
Interval(u64),
177+
Bandwidth(u64)
178+
}
179+
169180
pub struct ScanOptions {
170181
pub profile: ProfileType,
171182
pub interface_name: Option<String>,
@@ -177,7 +188,7 @@ pub struct ScanOptions {
177188
pub destination_mac: Option<MacAddr>,
178189
pub vlan_id: Option<u16>,
179190
pub retry_count: usize,
180-
pub interval_ms: u64,
191+
pub scan_timing: ScanTiming,
181192
pub randomize_targets: bool,
182193
pub output: OutputFormat,
183194
pub oui_file: String,
@@ -230,6 +241,28 @@ impl ScanOptions {
230241
}).collect()
231242
})
232243
}
244+
245+
fn compute_interval(matches: &ArgMatches, profile: &ProfileType) -> ScanTiming {
246+
247+
match (matches.value_of("bandwidth"), matches.value_of("interval")) {
248+
(Some(bandwidth_text), None) => {
249+
let bits_second: u64 = bandwidth_text.parse().unwrap_or_else(|err| {
250+
eprintln!("Expected positive number, {}", err);
251+
process::exit(1);
252+
});
253+
ScanTiming::Bandwidth(bits_second)
254+
},
255+
(None, Some(interval_text)) => parse_to_milliseconds(interval_text).map(ScanTiming::Interval).unwrap_or_else(|err| {
256+
eprintln!("Expected correct interval, {}", err);
257+
process::exit(1);
258+
}),
259+
_ => match profile {
260+
ProfileType::Stealth => ScanTiming::Interval(REQUEST_MS_INTERVAL * 2),
261+
ProfileType::Fast => ScanTiming::Interval(0),
262+
_ => ScanTiming::Interval(REQUEST_MS_INTERVAL)
263+
}
264+
}
265+
}
233266

234267
/**
235268
* Build a new 'ScanOptions' struct that will be used in the whole CLI such
@@ -346,17 +379,7 @@ impl ScanOptions {
346379
}
347380
};
348381

349-
let interval_ms: u64 = match matches.value_of("interval") {
350-
Some(interval_text) => parse_to_milliseconds(interval_text).unwrap_or_else(|err| {
351-
eprintln!("Expected correct interval, {}", err);
352-
process::exit(1);
353-
}),
354-
None => match profile {
355-
ProfileType::Stealth => REQUEST_MS_INTERVAL * 2,
356-
ProfileType::Fast => 0,
357-
_ => REQUEST_MS_INTERVAL
358-
}
359-
};
382+
let scan_timing: ScanTiming = ScanOptions::compute_interval(matches, &profile);
360383

361384
let output = match matches.value_of("output") {
362385
Some(output_request) => {
@@ -463,7 +486,7 @@ impl ScanOptions {
463486
source_mac,
464487
vlan_id,
465488
retry_count,
466-
interval_ms,
489+
scan_timing,
467490
randomize_targets,
468491
output,
469492
oui_file,

src/main.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,14 @@ fn main() {
8888

8989
let network_size = utils::compute_network_size(&ip_networks);
9090

91+
let estimations = network::compute_scan_estimation(network_size, &scan_options);
92+
let interval_ms = estimations.interval_ms;
93+
9194
if scan_options.is_plain_output() {
9295

93-
let estimations = network::compute_scan_estimation(network_size, &scan_options);
9496
let formatted_ms = time::format_milliseconds(estimations.duration_ms);
9597
println!("Estimated scan time {} ({} bytes, {} bytes/s)", formatted_ms, estimations.request_size, estimations.bandwidth);
96-
println!("Sending {} ARP requests (waiting at least {}ms, {}ms request interval)", network_size, scan_options.timeout_ms, scan_options.interval_ms);
98+
println!("Sending {} ARP requests (waiting at least {}ms, {}ms request interval)", network_size, scan_options.timeout_ms, interval_ms);
9799
}
98100

99101
let finish_sleep = Arc::new(AtomicBool::new(false));
@@ -130,7 +132,7 @@ fn main() {
130132

131133
if let IpAddr::V4(ipv4_address) = ip_address {
132134
network::send_arp_request(&mut tx, selected_interface, source_ip, ipv4_address, Arc::clone(&scan_options));
133-
thread::sleep(Duration::from_millis(scan_options.interval_ms));
135+
thread::sleep(Duration::from_millis(interval_ms));
134136
}
135137
}
136138
}

src/network.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use rand::prelude::*;
1919
use crate::args::ScanOptions;
2020
use crate::vendor::Vendor;
2121
use crate::utils;
22+
use crate::args::ScanTiming;
2223

2324
pub const DATALINK_RCV_TIMEOUT: u64 = 500;
2425

@@ -34,6 +35,7 @@ const ETHERNET_VLAN_PACKET_SIZE: usize = 46;
3435
* starts and should give insights about the scan.
3536
*/
3637
pub struct ScanEstimation {
38+
pub interval_ms: u64,
3739
pub duration_ms: u128,
3840
pub request_size: u128,
3941
pub bandwidth: u128
@@ -110,7 +112,6 @@ pub fn compute_network_configuration<'a>(interfaces: &'a [NetworkInterface], sca
110112
*/
111113
pub fn compute_scan_estimation(host_count: u128, options: &Arc<ScanOptions>) -> ScanEstimation {
112114

113-
let interval: u128 = options.interval_ms.into();
114115
let timeout: u128 = options.timeout_ms.into();
115116
let packet_size: u128 = match options.has_vlan() {
116117
true => ETHERNET_VLAN_PACKET_SIZE.try_into().expect("Internal number conversion failed for VLAN packet size"),
@@ -120,16 +121,35 @@ pub fn compute_scan_estimation(host_count: u128, options: &Arc<ScanOptions>) ->
120121

121122
// The values below are averages based on an amount of performed network
122123
// scans. This may of course vary based on network configurations.
123-
let avg_arp_request_ms = 3;
124+
let avg_arp_request_ms: u128 = 3;
124125
let avg_resolve_ms = 500;
125126

126-
let request_phase_ms = (host_count * (avg_arp_request_ms+ interval)) * retry_count;
127-
let duration_ms = request_phase_ms + timeout + avg_resolve_ms;
128-
let request_size = host_count * packet_size;
127+
let request_size: u128 = host_count * packet_size;
128+
129+
let (interval_ms, bandwidth, request_phase_ms): (u64, u128, u128) = match options.scan_timing {
130+
ScanTiming::Bandwidth(bandwidth) => {
131+
132+
let bandwidth_lg: u128 = bandwidth.into();
133+
let request_phase_ms: u128 = (request_size * 1000) as u128 / bandwidth_lg;
134+
let interval_ms: u128 = (request_phase_ms/retry_count/host_count) - avg_arp_request_ms;
135+
136+
(interval_ms.try_into().unwrap(), bandwidth_lg, request_phase_ms)
137+
138+
},
139+
ScanTiming::Interval(interval) => {
129140

130-
let bandwidth = (request_size * 1000) / request_phase_ms;
141+
let interval_ms_lg: u128 = interval.into();
142+
let request_phase_ms: u128 = (host_count * (avg_arp_request_ms + interval_ms_lg)) * retry_count;
143+
let bandwidth = (request_size * 1000) / request_phase_ms;
144+
145+
(interval, bandwidth, request_phase_ms)
146+
}
147+
};
148+
149+
let duration_ms = request_phase_ms + timeout + avg_resolve_ms;
131150

132151
ScanEstimation {
152+
interval_ms,
133153
duration_ms,
134154
request_size,
135155
bandwidth

src/utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub fn select_default_interface(interfaces: &[NetworkInterface]) -> Option<Netwo
9090
* Display scan settings before launching an ARP scan. This includes network
9191
* details (IP range, interface, ...) and timing informations.
9292
*/
93-
pub fn display_prescan_details(ip_networks: &Vec<&IpNetwork>, selected_interface: &NetworkInterface, scan_options: Arc<ScanOptions>) -> () {
93+
pub fn display_prescan_details(ip_networks: &[&IpNetwork], selected_interface: &NetworkInterface, scan_options: Arc<ScanOptions>) {
9494

9595
let mut network_list = ip_networks.iter().take(5).map(|network| network.to_string()).collect::<Vec<String>>().join(", ");
9696
if ip_networks.len() > 5 {

0 commit comments

Comments
 (0)