aya/programs/
sk_msg.rs

1//! Skmsg programs.
2
3use std::os::fd::AsFd as _;
4
5use crate::{
6    generated::{bpf_attach_type::BPF_SK_MSG_VERDICT, bpf_prog_type::BPF_PROG_TYPE_SK_MSG},
7    maps::sock::SockMapFd,
8    programs::{
9        define_link_wrapper, load_program, CgroupAttachMode, ProgAttachLink, ProgAttachLinkId,
10        ProgramData, ProgramError,
11    },
12};
13
14/// A program used to intercept messages sent with `sendmsg()`/`sendfile()`.
15///
16/// [`SkMsg`] programs are attached to [socket maps], and can be used inspect,
17/// filter and redirect messages sent on sockets. See also [`SockMap`] and
18/// [`SockHash`].
19///
20/// # Minimum kernel version
21///
22/// The minimum kernel version required to use this feature is 4.17.
23///
24/// # Examples
25///
26/// ```no_run
27/// # #[derive(Debug, thiserror::Error)]
28/// # enum Error {
29/// #     #[error(transparent)]
30/// #     IO(#[from] std::io::Error),
31/// #     #[error(transparent)]
32/// #     Map(#[from] aya::maps::MapError),
33/// #     #[error(transparent)]
34/// #     Program(#[from] aya::programs::ProgramError),
35/// #     #[error(transparent)]
36/// #     Ebpf(#[from] aya::EbpfError)
37/// # }
38/// # let mut bpf = aya::Ebpf::load(&[])?;
39/// use std::io::Write;
40/// use std::net::TcpStream;
41/// use std::os::fd::AsRawFd;
42/// use aya::maps::SockHash;
43/// use aya::programs::SkMsg;
44///
45/// let intercept_egress: SockHash<_, u32> = bpf.map("INTERCEPT_EGRESS").unwrap().try_into()?;
46/// let map_fd = intercept_egress.fd().try_clone()?;
47///
48/// let prog: &mut SkMsg = bpf.program_mut("intercept_egress_packet").unwrap().try_into()?;
49/// prog.load()?;
50/// prog.attach(&map_fd)?;
51///
52/// let mut client = TcpStream::connect("127.0.0.1:1234")?;
53/// let mut intercept_egress: SockHash<_, u32> = bpf.map_mut("INTERCEPT_EGRESS").unwrap().try_into()?;
54///
55/// intercept_egress.insert(1234, client.as_raw_fd(), 0)?;
56///
57/// // the write will be intercepted
58/// client.write_all(b"foo")?;
59/// # Ok::<(), Error>(())
60/// ```
61///
62/// [socket maps]: crate::maps::sock
63/// [`SockMap`]: crate::maps::SockMap
64/// [`SockHash`]: crate::maps::SockHash
65#[derive(Debug)]
66#[doc(alias = "BPF_PROG_TYPE_SK_MSG")]
67pub struct SkMsg {
68    pub(crate) data: ProgramData<SkMsgLink>,
69}
70
71impl SkMsg {
72    /// Loads the program inside the kernel.
73    pub fn load(&mut self) -> Result<(), ProgramError> {
74        load_program(BPF_PROG_TYPE_SK_MSG, &mut self.data)
75    }
76
77    /// Attaches the program to the given sockmap.
78    ///
79    /// The returned value can be used to detach, see [SkMsg::detach].
80    pub fn attach(&mut self, map: &SockMapFd) -> Result<SkMsgLinkId, ProgramError> {
81        let prog_fd = self.fd()?;
82        let prog_fd = prog_fd.as_fd();
83        let link = ProgAttachLink::attach(
84            prog_fd,
85            map.as_fd(),
86            BPF_SK_MSG_VERDICT,
87            CgroupAttachMode::Single,
88        )?;
89
90        self.data.links.insert(SkMsgLink::new(link))
91    }
92
93    /// Detaches the program from a sockmap.
94    ///
95    /// See [SkMsg::attach].
96    pub fn detach(&mut self, link_id: SkMsgLinkId) -> Result<(), ProgramError> {
97        self.data.links.remove(link_id)
98    }
99
100    /// Takes ownership of the link referenced by the provided link_id.
101    ///
102    /// The link will be detached on `Drop` and the caller is now responsible
103    /// for managing its lifetime.
104    pub fn take_link(&mut self, link_id: SkMsgLinkId) -> Result<SkMsgLink, ProgramError> {
105        self.data.take_link(link_id)
106    }
107}
108
109define_link_wrapper!(
110    /// The link used by [SkMsg] programs.
111    SkMsgLink,
112    /// The type returned by [SkMsg::attach]. Can be passed to [SkMsg::detach].
113    SkMsgLinkId,
114    ProgAttachLink,
115    ProgAttachLinkId
116);