1#![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#[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#[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 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 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 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 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 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 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 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 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#[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#[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#[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#[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#[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#[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#[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, _ => -1, };
681
682 #item
683 }
684 };
685
686 probe_impl("streamparser", attrs, wrapper, name)
687}
688
689#[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#[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#[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}