cgroup_skb.rs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. //! Cgroup skb programs.
  2. use crate::util::KernelVersion;
  3. use std::{
  4. hash::Hash,
  5. os::fd::{AsFd as _, AsRawFd},
  6. path::Path,
  7. };
  8. use crate::{
  9. generated::{
  10. bpf_attach_type::{BPF_CGROUP_INET_EGRESS, BPF_CGROUP_INET_INGRESS},
  11. bpf_prog_type::BPF_PROG_TYPE_CGROUP_SKB,
  12. },
  13. programs::{
  14. define_link_wrapper, load_program, FdLink, Link, ProgAttachLink, ProgramData, ProgramError,
  15. },
  16. sys::{bpf_link_create, bpf_prog_attach, SyscallError},
  17. VerifierLogLevel,
  18. };
  19. /// A program used to inspect or filter network activity for a given cgroup.
  20. ///
  21. /// [`CgroupSkb`] programs can be used to inspect or filter network activity
  22. /// generated on all the sockets belonging to a given [cgroup]. They can be
  23. /// attached to both _ingress_ and _egress_.
  24. ///
  25. /// [cgroup]: https://man7.org/linux/man-pages/man7/cgroups.7.html
  26. ///
  27. /// # Minimum kernel version
  28. ///
  29. /// The minimum kernel version required to use this feature is 4.10.
  30. ///
  31. /// # Examples
  32. ///
  33. /// ```no_run
  34. /// # #[derive(thiserror::Error, Debug)]
  35. /// # enum Error {
  36. /// # #[error(transparent)]
  37. /// # IO(#[from] std::io::Error),
  38. /// # #[error(transparent)]
  39. /// # Map(#[from] aya::maps::MapError),
  40. /// # #[error(transparent)]
  41. /// # Program(#[from] aya::programs::ProgramError),
  42. /// # #[error(transparent)]
  43. /// # Bpf(#[from] aya::BpfError)
  44. /// # }
  45. /// # let mut bpf = aya::Bpf::load(&[])?;
  46. /// use std::fs::File;
  47. /// use aya::programs::{CgroupSkb, CgroupSkbAttachType};
  48. ///
  49. /// let file = File::open("/sys/fs/cgroup/unified")?;
  50. /// let egress: &mut CgroupSkb = bpf.program_mut("egress_filter").unwrap().try_into()?;
  51. /// egress.load()?;
  52. /// egress.attach(file, CgroupSkbAttachType::Egress)?;
  53. /// # Ok::<(), Error>(())
  54. /// ```
  55. #[derive(Debug)]
  56. #[doc(alias = "BPF_PROG_TYPE_CGROUP_SKB")]
  57. pub struct CgroupSkb {
  58. pub(crate) data: ProgramData<CgroupSkbLink>,
  59. pub(crate) expected_attach_type: Option<CgroupSkbAttachType>,
  60. }
  61. impl CgroupSkb {
  62. /// Loads the program inside the kernel.
  63. pub fn load(&mut self) -> Result<(), ProgramError> {
  64. self.data.expected_attach_type =
  65. self.expected_attach_type
  66. .map(|attach_type| match attach_type {
  67. CgroupSkbAttachType::Ingress => BPF_CGROUP_INET_INGRESS,
  68. CgroupSkbAttachType::Egress => BPF_CGROUP_INET_EGRESS,
  69. });
  70. load_program(BPF_PROG_TYPE_CGROUP_SKB, &mut self.data)
  71. }
  72. /// Returns the expected attach type of the program.
  73. ///
  74. /// [`CgroupSkb`] programs can specify the expected attach type in their ELF
  75. /// section name, eg `cgroup_skb/ingress` or `cgroup_skb/egress`. This
  76. /// method returns `None` for programs defined with the generic section
  77. /// `cgroup/skb`.
  78. pub fn expected_attach_type(&self) -> &Option<CgroupSkbAttachType> {
  79. &self.expected_attach_type
  80. }
  81. /// Attaches the program to the given cgroup.
  82. ///
  83. /// The returned value can be used to detach, see [CgroupSkb::detach].
  84. pub fn attach<T: AsRawFd>(
  85. &mut self,
  86. cgroup: T,
  87. attach_type: CgroupSkbAttachType,
  88. ) -> Result<CgroupSkbLinkId, ProgramError> {
  89. let prog_fd = self.fd()?;
  90. let prog_fd = prog_fd.as_fd();
  91. let cgroup_fd = cgroup.as_raw_fd();
  92. let attach_type = match attach_type {
  93. CgroupSkbAttachType::Ingress => BPF_CGROUP_INET_INGRESS,
  94. CgroupSkbAttachType::Egress => BPF_CGROUP_INET_EGRESS,
  95. };
  96. if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) {
  97. let link_fd = bpf_link_create(prog_fd, cgroup_fd, attach_type, None, 0).map_err(
  98. |(_, io_error)| SyscallError {
  99. call: "bpf_link_create",
  100. io_error,
  101. },
  102. )?;
  103. self.data
  104. .links
  105. .insert(CgroupSkbLink::new(CgroupSkbLinkInner::Fd(FdLink::new(
  106. link_fd,
  107. ))))
  108. } else {
  109. bpf_prog_attach(prog_fd, cgroup_fd, attach_type).map_err(|(_, io_error)| {
  110. SyscallError {
  111. call: "bpf_prog_attach",
  112. io_error,
  113. }
  114. })?;
  115. self.data
  116. .links
  117. .insert(CgroupSkbLink::new(CgroupSkbLinkInner::ProgAttach(
  118. ProgAttachLink::new(prog_fd, cgroup_fd, attach_type),
  119. )))
  120. }
  121. }
  122. /// Takes ownership of the link referenced by the provided link_id.
  123. ///
  124. /// The link will be detached on `Drop` and the caller is now responsible
  125. /// for managing its lifetime.
  126. pub fn take_link(&mut self, link_id: CgroupSkbLinkId) -> Result<CgroupSkbLink, ProgramError> {
  127. self.data.take_link(link_id)
  128. }
  129. /// Detaches the program.
  130. ///
  131. /// See [CgroupSkb::attach].
  132. pub fn detach(&mut self, link_id: CgroupSkbLinkId) -> Result<(), ProgramError> {
  133. self.data.links.remove(link_id)
  134. }
  135. /// Creates a program from a pinned entry on a bpffs.
  136. ///
  137. /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`].
  138. ///
  139. /// On drop, any managed links are detached and the program is unloaded. This will not result in
  140. /// the program being unloaded from the kernel if it is still pinned.
  141. pub fn from_pin<P: AsRef<Path>>(
  142. path: P,
  143. expected_attach_type: CgroupSkbAttachType,
  144. ) -> Result<Self, ProgramError> {
  145. let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?;
  146. Ok(Self {
  147. data,
  148. expected_attach_type: Some(expected_attach_type),
  149. })
  150. }
  151. }
  152. #[derive(Debug, Hash, Eq, PartialEq)]
  153. enum CgroupSkbLinkIdInner {
  154. Fd(<FdLink as Link>::Id),
  155. ProgAttach(<ProgAttachLink as Link>::Id),
  156. }
  157. #[derive(Debug)]
  158. enum CgroupSkbLinkInner {
  159. Fd(FdLink),
  160. ProgAttach(ProgAttachLink),
  161. }
  162. impl Link for CgroupSkbLinkInner {
  163. type Id = CgroupSkbLinkIdInner;
  164. fn id(&self) -> Self::Id {
  165. match self {
  166. Self::Fd(fd) => CgroupSkbLinkIdInner::Fd(fd.id()),
  167. Self::ProgAttach(p) => CgroupSkbLinkIdInner::ProgAttach(p.id()),
  168. }
  169. }
  170. fn detach(self) -> Result<(), ProgramError> {
  171. match self {
  172. Self::Fd(fd) => fd.detach(),
  173. Self::ProgAttach(p) => p.detach(),
  174. }
  175. }
  176. }
  177. define_link_wrapper!(
  178. /// The link used by [CgroupSkb] programs.
  179. CgroupSkbLink,
  180. /// The type returned by [CgroupSkb::attach]. Can be passed to [CgroupSkb::detach].
  181. CgroupSkbLinkId,
  182. CgroupSkbLinkInner,
  183. CgroupSkbLinkIdInner
  184. );
  185. /// Defines where to attach a [`CgroupSkb`] program.
  186. #[derive(Copy, Clone, Debug)]
  187. pub enum CgroupSkbAttachType {
  188. /// Attach to ingress.
  189. Ingress,
  190. /// Attach to egress.
  191. Egress,
  192. }