aya_ebpf_macros/
lib.rs

1#![cfg_attr(test, expect(unused_crate_dependencies, reason = "used in doctests"))]
2
3pub(crate) mod args;
4mod btf_map;
5mod btf_tracepoint;
6mod cgroup_device;
7mod cgroup_skb;
8mod cgroup_sock;
9mod cgroup_sock_addr;
10mod cgroup_sockopt;
11mod cgroup_sysctl;
12mod fentry;
13mod fexit;
14mod flow_dissector;
15mod kprobe;
16mod lsm;
17mod lsm_cgroup;
18mod map;
19mod perf_event;
20mod raw_tracepoint;
21mod sk_lookup;
22mod sk_msg;
23mod sk_skb;
24mod sock_ops;
25mod socket_filter;
26mod tc;
27mod tracepoint;
28mod uprobe;
29mod xdp;
30
31use btf_map::BtfMap;
32use btf_tracepoint::BtfTracePoint;
33use cgroup_device::CgroupDevice;
34use cgroup_skb::CgroupSkb;
35use cgroup_sock::CgroupSock;
36use cgroup_sock_addr::CgroupSockAddr;
37use cgroup_sockopt::CgroupSockopt;
38use cgroup_sysctl::CgroupSysctl;
39use fentry::FEntry;
40use fexit::FExit;
41use flow_dissector::FlowDissector;
42use kprobe::{KProbe, KProbeKind};
43use lsm::Lsm;
44use lsm_cgroup::LsmCgroup;
45use map::Map;
46use perf_event::PerfEvent;
47use proc_macro::TokenStream;
48use raw_tracepoint::RawTracePoint;
49use sk_lookup::SkLookup;
50use sk_msg::SkMsg;
51use sk_skb::{SkSkb, SkSkbKind};
52use sock_ops::SockOps;
53use socket_filter::SocketFilter;
54use tc::SchedClassifier;
55use tracepoint::TracePoint;
56use uprobe::{UProbe, UProbeKind};
57use xdp::Xdp;
58
59#[proc_macro_attribute]
60pub fn btf_map(attrs: TokenStream, item: TokenStream) -> TokenStream {
61    match BtfMap::parse(attrs.into(), item.into()) {
62        Ok(prog) => prog.expand(),
63        Err(err) => err.into_compile_error(),
64    }
65    .into()
66}
67
68#[proc_macro_attribute]
69pub fn map(attrs: TokenStream, item: TokenStream) -> TokenStream {
70    match Map::parse(attrs.into(), item.into()) {
71        Ok(prog) => prog.expand(),
72        Err(err) => err.into_compile_error(),
73    }
74    .into()
75}
76#[proc_macro_attribute]
77pub fn kprobe(attrs: TokenStream, item: TokenStream) -> TokenStream {
78    match KProbe::parse(KProbeKind::KProbe, attrs.into(), item.into()) {
79        Ok(prog) => prog.expand(),
80        Err(err) => err.emit_as_expr_tokens(),
81    }
82    .into()
83}
84#[proc_macro_attribute]
85pub fn kretprobe(attrs: TokenStream, item: TokenStream) -> TokenStream {
86    match KProbe::parse(KProbeKind::KRetProbe, attrs.into(), item.into()) {
87        Ok(prog) => prog.expand(),
88        Err(err) => err.emit_as_expr_tokens(),
89    }
90    .into()
91}
92#[proc_macro_attribute]
93pub fn uprobe(attrs: TokenStream, item: TokenStream) -> TokenStream {
94    match UProbe::parse(UProbeKind::UProbe, attrs.into(), item.into()) {
95        Ok(prog) => match prog.expand() {
96            Ok(tokens) => tokens,
97            Err(err) => err.emit_as_expr_tokens(),
98        },
99        Err(err) => err.emit_as_expr_tokens(),
100    }
101    .into()
102}
103#[proc_macro_attribute]
104pub fn uretprobe(attrs: TokenStream, item: TokenStream) -> TokenStream {
105    match UProbe::parse(UProbeKind::URetProbe, attrs.into(), item.into()) {
106        Ok(prog) => match prog.expand() {
107            Ok(tokens) => tokens,
108            Err(err) => err.emit_as_expr_tokens(),
109        },
110        Err(err) => err.emit_as_expr_tokens(),
111    }
112    .into()
113}
114#[proc_macro_attribute]
115pub fn sock_ops(attrs: TokenStream, item: TokenStream) -> TokenStream {
116    match SockOps::parse(attrs.into(), item.into()) {
117        Ok(prog) => prog.expand(),
118        Err(err) => err.emit_as_expr_tokens(),
119    }
120    .into()
121}
122
123#[proc_macro_attribute]
124pub fn sk_msg(attrs: TokenStream, item: TokenStream) -> TokenStream {
125    match SkMsg::parse(attrs.into(), item.into()) {
126        Ok(prog) => prog.expand(),
127        Err(err) => err.emit_as_expr_tokens(),
128    }
129    .into()
130}
131
132/// Marks a function as an eBPF XDP program that can be attached to a network interface.
133///
134/// On some NIC drivers, XDP probes are compatible with jumbo frames through the use of
135/// multi-buffer packets. Programs can opt-in this support by passing the `frags` argument.
136///
137/// XDP programs can also be chained through the use of CPU maps and dev maps, but must opt-in
138/// with the `map = "cpumap"` or `map = "devmap"` arguments.
139///
140/// # Minimum kernel version
141///
142/// The minimum kernel version required to use this feature is 4.8.
143///
144/// # Examples
145///
146/// ```no_run
147/// use aya_ebpf::{bindings::xdp_action::XDP_PASS, macros::xdp, programs::XdpContext};
148///
149/// #[xdp(frags)]
150/// pub fn xdp(ctx: XdpContext) -> u32 {
151///     XDP_PASS
152/// }
153/// ```
154#[proc_macro_attribute]
155pub fn xdp(attrs: TokenStream, item: TokenStream) -> TokenStream {
156    match Xdp::parse(attrs.into(), item.into()) {
157        Ok(prog) => prog.expand(),
158        Err(err) => err.emit_as_expr_tokens(),
159    }
160    .into()
161}
162#[proc_macro_attribute]
163pub fn classifier(attrs: TokenStream, item: TokenStream) -> TokenStream {
164    match SchedClassifier::parse(attrs.into(), item.into()) {
165        Ok(prog) => prog.expand(),
166        Err(err) => err.emit_as_expr_tokens(),
167    }
168    .into()
169}
170
171#[proc_macro_attribute]
172pub fn cgroup_sysctl(attrs: TokenStream, item: TokenStream) -> TokenStream {
173    match CgroupSysctl::parse(attrs.into(), item.into()) {
174        Ok(prog) => prog.expand(),
175        Err(err) => err.emit_as_expr_tokens(),
176    }
177    .into()
178}
179#[proc_macro_attribute]
180pub fn cgroup_sockopt(attrs: TokenStream, item: TokenStream) -> TokenStream {
181    match CgroupSockopt::parse(attrs.into(), item.into()) {
182        Ok(prog) => prog.expand(),
183        Err(err) => err.emit_as_expr_tokens(),
184    }
185    .into()
186}
187#[proc_macro_attribute]
188pub fn cgroup_skb(attrs: TokenStream, item: TokenStream) -> TokenStream {
189    match CgroupSkb::parse(attrs.into(), item.into()) {
190        Ok(prog) => prog.expand(),
191        Err(err) => err.emit_as_expr_tokens(),
192    }
193    .into()
194}
195
196/// Marks a function as a [`CgroupSockAddr`] eBPF program.
197///
198/// [`CgroupSockAddr`] programs can be used to inspect or modify socket addresses passed to
199/// various syscalls within a [cgroup]. The `attach_type` argument specifies a place to attach
200/// the eBPF program to. See [`CgroupSockAddrAttachType`] for more details.
201///
202/// [cgroup]: https://man7.org/linux/man-pages/man7/cgroups.7.html
203/// [`CgroupSockAddrAttachType`]: ../aya/programs/cgroup_sock_addr/enum.CgroupSockAddrAttachType.html
204/// [`CgroupSockAddr`]: ../aya/programs/cgroup_sock_addr/struct.CgroupSockAddr.html
205///
206/// # Minimum kernel version
207///
208/// The minimum kernel version required to use this feature is 4.17.
209///
210/// # Examples
211///
212/// ```no_run
213/// use aya_ebpf::{macros::cgroup_sock_addr, programs::SockAddrContext};
214///
215/// #[cgroup_sock_addr(connect4)]
216/// pub fn connect4(ctx: SockAddrContext) -> i32 {
217///     match try_connect4(ctx) {
218///         Ok(ret) => ret,
219///         Err(ret) => ret,
220///     }
221/// }
222///
223/// fn try_connect4(ctx: SockAddrContext) -> Result<i32, i32> {
224///     Ok(0)
225/// }
226/// ```
227#[proc_macro_attribute]
228pub fn cgroup_sock_addr(attrs: TokenStream, item: TokenStream) -> TokenStream {
229    match CgroupSockAddr::parse(attrs.into(), item.into()) {
230        Ok(prog) => prog.expand(),
231        Err(err) => err.emit_as_expr_tokens(),
232    }
233    .into()
234}
235#[proc_macro_attribute]
236pub fn cgroup_sock(attrs: TokenStream, item: TokenStream) -> TokenStream {
237    match CgroupSock::parse(attrs.into(), item.into()) {
238        Ok(prog) => prog.expand(),
239        Err(err) => err.emit_as_expr_tokens(),
240    }
241    .into()
242}
243
244#[proc_macro_attribute]
245pub fn tracepoint(attrs: TokenStream, item: TokenStream) -> TokenStream {
246    match TracePoint::parse(attrs.into(), item.into()) {
247        Ok(prog) => prog.expand(),
248        Err(err) => err.emit_as_expr_tokens(),
249    }
250    .into()
251}
252
253#[proc_macro_attribute]
254pub fn perf_event(attrs: TokenStream, item: TokenStream) -> TokenStream {
255    match PerfEvent::parse(attrs.into(), item.into()) {
256        Ok(prog) => prog.expand(),
257        Err(err) => err.emit_as_expr_tokens(),
258    }
259    .into()
260}
261
262/// Marks a function as a raw tracepoint eBPF program that can be attached at a
263/// pre-defined kernel trace point.
264///
265/// The kernel provides a set of pre-defined trace points that eBPF programs can
266/// be attached to. See `/sys/kernel/debug/tracing/events` for a list of which
267/// events can be traced.
268///
269/// # Minimum kernel version
270///
271/// The minimum kernel version required to use this feature is 4.7.
272///
273/// # Examples
274///
275/// ```no_run
276/// use aya_ebpf::{macros::raw_tracepoint, programs::RawTracePointContext};
277///
278/// #[raw_tracepoint(tracepoint = "sys_enter")]
279/// pub fn sys_enter(ctx: RawTracePointContext) -> i32 {
280///     match unsafe { try_sys_enter(ctx) } {
281///         Ok(ret) => ret,
282///         Err(ret) => ret,
283///     }
284/// }
285///
286/// unsafe fn try_sys_enter(_ctx: RawTracePointContext) -> Result<i32, i32> {
287///     Ok(0)
288/// }
289/// ```
290#[proc_macro_attribute]
291pub fn raw_tracepoint(attrs: TokenStream, item: TokenStream) -> TokenStream {
292    match RawTracePoint::parse(attrs.into(), item.into()) {
293        Ok(prog) => prog.expand(),
294        Err(err) => err.into_compile_error(),
295    }
296    .into()
297}
298
299/// Marks a function as an LSM program that can be attached to Linux LSM hooks.
300/// Used to implement security policy and audit logging.
301///
302/// The hook name is the first argument to the macro.
303/// You may also provide `sleepable` to mark the program as sleepable.
304/// Arguments should be comma separated.
305///
306/// LSM probes can be attached to the kernel's security hooks to implement mandatory
307/// access control policy and security auditing.
308///
309/// LSM probes require a kernel compiled with `CONFIG_BPF_LSM=y` and `CONFIG_DEBUG_INFO_BTF=y`.
310/// In order for the probes to fire, you also need the BPF LSM to be enabled through your
311/// kernel's boot parameters (like `lsm=lockdown,yama,bpf`).
312///
313/// # Minimum kernel version
314///
315/// The minimum kernel version required to use this feature is 5.7.
316///
317/// # Examples
318///
319/// ```no_run
320/// use aya_ebpf::{macros::lsm, programs::LsmContext};
321///
322/// #[lsm(hook = "file_open")]
323/// pub fn file_open(ctx: LsmContext) -> i32 {
324///     match unsafe { try_file_open(ctx) } {
325///         Ok(ret) => ret,
326///         Err(ret) => ret,
327///     }
328/// }
329///
330/// unsafe fn try_file_open(_ctx: LsmContext) -> Result<i32, i32> {
331///     Ok(0)
332/// }
333/// ```
334#[proc_macro_attribute]
335pub fn lsm(attrs: TokenStream, item: TokenStream) -> TokenStream {
336    match Lsm::parse(attrs.into(), item.into()) {
337        Ok(prog) => prog.expand(),
338        Err(err) => err.into_compile_error(),
339    }
340    .into()
341}
342
343/// Marks a function as an LSM program that can be attached to cgroups.
344/// This program will only trigger for workloads in the attached cgroups.
345/// Used to implement security policy and audit logging.
346///
347/// The hook name is the first and only argument to the macro.
348///
349/// LSM probes can be attached to the kernel's security hooks to implement mandatory
350/// access control policy and security auditing.
351///
352/// LSM probes require a kernel compiled with `CONFIG_BPF_LSM=y` and `CONFIG_DEBUG_INFO_BTF=y`.
353/// In order for the probes to fire, you also need the BPF LSM to be enabled through your
354/// kernel's boot parameters (like `lsm=lockdown,yama,bpf`).
355///
356/// # Minimum kernel version
357///
358/// The minimum kernel version required to use this feature is 6.0.
359///
360/// # Examples
361///
362/// ```no_run
363/// use aya_ebpf::{macros::lsm_cgroup, programs::LsmContext};
364///
365/// #[lsm_cgroup(hook = "file_open")]
366/// pub fn file_open(ctx: LsmContext) -> i32 {
367///     match unsafe { try_file_open(ctx) } {
368///         Ok(ret) => ret,
369///         Err(ret) => ret,
370///     }
371/// }
372///
373/// unsafe fn try_file_open(_ctx: LsmContext) -> Result<i32, i32> {
374///     Err(0)
375/// }
376/// ```
377#[proc_macro_attribute]
378pub fn lsm_cgroup(attrs: TokenStream, item: TokenStream) -> TokenStream {
379    match LsmCgroup::parse(attrs.into(), item.into()) {
380        Ok(prog) => prog.expand(),
381        Err(err) => err.into_compile_error(),
382    }
383    .into()
384}
385
386/// Marks a function as a [BTF-enabled raw tracepoint][1] eBPF program that can be attached at
387/// a pre-defined kernel trace point.
388///
389/// The kernel provides a set of pre-defined trace points that eBPF programs can
390/// be attached to. See `/sys/kernel/debug/tracing/events` for a list of which
391/// events can be traced.
392///
393/// # Minimum kernel version
394///
395/// The minimum kernel version required to use this feature is 5.5.
396///
397/// # Examples
398///
399/// ```no_run
400/// use aya_ebpf::{macros::btf_tracepoint, programs::BtfTracePointContext};
401///
402/// #[btf_tracepoint(function = "sched_process_fork")]
403/// pub fn sched_process_fork(ctx: BtfTracePointContext) -> u32 {
404///     match unsafe { try_sched_process_fork(ctx) } {
405///         Ok(ret) => ret,
406///         Err(ret) => ret,
407///     }
408/// }
409///
410/// unsafe fn try_sched_process_fork(_ctx: BtfTracePointContext) -> Result<u32, u32> {
411///     Ok(0)
412/// }
413/// ```
414///
415/// [1]: https://github.com/torvalds/linux/commit/9e15db66136a14cde3f35691f1d839d950118826
416#[proc_macro_attribute]
417pub fn btf_tracepoint(attrs: TokenStream, item: TokenStream) -> TokenStream {
418    match BtfTracePoint::parse(attrs.into(), item.into()) {
419        Ok(prog) => prog.expand(),
420        Err(err) => err.into_compile_error(),
421    }
422    .into()
423}
424
425/// Marks a function as a SK_SKB Stream Parser eBPF program that can be attached
426/// to a SockMap
427///
428/// # Minimum kernel version
429///
430/// The minimum kernel version required to use this feature is 4.14
431///
432/// # Examples
433///
434/// ```no_run
435/// use aya_ebpf::{macros::stream_parser, programs::SkBuffContext};
436///
437///
438///#[stream_parser]
439///fn stream_parser(ctx: SkBuffContext) -> u32 {
440///    match try_stream_parser(ctx) {
441///        Ok(ret) => ret,
442///        Err(ret) => ret,
443///    }
444///}
445///
446///fn try_stream_parser(ctx: SkBuffContext) -> Result<u32, u32> {
447///    Ok(ctx.len())
448///}
449/// ```
450#[proc_macro_attribute]
451pub fn stream_parser(attrs: TokenStream, item: TokenStream) -> TokenStream {
452    sk_skb(SkSkbKind::StreamParser, attrs, item)
453}
454
455/// Marks a function as a SK_SKB Stream Verdict eBPF program that can be attached
456/// to a SockMap
457///
458/// # Minimum kernel version
459///
460/// The minimum kernel version required to use this feature is 4.14
461///
462/// # Examples
463///
464/// ```no_run
465/// use aya_ebpf::{macros::stream_verdict, programs::SkBuffContext, bindings::sk_action};
466///
467///
468///#[stream_verdict]
469///fn stream_verdict(ctx: SkBuffContext) -> u32 {
470///    match try_stream_verdict(ctx) {
471///        Ok(ret) => ret,
472///        Err(ret) => ret,
473///    }
474///}
475///
476///fn try_stream_verdict(_ctx: SkBuffContext) -> Result<u32, u32> {
477///    Ok(sk_action::SK_PASS)
478///}
479/// ```
480#[proc_macro_attribute]
481pub fn stream_verdict(attrs: TokenStream, item: TokenStream) -> TokenStream {
482    sk_skb(SkSkbKind::StreamVerdict, attrs, item)
483}
484
485fn sk_skb(kind: SkSkbKind, attrs: TokenStream, item: TokenStream) -> TokenStream {
486    match SkSkb::parse(kind, attrs.into(), item.into()) {
487        Ok(prog) => prog.expand(),
488        Err(err) => err.emit_as_expr_tokens(),
489    }
490    .into()
491}
492
493/// Marks a function as a eBPF Socket Filter program that can be attached to
494/// a socket.
495///
496/// # Minimum kernel version
497///
498/// The minimum kernel version required to use this feature is 3.19
499///
500/// # Examples
501///
502/// ```no_run
503/// use aya_ebpf::{macros::socket_filter, programs::SkBuffContext};
504///
505/// #[socket_filter]
506/// pub fn accept_all(_ctx: SkBuffContext) -> i64 {
507///     0
508/// }
509/// ```
510#[proc_macro_attribute]
511pub fn socket_filter(attrs: TokenStream, item: TokenStream) -> TokenStream {
512    match SocketFilter::parse(attrs.into(), item.into()) {
513        Ok(prog) => prog.expand(),
514        Err(err) => err.emit_as_expr_tokens(),
515    }
516    .into()
517}
518
519/// Marks a function as a fentry eBPF program that can be attached to almost
520/// any function inside the kernel. The difference between fentry and kprobe
521/// is that fexit has practically zero overhead to call before kernel function.
522/// fentry programs can be also attached to other eBPF programs.
523///
524/// # Minimumm kernel version
525///
526/// The minimum kernel version required to use this feature is 5.5.
527///
528/// # Examples
529///
530/// ```no_run
531/// use aya_ebpf::{macros::fentry, programs::FEntryContext};
532/// # #[expect(non_camel_case_types)]
533/// # type filename = u32;
534/// # #[expect(non_camel_case_types)]
535/// # type path = u32;
536///
537/// #[fentry(function = "filename_lookup")]
538/// fn filename_lookup(ctx: FEntryContext) -> i32 {
539///     match unsafe { try_filename_lookup(ctx) } {
540///         Ok(ret) => ret,
541///         Err(ret) => ret,
542///     }
543/// }
544///
545/// unsafe fn try_filename_lookup(ctx: FEntryContext) -> Result<i32, i32> {
546///     let _f: *const filename = ctx.arg(1);
547///     let _p: *const path = ctx.arg(3);
548///
549///     Ok(0)
550/// }
551/// ```
552#[proc_macro_attribute]
553pub fn fentry(attrs: TokenStream, item: TokenStream) -> TokenStream {
554    match FEntry::parse(attrs.into(), item.into()) {
555        Ok(prog) => prog.expand(),
556        Err(err) => err.into_compile_error(),
557    }
558    .into()
559}
560
561/// Marks a function as a fexit eBPF program that can be attached to almost
562/// any function inside the kernel. The difference between fexit and kretprobe
563/// is that fexit has practically zero overhead to call after kernel function
564/// and it focuses on access to arguments rather than the return value. fexit
565/// programs can be also attached to other eBPF programs
566///
567/// # Minimumm kernel version
568///
569/// The minimum kernel version required to use this feature is 5.5.
570///
571/// # Examples
572///
573/// ```no_run
574/// use aya_ebpf::{macros::fexit, programs::FExitContext};
575/// # #[expect(non_camel_case_types)]
576/// # type filename = u32;
577/// # #[expect(non_camel_case_types)]
578/// # type path = u32;
579///
580/// #[fexit(function = "filename_lookup")]
581/// fn filename_lookup(ctx: FExitContext) -> i32 {
582///     match unsafe { try_filename_lookup(ctx) } {
583///         Ok(ret) => ret,
584///         Err(ret) => ret,
585///     }
586/// }
587///
588/// unsafe fn try_filename_lookup(ctx: FExitContext) -> Result<i32, i32> {
589///     let _f: *const filename = ctx.arg(1);
590///     let _p: *const path = ctx.arg(3);
591///
592///     Ok(0)
593/// }
594/// ```
595#[proc_macro_attribute]
596pub fn fexit(attrs: TokenStream, item: TokenStream) -> TokenStream {
597    match FExit::parse(attrs.into(), item.into()) {
598        Ok(prog) => prog.expand(),
599        Err(err) => err.into_compile_error(),
600    }
601    .into()
602}
603
604/// Marks a function as an eBPF Flow Dissector program.
605///
606/// Flow dissector is a program type that parses metadata out of the packets.
607///
608/// BPF flow dissectors can be attached per network namespace. These programs
609/// are given a packet and expected to populate the fields of
610/// `FlowDissectorContext::flow_keys`. The return code of the BPF program is
611/// either [`BPF_OK`] to indicate successful dissection, [`BPF_DROP`] to
612/// indicate parsing error, or [`BPF_FLOW_DISSECTOR_CONTINUE`] to indicate that
613/// no custom dissection was performed, and fallback to standard dissector is
614/// requested.
615///
616/// # Minimum kernel version
617///
618/// The minimum kernel version required to use this feature is 4.20.
619///
620/// # Examples
621///
622/// ```no_run
623/// use aya_ebpf::{bindings::bpf_ret_code, macros::flow_dissector, programs::FlowDissectorContext};
624///
625/// #[flow_dissector]
626/// pub fn dissect(_ctx: FlowDissectorContext) -> u32 {
627///     // TODO: do something useful here.
628///     bpf_ret_code::BPF_FLOW_DISSECTOR_CONTINUE
629/// }
630/// ```
631///
632/// [`FlowDissectorContext::flow_keys`]: ../aya_ebpf/programs/flow_dissector/struct.FlowDissectorContext.html#method.flow_keys
633/// [`BPF_OK`]: ../aya_ebpf/bindings/bpf_ret_code/constant.bpf_ok
634/// [`BPF_DROP`]: ../aya_ebpf/bindings/bpf_ret_code/constant.bpf_drop
635/// [`BPF_FLOW_DISSECTOR_CONTINUE`]: ../aya_ebpf/bindings/bpf_ret_code/constant.bpf_flow_dissector_continue
636#[proc_macro_attribute]
637pub fn flow_dissector(attrs: TokenStream, item: TokenStream) -> TokenStream {
638    match FlowDissector::parse(attrs.into(), item.into()) {
639        Ok(prog) => prog.expand(),
640        Err(err) => err.emit_as_expr_tokens(),
641    }
642    .into()
643}
644
645/// Marks a function as an eBPF Socket Lookup program that can be attached to
646/// a network namespace.
647///
648/// # Minimum kernel version
649///
650/// The minimum kernel version required to use this feature is 5.9
651///
652/// # Examples
653///
654/// ```no_run
655/// use aya_ebpf::{macros::sk_lookup, programs::SkLookupContext};
656///
657/// #[sk_lookup]
658/// pub fn accept_all(_ctx: SkLookupContext) -> u32 {
659///     // use sk_assign to redirect
660///     0
661/// }
662/// ```
663#[proc_macro_attribute]
664pub fn sk_lookup(attrs: TokenStream, item: TokenStream) -> TokenStream {
665    match SkLookup::parse(attrs.into(), item.into()) {
666        Ok(prog) => prog.expand(),
667        Err(err) => err.emit_as_expr_tokens(),
668    }
669    .into()
670}
671
672/// Marks a function as a cgroup device eBPF program that can be attached to a
673/// cgroup.
674///
675/// # Minimum kernel version
676///
677/// The minimum kernel version required to use this feature is 4.15.
678///
679/// # Examples
680///
681/// ```no_run
682/// use aya_ebpf::{
683///     macros::cgroup_device,
684///     programs::DeviceContext,
685/// };
686///
687/// #[cgroup_device]
688/// pub fn cgroup_dev(ctx: DeviceContext) -> i32 {
689///     // Reject all device access
690///     0
691/// }
692/// ```
693#[proc_macro_attribute]
694pub fn cgroup_device(attrs: TokenStream, item: TokenStream) -> TokenStream {
695    match CgroupDevice::parse(attrs.into(), item.into()) {
696        Ok(prog) => prog.expand(),
697        Err(err) => err.emit_as_expr_tokens(),
698    }
699    .into()
700}