1 //! Query the host about BPF 2 //! 3 //! For example, to list the name of every bpf program running on the system: 4 //! ``` 5 //! use libbpf_rs::query::ProgInfoIter; 6 //! 7 //! let mut iter = ProgInfoIter::default(); 8 //! for prog in iter { 9 //! println!("{}", prog.name.to_string_lossy()); 10 //! } 11 //! ``` 12 13 use std::ffi::c_void; 14 use std::ffi::CString; 15 use std::io; 16 use std::mem::size_of_val; 17 use std::os::fd::AsFd; 18 use std::os::fd::AsRawFd; 19 use std::os::fd::BorrowedFd; 20 use std::os::fd::FromRawFd; 21 use std::os::fd::OwnedFd; 22 use std::os::raw::c_char; 23 use std::ptr; 24 use std::time::Duration; 25 26 use crate::util; 27 use crate::MapType; 28 use crate::ProgramAttachType; 29 use crate::ProgramType; 30 use crate::Result; 31 32 macro_rules! gen_info_impl { 33 // This magic here allows us to embed doc comments into macro expansions 34 ($(#[$attr:meta])* 35 $name:ident, $info_ty:ty, $uapi_info_ty:ty, $next_id:expr, $fd_by_id:expr) => { 36 $(#[$attr])* 37 #[derive(Default, Debug)] 38 pub struct $name { 39 cur_id: u32, 40 } 41 42 impl $name { 43 // Returns Some(next_valid_fd), None on none left 44 fn next_valid_fd(&mut self) -> Option<OwnedFd> { 45 loop { 46 if unsafe { $next_id(self.cur_id, &mut self.cur_id) } != 0 { 47 return None; 48 } 49 50 let fd = unsafe { $fd_by_id(self.cur_id) }; 51 if fd < 0 { 52 let err = io::Error::last_os_error(); 53 if err.kind() == io::ErrorKind::NotFound { 54 continue; 55 } 56 57 return None; 58 } 59 60 return Some(unsafe { OwnedFd::from_raw_fd(fd)}); 61 } 62 } 63 } 64 65 impl Iterator for $name { 66 type Item = $info_ty; 67 68 fn next(&mut self) -> Option<Self::Item> { 69 let fd = self.next_valid_fd()?; 70 71 // We need to use std::mem::zeroed() instead of just using 72 // ::default() because padding bytes need to be zero as well. 73 // Old kernels which know about fewer fields than we do will 74 // check to make sure every byte past what they know is zero 75 // and will return E2BIG otherwise. 76 let mut item: $uapi_info_ty = unsafe { std::mem::zeroed() }; 77 let item_ptr: *mut $uapi_info_ty = &mut item; 78 let mut len = size_of_val(&item) as u32; 79 80 let ret = unsafe { libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr as *mut c_void, &mut len) }; 81 let parsed_uapi = if ret != 0 { 82 None 83 } else { 84 <$info_ty>::from_uapi(fd.as_fd(), item) 85 }; 86 87 parsed_uapi 88 } 89 } 90 }; 91 } 92 93 /// BTF Line information 94 #[derive(Clone, Debug)] 95 pub struct LineInfo { 96 /// Offset of instruction in vector 97 pub insn_off: u32, 98 /// File name offset 99 pub file_name_off: u32, 100 /// Line offset in debug info 101 pub line_off: u32, 102 /// Line number 103 pub line_num: u32, 104 /// Line column number 105 pub line_col: u32, 106 } 107 108 impl From<&libbpf_sys::bpf_line_info> for LineInfo { from(item: &libbpf_sys::bpf_line_info) -> Self109 fn from(item: &libbpf_sys::bpf_line_info) -> Self { 110 LineInfo { 111 insn_off: item.insn_off, 112 file_name_off: item.file_name_off, 113 line_off: item.line_off, 114 line_num: item.line_col >> 10, 115 line_col: item.line_col & 0x3ff, 116 } 117 } 118 } 119 120 /// Bpf identifier tag 121 #[derive(Debug, Clone, Default)] 122 #[repr(C)] 123 pub struct Tag(pub [u8; 8]); 124 125 /// Information about a BPF program 126 #[derive(Debug, Clone)] 127 // TODO: Document members. 128 #[allow(missing_docs)] 129 pub struct ProgramInfo { 130 pub name: CString, 131 pub ty: ProgramType, 132 pub tag: Tag, 133 pub id: u32, 134 pub jited_prog_insns: Vec<u8>, 135 pub xlated_prog_insns: Vec<u8>, 136 /// Duration since system boot 137 pub load_time: Duration, 138 pub created_by_uid: u32, 139 pub map_ids: Vec<u32>, 140 pub ifindex: u32, 141 pub gpl_compatible: bool, 142 pub netns_dev: u64, 143 pub netns_ino: u64, 144 pub jited_ksyms: Vec<*const c_void>, 145 pub jited_func_lens: Vec<u32>, 146 pub btf_id: u32, 147 pub func_info_rec_size: u32, 148 pub func_info: Vec<libbpf_sys::bpf_func_info>, 149 pub line_info: Vec<LineInfo>, 150 pub jited_line_info: Vec<*const c_void>, 151 pub line_info_rec_size: u32, 152 pub jited_line_info_rec_size: u32, 153 pub prog_tags: Vec<Tag>, 154 pub run_time_ns: u64, 155 pub run_cnt: u64, 156 /// Skipped BPF executions due to recursion or concurrent execution prevention. 157 pub recursion_misses: u64, 158 } 159 160 /// An iterator for the information of loaded bpf programs 161 #[derive(Default, Debug)] 162 pub struct ProgInfoIter { 163 cur_id: u32, 164 opts: ProgInfoQueryOptions, 165 } 166 167 /// Options to query the program info currently loaded 168 #[derive(Clone, Default, Debug)] 169 pub struct ProgInfoQueryOptions { 170 /// Include the vector of bpf instructions in the result 171 include_xlated_prog_insns: bool, 172 /// Include the vector of jited instructions in the result 173 include_jited_prog_insns: bool, 174 /// Include the ids of maps associated with the program 175 include_map_ids: bool, 176 /// Include source line information corresponding to xlated code 177 include_line_info: bool, 178 /// Include function type information corresponding to xlated code 179 include_func_info: bool, 180 /// Include source line information corresponding to jited code 181 include_jited_line_info: bool, 182 /// Include function type information corresponding to jited code 183 include_jited_func_lens: bool, 184 /// Include program tags 185 include_prog_tags: bool, 186 /// Include the jited kernel symbols 187 include_jited_ksyms: bool, 188 } 189 190 impl ProgInfoIter { 191 /// Generate an iter from more specific query options with_query_opts(opts: ProgInfoQueryOptions) -> Self192 pub fn with_query_opts(opts: ProgInfoQueryOptions) -> Self { 193 Self { 194 opts, 195 ..Self::default() 196 } 197 } 198 } 199 200 impl ProgInfoQueryOptions { 201 /// Include the vector of jited bpf instructions in the result include_xlated_prog_insns(mut self, v: bool) -> Self202 pub fn include_xlated_prog_insns(mut self, v: bool) -> Self { 203 self.include_xlated_prog_insns = v; 204 self 205 } 206 207 /// Include the vector of jited instructions in the result include_jited_prog_insns(mut self, v: bool) -> Self208 pub fn include_jited_prog_insns(mut self, v: bool) -> Self { 209 self.include_jited_prog_insns = v; 210 self 211 } 212 213 /// Include the ids of maps associated with the program include_map_ids(mut self, v: bool) -> Self214 pub fn include_map_ids(mut self, v: bool) -> Self { 215 self.include_map_ids = v; 216 self 217 } 218 219 /// Include source line information corresponding to xlated code include_line_info(mut self, v: bool) -> Self220 pub fn include_line_info(mut self, v: bool) -> Self { 221 self.include_line_info = v; 222 self 223 } 224 225 /// Include function type information corresponding to xlated code include_func_info(mut self, v: bool) -> Self226 pub fn include_func_info(mut self, v: bool) -> Self { 227 self.include_func_info = v; 228 self 229 } 230 231 /// Include source line information corresponding to jited code include_jited_line_info(mut self, v: bool) -> Self232 pub fn include_jited_line_info(mut self, v: bool) -> Self { 233 self.include_jited_line_info = v; 234 self 235 } 236 237 /// Include function type information corresponding to jited code include_jited_func_lens(mut self, v: bool) -> Self238 pub fn include_jited_func_lens(mut self, v: bool) -> Self { 239 self.include_jited_func_lens = v; 240 self 241 } 242 243 /// Include program tags include_prog_tags(mut self, v: bool) -> Self244 pub fn include_prog_tags(mut self, v: bool) -> Self { 245 self.include_prog_tags = v; 246 self 247 } 248 249 /// Include the jited kernel symbols include_jited_ksyms(mut self, v: bool) -> Self250 pub fn include_jited_ksyms(mut self, v: bool) -> Self { 251 self.include_jited_ksyms = v; 252 self 253 } 254 255 /// Include everything there is in the query results include_all(self) -> Self256 pub fn include_all(self) -> Self { 257 Self { 258 include_xlated_prog_insns: true, 259 include_jited_prog_insns: true, 260 include_map_ids: true, 261 include_line_info: true, 262 include_func_info: true, 263 include_jited_line_info: true, 264 include_jited_func_lens: true, 265 include_prog_tags: true, 266 include_jited_ksyms: true, 267 } 268 } 269 } 270 271 impl ProgramInfo { load_from_fd(fd: BorrowedFd<'_>, opts: &ProgInfoQueryOptions) -> Result<Self>272 fn load_from_fd(fd: BorrowedFd<'_>, opts: &ProgInfoQueryOptions) -> Result<Self> { 273 let mut item = libbpf_sys::bpf_prog_info::default(); 274 275 let mut xlated_prog_insns: Vec<u8> = Vec::new(); 276 let mut jited_prog_insns: Vec<u8> = Vec::new(); 277 let mut map_ids: Vec<u32> = Vec::new(); 278 let mut jited_line_info: Vec<*const c_void> = Vec::new(); 279 let mut line_info: Vec<libbpf_sys::bpf_line_info> = Vec::new(); 280 let mut func_info: Vec<libbpf_sys::bpf_func_info> = Vec::new(); 281 let mut jited_func_lens: Vec<u32> = Vec::new(); 282 let mut prog_tags: Vec<Tag> = Vec::new(); 283 let mut jited_ksyms: Vec<*const c_void> = Vec::new(); 284 285 let item_ptr: *mut libbpf_sys::bpf_prog_info = &mut item; 286 let mut len = size_of_val(&item) as u32; 287 288 let ret = unsafe { 289 libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr as *mut c_void, &mut len) 290 }; 291 util::parse_ret(ret)?; 292 293 // SANITY: `libbpf` should guarantee NUL termination. 294 let name = util::c_char_slice_to_cstr(&item.name).unwrap(); 295 let ty = ProgramType::from(item.type_); 296 297 if opts.include_xlated_prog_insns { 298 xlated_prog_insns.resize(item.xlated_prog_len as usize, 0u8); 299 item.xlated_prog_insns = xlated_prog_insns.as_mut_ptr() as *mut c_void as u64; 300 } else { 301 item.xlated_prog_len = 0; 302 } 303 304 if opts.include_jited_prog_insns { 305 jited_prog_insns.resize(item.jited_prog_len as usize, 0u8); 306 item.jited_prog_insns = jited_prog_insns.as_mut_ptr() as *mut c_void as u64; 307 } else { 308 item.jited_prog_len = 0; 309 } 310 311 if opts.include_map_ids { 312 map_ids.resize(item.nr_map_ids as usize, 0u32); 313 item.map_ids = map_ids.as_mut_ptr() as *mut c_void as u64; 314 } else { 315 item.nr_map_ids = 0; 316 } 317 318 if opts.include_line_info { 319 line_info.resize( 320 item.nr_line_info as usize, 321 libbpf_sys::bpf_line_info::default(), 322 ); 323 item.line_info = line_info.as_mut_ptr() as *mut c_void as u64; 324 } else { 325 item.nr_line_info = 0; 326 } 327 328 if opts.include_func_info { 329 func_info.resize( 330 item.nr_func_info as usize, 331 libbpf_sys::bpf_func_info::default(), 332 ); 333 item.func_info = func_info.as_mut_ptr() as *mut c_void as u64; 334 } else { 335 item.nr_func_info = 0; 336 } 337 338 if opts.include_jited_line_info { 339 jited_line_info.resize(item.nr_jited_line_info as usize, ptr::null()); 340 item.jited_line_info = jited_line_info.as_mut_ptr() as *mut c_void as u64; 341 } else { 342 item.nr_jited_line_info = 0; 343 } 344 345 if opts.include_jited_func_lens { 346 jited_func_lens.resize(item.nr_jited_func_lens as usize, 0); 347 item.jited_func_lens = jited_func_lens.as_mut_ptr() as *mut c_void as u64; 348 } else { 349 item.nr_jited_func_lens = 0; 350 } 351 352 if opts.include_prog_tags { 353 prog_tags.resize(item.nr_prog_tags as usize, Tag::default()); 354 item.prog_tags = prog_tags.as_mut_ptr() as *mut c_void as u64; 355 } else { 356 item.nr_prog_tags = 0; 357 } 358 359 if opts.include_jited_ksyms { 360 jited_ksyms.resize(item.nr_jited_ksyms as usize, ptr::null()); 361 item.jited_ksyms = jited_ksyms.as_mut_ptr() as *mut c_void as u64; 362 } else { 363 item.nr_jited_ksyms = 0; 364 } 365 366 let ret = unsafe { 367 libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr as *mut c_void, &mut len) 368 }; 369 util::parse_ret(ret)?; 370 371 return Ok(ProgramInfo { 372 name: name.to_owned(), 373 ty, 374 tag: Tag(item.tag), 375 id: item.id, 376 jited_prog_insns, 377 xlated_prog_insns, 378 load_time: Duration::from_nanos(item.load_time), 379 created_by_uid: item.created_by_uid, 380 map_ids, 381 ifindex: item.ifindex, 382 gpl_compatible: item._bitfield_1.get_bit(0), 383 netns_dev: item.netns_dev, 384 netns_ino: item.netns_ino, 385 jited_ksyms, 386 jited_func_lens, 387 btf_id: item.btf_id, 388 func_info_rec_size: item.func_info_rec_size, 389 func_info, 390 line_info: line_info.iter().map(|li| li.into()).collect(), 391 jited_line_info, 392 line_info_rec_size: item.line_info_rec_size, 393 jited_line_info_rec_size: item.jited_line_info_rec_size, 394 prog_tags, 395 run_time_ns: item.run_time_ns, 396 run_cnt: item.run_cnt, 397 recursion_misses: item.recursion_misses, 398 }); 399 } 400 } 401 402 impl ProgInfoIter { next_valid_fd(&mut self) -> Option<OwnedFd>403 fn next_valid_fd(&mut self) -> Option<OwnedFd> { 404 loop { 405 if unsafe { libbpf_sys::bpf_prog_get_next_id(self.cur_id, &mut self.cur_id) } != 0 { 406 return None; 407 } 408 409 let fd = unsafe { libbpf_sys::bpf_prog_get_fd_by_id(self.cur_id) }; 410 if fd < 0 { 411 let err = io::Error::last_os_error(); 412 if err.kind() == io::ErrorKind::NotFound { 413 continue; 414 } 415 return None; 416 } 417 418 return Some(unsafe { OwnedFd::from_raw_fd(fd) }); 419 } 420 } 421 } 422 423 impl Iterator for ProgInfoIter { 424 type Item = ProgramInfo; 425 next(&mut self) -> Option<Self::Item>426 fn next(&mut self) -> Option<Self::Item> { 427 let fd = self.next_valid_fd()?; 428 429 let prog = ProgramInfo::load_from_fd(fd.as_fd(), &self.opts); 430 431 match prog { 432 Ok(p) => Some(p), 433 // TODO: We should consider bubbling up errors properly. 434 Err(_err) => None, 435 } 436 } 437 } 438 439 /// Information about a BPF map 440 #[derive(Debug, Clone)] 441 // TODO: Document members. 442 #[allow(missing_docs)] 443 pub struct MapInfo { 444 pub name: CString, 445 pub ty: MapType, 446 pub id: u32, 447 pub key_size: u32, 448 pub value_size: u32, 449 pub max_entries: u32, 450 pub map_flags: u32, 451 pub ifindex: u32, 452 pub btf_vmlinux_value_type_id: u32, 453 pub netns_dev: u64, 454 pub netns_ino: u64, 455 pub btf_id: u32, 456 pub btf_key_type_id: u32, 457 pub btf_value_type_id: u32, 458 } 459 460 impl MapInfo { from_uapi(_fd: BorrowedFd<'_>, s: libbpf_sys::bpf_map_info) -> Option<Self>461 fn from_uapi(_fd: BorrowedFd<'_>, s: libbpf_sys::bpf_map_info) -> Option<Self> { 462 // SANITY: `libbpf` should guarantee NUL termination. 463 let name = util::c_char_slice_to_cstr(&s.name).unwrap(); 464 let ty = MapType::from(s.type_); 465 466 Some(Self { 467 name: name.to_owned(), 468 ty, 469 id: s.id, 470 key_size: s.key_size, 471 value_size: s.value_size, 472 max_entries: s.max_entries, 473 map_flags: s.map_flags, 474 ifindex: s.ifindex, 475 btf_vmlinux_value_type_id: s.btf_vmlinux_value_type_id, 476 netns_dev: s.netns_dev, 477 netns_ino: s.netns_ino, 478 btf_id: s.btf_id, 479 btf_key_type_id: s.btf_key_type_id, 480 btf_value_type_id: s.btf_value_type_id, 481 }) 482 } 483 } 484 485 gen_info_impl!( 486 /// Iterator that returns [`MapInfo`]s. 487 MapInfoIter, 488 MapInfo, 489 libbpf_sys::bpf_map_info, 490 libbpf_sys::bpf_map_get_next_id, 491 libbpf_sys::bpf_map_get_fd_by_id 492 ); 493 494 /// Information about BPF type format 495 #[derive(Debug, Clone)] 496 pub struct BtfInfo { 497 /// The name associated with this btf information in the kernel 498 pub name: CString, 499 /// The raw btf bytes from the kernel 500 pub btf: Vec<u8>, 501 /// The btf id associated with this btf information in the kernel 502 pub id: u32, 503 } 504 505 impl BtfInfo { load_from_fd(fd: BorrowedFd<'_>) -> Result<Self>506 fn load_from_fd(fd: BorrowedFd<'_>) -> Result<Self> { 507 let mut item = libbpf_sys::bpf_btf_info::default(); 508 let mut btf: Vec<u8> = Vec::new(); 509 let mut name: Vec<u8> = Vec::new(); 510 511 let item_ptr: *mut libbpf_sys::bpf_btf_info = &mut item; 512 let mut len = size_of_val(&item) as u32; 513 514 let ret = unsafe { 515 libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr as *mut c_void, &mut len) 516 }; 517 util::parse_ret(ret)?; 518 519 // The API gives you the ascii string length while expecting 520 // you to give it back space for a nul-terminator 521 item.name_len += 1; 522 name.resize(item.name_len as usize, 0u8); 523 item.name = name.as_mut_ptr() as *mut c_void as u64; 524 525 btf.resize(item.btf_size as usize, 0u8); 526 item.btf = btf.as_mut_ptr() as *mut c_void as u64; 527 528 let ret = unsafe { 529 libbpf_sys::bpf_obj_get_info_by_fd(fd.as_raw_fd(), item_ptr as *mut c_void, &mut len) 530 }; 531 util::parse_ret(ret)?; 532 533 Ok(BtfInfo { 534 // SANITY: Our buffer contained space for a NUL byte and we set its 535 // contents to 0. Barring a `libbpf` bug a NUL byte will be 536 // present. 537 name: CString::from_vec_with_nul(name).unwrap(), 538 btf, 539 id: item.id, 540 }) 541 } 542 } 543 544 #[derive(Debug, Default)] 545 /// An iterator for the btf type information of modules and programs 546 /// in the kernel 547 pub struct BtfInfoIter { 548 cur_id: u32, 549 } 550 551 impl BtfInfoIter { 552 // Returns Some(next_valid_fd), None on none left next_valid_fd(&mut self) -> Option<OwnedFd>553 fn next_valid_fd(&mut self) -> Option<OwnedFd> { 554 loop { 555 if unsafe { libbpf_sys::bpf_btf_get_next_id(self.cur_id, &mut self.cur_id) } != 0 { 556 return None; 557 } 558 559 let fd = unsafe { libbpf_sys::bpf_btf_get_fd_by_id(self.cur_id) }; 560 if fd < 0 { 561 let err = io::Error::last_os_error(); 562 if err.kind() == io::ErrorKind::NotFound { 563 continue; 564 } 565 return None; 566 } 567 568 return Some(unsafe { OwnedFd::from_raw_fd(fd) }); 569 } 570 } 571 } 572 573 impl Iterator for BtfInfoIter { 574 type Item = BtfInfo; 575 next(&mut self) -> Option<Self::Item>576 fn next(&mut self) -> Option<Self::Item> { 577 let fd = self.next_valid_fd()?; 578 579 let info = BtfInfo::load_from_fd(fd.as_fd()); 580 581 match info { 582 Ok(i) => Some(i), 583 // TODO: We should consider bubbling up errors properly. 584 Err(_err) => None, 585 } 586 } 587 } 588 589 #[derive(Debug, Clone)] 590 // TODO: Document members. 591 #[allow(missing_docs)] 592 pub struct RawTracepointLinkInfo { 593 pub name: String, 594 } 595 596 #[derive(Debug, Clone)] 597 // TODO: Document members. 598 #[allow(missing_docs)] 599 pub struct TracingLinkInfo { 600 pub attach_type: ProgramAttachType, 601 } 602 603 #[derive(Debug, Clone)] 604 // TODO: Document members. 605 #[allow(missing_docs)] 606 pub struct CgroupLinkInfo { 607 pub cgroup_id: u64, 608 pub attach_type: ProgramAttachType, 609 } 610 611 #[derive(Debug, Clone)] 612 // TODO: Document members. 613 #[allow(missing_docs)] 614 pub struct NetNsLinkInfo { 615 pub ino: u32, 616 pub attach_type: ProgramAttachType, 617 } 618 619 #[derive(Debug, Clone)] 620 // TODO: Document variants. 621 #[allow(missing_docs)] 622 pub enum LinkTypeInfo { 623 RawTracepoint(RawTracepointLinkInfo), 624 Tracing(TracingLinkInfo), 625 Cgroup(CgroupLinkInfo), 626 Iter, 627 NetNs(NetNsLinkInfo), 628 Unknown, 629 } 630 631 /// Information about a BPF link 632 #[derive(Debug, Clone)] 633 // TODO: Document members. 634 #[allow(missing_docs)] 635 pub struct LinkInfo { 636 pub info: LinkTypeInfo, 637 pub id: u32, 638 pub prog_id: u32, 639 } 640 641 impl LinkInfo { from_uapi(fd: BorrowedFd<'_>, mut s: libbpf_sys::bpf_link_info) -> Option<Self>642 fn from_uapi(fd: BorrowedFd<'_>, mut s: libbpf_sys::bpf_link_info) -> Option<Self> { 643 let type_info = match s.type_ { 644 libbpf_sys::BPF_LINK_TYPE_RAW_TRACEPOINT => { 645 let mut buf = [0; 256]; 646 s.__bindgen_anon_1.raw_tracepoint.tp_name = buf.as_mut_ptr() as u64; 647 s.__bindgen_anon_1.raw_tracepoint.tp_name_len = buf.len() as u32; 648 let item_ptr: *mut libbpf_sys::bpf_link_info = &mut s; 649 let mut len = size_of_val(&s) as u32; 650 651 let ret = unsafe { 652 libbpf_sys::bpf_obj_get_info_by_fd( 653 fd.as_raw_fd(), 654 item_ptr as *mut c_void, 655 &mut len, 656 ) 657 }; 658 if ret != 0 { 659 return None; 660 } 661 662 LinkTypeInfo::RawTracepoint(RawTracepointLinkInfo { 663 name: util::c_ptr_to_string( 664 unsafe { s.__bindgen_anon_1.raw_tracepoint.tp_name } as *const c_char, 665 ) 666 .unwrap_or_else(|_| "?".to_string()), 667 }) 668 } 669 libbpf_sys::BPF_LINK_TYPE_TRACING => LinkTypeInfo::Tracing(TracingLinkInfo { 670 attach_type: ProgramAttachType::from(unsafe { 671 s.__bindgen_anon_1.tracing.attach_type 672 }), 673 }), 674 libbpf_sys::BPF_LINK_TYPE_CGROUP => LinkTypeInfo::Cgroup(CgroupLinkInfo { 675 cgroup_id: unsafe { s.__bindgen_anon_1.cgroup.cgroup_id }, 676 attach_type: ProgramAttachType::from(unsafe { 677 s.__bindgen_anon_1.cgroup.attach_type 678 }), 679 }), 680 libbpf_sys::BPF_LINK_TYPE_ITER => LinkTypeInfo::Iter, 681 libbpf_sys::BPF_LINK_TYPE_NETNS => LinkTypeInfo::NetNs(NetNsLinkInfo { 682 ino: unsafe { s.__bindgen_anon_1.netns.netns_ino }, 683 attach_type: ProgramAttachType::from(unsafe { 684 s.__bindgen_anon_1.netns.attach_type 685 }), 686 }), 687 _ => LinkTypeInfo::Unknown, 688 }; 689 690 Some(Self { 691 info: type_info, 692 id: s.id, 693 prog_id: s.prog_id, 694 }) 695 } 696 } 697 698 gen_info_impl!( 699 /// Iterator that returns [`LinkInfo`]s. 700 LinkInfoIter, 701 LinkInfo, 702 libbpf_sys::bpf_link_info, 703 libbpf_sys::bpf_link_get_next_id, 704 libbpf_sys::bpf_link_get_fd_by_id 705 ); 706