4
0

dev_map.rs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. use core::{cell::UnsafeCell, mem, num::NonZeroU32, ptr::NonNull};
  2. use aya_bpf_bindings::bindings::bpf_devmap_val;
  3. use aya_bpf_cty::c_void;
  4. use crate::{
  5. bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_DEVMAP},
  6. helpers::bpf_map_lookup_elem,
  7. maps::PinningType,
  8. };
  9. use super::try_redirect_map;
  10. /// An array of network devices.
  11. ///
  12. /// XDP programs can use this map to redirect packets to other network deviecs.
  13. ///
  14. /// # Minimum kernel version
  15. ///
  16. /// The minimum kernel version required to use this feature is 4.14.
  17. ///
  18. /// # Examples
  19. ///
  20. /// ```rust,no_run
  21. /// use aya_bpf::{bindings::xdp_action, macros::{map, xdp}, maps::DevMap, programs::XdpContext};
  22. ///
  23. /// #[map]
  24. /// static MAP: DevMap = DevMap::with_max_entries(1, 0);
  25. ///
  26. /// #[xdp]
  27. /// fn xdp(_ctx: XdpContext) -> i32 {
  28. /// MAP.redirect(0, xdp_action::XDP_PASS as u64)
  29. /// }
  30. /// ```
  31. #[repr(transparent)]
  32. pub struct DevMap {
  33. def: UnsafeCell<bpf_map_def>,
  34. }
  35. unsafe impl Sync for DevMap {}
  36. impl DevMap {
  37. /// Creates a [`DevMap`] with a set maximum number of elements.
  38. ///
  39. /// # Examples
  40. ///
  41. /// ```rust,no_run
  42. /// use aya_bpf::{macros::map, maps::DevMap};
  43. ///
  44. /// #[map]
  45. /// static MAP: DevMap = DevMap::with_max_entries(8, 0);
  46. /// ```
  47. pub const fn with_max_entries(max_entries: u32, flags: u32) -> DevMap {
  48. DevMap {
  49. def: UnsafeCell::new(bpf_map_def {
  50. type_: BPF_MAP_TYPE_DEVMAP,
  51. key_size: mem::size_of::<u32>() as u32,
  52. value_size: mem::size_of::<bpf_devmap_val>() as u32,
  53. max_entries,
  54. map_flags: flags,
  55. id: 0,
  56. pinning: PinningType::None as u32,
  57. }),
  58. }
  59. }
  60. /// Creates a [`DevMap`] with a set maximum number of elements that can be pinned to the BPF
  61. /// File System (bpffs).
  62. ///
  63. /// # Examples
  64. ///
  65. /// ```rust,no_run
  66. /// use aya_bpf::{macros::map, maps::DevMap};
  67. ///
  68. /// #[map]
  69. /// static MAP: DevMap = DevMap::pinned(8, 0);
  70. /// ```
  71. pub const fn pinned(max_entries: u32, flags: u32) -> DevMap {
  72. DevMap {
  73. def: UnsafeCell::new(bpf_map_def {
  74. type_: BPF_MAP_TYPE_DEVMAP,
  75. key_size: mem::size_of::<u32>() as u32,
  76. value_size: mem::size_of::<bpf_devmap_val>() as u32,
  77. max_entries,
  78. map_flags: flags,
  79. id: 0,
  80. pinning: PinningType::ByName as u32,
  81. }),
  82. }
  83. }
  84. /// Retrieves the interface index at `index` in the array.
  85. ///
  86. /// To actually redirect a packet, see [`DevMap::redirect`].
  87. ///
  88. /// # Examples
  89. ///
  90. /// ```rust,no_run
  91. /// use aya_bpf::{macros::map, maps::DevMap};
  92. ///
  93. /// #[map]
  94. /// static MAP: DevMap = DevMap::with_max_entries(1, 0);
  95. ///
  96. /// let target_if_index = MAP.get(0).target_if_index;
  97. ///
  98. /// // redirect to if_index
  99. /// ```
  100. #[inline(always)]
  101. pub fn get(&self, index: u32) -> Option<DevMapValue> {
  102. unsafe {
  103. let value = bpf_map_lookup_elem(
  104. self.def.get() as *mut _,
  105. &index as *const _ as *const c_void,
  106. );
  107. NonNull::new(value as *mut bpf_devmap_val).map(|p| DevMapValue {
  108. if_index: p.as_ref().ifindex,
  109. // SAFETY: map writes use fd, map reads use id.
  110. // https://elixir.bootlin.com/linux/v6.2/source/include/uapi/linux/bpf.h#L6136
  111. prog_id: NonZeroU32::new(p.as_ref().bpf_prog.id),
  112. })
  113. }
  114. }
  115. /// Redirects the current packet on the interface at `index`.
  116. ///
  117. /// The lower two bits of `flags` are used for the return code if the map lookup fails, which
  118. /// can be used as the XDP program's return code if a CPU cannot be found.
  119. ///
  120. /// # Examples
  121. ///
  122. /// ```rust,no_run
  123. /// use aya_bpf::{bindings::xdp_action, macros::{map, xdp}, maps::DevMap, programs::XdpContext};
  124. ///
  125. /// #[map]
  126. /// static MAP: DevMap = DevMap::with_max_entries(8, 0);
  127. ///
  128. /// #[xdp]
  129. /// fn xdp(_ctx: XdpContext) -> u32 {
  130. /// MAP.redirect(7, 0).unwrap_or(xdp_action::XDP_DROP)
  131. /// }
  132. /// ```
  133. #[inline(always)]
  134. pub fn redirect(&self, index: u32, flags: u64) -> Result<u32, u32> {
  135. try_redirect_map(&self.def, index, flags)
  136. }
  137. }
  138. #[derive(Clone, Copy)]
  139. /// The value of a device map.
  140. pub struct DevMapValue {
  141. /// Target interface index to redirect to.
  142. pub if_index: u32,
  143. /// Chained XDP program ID.
  144. pub prog_id: Option<NonZeroU32>,
  145. }