1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use crate::{
ENOENT, EbpfContext as _,
bindings::bpf_sock_ops,
btf_maps::btf_map_def,
cty::c_long,
helpers::{
bpf_msg_redirect_map, bpf_sk_assign, bpf_sk_redirect_map, bpf_sk_release,
bpf_sock_map_update,
},
lookup,
programs::{SkBuffContext, SkLookupContext, SkMsgContext},
};
btf_map_def!(
/// A BTF-compatible BPF sockmap.
///
/// `SockMap` stores sockets keyed by a `u32` index. Sockets can be
/// inserted from `SOCK_OPS` programs via [`SockMap::update`] or from
/// userspace, and consumed by `SK_SKB`, `SK_MSG`, or `SK_LOOKUP`
/// programs to redirect or assign packets.
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this feature is 4.14.
///
/// # Example
///
/// ```rust
/// use aya_ebpf::{btf_maps::SockMap, macros::btf_map};
///
/// #[btf_map]
/// static SOCKS: SockMap<128> = SockMap::new();
/// ```
pub struct SockMap<; const MAX_ENTRIES: usize, const FLAGS: usize = 0>,
map_type: BPF_MAP_TYPE_SOCKMAP,
max_entries: MAX_ENTRIES,
map_flags: FLAGS,
key_type: u32,
value_type: u32,
);
impl<const MAX_ENTRIES: usize, const FLAGS: usize> SockMap<MAX_ENTRIES, FLAGS> {
// Enforces kernel constraints (kernel/net/core/sock_map.c sock_map_alloc):
// max_entries must be > 0. `const _: ()` is forbidden in a generic impl,
// and a named associated const is lazy without a reference, hence
// `let () = Self::_CHECK` in every method.
const _CHECK: () = {
assert!(
MAX_ENTRIES > 0,
"SockMap max_entries must be greater than zero.",
);
};
/// Inserts the socket from `sk_ops` into the map at `index`.
///
/// Wraps `bpf_sock_map_update`. Intended for `SOCK_OPS` programs.
///
/// # Safety
///
/// `sk_ops` must be a valid pointer to a `bpf_sock_ops` structure,
/// typically the `ops` field of a [`SockOpsContext`]. The kernel
/// guarantees its validity within the program's execution context.
///
/// [`SockOpsContext`]: crate::programs::SockOpsContext
pub unsafe fn update(
&self,
mut index: u32,
sk_ops: *mut bpf_sock_ops,
flags: u64,
) -> Result<(), i32> {
let () = Self::_CHECK;
let ret = unsafe {
bpf_sock_map_update(
sk_ops,
self.as_ptr().cast(),
core::ptr::from_mut(&mut index).cast(),
flags,
)
};
if ret == 0 { Ok(()) } else { Err(ret as i32) }
}
/// Redirects the message in `ctx` to the socket at `index`.
pub fn redirect_msg(&self, ctx: &SkMsgContext, index: u32, flags: u64) -> c_long {
let () = Self::_CHECK;
unsafe { bpf_msg_redirect_map(ctx.as_ptr().cast(), self.as_ptr().cast(), index, flags) }
}
/// Redirects the socket buffer in `ctx` to the socket at `index`.
pub fn redirect_skb(&self, ctx: &SkBuffContext, index: u32, flags: u64) -> c_long {
let () = Self::_CHECK;
unsafe { bpf_sk_redirect_map(ctx.as_ptr().cast(), self.as_ptr().cast(), index, flags) }
}
/// Assigns the socket at `index` as the result of the `SK_LOOKUP` `ctx`.
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this method is
/// [5.9](https://github.com/torvalds/linux/commit/e9ddbb7707ff5891616240026062b8c1e29864ca),
/// when `bpf_sk_assign` was extended to `SK_LOOKUP` programs.
///
/// # Errors
///
/// Returns `Err(-ENOENT)` if `index` is not present in the map. Otherwise
/// propagates the [`bpf_sk_assign`] errno.
///
/// [`bpf_sk_assign`]: https://docs.ebpf.io/linux/helper-function/bpf_sk_assign/
pub fn redirect_sk_lookup(
&self,
ctx: &SkLookupContext,
index: u32,
flags: u64,
) -> Result<(), i32> {
let () = Self::_CHECK;
let sk = lookup(self.as_ptr(), &index).ok_or(-ENOENT)?;
let ret = unsafe { bpf_sk_assign(ctx.as_ptr().cast(), sk.as_ptr(), flags) };
let _: c_long = unsafe { bpf_sk_release(sk.as_ptr()) };
if ret == 0 { Ok(()) } else { Err(ret as i32) }
}
}