4
0

expand.rs 17 KB

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