expand.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. use proc_macro2::TokenStream;
  2. use quote::quote;
  3. use syn::{
  4. parse::{Parse, ParseStream},
  5. punctuated::{Pair, Punctuated},
  6. token::Eq,
  7. Error, Ident, ItemFn, ItemStatic, LitStr, Result, Token,
  8. };
  9. pub struct NameValue {
  10. name: Ident,
  11. _eq: Eq,
  12. value: LitStr,
  13. }
  14. pub struct Args {
  15. args: Vec<NameValue>,
  16. }
  17. impl Parse for Args {
  18. fn parse(input: ParseStream) -> Result<Args> {
  19. let args = Punctuated::<NameValue, Token![,]>::parse_terminated_with(input, |input| {
  20. Ok(NameValue {
  21. name: input.parse()?,
  22. _eq: input.parse()?,
  23. value: input.parse()?,
  24. })
  25. })?
  26. .into_pairs()
  27. .map(|pair| match pair {
  28. Pair::Punctuated(name_val, _) => name_val,
  29. Pair::End(name_val) => name_val,
  30. })
  31. .collect();
  32. Ok(Args { args })
  33. }
  34. }
  35. pub struct Map {
  36. item: ItemStatic,
  37. name: String,
  38. }
  39. impl Map {
  40. pub fn from_syn(mut args: Args, item: ItemStatic) -> Result<Map> {
  41. let name = name_arg(&mut args)?.unwrap_or_else(|| item.ident.to_string());
  42. Ok(Map { item, name })
  43. }
  44. pub fn expand(&self) -> Result<TokenStream> {
  45. let section_name = "maps".to_string();
  46. let name = &self.name;
  47. let item = &self.item;
  48. Ok(quote! {
  49. #[no_mangle]
  50. #[link_section = #section_name]
  51. #[export_name = #name]
  52. #item
  53. })
  54. }
  55. }
  56. pub struct Probe {
  57. kind: ProbeKind,
  58. item: ItemFn,
  59. name: String,
  60. }
  61. impl Probe {
  62. pub fn from_syn(kind: ProbeKind, mut args: Args, item: ItemFn) -> Result<Probe> {
  63. let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
  64. Ok(Probe { kind, item, name })
  65. }
  66. pub fn expand(&self) -> Result<TokenStream> {
  67. let section_name = format!("{}/{}", self.kind, self.name);
  68. let fn_name = &self.item.sig.ident;
  69. let item = &self.item;
  70. Ok(quote! {
  71. #[no_mangle]
  72. #[link_section = #section_name]
  73. fn #fn_name(ctx: *mut ::core::ffi::c_void) -> u32 {
  74. let _ = #fn_name(::aya_bpf::programs::ProbeContext::new(ctx));
  75. return 0;
  76. #item
  77. }
  78. })
  79. }
  80. }
  81. pub struct SockOps {
  82. item: ItemFn,
  83. name: Option<String>,
  84. }
  85. impl SockOps {
  86. pub fn from_syn(mut args: Args, item: ItemFn) -> Result<SockOps> {
  87. let name = name_arg(&mut args)?;
  88. Ok(SockOps { item, name })
  89. }
  90. pub fn expand(&self) -> Result<TokenStream> {
  91. let section_name = if let Some(name) = &self.name {
  92. format!("sockops/{}", name)
  93. } else {
  94. "sockops".to_owned()
  95. };
  96. let fn_name = &self.item.sig.ident;
  97. let item = &self.item;
  98. Ok(quote! {
  99. #[no_mangle]
  100. #[link_section = #section_name]
  101. fn #fn_name(ctx: *mut ::aya_bpf::bindings::bpf_sock_ops) -> u32 {
  102. return #fn_name(::aya_bpf::programs::SockOpsContext::new(ctx));
  103. #item
  104. }
  105. })
  106. }
  107. }
  108. pub struct SkMsg {
  109. item: ItemFn,
  110. name: String,
  111. }
  112. impl SkMsg {
  113. pub fn from_syn(mut args: Args, item: ItemFn) -> Result<SkMsg> {
  114. let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
  115. Ok(SkMsg { item, name })
  116. }
  117. pub fn expand(&self) -> Result<TokenStream> {
  118. let section_name = format!("sk_msg/{}", self.name);
  119. let fn_name = &self.item.sig.ident;
  120. let item = &self.item;
  121. Ok(quote! {
  122. #[no_mangle]
  123. #[link_section = #section_name]
  124. fn #fn_name(ctx: *mut ::aya_bpf::bindings::sk_msg_md) -> u32 {
  125. return #fn_name(::aya_bpf::programs::SkMsgContext::new(ctx));
  126. #item
  127. }
  128. })
  129. }
  130. }
  131. pub struct Xdp {
  132. item: ItemFn,
  133. name: Option<String>,
  134. }
  135. impl Xdp {
  136. pub fn from_syn(mut args: Args, item: ItemFn) -> Result<Xdp> {
  137. let name = name_arg(&mut args)?;
  138. Ok(Xdp { item, name })
  139. }
  140. pub fn expand(&self) -> Result<TokenStream> {
  141. let section_name = if let Some(name) = &self.name {
  142. format!("xdp/{}", name)
  143. } else {
  144. "xdp".to_owned()
  145. };
  146. let fn_name = &self.item.sig.ident;
  147. let item = &self.item;
  148. Ok(quote! {
  149. #[no_mangle]
  150. #[link_section = #section_name]
  151. fn #fn_name(ctx: *mut ::aya_bpf::bindings::xdp_md) -> u32 {
  152. return #fn_name(::aya_bpf::programs::XdpContext::new(ctx));
  153. #item
  154. }
  155. })
  156. }
  157. }
  158. pub struct SchedClassifier {
  159. item: ItemFn,
  160. name: Option<String>,
  161. }
  162. impl SchedClassifier {
  163. pub fn from_syn(mut args: Args, item: ItemFn) -> Result<SchedClassifier> {
  164. let name = name_arg(&mut args)?;
  165. Ok(SchedClassifier { item, name })
  166. }
  167. pub fn expand(&self) -> Result<TokenStream> {
  168. let section_name = if let Some(name) = &self.name {
  169. format!("classifier/{}", name)
  170. } else {
  171. "classifier".to_owned()
  172. };
  173. let fn_name = &self.item.sig.ident;
  174. let item = &self.item;
  175. Ok(quote! {
  176. #[no_mangle]
  177. #[link_section = #section_name]
  178. fn #fn_name(ctx: *mut ::aya_bpf::bindings::__sk_buff) -> i32 {
  179. return #fn_name(::aya_bpf::programs::SkBuffContext::new(ctx));
  180. #item
  181. }
  182. })
  183. }
  184. }
  185. pub struct CgroupSkb {
  186. item: ItemFn,
  187. expected_attach_type: Option<String>,
  188. name: Option<String>,
  189. }
  190. impl CgroupSkb {
  191. pub fn from_syn(mut args: Args, item: ItemFn) -> Result<CgroupSkb> {
  192. let name = pop_arg(&mut args, "name");
  193. let expected_attach_type = pop_arg(&mut args, "attach");
  194. Ok(CgroupSkb {
  195. item,
  196. expected_attach_type,
  197. name,
  198. })
  199. }
  200. pub fn expand(&self) -> Result<TokenStream> {
  201. let section_name = if let Some(attach) = &self.expected_attach_type {
  202. if let Some(name) = &self.name {
  203. format!("cgroup_skb/{}/{}", attach, name)
  204. } else {
  205. format!("cgroup_skb/{}", attach)
  206. }
  207. } else if let Some(name) = &self.name {
  208. format!("cgroup/skb/{}", name)
  209. } else {
  210. ("cgroup/skb").to_owned()
  211. };
  212. let fn_name = &self.item.sig.ident;
  213. let item = &self.item;
  214. Ok(quote! {
  215. #[no_mangle]
  216. #[link_section = #section_name]
  217. fn #fn_name(ctx: *mut ::aya_bpf::bindings::__sk_buff) -> i32 {
  218. return #fn_name(::aya_bpf::programs::SkBuffContext::new(ctx));
  219. #item
  220. }
  221. })
  222. }
  223. }
  224. fn pop_arg(args: &mut Args, name: &str) -> Option<String> {
  225. match args.args.iter().position(|arg| arg.name == name) {
  226. Some(index) => Some(args.args.remove(index).value.value()),
  227. None => None,
  228. }
  229. }
  230. fn err_on_unknown_args(args: &Args) -> Result<()> {
  231. if let Some(arg) = args.args.get(0) {
  232. return Err(Error::new_spanned(&arg.name, "invalid argument"));
  233. }
  234. Ok(())
  235. }
  236. fn name_arg(args: &mut Args) -> Result<Option<String>> {
  237. let name = pop_arg(args, "name");
  238. err_on_unknown_args(args)?;
  239. Ok(name)
  240. }
  241. #[allow(clippy::enum_variant_names)]
  242. #[derive(Debug, Copy, Clone)]
  243. pub enum ProbeKind {
  244. KProbe,
  245. KRetProbe,
  246. UProbe,
  247. URetProbe,
  248. }
  249. impl std::fmt::Display for ProbeKind {
  250. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  251. use ProbeKind::*;
  252. match self {
  253. KProbe => write!(f, "kprobe"),
  254. KRetProbe => write!(f, "kretprobe"),
  255. UProbe => write!(f, "uprobe"),
  256. URetProbe => write!(f, "uretprobe"),
  257. }
  258. }
  259. }
  260. pub struct TracePoint {
  261. item: ItemFn,
  262. name: String,
  263. }
  264. impl TracePoint {
  265. pub fn from_syn(mut args: Args, item: ItemFn) -> Result<TracePoint> {
  266. let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
  267. Ok(TracePoint { item, name })
  268. }
  269. pub fn expand(&self) -> Result<TokenStream> {
  270. let section_name = format!("tp/{}", self.name);
  271. let fn_name = &self.item.sig.ident;
  272. let item = &self.item;
  273. Ok(quote! {
  274. #[no_mangle]
  275. #[link_section = #section_name]
  276. fn #fn_name(ctx: *mut ::core::ffi::c_void) -> u32 {
  277. let _ = #fn_name(::aya_bpf::programs::TracePointContext::new(ctx));
  278. return 0;
  279. #item
  280. }
  281. })
  282. }
  283. }
  284. pub struct PerfEvent {
  285. item: ItemFn,
  286. name: String,
  287. }
  288. impl PerfEvent {
  289. pub fn from_syn(mut args: Args, item: ItemFn) -> Result<PerfEvent> {
  290. let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
  291. Ok(PerfEvent { item, name })
  292. }
  293. pub fn expand(&self) -> Result<TokenStream> {
  294. let section_name = format!("perf_event/{}", self.name);
  295. let fn_name = &self.item.sig.ident;
  296. let item = &self.item;
  297. Ok(quote! {
  298. #[no_mangle]
  299. #[link_section = #section_name]
  300. fn #fn_name(ctx: *mut ::core::ffi::c_void) -> u32 {
  301. let _ = #fn_name(::aya_bpf::programs::PerfEventContext::new(ctx));
  302. return 0;
  303. #item
  304. }
  305. })
  306. }
  307. }
  308. pub struct RawTracePoint {
  309. item: ItemFn,
  310. name: String,
  311. }
  312. impl RawTracePoint {
  313. pub fn from_syn(mut args: Args, item: ItemFn) -> Result<RawTracePoint> {
  314. let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
  315. Ok(RawTracePoint { item, name })
  316. }
  317. pub fn expand(&self) -> Result<TokenStream> {
  318. let section_name = format!("raw_tp/{}", self.name);
  319. let fn_name = &self.item.sig.ident;
  320. let item = &self.item;
  321. Ok(quote! {
  322. #[no_mangle]
  323. #[link_section = #section_name]
  324. fn #fn_name(ctx: *mut ::core::ffi::c_void) -> u32 {
  325. let _ = #fn_name(::aya_bpf::programs::RawTracePointContext::new(ctx));
  326. return 0;
  327. #item
  328. }
  329. })
  330. }
  331. }
  332. pub struct Lsm {
  333. item: ItemFn,
  334. name: String,
  335. }
  336. impl Lsm {
  337. pub fn from_syn(mut args: Args, item: ItemFn) -> Result<Lsm> {
  338. let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
  339. Ok(Lsm { item, name })
  340. }
  341. pub fn expand(&self) -> Result<TokenStream> {
  342. let section_name = format!("lsm/{}", self.name);
  343. let fn_name = &self.item.sig.ident;
  344. let item = &self.item;
  345. // LSM probes need to return an integer corresponding to the correct
  346. // policy decision. Therefore we do not simply default to a return value
  347. // of 0 as in other program types.
  348. Ok(quote! {
  349. #[no_mangle]
  350. #[link_section = #section_name]
  351. fn #fn_name(ctx: *mut ::core::ffi::c_void) -> i32 {
  352. return #fn_name(::aya_bpf::programs::LsmContext::new(ctx));
  353. #item
  354. }
  355. })
  356. }
  357. }
  358. pub struct BtfTracePoint {
  359. item: ItemFn,
  360. name: String,
  361. }
  362. impl BtfTracePoint {
  363. pub fn from_syn(mut args: Args, item: ItemFn) -> Result<BtfTracePoint> {
  364. let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
  365. Ok(BtfTracePoint { item, name })
  366. }
  367. pub fn expand(&self) -> Result<TokenStream> {
  368. let section_name = format!("tp_btf/{}", self.name);
  369. let fn_name = &self.item.sig.ident;
  370. let item = &self.item;
  371. Ok(quote! {
  372. #[no_mangle]
  373. #[link_section = #section_name]
  374. fn #fn_name(ctx: *mut ::core::ffi::c_void) -> i32 {
  375. let _ = #fn_name(::aya_bpf::programs::BtfTracePointContext::new(ctx));
  376. return 0;
  377. #item
  378. }
  379. })
  380. }
  381. }
  382. #[allow(clippy::enum_variant_names)]
  383. #[derive(Debug, Copy, Clone)]
  384. pub enum SkSkbKind {
  385. StreamVerdict,
  386. StreamParser,
  387. }
  388. impl std::fmt::Display for SkSkbKind {
  389. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  390. use SkSkbKind::*;
  391. match self {
  392. StreamVerdict => write!(f, "stream_verdict"),
  393. StreamParser => write!(f, "stream_parser"),
  394. }
  395. }
  396. }
  397. pub struct SkSkb {
  398. kind: SkSkbKind,
  399. item: ItemFn,
  400. name: Option<String>,
  401. }
  402. impl SkSkb {
  403. pub fn from_syn(kind: SkSkbKind, mut args: Args, item: ItemFn) -> Result<SkSkb> {
  404. let name = pop_arg(&mut args, "name");
  405. Ok(SkSkb { item, kind, name })
  406. }
  407. pub fn expand(&self) -> Result<TokenStream> {
  408. let kind = &self.kind;
  409. let section_name = if let Some(name) = &self.name {
  410. format!("sk_skb/{}/{}", kind, name)
  411. } else {
  412. format!("sk_skb/{}", kind)
  413. };
  414. let fn_name = &self.item.sig.ident;
  415. let item = &self.item;
  416. Ok(quote! {
  417. #[no_mangle]
  418. #[link_section = #section_name]
  419. fn #fn_name(ctx: *mut ::aya_bpf::bindings::__sk_buff) -> u32 {
  420. return #fn_name(::aya_bpf::programs::SkBuffContext::new(ctx));
  421. #item
  422. }
  423. })
  424. }
  425. }
  426. pub struct SocketFilter {
  427. item: ItemFn,
  428. name: Option<String>,
  429. }
  430. impl SocketFilter {
  431. pub fn from_syn(mut args: Args, item: ItemFn) -> Result<SocketFilter> {
  432. let name = name_arg(&mut args)?;
  433. Ok(SocketFilter { item, name })
  434. }
  435. pub fn expand(&self) -> Result<TokenStream> {
  436. let section_name = if let Some(name) = &self.name {
  437. format!("socket/{}", name)
  438. } else {
  439. "socket".to_owned()
  440. };
  441. let fn_name = &self.item.sig.ident;
  442. let item = &self.item;
  443. Ok(quote! {
  444. #[no_mangle]
  445. #[link_section = #section_name]
  446. fn #fn_name(ctx: *mut ::aya_bpf::bindings::__sk_buff) -> i64 {
  447. return #fn_name(::aya_bpf::programs::SkBuffContext::new(ctx));
  448. #item
  449. }
  450. })
  451. }
  452. }
  453. pub struct FEntry {
  454. item: ItemFn,
  455. name: String,
  456. }
  457. impl FEntry {
  458. pub fn from_syn(mut args: Args, item: ItemFn) -> Result<FEntry> {
  459. let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
  460. Ok(FEntry { item, name })
  461. }
  462. pub fn expand(&self) -> Result<TokenStream> {
  463. let section_name = format!("fentry/{}", self.name);
  464. let fn_name = &self.item.sig.ident;
  465. let item = &self.item;
  466. Ok(quote! {
  467. #[no_mangle]
  468. #[link_section = #section_name]
  469. fn #fn_name(ctx: *mut ::core::ffi::c_void) -> i32 {
  470. let _ = #fn_name(::aya_bpf::programs::FEntryContext::new(ctx));
  471. return 0;
  472. #item
  473. }
  474. })
  475. }
  476. }
  477. pub struct FExit {
  478. item: ItemFn,
  479. name: String,
  480. }
  481. impl FExit {
  482. pub fn from_syn(mut args: Args, item: ItemFn) -> Result<FExit> {
  483. let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
  484. Ok(FExit { item, name })
  485. }
  486. pub fn expand(&self) -> Result<TokenStream> {
  487. let section_name = format!("fexit/{}", self.name);
  488. let fn_name = &self.item.sig.ident;
  489. let item = &self.item;
  490. Ok(quote! {
  491. #[no_mangle]
  492. #[link_section = #section_name]
  493. fn #fn_name(ctx: *mut ::core::ffi::c_void) -> i32 {
  494. let _ = #fn_name(::aya_bpf::programs::FExitContext::new(ctx));
  495. return 0;
  496. #item
  497. }
  498. })
  499. }
  500. }
  501. #[cfg(test)]
  502. mod tests {
  503. use syn::parse_quote;
  504. use super::*;
  505. #[test]
  506. fn cgroup_skb_with_attach_and_name() {
  507. let prog = CgroupSkb::from_syn(
  508. parse_quote!(name = "foo", attach = "ingress"),
  509. parse_quote!(
  510. fn foo(ctx: SkBuffContext) -> i32 {
  511. 0
  512. }
  513. ),
  514. )
  515. .unwrap();
  516. let stream = prog.expand().unwrap();
  517. assert!(stream
  518. .to_string()
  519. .contains("[link_section = \"cgroup_skb/ingress/foo\"]"));
  520. }
  521. #[test]
  522. fn cgroup_skb_with_name() {
  523. let prog = CgroupSkb::from_syn(
  524. parse_quote!(name = "foo"),
  525. parse_quote!(
  526. fn foo(ctx: SkBuffContext) -> i32 {
  527. 0
  528. }
  529. ),
  530. )
  531. .unwrap();
  532. let stream = prog.expand().unwrap();
  533. assert!(stream
  534. .to_string()
  535. .contains("[link_section = \"cgroup/skb/foo\"]"));
  536. }
  537. #[test]
  538. fn cgroup_skb_no_name() {
  539. let prog = CgroupSkb::from_syn(
  540. parse_quote!(),
  541. parse_quote!(
  542. fn foo(ctx: SkBuffContext) -> i32 {
  543. 0
  544. }
  545. ),
  546. )
  547. .unwrap();
  548. let stream = prog.expand().unwrap();
  549. assert!(stream
  550. .to_string()
  551. .contains("[link_section = \"cgroup/skb\"]"));
  552. }
  553. #[test]
  554. fn cgroup_skb_with_attach_no_name() {
  555. let prog = CgroupSkb::from_syn(
  556. parse_quote!(attach = "egress"),
  557. parse_quote!(
  558. fn foo(ctx: SkBuffContext) -> i32 {
  559. 0
  560. }
  561. ),
  562. )
  563. .unwrap();
  564. let stream = prog.expand().unwrap();
  565. assert!(stream
  566. .to_string()
  567. .contains("[link_section = \"cgroup_skb/egress\"]"));
  568. }
  569. }