Skip to content

Commit 905f579

Browse files
authored
Merge pull request #11 from BugenZhao/bz-list-input
tracerouter: allow ip list file as input
2 parents 3aa1acc + 50b592c commit 905f579

File tree

6 files changed

+104
-51
lines changed

6 files changed

+104
-51
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ Compared to the original implementation, the main features of *flashroute.rs* ar
3838
```shell
3939
cargo run --release -- 192.168.1.1/32 --grain 0
4040
```
41+
- Probe all hosts listed in a file line by line
42+
```shell
43+
cargo run --release -- path/to/file --grain 8
44+
```
4145

4246
Listening on ICMP socket requires superuser permission, the *flashroute.rs* may automatically restart in sudo mode.
4347

res/test.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
8.8.8.8
2+
115.159.1.233
3+
39.156.27.1

src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ pub enum Error {
1616
UnexpectedIcmpType(pnet::packet::icmp::IcmpType, pnet::packet::icmp::IcmpCode),
1717
#[error("")]
1818
BadGrainOrNet(u8, ipnet::Ipv4Net),
19+
#[error("")]
20+
InvalidIpv4Addr(String),
21+
#[error("")]
22+
CannotResolveTargets(String),
1923
#[error("io error: {0}")]
2024
IoError(#[from] std::io::Error), // thus io::Error can implicitly `into` IoError
2125
}

src/main.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ async fn main() -> Result<()> {
4141
#[cfg(unix)]
4242
utils::ensure_su();
4343

44+
log::info!("{:?}", *OPT);
45+
4446
#[cfg(debug_assertions)]
4547
log::warn!(
4648
"{} is built in DEBUG mode, thus may perform quite poorly.",
@@ -49,13 +51,12 @@ async fn main() -> Result<()> {
4951

5052
let tr = Arc::new(Tracerouter::new()?);
5153
let r = tr.clone();
52-
let int_handler = tokio::spawn(async move {
54+
tokio::spawn(async move {
5355
tokio::signal::ctrl_c().await.unwrap();
5456
r.stop();
5557
});
5658

5759
let topo = tr.run().await?;
58-
int_handler.abort();
5960
process_topo(topo).await?;
6061

6162
#[cfg(windows)]

src/opt.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::path::PathBuf;
22
use structopt::StructOpt;
33

4+
use crate::error::*;
45
use crate::utils;
56

67
#[derive(Debug, StructOpt)]
@@ -60,8 +61,8 @@ pub struct Opt {
6061
// Target
6162
#[structopt(short, long, default_value = "8")]
6263
pub grain: u8,
63-
#[structopt()]
64-
pub targets: ipnet::Ipv4Net,
64+
#[structopt(parse(try_from_str = parse_targets))]
65+
pub targets: Targets,
6566
#[structopt(long)]
6667
pub global_only: bool,
6768
#[structopt(long)]
@@ -72,6 +73,22 @@ pub struct Opt {
7273
pub local_addr: std::net::Ipv4Addr,
7374
}
7475

76+
#[derive(Debug, Clone)]
77+
pub enum Targets {
78+
Net(ipnet::Ipv4Net),
79+
List(PathBuf),
80+
}
81+
82+
pub fn parse_targets(arg: &str) -> Result<Targets> {
83+
if let Ok(net) = arg.parse() {
84+
Ok(Targets::Net(net))
85+
} else if let Ok(path) = arg.parse() {
86+
Ok(Targets::List(path))
87+
} else {
88+
Err(Error::CannotResolveTargets(arg.to_owned()))
89+
}
90+
}
91+
7592
pub fn get_opt() -> Opt {
7693
let mut opt: Opt = Opt::from_args();
7794
opt.local_addr = crate::utils::get_interface_ipv4_addr(&opt.interface).unwrap();

src/tracerouter.rs

Lines changed: 71 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::{
1717
dcb::DstCtrlBlock,
1818
error::*,
1919
network::NetworkManager,
20+
opt::Targets,
2021
prober::ProbePhase,
2122
prober::ProbeResult,
2223
prober::Prober,
@@ -46,31 +47,14 @@ pub struct Tracerouter {
4647

4748
impl Tracerouter {
4849
pub fn new() -> Result<Self> {
49-
if OPT.grain > (OPT.targets.max_prefix_len() - OPT.targets.prefix_len()) {
50-
return Err(Error::BadGrainOrNet(OPT.grain, OPT.targets));
51-
}
52-
5350
log::info!(
5451
"Using interface `{}` ({})",
5552
OPT.interface.name,
5653
crate::utils::get_interface_ipv4_addr(&OPT.interface).unwrap()
5754
);
5855

59-
log::info!("Generating targets...");
60-
let all_count = Self::targets_count();
61-
let mut targets = DcbMap::with_capacity(all_count);
62-
for addr in Self::random_targets() {
63-
targets.insert(
64-
Self::addr_to_key(addr),
65-
DstCtrlBlock::new(addr, OPT.default_ttl),
66-
);
67-
}
68-
let filtered_count = targets.len();
69-
log::info!(
70-
"Generated {} targets, {} removed",
71-
filtered_count,
72-
all_count - filtered_count
73-
);
56+
log::info!("Initializing targets...");
57+
let targets = Self::generate_targets()?;
7458

7559
Ok(Self {
7660
targets: Arc::new(targets),
@@ -83,28 +67,67 @@ impl Tracerouter {
8367
(u >> (OPT.grain)) as AddrKey
8468
}
8569

86-
fn targets_count() -> usize {
87-
1 << ((OPT.targets.max_prefix_len() - OPT.targets.prefix_len()) - OPT.grain)
88-
}
70+
fn generate_targets() -> Result<DcbMap> {
71+
match OPT.targets.clone() {
72+
Targets::Net(net) => {
73+
if OPT.grain > (net.max_prefix_len() - net.prefix_len()) {
74+
return Err(Error::BadGrainOrNet(OPT.grain, net));
75+
}
8976

90-
fn random_targets() -> impl Iterator<Item = Ipv4Addr> {
91-
let mut rng = StdRng::seed_from_u64(OPT.seed);
92-
let subnets = OPT
93-
.targets
94-
.subnets(OPT.targets.max_prefix_len() - OPT.grain)
95-
.unwrap();
96-
97-
subnets
98-
.map(move |net| net.addr().saturating_add(rng.gen_range(0, 1 << OPT.grain)))
99-
.filter(|addr| {
100-
if OPT.global_only && OPT.allow_private {
101-
addr.is_bz_global() || addr.is_private()
102-
} else if OPT.global_only {
103-
addr.is_bz_global()
104-
} else {
105-
true
77+
let mut rng = StdRng::seed_from_u64(OPT.seed);
78+
let subnets = net.subnets(net.max_prefix_len() - OPT.grain).unwrap();
79+
80+
let iter = subnets
81+
.map(move |net| net.addr().saturating_add(rng.gen_range(0, 1 << OPT.grain)))
82+
.filter(|addr| {
83+
if OPT.global_only && OPT.allow_private {
84+
addr.is_bz_global() || addr.is_private()
85+
} else if OPT.global_only {
86+
addr.is_bz_global()
87+
} else {
88+
true
89+
}
90+
});
91+
92+
let all_count = 1 << ((net.max_prefix_len() - net.prefix_len()) - OPT.grain);
93+
let mut generated_targets = DcbMap::with_capacity(all_count);
94+
for addr in iter {
95+
generated_targets.insert(
96+
Self::addr_to_key(addr),
97+
DstCtrlBlock::new(addr, OPT.default_ttl),
98+
);
10699
}
107-
})
100+
let filtered_count = generated_targets.len();
101+
log::info!(
102+
"Generated {} targets, {} removed",
103+
filtered_count,
104+
all_count - filtered_count
105+
);
106+
107+
Ok(generated_targets)
108+
}
109+
110+
Targets::List(path) => {
111+
let mut generated_targets = DcbMap::new();
112+
113+
let content = std::fs::read_to_string(path)?;
114+
for line in content.lines() {
115+
if line.is_empty() {
116+
continue;
117+
}
118+
let addr = line
119+
.parse()
120+
.or(Err(Error::InvalidIpv4Addr(line.to_owned())))?;
121+
generated_targets.insert(
122+
Self::addr_to_key(addr),
123+
DstCtrlBlock::new(addr, OPT.default_ttl),
124+
);
125+
}
126+
log::info!("Imported {} targets from file", generated_targets.len());
127+
128+
Ok(generated_targets)
129+
}
130+
}
108131
}
109132
}
110133

@@ -349,13 +372,14 @@ mod test {
349372
#[test]
350373
fn test_generation() {
351374
let tr = Tracerouter::new().unwrap();
352-
assert_eq!(
353-
tr.targets.len(),
354-
1 << (32 - OPT.targets.prefix_len() - OPT.grain)
355-
);
356-
assert!(tr
357-
.targets
358-
.values()
359-
.all(|dcb| OPT.targets.contains(&dcb.addr)));
375+
if let Targets::Net(targets) = OPT.targets {
376+
assert_eq!(
377+
tr.targets.len(),
378+
1 << (32 - targets.prefix_len() - OPT.grain)
379+
);
380+
assert!(tr.targets.values().all(|dcb| targets.contains(&dcb.addr)));
381+
} else {
382+
panic!();
383+
}
360384
}
361385
}

0 commit comments

Comments
 (0)