xdp.rs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. use std::{net::UdpSocket, num::NonZeroU32, time::Duration};
  2. use aya::{
  3. Ebpf,
  4. maps::{Array, CpuMap, XskMap},
  5. programs::{Xdp, XdpFlags},
  6. };
  7. use object::{Object, ObjectSection, ObjectSymbol, SymbolSection};
  8. use test_log::test;
  9. use xdpilone::{BufIdx, IfInfo, Socket, SocketConfig, Umem, UmemConfig};
  10. use crate::utils::NetNsGuard;
  11. #[test]
  12. fn af_xdp() {
  13. let _netns = NetNsGuard::new();
  14. let mut bpf = Ebpf::load(crate::REDIRECT).unwrap();
  15. let mut socks: XskMap<_> = bpf.take_map("SOCKS").unwrap().try_into().unwrap();
  16. let xdp: &mut Xdp = bpf
  17. .program_mut("redirect_sock")
  18. .unwrap()
  19. .try_into()
  20. .unwrap();
  21. xdp.load().unwrap();
  22. xdp.attach("lo", XdpFlags::default()).unwrap();
  23. const SIZE: usize = 2 * 4096;
  24. // So this needs to be page aligned. Pages are 4k on all mainstream architectures except for
  25. // Apple Silicon which uses 16k pages. So let's align on that for tests to run natively there.
  26. #[repr(C, align(16384))]
  27. struct PageAligned([u8; SIZE]);
  28. let mut alloc = Box::new(PageAligned([0; SIZE]));
  29. let umem = {
  30. let PageAligned(mem) = alloc.as_mut();
  31. let mem = mem.as_mut().into();
  32. // Safety: we cannot access `mem` further down the line because it falls out of scope.
  33. unsafe { Umem::new(UmemConfig::default(), mem).unwrap() }
  34. };
  35. let mut iface = IfInfo::invalid();
  36. iface.from_name(c"lo").unwrap();
  37. let sock = Socket::with_shared(&iface, &umem).unwrap();
  38. let mut fq_cq = umem.fq_cq(&sock).unwrap(); // Fill Queue / Completion Queue
  39. let cfg = SocketConfig {
  40. rx_size: NonZeroU32::new(32),
  41. ..Default::default()
  42. };
  43. let rxtx = umem.rx_tx(&sock, &cfg).unwrap(); // RX + TX Queues
  44. let mut rx = rxtx.map_rx().unwrap();
  45. umem.bind(&rxtx).unwrap();
  46. socks.set(0, rx.as_raw_fd(), 0).unwrap();
  47. let frame = umem.frame(BufIdx(0)).unwrap();
  48. let frame1 = umem.frame(BufIdx(1)).unwrap();
  49. // Produce two frames to be filled by the kernel
  50. let mut writer = fq_cq.fill(2);
  51. writer.insert_once(frame.offset);
  52. writer.insert_once(frame1.offset);
  53. writer.commit();
  54. let sock = UdpSocket::bind("127.0.0.1:0").unwrap();
  55. let port = sock.local_addr().unwrap().port();
  56. sock.send_to(b"hello AF_XDP", "127.0.0.1:1777").unwrap();
  57. assert_eq!(rx.available(), 1);
  58. let desc = rx.receive(1).read().unwrap();
  59. let buf = unsafe {
  60. &frame.addr.as_ref()[desc.addr as usize..(desc.addr as usize + desc.len as usize)]
  61. };
  62. let (eth, buf) = buf.split_at(14);
  63. assert_eq!(eth[12..14], [0x08, 0x00]); // IP
  64. let (ip, buf) = buf.split_at(20);
  65. assert_eq!(ip[9], 17); // UDP
  66. let (udp, payload) = buf.split_at(8);
  67. assert_eq!(&udp[0..2], port.to_be_bytes().as_slice()); // Source
  68. assert_eq!(&udp[2..4], 1777u16.to_be_bytes().as_slice()); // Dest
  69. assert_eq!(payload, b"hello AF_XDP");
  70. assert_eq!(rx.available(), 1);
  71. // Removes socket from map, no more packets will be redirected.
  72. socks.unset(0).unwrap();
  73. assert_eq!(rx.available(), 1);
  74. sock.send_to(b"hello AF_XDP", "127.0.0.1:1777").unwrap();
  75. assert_eq!(rx.available(), 1);
  76. // Adds socket to map again, packets will be redirected again.
  77. socks.set(0, rx.as_raw_fd(), 0).unwrap();
  78. sock.send_to(b"hello AF_XDP", "127.0.0.1:1777").unwrap();
  79. assert_eq!(rx.available(), 2);
  80. }
  81. #[test]
  82. fn prog_sections() {
  83. let obj_file = object::File::parse(crate::XDP_SEC).unwrap();
  84. ensure_symbol(&obj_file, "xdp", "xdp_plain");
  85. ensure_symbol(&obj_file, "xdp.frags", "xdp_frags");
  86. ensure_symbol(&obj_file, "xdp/cpumap", "xdp_cpumap");
  87. ensure_symbol(&obj_file, "xdp/devmap", "xdp_devmap");
  88. ensure_symbol(&obj_file, "xdp.frags/cpumap", "xdp_frags_cpumap");
  89. ensure_symbol(&obj_file, "xdp.frags/devmap", "xdp_frags_devmap");
  90. }
  91. #[track_caller]
  92. fn ensure_symbol(obj_file: &object::File, sec_name: &str, sym_name: &str) {
  93. let sec = obj_file.section_by_name(sec_name).unwrap_or_else(|| {
  94. let secs = obj_file
  95. .sections()
  96. .flat_map(|sec| sec.name().ok().map(|name| name.to_owned()))
  97. .collect::<Vec<_>>();
  98. panic!("section {sec_name} not found. available sections: {secs:?}");
  99. });
  100. let sec = SymbolSection::Section(sec.index());
  101. let syms = obj_file
  102. .symbols()
  103. .filter(|sym| sym.section() == sec)
  104. .filter_map(|sym| sym.name().ok())
  105. .collect::<Vec<_>>();
  106. assert!(
  107. syms.contains(&sym_name),
  108. "symbol not found. available symbols in section: {syms:?}"
  109. );
  110. }
  111. #[test]
  112. fn map_load() {
  113. let bpf = Ebpf::load(crate::XDP_SEC).unwrap();
  114. bpf.program("xdp_plain").unwrap();
  115. bpf.program("xdp_frags").unwrap();
  116. bpf.program("xdp_cpumap").unwrap();
  117. bpf.program("xdp_devmap").unwrap();
  118. bpf.program("xdp_frags_cpumap").unwrap();
  119. bpf.program("xdp_frags_devmap").unwrap();
  120. }
  121. #[test]
  122. fn cpumap_chain() {
  123. let _netns = NetNsGuard::new();
  124. let mut bpf = Ebpf::load(crate::REDIRECT).unwrap();
  125. // Load our cpumap and our canary map
  126. let mut cpus: CpuMap<_> = bpf.take_map("CPUS").unwrap().try_into().unwrap();
  127. let hits: Array<_, u32> = bpf.take_map("HITS").unwrap().try_into().unwrap();
  128. let xdp_chain_fd = {
  129. // Load the chained program to run on the target CPU
  130. let xdp: &mut Xdp = bpf
  131. .program_mut("redirect_cpu_chain")
  132. .unwrap()
  133. .try_into()
  134. .unwrap();
  135. xdp.load().unwrap();
  136. xdp.fd().unwrap()
  137. };
  138. cpus.set(0, 2048, Some(xdp_chain_fd), 0).unwrap();
  139. // Load the main program
  140. let xdp: &mut Xdp = bpf.program_mut("redirect_cpu").unwrap().try_into().unwrap();
  141. xdp.load().unwrap();
  142. xdp.attach("lo", XdpFlags::default()).unwrap();
  143. const PAYLOAD: &str = "hello cpumap";
  144. let sock = UdpSocket::bind("127.0.0.1:0").unwrap();
  145. let addr = sock.local_addr().unwrap();
  146. sock.set_read_timeout(Some(Duration::from_secs(60)))
  147. .unwrap();
  148. sock.send_to(PAYLOAD.as_bytes(), addr).unwrap();
  149. // Read back the packet to ensure it went through the entire network stack, including our two
  150. // probes.
  151. let mut buf = [0u8; PAYLOAD.len() + 1];
  152. let n = sock.recv(&mut buf).unwrap();
  153. assert_eq!(&buf[..n], PAYLOAD.as_bytes());
  154. assert_eq!(hits.get(&0, 0).unwrap(), 1);
  155. assert_eq!(hits.get(&1, 0).unwrap(), 1);
  156. }