program_array.rs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. //! An array of eBPF program file descriptors used as a jump table.
  2. use std::{
  3. convert::TryFrom,
  4. mem,
  5. ops::{Deref, DerefMut},
  6. os::unix::prelude::RawFd,
  7. };
  8. use crate::{
  9. generated::bpf_map_type::BPF_MAP_TYPE_PROG_ARRAY,
  10. maps::{Map, MapError, MapKeys, MapRef, MapRefMut},
  11. programs::ProgramFd,
  12. sys::{bpf_map_delete_elem, bpf_map_update_elem},
  13. };
  14. /// An array of eBPF program file descriptors used as a jump table.
  15. ///
  16. /// eBPF programs can jump to other programs calling `bpf_tail_call(ctx,
  17. /// prog_array, index)`. You can use [`ProgramArray`] to configure which
  18. /// programs correspond to which jump indexes.
  19. ///
  20. /// # Examples
  21. /// ```no_run
  22. /// # let bpf = aya::Bpf::load(&[], None)?;
  23. /// use aya::maps::ProgramArray;
  24. /// use aya::programs::CgroupSkb;
  25. /// use std::convert::{TryFrom, TryInto};
  26. ///
  27. /// let mut prog_array = ProgramArray::try_from(bpf.map_mut("JUMP_TABLE")?)?;
  28. /// let prog_0: &CgroupSkb = bpf.program("example_prog_0")?.try_into()?;
  29. /// let prog_1: &CgroupSkb = bpf.program("example_prog_1")?.try_into()?;
  30. /// let prog_2: &CgroupSkb = bpf.program("example_prog_2")?.try_into()?;
  31. ///
  32. /// let flags = 0;
  33. ///
  34. /// // bpf_tail_call(ctx, JUMP_TABLE, 0) will jump to prog_0
  35. /// prog_array.set(0, prog_0, flags);
  36. ///
  37. /// // bpf_tail_call(ctx, JUMP_TABLE, 1) will jump to prog_1
  38. /// prog_array.set(1, prog_1, flags);
  39. ///
  40. /// // bpf_tail_call(ctx, JUMP_TABLE, 2) will jump to prog_2
  41. /// prog_array.set(2, prog_2, flags);
  42. /// # Ok::<(), aya::BpfError>(())
  43. /// ```
  44. #[doc(alias = "BPF_MAP_TYPE_PROG_ARRAY")]
  45. pub struct ProgramArray<T: Deref<Target = Map>> {
  46. inner: T,
  47. }
  48. impl<T: Deref<Target = Map>> ProgramArray<T> {
  49. fn new(map: T) -> Result<ProgramArray<T>, MapError> {
  50. let map_type = map.obj.def.map_type;
  51. if map_type != BPF_MAP_TYPE_PROG_ARRAY as u32 {
  52. return Err(MapError::InvalidMapType {
  53. map_type: map_type as u32,
  54. })?;
  55. }
  56. let expected = mem::size_of::<u32>();
  57. let size = map.obj.def.key_size as usize;
  58. if size != expected {
  59. return Err(MapError::InvalidKeySize { size, expected });
  60. }
  61. let expected = mem::size_of::<RawFd>();
  62. let size = map.obj.def.value_size as usize;
  63. if size != expected {
  64. return Err(MapError::InvalidValueSize { size, expected });
  65. }
  66. let _fd = map.fd_or_err()?;
  67. Ok(ProgramArray { inner: map })
  68. }
  69. /// An iterator over the indices of the array that point to a program. The iterator item type
  70. /// is `Result<u32, MapError>`.
  71. pub unsafe fn indices(&self) -> MapKeys<'_, u32> {
  72. MapKeys::new(&self.inner)
  73. }
  74. fn check_bounds(&self, index: u32) -> Result<(), MapError> {
  75. let max_entries = self.inner.obj.def.max_entries;
  76. if index >= self.inner.obj.def.max_entries {
  77. Err(MapError::OutOfBounds { index, max_entries })
  78. } else {
  79. Ok(())
  80. }
  81. }
  82. }
  83. impl<T: Deref<Target = Map> + DerefMut<Target = Map>> ProgramArray<T> {
  84. /// Sets the target program file descriptor for the given index in the jump table.
  85. ///
  86. /// When an eBPF program calls `bpf_tail_call(ctx, prog_array, index)`, control
  87. /// flow will jump to `program`.
  88. pub fn set(&mut self, index: u32, program: &dyn ProgramFd, flags: u64) -> Result<(), MapError> {
  89. let fd = self.inner.fd_or_err()?;
  90. self.check_bounds(index)?;
  91. let prog_fd = program.fd().ok_or(MapError::ProgramNotLoaded)?;
  92. bpf_map_update_elem(fd, &index, &prog_fd, flags).map_err(|(code, io_error)| {
  93. MapError::SyscallError {
  94. call: "bpf_map_update_elem".to_owned(),
  95. code,
  96. io_error,
  97. }
  98. })?;
  99. Ok(())
  100. }
  101. /// Clears the value at index in the jump table.
  102. ///
  103. /// Calling `bpf_tail_call(ctx, prog_array, index)` on an index that has been cleared returns an
  104. /// error.
  105. pub fn clear_index(&mut self, index: &u32) -> Result<(), MapError> {
  106. let fd = self.inner.fd_or_err()?;
  107. self.check_bounds(*index)?;
  108. bpf_map_delete_elem(fd, index)
  109. .map(|_| ())
  110. .map_err(|(code, io_error)| MapError::SyscallError {
  111. call: "bpf_map_delete_elem".to_owned(),
  112. code,
  113. io_error,
  114. })
  115. }
  116. }
  117. impl TryFrom<MapRef> for ProgramArray<MapRef> {
  118. type Error = MapError;
  119. fn try_from(a: MapRef) -> Result<ProgramArray<MapRef>, MapError> {
  120. ProgramArray::new(a)
  121. }
  122. }
  123. impl TryFrom<MapRefMut> for ProgramArray<MapRefMut> {
  124. type Error = MapError;
  125. fn try_from(a: MapRefMut) -> Result<ProgramArray<MapRefMut>, MapError> {
  126. ProgramArray::new(a)
  127. }
  128. }