main.rs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. use std::{path::PathBuf, time::Instant, fs::File, io::{Read, self, Write}, os::unix::prelude::AsRawFd, process};
  2. use aya::{Bpf, maps::Array as BpfArray};
  3. use anyhow::{anyhow, Context};
  4. use aya::programs::{Xdp, XdpFlags};
  5. use aya_log::BpfLogger;
  6. use clap::Parser;
  7. use env_logger::Env;
  8. use log::{info, warn};
  9. use responder_common::filter;
  10. use responder_common::filter::bloom_filter;
  11. #[cfg(all(debug_assertions, not(feature = "default_artifact_build")))]
  12. const DEFAULT_TARGET: &str = "target/bpfel-unknown-none/debug";
  13. #[cfg(all(not(debug_assertions), not(feature = "default_artifact_build")))]
  14. const DEFAULT_TARGET: &str = "target/bpfel-unknown-none/release";
  15. #[cfg(feature = "default_artifact_build")]
  16. const DEFAULT_TARGET: &str = "bin/ebpf";
  17. #[derive(Debug, Parser)]
  18. struct Opt {
  19. #[clap(short, long, default_value = "lo")]
  20. iface: String,
  21. #[clap(short, long, default_value = "syn")]
  22. scan_type: String,
  23. #[clap(long, default_value = "bitmap")]
  24. filter_type: String,
  25. #[clap(long)]
  26. filter_path: PathBuf,
  27. #[clap(default_value = DEFAULT_TARGET, long)]
  28. target: PathBuf,
  29. #[clap(short, long)]
  30. fd_info_out_path: Option<PathBuf>
  31. }
  32. #[tokio::main]
  33. async fn main() -> Result<(), anyhow::Error> {
  34. let start = Instant::now();
  35. let opt = Opt::parse();
  36. env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
  37. let mut bpf_path = PathBuf::new();
  38. bpf_path.push(opt.target);
  39. let xdp_name = format!("{}-{}", opt.scan_type, opt.filter_type);
  40. bpf_path.push(xdp_name.clone());
  41. let mut bpf = Bpf::load_file(bpf_path)?;
  42. if let Err(e) = BpfLogger::init(&mut bpf) {
  43. warn!("failed to initialize eBPF logger: {}
  44. This can happen if the loaded eBPF program has no log statements.", e);
  45. }
  46. // Obtain and load the XDP program called "responder" defined in the XDP file loaded above
  47. let program: &mut Xdp = bpf.program_mut("responder").unwrap().try_into()?;
  48. program.load()?;
  49. program.attach(&opt.iface, XdpFlags::default())
  50. .context(format!(
  51. "failed to attach the {} XDP program with default flags \
  52. - try changing XdpFlags::default() to XdpFlags::SKB_MODE",
  53. xdp_name
  54. ))?;
  55. info!("Loaded {} XDP program; FD: {}", xdp_name, program.fd().unwrap().as_raw_fd());
  56. if let Some(path) = opt.fd_info_out_path {
  57. info!("Emitting pid and fd to {:?}", &path);
  58. let info = format!("{{\"fd\": {}, \"pid\": {}}}\n", program.fd().unwrap().as_raw_fd(), process::id());
  59. File::create(path)?.write_all(info.as_bytes())?;
  60. }
  61. info!("Installing filter rules from {:?}", opt.filter_path);
  62. let mut filter_map: BpfArray<_, [u8; filter::CHUNK_BYTES]> =
  63. BpfArray::try_from(bpf.map_mut("FILTER_MAP")
  64. .ok_or(anyhow!("Could not construct mutable FILTER_MAP"))?)?;
  65. let mut filter_binary = File::open(opt.filter_path)?;
  66. let mut dimensions_buf: [u8; 8] = [0u8; 8];
  67. filter_binary.read_exact(&mut dimensions_buf)?;
  68. let file_address_bits = u64::from_be_bytes(dimensions_buf);
  69. if file_address_bits != (filter::ADDRESS_BITS as u64) {
  70. return Err(anyhow!(
  71. "Filter dimension ADDRESS_BITS does not match binary. Please rebuild the filter or recompile the binaries\n\tFile:\t{}\n\tBinary:\t{}",
  72. file_address_bits, filter::ADDRESS_BITS
  73. ));
  74. }
  75. filter_binary.read_exact(&mut dimensions_buf)?;
  76. let file_address_bits_chunk = u64::from_be_bytes(dimensions_buf);
  77. if file_address_bits_chunk != (filter::ADDRESS_BITS_CHUNK as u64) {
  78. return Err(anyhow!(
  79. "Filter dimension ADDRESS_BITS_CHUNK does not match binary. Please rebuild the filter or recompile the binaries\n\tFile:\t{}\n\tBinary:\t{}",
  80. file_address_bits_chunk, filter::ADDRESS_BITS_CHUNK
  81. ));
  82. }
  83. if opt.filter_type == "bloom" {
  84. filter_binary.read_exact(&mut dimensions_buf)?;
  85. let file_hash_count = u64::from_be_bytes(dimensions_buf);
  86. if file_hash_count != (bloom_filter::HASH_COUNT as u64) {
  87. return Err(anyhow!(
  88. "Filter dimension HASH_COUNT does not match binary. Please rebuild the filter or recompile the binaries\n\tFile:\t{}\n\tBinary:\t{}",
  89. file_hash_count, bloom_filter::HASH_COUNT
  90. ));
  91. }
  92. }
  93. let mut chunk = [0 as u8; filter::CHUNK_BYTES];
  94. let mut i = 0;
  95. loop {
  96. let read = filter_binary.read(&mut chunk)?;
  97. if read > 0 {
  98. filter_map.set(i, chunk, 0)?;
  99. i += 1;
  100. }
  101. if read < filter::CHUNK_BYTES {
  102. break;
  103. }
  104. }
  105. if (i as usize) != filter::MAP_SIZE {
  106. return Err(anyhow!(
  107. "Number of chunks in file ({}) do not match expected number ({})",
  108. i, filter::MAP_SIZE
  109. ))
  110. }
  111. info!("Loading XDP program took {:?}", start.elapsed());
  112. println!("press any key to exit");
  113. let mut buf = [0u8];
  114. io::stdin().read_exact(&mut buf)?;
  115. // signal::ctrl_c().await?;
  116. info!("Exiting...");
  117. Ok(())
  118. }