redbpf_macros/
lib.rs

1// Copyright 2019 Authors of Red Sift
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8/*!
9Procedural macros to help writing eBPF programs using the `redbpf-probes`
10crate.
11
12# Overview
13
14`redbpf-macros` is part of the `redbpf` project. Together with
15[`redbpf-probes`](../../redbpf_probes/), it provides an idiomatic Rust API to
16write programs that can be compiled to eBPF bytecode and executed by the linux
17in-kernel eBPF virtual machine.
18
19To streamline the process of working with eBPF programs even further, `redbpf`
20also provides [`cargo-bpf`](../../cargo_bpf/) - a cargo subcommand to simplify
21creating and building eBPF programs.
22
23# Example
24
25```no_run
26#![no_std]
27#![no_main]
28use redbpf_probes::xdp::prelude::*;
29
30// configure kernel version compatibility and license
31program!(0xFFFFFFFE, "GPL");
32
33#[xdp]
34fn example_xdp_probe(ctx: XdpContext) -> XdpResult {
35
36    // do something here
37
38    Ok(XdpAction::Pass)
39}
40```
41*/
42
43#![cfg_attr(RUSTC_IS_NIGHTLY, feature(proc_macro_diagnostic))]
44
45extern crate proc_macro;
46extern crate proc_macro2;
47use proc_macro::TokenStream;
48use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
49use quote::quote;
50use std::str;
51use syn::parse::{Parse, ParseStream};
52use syn::punctuated::Punctuated;
53use syn::token::Comma;
54use syn::{
55    parse_macro_input, parse_quote, AttributeArgs, Expr, ExprLit, GenericArgument, ItemFn,
56    ItemStatic, Lit, Meta, NestedMeta, PathArguments, Result, Type,
57};
58use uuid::Uuid;
59
60fn inline_string_literal(e: &Expr) -> (TokenStream2, TokenStream2) {
61    let bytes = match e {
62        Expr::Lit(ExprLit {
63            lit: Lit::Str(s), ..
64        }) => s.value().into_bytes(),
65        _ => panic!("expected string literal"),
66    };
67
68    inline_bytes(bytes)
69}
70
71fn inline_bytes(mut bytes: Vec<u8>) -> (TokenStream2, TokenStream2) {
72    bytes.push(0u8);
73    let len = bytes.len();
74    let bytes = bytes;
75    let ty = quote!([u8; #len]);
76    let array_lit = quote!([#(#bytes),*]);
77
78    (ty, array_lit)
79}
80
81struct Args(Punctuated<Expr, Comma>);
82
83impl Parse for Args {
84    fn parse(input: ParseStream) -> Result<Args> {
85        Ok(Args(Punctuated::parse_terminated(input)?))
86    }
87}
88
89/// Generates program metadata.
90///
91/// Takes two arguments, the `LINUX_VERSION_CODE` the program is compatible with,
92/// and the license. The special version code `0xFFFFFFFE` can be used to signify
93/// any kernel version.
94///
95/// # Example
96///
97/// ```no_run
98/// #![no_std]
99/// #![no_main]
100/// use redbpf_macros::program;
101/// program!(0xFFFFFFFE, "GPL");
102/// # fn main() {}
103/// ```
104///
105#[proc_macro]
106pub fn program(input: TokenStream) -> TokenStream {
107    let input = parse_macro_input!(input as Args);
108    let mut args = input.0.iter();
109    let version = args.next().expect("no version");
110    let license = args.next().expect("no license");
111    let (license_ty, license) = inline_string_literal(&license);
112    let (panic_ty, panic_msg) = inline_bytes(b"panic".to_vec());
113    let tokens = quote! {
114        #[no_mangle]
115        #[link_section = "license"]
116        pub static _license: #license_ty = #license;
117
118        #[no_mangle]
119        #[link_section = "version"]
120        pub static _version: u32 = #version;
121
122        #[panic_handler]
123        #[no_mangle]
124        pub extern "C" fn rust_begin_panic(info: &::core::panic::PanicInfo) -> ! {
125            use ::redbpf_probes::helpers::{bpf_trace_printk};
126
127            let msg: #panic_ty = #panic_msg;
128            bpf_trace_printk(&msg);
129
130            unsafe { core::hint::unreachable_unchecked() }
131        }
132    };
133
134    tokens.into()
135}
136
137#[doc(hidden)]
138#[proc_macro]
139pub fn impl_network_buffer_array(_: TokenStream) -> TokenStream {
140    let mut tokens = TokenStream2::new();
141    for i in 1..=512usize {
142        tokens.extend(quote! {
143            impl NetworkBufferArray for [u8; #i] {}
144        });
145    }
146
147    tokens.into()
148}
149
150/// Attribute macro that must be used when creating [eBPF
151/// maps](../../redbpf_probes/maps/index.html).
152///
153/// The default `#[map]` places the map into a section of the resulting
154/// ELF binary called `maps/<item_name>`.
155///
156/// If you wish to set the section name manually for BPF programs that
157/// require strict naming conventions use `#[map(link_section = "foo")]`
158/// which place the map into a section called `foo`.
159///
160/// # Example
161///
162/// ```no_run
163/// # use redbpf_probes::kprobe::prelude::*;
164/// // Will be linked into the ELF in the section 'maps/counts'
165/// #[map]
166/// static mut counts: PerfMap<u64> = PerfMap::with_max_entries(10240);
167///
168/// // Will be linked into the ELF in the section 'dns_queries'
169/// #[map(link_section = "dns_queries")]
170/// static mut queries: PerfMap<Query> = PerfMap::with_max_entries(1024);
171///
172/// struct Query {
173/// // ...
174/// }
175/// ```
176#[proc_macro_attribute]
177pub fn map(attrs: TokenStream, item: TokenStream) -> TokenStream {
178    let mut link_section: Option<String> = None;
179    for attr in parse_macro_input!(attrs as AttributeArgs) {
180        let mut allowed = false;
181        match attr {
182            NestedMeta::Meta(meta) => {
183                if let Meta::NameValue(mnv) = meta {
184                    if let Some(id) = mnv.path.get_ident() {
185                        // In case of #[map(link_section = "...", something_else = "...")]
186                        match id.to_string().as_str() {
187                            "link_section" => {
188                                if let Lit::Str(name) = mnv.lit {
189                                    if link_section.is_some() {
190                                        panic!(
191                                            "#[map(link_section = \"...\")] is used more than once"
192                                        );
193                                    }
194                                    link_section = Some(name.value());
195                                    allowed = true;
196                                }
197                            }
198                            _ => panic!("expected `link_section' as metadata of #[map]"),
199                        }
200                    }
201                }
202            }
203            NestedMeta::Lit(lit) => {
204                // panic if #[map("...")] is declared
205                if let Lit::Str(name) = lit {
206                    panic!("expected #[map(link_section = \"maps/{}\")]", name.value());
207                }
208            }
209        }
210
211        if !allowed {
212            panic!("expected #[map(link_section = \"...\")]");
213        }
214    }
215    let static_item = {
216        let item = item.clone();
217        parse_macro_input!(item as ItemStatic)
218    };
219    let section_name = link_section.unwrap_or_else(|| {
220        // In case of just #[map] without any metadata
221        format!("maps/{}", static_item.ident.to_string())
222    });
223    let mut tokens = {
224        let item = TokenStream2::from(item);
225        quote! {
226            #[no_mangle]
227            #[link_section = #section_name]
228            #item
229        }
230    };
231
232    let mut tc_compatible = false;
233    let mut key_type: Option<GenericArgument> = None;
234    let mut value_type: Option<GenericArgument> = None;
235    if let Type::Path(path) = *static_item.ty {
236        if let Some(seg) = path.path.segments.last() {
237            let map_type_name = seg.ident.to_string();
238            if let PathArguments::AngleBracketed(bracket) = &seg.arguments {
239                // <K, V> or <V>
240                match map_type_name.as_str() {
241                    "Array" | "PerCpuArray" => {
242                        if bracket.args.len() == 1 {
243                            key_type = Some(parse_quote!(u32));
244                            value_type = Some(bracket.args.first().unwrap().clone());
245                        }
246                    }
247                    "HashMap" | "PerCpuHashMap" | "LruHashMap" | "LruPerCpuHashMap"
248                    | "TcHashMap" => {
249                        if bracket.args.len() == 2 {
250                            key_type = Some(bracket.args.first().unwrap().clone());
251                            value_type = Some(bracket.args.last().unwrap().clone());
252                        }
253                    }
254                    "PerfMap" => {}
255                    _ => {
256                        panic!("unknown map type name: {}", map_type_name);
257                    }
258                }
259
260                if map_type_name == "TcHashMap" {
261                    tc_compatible = true;
262                }
263            } else {
264                // without generic types
265                match map_type_name.as_str() {
266                    "StackTrace" | "SockMap" | "ProgramArray" | "DevMap" => {}
267                    _ => {
268                        panic!("unknown map type name: {}", map_type_name);
269                    }
270                }
271            }
272        }
273    }
274    if key_type.is_some() && value_type.is_some() {
275        let mod_name = format!("_{}", Uuid::new_v4().to_simple().to_string());
276        let mod_ident = syn::Ident::new(&mod_name, static_item.ident.span());
277        let ktype = key_type.unwrap();
278        let vtype = value_type.unwrap();
279        // CAUTION: When you change the names (MAP_BTF_XXXX and
280        // MAP_VALUE_ALIGN_XXXX) you should consider changing corresponding
281        // parts that use them.
282        let map_btf_name = format!("MAP_BTF_{}", static_item.ident.to_string());
283        let map_btf_ident = syn::Ident::new(&map_btf_name, static_item.ident.span());
284        let value_align_name = format!("MAP_VALUE_ALIGN_{}", static_item.ident.to_string());
285        let value_align_ident = syn::Ident::new(&value_align_name, static_item.ident.span());
286        if tc_compatible {
287            let btf_type_name = format!("____btf_map_{}", static_item.ident.to_string());
288            let btf_map_type = syn::Ident::new(&btf_type_name, static_item.ident.span());
289            tokens.extend(quote! {
290                mod #mod_ident {
291                    #[allow(unused_imports)]
292                    use super::*;
293                    use core::mem::{self, MaybeUninit};
294
295                    #[no_mangle]
296                    static #value_align_ident: MaybeUninit<#vtype> = MaybeUninit::uninit();
297
298                    #[repr(C)]
299                    struct #btf_map_type {
300                        key: #ktype,
301                        value: #vtype,
302                    }
303                    // `impl Sync` is needed to allow pointer types of keys and values
304                    unsafe impl Sync for #btf_map_type {}
305                    const N: usize = mem::size_of::<#btf_map_type>();
306                    #[no_mangle]
307                    #[link_section = "maps.ext"]
308                    static #map_btf_ident: #btf_map_type = unsafe { mem::transmute::<[u8; N], #btf_map_type>([0u8; N]) };
309                }
310            });
311        } else {
312            tokens.extend(quote! {
313                mod #mod_ident {
314                    #[allow(unused_imports)]
315                    use super::*;
316                    use core::mem::{self, MaybeUninit};
317
318                    #[no_mangle]
319                    static #value_align_ident: MaybeUninit<#vtype> = MaybeUninit::uninit();
320
321                    #[repr(C)]
322                    struct MapBtf {
323                        key_type: #ktype,
324                        value_type: #vtype,
325                    }
326                    // `impl Sync` is needed to allow pointer types of keys and values
327                    unsafe impl Sync for MapBtf {}
328                    const N: usize = mem::size_of::<MapBtf>();
329                    #[no_mangle]
330                    #[link_section = "maps.ext"]
331                    static #map_btf_ident: MapBtf = unsafe { mem::transmute::<[u8; N], MapBtf>([0u8; N]) };
332                }
333            });
334        }
335    }
336    tokens.into()
337}
338
339fn probe_impl(ty: &str, attrs: TokenStream, item: ItemFn, mut name: String) -> TokenStream {
340    if !attrs.is_empty() {
341        name = match parse_macro_input!(attrs as Expr) {
342            Expr::Lit(ExprLit {
343                lit: Lit::Str(s), ..
344            }) => s.value(),
345            _ => panic!("expected string literal"),
346        }
347    };
348
349    let section_name = format!("{}/{}", ty, name);
350    let tokens = quote! {
351        #[no_mangle]
352        #[link_section = #section_name]
353        #item
354    };
355
356    tokens.into()
357}
358
359fn probe_pair_impl(pre: &str, attrs: TokenStream, item: ItemFn, mut name: String) -> TokenStream {
360    if !attrs.is_empty() {
361        name = match parse_macro_input!(attrs as Expr) {
362            Expr::Lit(ExprLit {
363                lit: Lit::Str(s), ..
364            }) => s.value(),
365            _ => panic!("expected string literal"),
366        }
367    };
368
369    let ident = item.sig.ident.clone();
370    let map_ident = Ident::new(&format!("PARMS_{}", ident), Span::call_site());
371    let enter_ident = Ident::new(&format!("enter_{}", ident), Span::call_site());
372    let exit_ident = Ident::new(&format!("exit_{}", ident), Span::call_site());
373    let probe_ident = Ident::new(&format!("{}probe", pre), Span::call_site());
374    let retprobe_ident = Ident::new(&format!("{}retprobe", pre), Span::call_site());
375
376    let tokens = quote! {
377        #[map]
378        static mut #map_ident: HashMap<u64, [u64; 5]> = HashMap::with_max_entries(10240);
379
380        #[#probe_ident(#name)]
381        fn #enter_ident(regs: Registers) {
382            let pid_tgid = bpf_get_current_pid_tgid();
383            let parms = [regs.parm1(), regs.parm2(), regs.parm3(), regs.parm4(), regs.parm5()];
384            unsafe {
385                #map_ident.set(&pid_tgid, &parms);
386            }
387        }
388
389        #[#retprobe_ident(#name)]
390        fn #exit_ident(regs: Registers) {
391            let pid_tgid = bpf_get_current_pid_tgid();
392            let parms = unsafe {
393                match #map_ident.get(&pid_tgid) {
394                    Some(parms) => {
395                        let parms = *parms;
396                        #map_ident.delete(&pid_tgid);
397                        parms
398                    }
399                    None => return,
400                }
401            };
402            let _ = unsafe { #ident(regs, parms) };
403        }
404
405        #item
406    };
407
408    tokens.into()
409}
410
411fn wrap_kprobe(item: ItemFn) -> ItemFn {
412    let ident = item.sig.ident.clone();
413    let outer_ident = Ident::new(&format!("outer_{}", ident), Span::call_site());
414    parse_quote! {
415        fn #outer_ident(ctx: *mut c_void) -> i32 {
416            let regs = ::redbpf_probes::registers::Registers::from(ctx);
417            let _ = unsafe { #ident(regs) };
418            return 0;
419
420            #item
421        }
422    }
423}
424
425/// Attribute macro that must be used to define [`kprobes`](https://www.kernel.org/doc/Documentation/kprobes.txt).
426///
427/// # Example
428/// ```no_run
429/// use redbpf_probes::kprobe::prelude::*;
430///
431/// #[kprobe("__x64_sys_clone")]
432/// fn clone_enter(regs: Registers) {
433///     // this is executed when clone() is invoked
434/// }
435/// ```
436#[proc_macro_attribute]
437pub fn kprobe(attrs: TokenStream, item: TokenStream) -> TokenStream {
438    let item = parse_macro_input!(item as ItemFn);
439    let name = item.sig.ident.to_string();
440    let wrapper = wrap_kprobe(item);
441    probe_impl("kprobe", attrs, wrapper, name)
442}
443
444/// Attribute macro that must be used to define [`kretprobes`](https://www.kernel.org/doc/Documentation/kprobes.txt).
445///
446/// # Example
447///
448/// ```no_run
449/// use redbpf_probes::kprobe::prelude::*;
450///
451/// #[kretprobe("__x64_sys_clone")]
452/// fn clone_exit(regs: Registers) {
453///     // this is executed when clone() returns
454/// }
455/// ```
456///
457/// # Function parameters
458///
459/// In general, the `parmX` methods of the `regs` argument do **not**
460/// return the original parameter values that the function being probed
461/// was called with. The reason is that those parameters are passed
462/// via (architecture-dependent) general purpose registers, and the
463/// function code most likely overwrites some or all of those registers.
464/// RedBPF provides a convenient way to access original function parameters
465/// by declaring the retprobe with an additional array argument that
466/// receives function parameters 1-5:
467///
468/// ```no_run
469/// use redbpf_probes::kprobe::prelude::*;
470///
471/// #[kretprobe("__x64_sys_clone")]
472/// fn clone_exit(regs: Registers, parms: [u64; 5]) {
473///     // this is executed when clone() returns
474/// }
475/// ```
476///
477/// To make this possible, RedBPF generates a global map, and an entry probe
478/// corresponding to the retprobe which stores the original parameters
479/// in that map. A generated retprobe wrapper retrieves the parameters from
480/// the map, and calls the provided function with the parameter array as
481/// an argument.
482///
483/// Note that if no parameters for the current thread are found in the map
484/// (for example because the capacity of the map has been exhausted, or
485/// the retprobe was registered after the function had already been entered),
486/// **the retprobe is not called** for that function invocation.
487#[proc_macro_attribute]
488pub fn kretprobe(attrs: TokenStream, item: TokenStream) -> TokenStream {
489    let item = parse_macro_input!(item as ItemFn);
490    let name = item.sig.ident.to_string();
491    if item.sig.inputs.len() == 2 {
492        return probe_pair_impl("k", attrs, item, name);
493    }
494    let wrapper = wrap_kprobe(item);
495    probe_impl("kretprobe", attrs, wrapper, name)
496}
497
498/// Attribute macro that must be used to define [`uprobes`](https://www.kernel.org/doc/Documentation/trace/uprobetracer.txt).
499///
500/// # Example
501/// ```no_run
502/// use redbpf_probes::uprobe::prelude::*;
503///
504/// #[uprobe]
505/// fn getaddrinfo(regs: Registers) {
506///     // this is executed when getaddrinfo() is invoked
507/// }
508/// ```
509#[proc_macro_attribute]
510pub fn uprobe(attrs: TokenStream, item: TokenStream) -> TokenStream {
511    let item = parse_macro_input!(item as ItemFn);
512    let name = item.sig.ident.to_string();
513    let wrapper = wrap_kprobe(item);
514    probe_impl("uprobe", attrs, wrapper, name)
515}
516
517/// Attribute macro that must be used to define [`uretprobes`](https://www.kernel.org/doc/Documentation/trace/uprobetracer.txt).
518///
519/// # Example
520///
521/// ```no_run
522/// use redbpf_probes::uprobe::prelude::*;
523///
524/// #[uretprobe]
525/// fn getaddrinfo(regs: Registers) {
526///     // this is executed when getaddrinfo() returns
527/// }
528/// ```
529///
530/// # Function parameters
531///
532/// In general, the `parmX` methods of the `regs` argument do **not**
533/// return the original parameter values that the function being probed
534/// was called with. The reason is that those parameters are passed
535/// via (architecture-dependent) general purpose registers, and the
536/// function code most likely overwrites some or all of those registers.
537/// RedBPF provides a convenient way to access original function parameters
538/// by declaring the retprobe with an additional array argument that
539/// receives function parameters 1-5:
540///
541/// ```no_run
542/// use redbpf_probes::uprobe::prelude::*;
543///
544/// #[uretprobe]
545/// fn getaddrinfo(regs: Registers, parms: [u64; 5]) {
546///     // this is executed when getaddrinfo() returns
547/// }
548/// ```
549///
550/// To make this possible, RedBPF generates a global map, and an entry probe
551/// corresponding to the retprobe which stores the original parameters
552/// in that map. A generated retprobe wrapper retrieves the parameters from
553/// the map, and calls the provided function with the parameter array as
554/// an argument.
555///
556/// Note that if no parameters for the current thread are found in the map
557/// (for example because the capacity of the map has been exhausted, or
558/// the retprobe was registered after the function had already been entered),
559/// **the retprobe is not called** for that function invocation.
560#[proc_macro_attribute]
561pub fn uretprobe(attrs: TokenStream, item: TokenStream) -> TokenStream {
562    let item = parse_macro_input!(item as ItemFn);
563    let name = item.sig.ident.to_string();
564    if item.sig.inputs.len() == 2 {
565        return probe_pair_impl("u", attrs, item, name);
566    }
567    let wrapper = wrap_kprobe(item);
568    probe_impl("uretprobe", attrs, wrapper, name)
569}
570
571/// Attribute macro that must be used to define [`XDP` probes](https://www.iovisor.org/technology/xdp).
572///
573/// See also the [`XDP` API provided by
574/// `redbpf-probes`](../../redbpf_probes/xdp/index.html).
575///
576/// # Example
577/// ```no_run
578/// use redbpf_probes::xdp::prelude::*;
579///
580/// #[xdp]
581/// fn probe(ctx: XdpContext) -> XdpResult {
582///     // do something with the packet
583///
584///     Ok(XdpAction::Pass)
585/// }
586/// ```
587#[proc_macro_attribute]
588pub fn xdp(attrs: TokenStream, item: TokenStream) -> TokenStream {
589    let item = parse_macro_input!(item as ItemFn);
590    let name = item.sig.ident.to_string();
591    let ident = item.sig.ident.clone();
592    let outer_ident = Ident::new(&format!("outer_{}", ident), Span::call_site());
593    let wrapper = parse_quote! {
594        fn #outer_ident(ctx: *mut ::redbpf_probes::bindings::xdp_md) -> ::redbpf_probes::xdp::XdpAction {
595            let ctx = ::redbpf_probes::xdp::XdpContext { ctx };
596            return match unsafe { #ident(ctx) } {
597                Ok(action) => action,
598                Err(_) => ::redbpf_probes::xdp::XdpAction::Pass
599            };
600
601            #item
602        }
603    };
604    probe_impl("xdp", attrs, wrapper, name)
605}
606
607/// Attribute macro that must be used to define [`socket
608/// filter`](https://www.kernel.org/doc/Documentation/networking/filter.txt)
609/// probes.
610///
611/// See also the [`socket filter` API provided by
612/// `redbpf-probes`](../../api/redbpf_probes/socket_filter/index.html).
613///
614/// # Example
615/// ```no_run
616/// use redbpf_probes::socket_filter::prelude::*;
617///
618/// #[socket_filter]
619/// fn probe(skb: SkBuff) -> SkBuffResult {
620///     Ok(SkBuffAction::SendToUserspace)
621/// }
622/// ```
623#[proc_macro_attribute]
624pub fn socket_filter(attrs: TokenStream, item: TokenStream) -> TokenStream {
625    let item = parse_macro_input!(item as ItemFn);
626    let name = item.sig.ident.to_string();
627    let ident = item.sig.ident.clone();
628    let outer_ident = Ident::new(&format!("outer_{}", ident), Span::call_site());
629    let wrapper = parse_quote! {
630        fn #outer_ident(skb: *const ::redbpf_probes::bindings::__sk_buff) -> i32 {
631            let skb = ::redbpf_probes::socket_filter::SkBuff { skb };
632            return match unsafe { #ident(skb) } {
633                Ok(::redbpf_probes::socket_filter::SkBuffAction::SendToUserspace) => -1,
634                _ => 0
635            };
636
637            #item
638        }
639    };
640
641    probe_impl("socketfilter", attrs, wrapper, name)
642}
643
644/// Attribute macro for defining BPF programs of `stream parser`s. A `sockmap`
645/// can be attached to the stream parser. The role of stream parsers is to find
646/// a message boundary of TCP stream and return the length of a message. If it
647/// returns proper length of a message then a `stream verdict` BPF program will
648/// be called.
649///
650/// # Example
651/// ```no_run
652/// use core::ptr;
653/// use memoffset::offset_of;
654/// use redbpf_probes::sockmap::prelude::*;
655///
656/// #[stream_parser]
657/// fn parse_message_boundary(skb: SkBuff) -> StreamParserResult {
658///     let len: u32 = unsafe {
659///         let addr = (skb.skb as usize + offset_of!(__sk_buff, len)) as *const u32;
660///         ptr::read(addr)
661///     };
662///     Ok(StreamParserAction::MessageLength(len))
663/// }
664/// ```
665#[proc_macro_attribute]
666pub fn stream_parser(attrs: TokenStream, item: TokenStream) -> TokenStream {
667    let item = parse_macro_input!(item as ItemFn);
668    let name = item.sig.ident.to_string();
669    let ident = item.sig.ident.clone();
670    let outer_ident = Ident::new(&format!("outer_{}", ident), Span::call_site());
671    let wrapper = parse_quote! {
672        fn #outer_ident(skb: *const ::redbpf_probes::bindings::__sk_buff) -> i32 {
673            let skb = ::redbpf_probes::socket::SkBuff { skb };
674            use ::redbpf_probes::sockmap::StreamParserAction::*;
675            return match unsafe { #ident(skb) } {
676                Ok(MessageLength(len)) if len > 0 => len as i32,
677                Ok(MoreDataWanted) => 0,
678                Ok(SendToUserspace) => -86,  // -ESTRPIPE
679                _ => -1,  // error
680            };
681
682            #item
683        }
684    };
685
686    probe_impl("streamparser", attrs, wrapper, name)
687}
688
689/// Attribute macro for defining BPF programs of `stream verdict`s. A `sockmap`
690/// can be attached to the stream verdict. The role of stream verdicts is to
691/// predicate to which socket a message should be redirected.
692///
693/// # Example
694/// ```no_run
695/// use redbpf_probes::sockmap::prelude::*;
696/// #[map(link_section = "maps/echo_sockmap")]
697/// static mut SOCKMAP: SockMap = SockMap::with_max_entries(10240);
698///
699/// #[stream_verdict]
700/// fn verdict(skb: SkBuff) -> SkAction {
701///     match unsafe { SOCKMAP.redirect(skb.skb as *mut _, 0) } {
702///         Ok(_) => SkAction::Pass,
703///         Err(_) => SkAction::Drop,
704///     }
705/// }
706/// ```
707#[proc_macro_attribute]
708pub fn stream_verdict(attrs: TokenStream, item: TokenStream) -> TokenStream {
709    let item = parse_macro_input!(item as ItemFn);
710    let name = item.sig.ident.to_string();
711    let ident = item.sig.ident.clone();
712    let outer_ident = Ident::new(&format!("outer_{}", ident), Span::call_site());
713    let wrapper = parse_quote! {
714        fn #outer_ident(skb: *const ::redbpf_probes::bindings::__sk_buff) -> i32 {
715            let skb = ::redbpf_probes::socket::SkBuff { skb };
716            use ::redbpf_probes::socket::SkAction;
717
718            return match unsafe { #ident(skb) } {
719                SkAction::Pass => ::redbpf_probes::bindings::sk_action_SK_PASS,
720                SkAction::Drop => ::redbpf_probes::bindings::sk_action_SK_DROP,
721            } as i32;
722
723            #item
724        }
725    };
726
727    probe_impl("streamverdict", attrs, wrapper, name)
728}
729
730/// Define [tc action BPF programs](https://man7.org/linux/man-pages/man8/tc-bpf.8.html)
731#[proc_macro_attribute]
732pub fn tc_action(attrs: TokenStream, item: TokenStream) -> TokenStream {
733    let item = parse_macro_input!(item as ItemFn);
734    let name = item.sig.ident.to_string();
735    let ident = item.sig.ident.clone();
736    let outer_ident = Ident::new(&format!("outer_{}", ident), Span::call_site());
737    let wrapper = parse_quote! {
738        fn #outer_ident(skb: *const ::redbpf_probes::bindings::__sk_buff) -> i32 {
739            let skb = ::redbpf_probes::socket::SkBuff { skb };
740            return match unsafe { #ident(skb) } {
741                Ok(act) => act as i32,
742                Err(_) => -1
743            };
744
745            #item
746        }
747    };
748
749    probe_impl("tc_action", attrs, wrapper, name)
750}
751
752/// Attribute macro for defining a BPF iterator of `task`
753#[proc_macro_attribute]
754pub fn task_iter(attrs: TokenStream, item: TokenStream) -> TokenStream {
755    let item = parse_macro_input!(item as ItemFn);
756    let name = item.sig.ident.to_string();
757    let ident = item.sig.ident.clone();
758    let outer_ident = Ident::new(&format!("outer_{}", ident), Span::call_site());
759    let wrapper = parse_quote! {
760        fn #outer_ident(ctx: *mut bpf_iter__task) -> i32 {
761            let task_ctx = ::redbpf_probes::bpf_iter::context::TaskIterContext { ctx };
762            return match unsafe { #ident(task_ctx) } {
763                BPFIterAction::Ok => 0,
764                BPFIterAction::Retry => 1,
765            };
766
767            #item
768        }
769    };
770
771    probe_impl("task_iter", attrs, wrapper, name)
772}