run.rs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. use std::{path::{Path, PathBuf}, fs::{File, self, OpenOptions}, io::{BufWriter, Write, self, Read, BufRead, BufReader}, net::Ipv4Addr, process::{Command, self, Stdio}, time::{Instant, Duration}, thread, collections::HashSet};
  2. use json::JsonValue;
  3. use log::info;
  4. use rand::prelude::*;
  5. use anyhow::{Context, ensure, anyhow, bail};
  6. mod args;
  7. mod error;
  8. pub use crate::run::args::*;
  9. pub use crate::run::error::*;
  10. pub use crate::run::args::TestType::*;
  11. pub use crate::run::args::FilterType::*;
  12. pub const BENCH_BASE_PATH: &str = "./bench";
  13. pub const BENCH_DATA_PATH: &str = "data";
  14. pub const BENCH_BIN_PATH: &str = "bin";
  15. pub const BENCH_LOG_PATH: &str = "log";
  16. const XDP_LOAD_TIMEOUT_SECS: u64 = 5;
  17. const HITRATE_ALLOWED_DIFFERENCE: f64 = 0.25;
  18. const HITRATE_GENERATION_ALLOWED_ATTEMPTS: usize = 50;
  19. const PRIVILEGE_RUNNER: [&str;1] = ["sudo"];
  20. #[derive(Clone, Debug)]
  21. struct IpDataSet {
  22. path: PathBuf,
  23. subnet_size: usize,
  24. set: HashSet<Ipv4Addr>
  25. }
  26. impl IpDataSet {
  27. fn from(path: PathBuf, subnet_size: usize) -> anyhow::Result<Self> {
  28. let ip_file = File::open(&path).context(format!("Failed to open ip file {:?}", &path))?;
  29. let reader = BufReader::new(ip_file);
  30. let ips: HashSet<_> = reader
  31. .lines()
  32. .map(|s| s
  33. .map_err(|e| anyhow!(e))
  34. .and_then(|s| s.parse::<Ipv4Addr>().context("Failed to parse ipv4 from ip file")))
  35. .collect::<Result<HashSet<_>,_>>()?;
  36. Ok(Self {
  37. path,
  38. subnet_size,
  39. set: ips
  40. })
  41. }
  42. fn generate_subnet(&self, subnet_size: usize, seed: u64) -> anyhow::Result<HashSet<Ipv4Addr>> {
  43. if subnet_size == self.subnet_size {
  44. return Ok(self.set.clone())
  45. } else if subnet_size > self.subnet_size {
  46. bail!("Could not generate subnet of size {}, bigger than dataset ({})", subnet_size, self.subnet_size);
  47. } else {
  48. let mut rng = SmallRng::seed_from_u64(seed);
  49. let mask = (1u64 << (self.subnet_size as u64)) - (1u64 << (subnet_size as u64));
  50. let mask = mask as u32;
  51. let hitrate = self.set.len() as f64 / ((1u64 << (self.subnet_size as u64)) as f64);
  52. info!("{} {} {}", hitrate * (1f64 - HITRATE_ALLOWED_DIFFERENCE), hitrate, hitrate*(1f64 + HITRATE_ALLOWED_DIFFERENCE));
  53. for _ in 0..HITRATE_GENERATION_ALLOWED_ATTEMPTS {
  54. let subnet = mask & rng.next_u32();
  55. if let Some(res) = self.try_generate_subnet(subnet_size, mask, subnet, hitrate) {
  56. if res.len() == 0 {
  57. bail!("Failed to generate subnet of size {} with hitrate {}: results in empty set", subnet_size, hitrate)
  58. }
  59. return Ok(res)
  60. }
  61. }
  62. bail!("Failed to generate subnet of size {} with hitrate {} after {} attempts", subnet_size, hitrate, HITRATE_GENERATION_ALLOWED_ATTEMPTS);
  63. }
  64. }
  65. fn try_generate_subnet(&self,subnet_size: usize, mask: u32, subnet: u32, hitrate: f64) -> Option<HashSet<Ipv4Addr>> {
  66. let mut new_set = HashSet::new();
  67. for ip in self.set.iter() {
  68. if u32::from(*ip) & mask == subnet {
  69. new_set.insert(*ip);
  70. }
  71. }
  72. let new_hitrate = (new_set.len() as f64) / ((1u64 << subnet_size) as f64);
  73. if (((new_hitrate as f64) / (hitrate as f64)) - 1f64).abs() > HITRATE_ALLOWED_DIFFERENCE {
  74. info!("{}/{}: {}", Ipv4Addr::from(subnet), 32 - subnet_size, new_hitrate);
  75. return None
  76. }
  77. Some(new_set)
  78. }
  79. }
  80. fn log<R>(log_path: &Path, reader: &mut R, name: &str) -> anyhow::Result<()>
  81. where
  82. R: ?Sized,
  83. R: Read
  84. {
  85. fs::create_dir_all(log_path)?;
  86. let path = log_path.join(format!("{}.log", name));
  87. let mut file = OpenOptions::new().append(true).create(true).open(&path)
  88. .context(format!("Failed to create logfile {}", path.to_str().unwrap()))?;
  89. io::copy(reader, &mut file)
  90. .context(format!("Failed to write to logfile {}", path.to_str().unwrap()))?;
  91. Ok(())
  92. }
  93. fn log_both<R1, R2>(log_path: &Path, stderr: &mut R1, stdout: &mut R2, name: &str) -> anyhow::Result<()>
  94. where
  95. R1: ?Sized,
  96. R2: ?Sized,
  97. R1: Read,
  98. R2: Read
  99. {
  100. let stderr_name = format!("{}.stderr", name);
  101. log(log_path, stderr, &stderr_name.as_str())?;
  102. let stdout_name = format!("{}.stdout", name);
  103. log(log_path, stdout, &stdout_name.as_str())?;
  104. Ok(())
  105. }
  106. pub fn run() -> Result<(), anyhow::Error> {
  107. clean().context("Cleaning bench folder")?;
  108. let cores: u32 = 4;
  109. let seed: u64 = 0x1337133713371337;
  110. let times: u64 = 2;
  111. let scan_sizes: Vec<u64> = vec![16];//, 24];//,32]; // TODO 8 only for test purposes
  112. // let scan_sizes: Vec<u64> = vec![24];
  113. // let hit_rates: Vec<f64> = vec![0.001, 0.0032,0.01,0.032,0.1];
  114. info!("Loading in data sets");
  115. let ip_data_sets: Vec<IpDataSet> = vec![IpDataSet::from(PathBuf::from("./data/http-scan.txt"), 32usize)?];
  116. // let false_positive_rates: Vec<TestType> = vec![Baseline, EmptyFilter,Normal(0.1),Normal(0.01),Normal(0.001),Normal(0.0001)];
  117. let false_positive_rates: Vec<TestType> = vec![Baseline, Normal(0.001), BpfStats(0.001)];
  118. let filter_types: Vec<FilterType> = vec![Bitmap, Bloom];
  119. let baseline_filter_types: Vec<FilterType> = vec![Bitmap];
  120. // let scan_rates: Vec<u64> = vec![316_000, 562_000, 1_000_000, 1_780_000, 3_160_000];
  121. let scan_rates: Vec<u64> = vec![500000, 629463, 792447, 997631, 1255943, 1581139, 1990536, 2505936, 3154787, 3971641, 5000000];
  122. let mut ip_sets = vec![];
  123. for (i, ip_data_set) in ip_data_sets.iter().enumerate() {
  124. for scan_size in &scan_sizes {
  125. let data_args = DataArgs::from(seed, *scan_size, i as u64);
  126. info!("Building IP file for {}", data_args);
  127. let (ip_file_path, subnet, entries, hitrate) = build_ip_file(ip_data_set, data_args)
  128. .context(format!("Building ip file for {}", data_args))?;
  129. ip_sets.push((*scan_size, i, ip_file_path, subnet, entries));
  130. info!("{}: subnet: {}, entries: {}, hitrate: {}", data_args, subnet, entries, hitrate);
  131. }
  132. }
  133. for time in 0..times {
  134. for (scan_size, data_index, ip_file_path, subnet, entries) in &ip_sets {
  135. let data_args = DataArgs::from(seed, *scan_size, *data_index as u64);
  136. for test_type in &false_positive_rates {
  137. let filter_types = match test_type {
  138. Normal(_) | BpfStats(_) => {
  139. &filter_types
  140. },
  141. Baseline => {
  142. &baseline_filter_types
  143. }
  144. };
  145. for filter_type in filter_types {
  146. let bloom_args = FilterArgs::from(data_args, *test_type, *filter_type, *entries);
  147. info!("Building binaries for {} {}", data_args, bloom_args);
  148. build_binaries(data_args, bloom_args)
  149. .context(format!("Failed to build binaries for {} {}", data_args, bloom_args))?;
  150. let filter_path = match test_type {
  151. Normal(_) | BpfStats(_) => {
  152. info!("Building filter for {} {}", data_args, bloom_args);
  153. let filter_path = build_filter(data_args, bloom_args, *filter_type, ip_file_path.as_path())
  154. .context(format!("Failed to build filter for {} {}", data_args, bloom_args))?;
  155. Some(filter_path)
  156. },
  157. Baseline => {
  158. None
  159. }
  160. };
  161. for scan_rate in &scan_rates {
  162. let scan_args = ScanArgs::new(*scan_rate);
  163. let args = BenchArgs {n: time, data_args, bloom_filter_args: bloom_args, scan_args};
  164. let run_output = (|| {
  165. fs::create_dir_all(args.wd_path())
  166. .context("Failed to create wd")
  167. .map_err(|e| (None, e))?;
  168. let (handle, stderr_handle, stdout_handle) = match test_type {
  169. Normal(_) | BpfStats(_) => {
  170. info!("Loading XDP program for {}", args);
  171. let (handle, stderr_handle, stdout_handle) = load_xdp(args, filter_path.clone().unwrap().as_path())
  172. .map_err(|(h, e)| (h, e.context(format!("Loading XDP program for {}", args))))?;
  173. (Some(handle), Some(stderr_handle), Some(stdout_handle))
  174. },
  175. Baseline => {
  176. info!("Not loading XDP program for {}", args);
  177. (None, None, None)
  178. }
  179. };
  180. if let BpfStats(_) = test_type {
  181. info!("Enabling bpf_stats");
  182. if let Err(e) = set_bpf_stats(args, true) {
  183. return Err((handle, e));
  184. }
  185. }
  186. info!("Running zmap for {}", args);
  187. let zmap_result = run_zmap(args, *subnet)
  188. .context(format!("Running zmap for {}", args));
  189. if let Err(e) = zmap_result {
  190. return Err((handle, e));
  191. }
  192. let zmap_output = zmap_result.unwrap();
  193. let bpf_stats = match test_type {
  194. BpfStats(_) => {
  195. info!("Disabling and collecting bpf_stats");
  196. if let Err(e) = set_bpf_stats(args, false) {
  197. return Err((handle, e));
  198. }
  199. let bpf_stats_result = read_bpf_stats(args)
  200. .context(format!("Failed to read bpf stats for {}", args));
  201. if let Err(e) = bpf_stats_result {
  202. return Err((handle, e));
  203. }
  204. let bpf_stats = bpf_stats_result.unwrap();
  205. Some(bpf_stats)
  206. }
  207. _ => {
  208. None
  209. }
  210. };
  211. let responder_output = match test_type {
  212. BpfStats(_) | Normal(_) => {
  213. info!("Telling 'responder' to unload XDP");
  214. let responder_output = unload_xdp(args, handle.unwrap())
  215. .map_err(|(h, e)| (h, e.context(format!("Could not successfully unload XDP program for {}", args))))?;
  216. Some(responder_output)
  217. }
  218. Baseline => {
  219. None
  220. }
  221. };
  222. Ok((zmap_output, responder_output, bpf_stats, stderr_handle, stdout_handle))
  223. })();
  224. let (zmap_output, _responder_output, bpf_stats, responder_stderr_handle, responder_stdout_handle) = run_output.map_err(|(handle, e)| {
  225. if let Some(mut handle) = handle {
  226. let kill_result = handle.kill();
  227. if let Err(kill_e) = kill_result {
  228. return anyhow!(kill_e)
  229. .context(e)
  230. .context(format!("Failed to kill responder process for {}; Killed because of reason below", args));
  231. }
  232. }
  233. e
  234. })?;
  235. if let Some(responder_stderr_handle) = responder_stderr_handle {
  236. responder_stderr_handle.join()
  237. .map_err(|_| anyhow!("stderr thread panicked"))
  238. .and(responder_stdout_handle.unwrap().join()
  239. .map_err(|_| anyhow!("stdout thread panicked")))
  240. .and_then(|res| res)
  241. .context(format!("Error occured in a log thread for {}", args))?;
  242. }
  243. File::create(args.wd_path().join("zmap_stats.txt"))
  244. .context(format!("Failed to create zmap_stats.txt file for {}", args))?
  245. .write_all(&zmap_output.stderr.as_slice())
  246. .context(format!("Failed to write to zmap_stats.txt file for {}", args))?;
  247. if let Some(bpf_stats) = bpf_stats {
  248. File::create(args.wd_path().join("bpf_stats.json"))
  249. .context(format!("Failed to create bpf_stats.json file for {}", args))?
  250. .write_all(format!("{{\"run_count\": {}, \"run_time\": {}, \"mem_lock\": {}}}", bpf_stats.0, bpf_stats.1, bpf_stats.2).as_bytes())
  251. .context(format!("Failed to write to bpf_stats.json file for {}", args))?;
  252. }
  253. }
  254. }
  255. }
  256. }
  257. }
  258. Ok(())
  259. }
  260. fn clean() -> anyhow::Result<()> {
  261. let path = PathBuf::from(BENCH_BASE_PATH);
  262. if (&path).exists() {
  263. fs::remove_dir_all(&path).context(format!("Failed to clean path: {:?}", &path))?;
  264. }
  265. Ok(())
  266. }
  267. fn build_ip_file(data_set: &IpDataSet, data_args: DataArgs) -> anyhow::Result<(PathBuf, Ipv4Addr, u64, f64)> {
  268. let mut path = PathBuf::new();
  269. path.push(BENCH_BASE_PATH);
  270. path.push(BENCH_DATA_PATH);
  271. path.push("build");
  272. path.push(data_args.rel_path());
  273. let mut info_path = path.clone();
  274. path.push("ips.txt");
  275. info_path.push("ips-info.json");
  276. fs::create_dir_all(path.parent().unwrap())?;
  277. let ip_set = data_set.generate_subnet(data_args.scan_subnet_size as usize, data_args.seed)?;
  278. let subnet = u32::from(*ip_set.iter().next().ok_or(anyhow!("dataset empty: {:?}", data_set.path))?) & ((((1u64 << 32u64) - 1) << data_args.scan_subnet_size) as u32);
  279. let subnet = Ipv4Addr::from(subnet);
  280. let entries = ip_set.len() as u64;
  281. let hitrate = (entries as f64) / ((1u64 << data_args.scan_subnet_size) as f64);
  282. let ip_file = File::create(&path)?;
  283. let mut writer = BufWriter::new(ip_file);
  284. for ip in ip_set.into_iter() {
  285. writer.write(ip.to_string().as_bytes())?;
  286. writer.write(b"\n")?;
  287. }
  288. let mut ip_info_file = File::create(&info_path)?;
  289. ip_info_file.write_all(format!("{{\"subnet\": \"{}\", \"entries\": {}, \"hitrate\": {}}}", subnet.to_string(), entries, hitrate).as_bytes())?;
  290. Ok((path, Ipv4Addr::from(subnet), entries, hitrate))
  291. }
  292. fn build_binaries(data_args: DataArgs, bloom_args: FilterArgs) -> anyhow::Result<()> {
  293. let bin_path = BenchArgs::bin_bin_path(data_args, bloom_args);
  294. if bin_path.exists() {
  295. return Ok(())
  296. }
  297. fs::create_dir_all(&bin_path).context("Failed to create bench dir")?;
  298. let output = Command::new("cargo")
  299. .args([
  300. "xtask",
  301. "build-artifacts",
  302. "--output-folder", bin_path.to_str().unwrap()
  303. ])
  304. .env("BLOOMFILTER_ADDRESS_BITS", bloom_args.address_bits.to_string())
  305. .env("BLOOMFILTER_ADDRESS_BITS_CHUNK", bloom_args.chunk_address_bits.to_string())
  306. .env("BLOOMFILTER_HASH_COUNT", bloom_args.hash_count.unwrap_or(0).to_string())
  307. .stdin(Stdio::null())
  308. .stderr(Stdio::piped())
  309. .stdout(Stdio::null())
  310. .output()
  311. .context("Failed to run cargo xtask build-artifacts")?;
  312. let log_path = BenchArgs::bin_log_path(data_args, bloom_args);
  313. log_both(&log_path, &mut output.stderr.as_slice(), &mut output.stdout.as_slice(), "build-artifacts")?;
  314. ensure!(output.status.success(), CommandError::new(output, log_path));
  315. Ok(())
  316. }
  317. fn build_filter(data_args: DataArgs, filter_args: FilterArgs, filter_type: FilterType, ip_file_path: &Path) -> anyhow::Result<PathBuf> {
  318. let filter_file = match filter_type {
  319. Bloom => "ips.bfb",
  320. Bitmap => "ips.fb",
  321. };
  322. let path = BenchArgs::bin_wd_path(data_args, filter_args).join(filter_file);
  323. if path.exists() {
  324. return Ok(path)
  325. }
  326. fs::create_dir_all(path.parent().unwrap()).context("Failed to create bench dir")?;
  327. let filter_type_string = filter_type.to_string().to_lowercase();
  328. let output = Command::new("/usr/bin/time")
  329. .args([
  330. // time args
  331. "-o", BenchArgs::bin_wd_path(data_args, filter_args).join("filter_extern_time.json").to_str().unwrap(),
  332. "--format", "{\"clock\": %e, \"cpu_p\": \"%P\", \"kernel_s\": %S, \"user_s\": %U}",
  333. // actual command
  334. BenchArgs::bin_bin_path(data_args, filter_args).join("tools/build_filter").to_str().unwrap(),
  335. "--force",
  336. "--timing-path", BenchArgs::bin_wd_path(data_args, filter_args).join("filter_intern_time.json").to_str().unwrap(),
  337. filter_type_string.as_str(),
  338. ip_file_path.to_str().unwrap(),
  339. path.to_str().unwrap()
  340. ])
  341. .env("RUST_LOG", "info")
  342. .stdin(Stdio::null())
  343. .stderr(Stdio::piped())
  344. .stdout(Stdio::piped())
  345. .output()
  346. .context("Failed to run build_filter binary")?;
  347. let log_path = BenchArgs::bin_log_path(data_args, filter_args);
  348. log_both(&log_path, &mut output.stderr.as_slice(), &mut output.stdout.as_slice(), "build-filter")?;
  349. ensure!(output.status.success(), CommandError::new(output, log_path));
  350. Ok(path)
  351. }
  352. type LogJoinHandle = thread::JoinHandle<anyhow::Result<()>>;
  353. fn load_xdp(bench_args: BenchArgs, filter_path: &Path) -> Result<(process::Child, LogJoinHandle, LogJoinHandle), (Option<process::Child>, anyhow::Error)> {
  354. let responder_path = bench_args.bin_path().join("responder");
  355. let target= bench_args.bin_path().join("ebpf");
  356. let fd_info_out_path = bench_args.wd_path().join("responder_info.json");
  357. let filter_type = bench_args.bloom_filter_args.filter_type.to_string().to_lowercase();
  358. let mut args = Vec::from(PRIVILEGE_RUNNER);
  359. args.extend_from_slice(&[
  360. responder_path.to_str().unwrap(),
  361. "--filter-path", filter_path.to_str().unwrap(),
  362. "--target", target.to_str().unwrap(),
  363. "--fd-info-out-path", fd_info_out_path.to_str().unwrap(),
  364. "--filter-type", filter_type.as_str(),
  365. ]);
  366. let mut handle = Command::new(args.remove(0))
  367. .args(args)
  368. .env("RUST_LOG", "info")
  369. .stdin(Stdio::piped())
  370. .stderr(Stdio::piped())
  371. .stdout(Stdio::piped())
  372. .spawn()
  373. .context("Failed to run responder to load XDP")
  374. .map_err(|e| (None, e))?;
  375. let mut stderr = handle.stderr.take().unwrap();
  376. let mut stdout = handle.stdout.take().unwrap();
  377. let stderr_handle = thread::spawn(move || log(&bench_args.log_path(), &mut stderr, "responder.stderr"));
  378. let stdout_handle = thread::spawn(move || log(&bench_args.log_path(), &mut stdout, "responder.stdout"));
  379. if let Err(e) = try_wait_xdp_loaded(bench_args, &mut handle) {
  380. return Err((Some(handle), e));
  381. }
  382. return Ok((handle, stderr_handle, stdout_handle));
  383. }
  384. fn try_wait_xdp_loaded(_bench_args: BenchArgs, handle: &mut process::Child) -> anyhow::Result<()> {
  385. let start = Instant::now();
  386. let mut last_ip_link = None;
  387. while start.elapsed().as_secs() < XDP_LOAD_TIMEOUT_SECS {
  388. if let Some(_) = handle.try_wait()? {
  389. return Err(anyhow!("Responder exited too early"));
  390. }
  391. let output = Command::new("ip")
  392. .args(["link", "show", "lo"])
  393. .output()
  394. .context("Failed to run 'ip link show lo'")?;
  395. let ip_link_info = String::from_utf8(output.stdout)?;
  396. last_ip_link = Some(ip_link_info.clone());
  397. if let Some(l) = ip_link_info.lines().skip(2).next() {
  398. if let Some(id) = l.strip_prefix(" prog/xdp id") {
  399. info!("XDP loaded; id:{}", id);
  400. return Ok(())
  401. }
  402. }
  403. thread::sleep(Duration::from_millis(100));
  404. }
  405. Err(anyhow!(
  406. "XDP program did not load within timeout ({}); last ip link show lo info: {}",
  407. XDP_LOAD_TIMEOUT_SECS,
  408. last_ip_link.unwrap_or(String::from("no ip link info"))
  409. ))
  410. }
  411. fn unload_xdp(bench_args: BenchArgs,mut handle: process::Child) -> Result<process::Output, (Option<process::Child>, anyhow::Error)> {
  412. let result = handle.stdin.take().unwrap().write(&[b'\n']);
  413. if let Err(e) = result {
  414. return Err((Some(handle), anyhow!(e)));
  415. }
  416. let output = handle.wait_with_output().map_err(|e| (None, anyhow!(e)))?;
  417. if !output.status.success() {
  418. return Err((None, anyhow!(CommandError::new(output, bench_args.log_path()))));
  419. }
  420. return Ok(output);
  421. }
  422. fn set_bpf_stats(bench_args: BenchArgs, enabled: bool) -> anyhow::Result<()> {
  423. let setting = format!("kernel.bpf_stats_enabled={}", if enabled {1} else {0});
  424. let mut args = Vec::from(PRIVILEGE_RUNNER);
  425. args.extend_from_slice(&[
  426. "sysctl", "-w", setting.as_str()
  427. ]);
  428. let output = Command::new(args.remove(0))
  429. .args(args)
  430. .stdin(Stdio::null())
  431. .stderr(Stdio::piped())
  432. .stdout(Stdio::piped())
  433. .output()
  434. .context("Failed to run sysctl")?;
  435. let name = format!("sysctl_{}", if enabled {"enable"} else {"disable"});
  436. log_both(&bench_args.log_path(), &mut output.stderr.as_slice(), &mut output.stdout.as_slice(), name.as_str())?;
  437. ensure!(output.status.success(), CommandError::new(output, bench_args.log_path()));
  438. Ok(())
  439. }
  440. fn read_bpf_stats(bench_args: BenchArgs) -> anyhow::Result<(u128, u128, u128)> {
  441. // TODO Also collect memlock
  442. let mut info = vec![];
  443. File::open(bench_args.wd_path().join("responder_info.json"))
  444. .context("Failed to open responder_info.json file")?
  445. .read_to_end(&mut info)?;
  446. let info = json::parse(String::from_utf8(info)?.as_str())?;
  447. if let JsonValue::Object(o) = info {
  448. let fd = o.get("fd").ok_or(anyhow!("No key fd found in responder_info.json file"))?.as_u64().unwrap();
  449. let pid = o.get("pid").ok_or(anyhow!("No key pid found in responder_info.json file"))?.as_u64().unwrap();
  450. let mut path = PathBuf::from("/proc");
  451. path.push(pid.to_string());
  452. path.push("fdinfo");
  453. path.push(fd.to_string());
  454. let mut args = Vec::from(PRIVILEGE_RUNNER);
  455. args.extend_from_slice(&[
  456. "cat", path.to_str().unwrap()
  457. ]);
  458. let output = Command::new(args.remove(0))
  459. .args(args)
  460. .stdin(Stdio::null())
  461. .stderr(Stdio::piped())
  462. .stdout(Stdio::piped())
  463. .output()
  464. .context("Failed to read fd info from /proc/[pid]/fdinfo/[fd]")?;
  465. log_both(&bench_args.log_path(), &mut output.stderr.as_slice(), &mut output.stdout.as_slice(), "procfs")?;
  466. ensure!(output.status.success(), CommandError::new(output, bench_args.log_path()));
  467. let mut run_time: Option<u128> = None;
  468. let mut run_count: Option<u128> = None;
  469. let mut mem_lock: Option<u128> = None;
  470. for line in output.stdout.lines() {
  471. let line = line?;
  472. if let Some(run_time_str) = line.as_str().strip_prefix("run_time_ns:") {
  473. run_time = Some(run_time_str.trim().parse()?);
  474. } else if let Some(run_count_str) = line.as_str().strip_prefix("run_cnt:") {
  475. run_count = Some(run_count_str.trim().parse()?);
  476. } else if let Some(mem_lock_str) = line.as_str().strip_prefix("memlock:") {
  477. mem_lock = Some(mem_lock_str.trim().parse()?);
  478. }
  479. }
  480. return match (run_count, run_time, mem_lock) {
  481. (None, _, _) => Err(anyhow!("Could not read run_cnt from fdinfo file")),
  482. (_, None, _) => Err(anyhow!("Could not read run_time_ns from fdinfo file")),
  483. (_, _, None) => Err(anyhow!("Could not read mem_lock from fdinfo file")),
  484. (Some(run_count), Some(run_time), Some(mem_lock)) => Ok((run_count, run_time, mem_lock))
  485. }
  486. } else {
  487. return Err(anyhow!("Could not read json object from responder_info.json file"));
  488. }
  489. }
  490. fn run_zmap(bench_args: BenchArgs, subnet: Ipv4Addr) -> anyhow::Result<process::Output> {
  491. let subnet_string = format!("{}/{}",subnet, 32 - bench_args.data_args.scan_subnet_size);
  492. let output_file = bench_args.wd_path().join("zmap_out_ips.txt");
  493. let rate_string = bench_args.scan_args.rate.to_string();
  494. let interface = match bench_args.bloom_filter_args.test_type {
  495. Baseline => "dummyif",
  496. _ => "lo",
  497. };
  498. let seed = bench_args.data_args.seed.to_string();
  499. let mut args = Vec::from(PRIVILEGE_RUNNER);
  500. args.extend_from_slice(&[
  501. "./zmap",
  502. subnet_string.as_str(),
  503. "--target-port=80",
  504. "--interface", interface,
  505. "--gateway-mac=00:00:00:00:00:00",
  506. "--output-file", output_file.to_str().unwrap(),
  507. "--rate", rate_string.as_str(),
  508. "--sender-threads=7",
  509. "--cooldown-time=1",
  510. "--seed", seed.as_str(),
  511. "--blocklist-file=blocklist",
  512. "--max-sendto-failures=-1"
  513. ]);
  514. let output = Command::new(args.remove(0))
  515. .args(args)
  516. .stdin(Stdio::null())
  517. .stderr(Stdio::piped())
  518. .stdout(Stdio::piped())
  519. .output()
  520. .context("Failed to run zmap")?;
  521. log_both(&bench_args.log_path(), &mut output.stderr.as_slice(), &mut output.stdout.as_slice(), "zmap")?;
  522. ensure!(output.status.success(), CommandError::new(output, bench_args.log_path()));
  523. return Ok(output);
  524. }