1#![allow(non_upper_case_globals)]
7
8use crate::bpf::context::EbpfRunContextImpl;
9use crate::bpf::fs::{BpfHandle, get_bpf_object};
10use crate::bpf::program::ProgramHandle;
11use crate::mm::PAGE_SIZE;
12use crate::task::CurrentTask;
13use crate::vfs::FdNumber;
14use crate::vfs::socket::{
15 SockOptValue, SocketDomain, SocketProtocol, SocketType, ZxioBackedSocket,
16};
17use ebpf::{EbpfProgram, EbpfProgramContext, ProgramArgument, Type};
18use ebpf_api::{
19 AttachType, BPF_SOCK_ADDR_TYPE, BPF_SOCK_TYPE, CgroupSockAddrProgramContext,
20 CgroupSockOptProgramContext, CgroupSockProgramContext, PinnedMap, ProgramType,
21 SocketCookieContext,
22};
23use fidl_fuchsia_net_filter as fnet_filter;
24use fuchsia_component::client::connect_to_protocol_sync;
25use starnix_logging::{log_error, log_warn, track_stub};
26use starnix_sync::{EbpfStateLock, FileOpsCore, Locked, OrderedRwLock, Unlocked};
27use starnix_syscalls::{SUCCESS, SyscallResult};
28use starnix_uapi::errors::{Errno, ErrnoCode};
29use starnix_uapi::{
30 CGROUP2_SUPER_MAGIC, bpf_attr__bindgen_ty_6, bpf_sock, bpf_sock_addr, errno, error,
31};
32use std::ops::{Deref, DerefMut};
33use std::sync::{Arc, OnceLock};
34use zerocopy::FromBytes;
35
36pub type BpfAttachAttr = bpf_attr__bindgen_ty_6;
37
38fn check_root_cgroup_fd(
39 locked: &mut Locked<Unlocked>,
40 current_task: &CurrentTask,
41 cgroup_fd: FdNumber,
42) -> Result<(), Errno> {
43 let file = current_task.files.get(cgroup_fd)?;
44
45 let is_cgroup =
47 file.node().fs().statfs(locked, current_task)?.f_type == CGROUP2_SUPER_MAGIC as i64;
48 if !is_cgroup {
49 log_warn!("bpf_prog_attach(BPF_PROG_ATTACH) is called with an invalid cgroup2 FD.");
50 return error!(EINVAL);
51 }
52
53 let is_root = file
57 .node()
58 .fs()
59 .maybe_root()
60 .map(|root| Arc::ptr_eq(&root.node, file.node()))
61 .unwrap_or(false);
62 if !is_root {
63 log_warn!("bpf_prog_attach(BPF_PROG_ATTACH) is supported only for root cgroup.");
64 return error!(EINVAL);
65 }
66
67 Ok(())
68}
69
70pub fn bpf_prog_attach(
71 locked: &mut Locked<Unlocked>,
72 current_task: &CurrentTask,
73 attr: BpfAttachAttr,
74) -> Result<SyscallResult, Errno> {
75 let bpf_fd = FdNumber::from_raw(attr.attach_bpf_fd as i32);
77 let object = get_bpf_object(current_task, bpf_fd)?;
78 if matches!(object, BpfHandle::ProgramStub(_)) {
79 log_warn!("Stub program. Faking successful attach");
80 return Ok(SUCCESS);
81 }
82 let program = object.as_program()?.clone();
83 let attach_type = AttachType::from(attr.attach_type);
84
85 let program_type = program.info.program_type;
86 if attach_type.get_program_type() != program_type {
87 log_warn!(
88 "bpf_prog_attach(BPF_PROG_ATTACH): program not compatible with attach_type \
89 attach_type: {attach_type:?}, program_type: {program_type:?}"
90 );
91 return error!(EINVAL);
92 }
93
94 if !attach_type.is_compatible_with_expected_attach_type(program.info.expected_attach_type) {
95 log_warn!(
96 "bpf_prog_attach(BPF_PROG_ATTACH): expected_attach_type didn't match attach_type \
97 expected_attach_type: {:?}, attach_type: {:?}",
98 program.info.expected_attach_type,
99 attach_type
100 );
101 return error!(EINVAL);
102 }
103
104 let target_fd = unsafe { attr.__bindgen_anon_1.target_fd };
106 let target_fd = FdNumber::from_raw(target_fd as i32);
107
108 current_task.kernel().ebpf_state.attachments.attach_prog(
109 locked,
110 current_task,
111 attach_type,
112 target_fd,
113 program,
114 )
115}
116
117pub fn bpf_prog_detach(
118 locked: &mut Locked<Unlocked>,
119 current_task: &CurrentTask,
120 attr: BpfAttachAttr,
121) -> Result<SyscallResult, Errno> {
122 let attach_type = AttachType::from(attr.attach_type);
123
124 let target_fd = unsafe { attr.__bindgen_anon_1.target_fd };
126 let target_fd = FdNumber::from_raw(target_fd as i32);
127
128 current_task.kernel().ebpf_state.attachments.detach_prog(
129 locked,
130 current_task,
131 attach_type,
132 target_fd,
133 )
134}
135
136#[repr(C)]
138pub struct BpfSockAddr<'a> {
139 sock_addr: bpf_sock_addr,
140
141 socket: &'a ZxioBackedSocket,
142}
143
144impl<'a> Deref for BpfSockAddr<'a> {
145 type Target = bpf_sock_addr;
146 fn deref(&self) -> &Self::Target {
147 &self.sock_addr
148 }
149}
150
151impl<'a> DerefMut for BpfSockAddr<'a> {
152 fn deref_mut(&mut self) -> &mut Self::Target {
153 &mut self.sock_addr
154 }
155}
156
157impl<'a> ProgramArgument for &'_ mut BpfSockAddr<'a> {
158 fn get_type() -> &'static Type {
159 &*BPF_SOCK_ADDR_TYPE
160 }
161}
162
163impl<'a, 'b> SocketCookieContext<&'a mut BpfSockAddr<'a>> for EbpfRunContextImpl<'b> {
164 fn get_socket_cookie(&self, bpf_sock_addr: &'a mut BpfSockAddr<'a>) -> u64 {
165 let v = bpf_sock_addr.socket.get_socket_cookie();
166 v.unwrap_or_else(|errno| {
167 log_error!("Failed to get socket cookie: {:?}", errno);
168 0
169 })
170 }
171}
172
173struct SockAddrProgram(EbpfProgram<SockAddrProgram>);
175
176impl EbpfProgramContext for SockAddrProgram {
177 type RunContext<'a> = EbpfRunContextImpl<'a>;
178 type Packet<'a> = ();
179 type Arg1<'a> = &'a mut BpfSockAddr<'a>;
180 type Arg2<'a> = ();
181 type Arg3<'a> = ();
182 type Arg4<'a> = ();
183 type Arg5<'a> = ();
184
185 type Map = PinnedMap;
186}
187
188ebpf_api::ebpf_program_context_type!(SockAddrProgram, CgroupSockAddrProgramContext);
189
190#[derive(Debug, PartialEq, Eq)]
191pub enum SockAddrProgramResult {
192 Allow,
193 Block,
194}
195
196impl SockAddrProgram {
197 fn run<'a>(
198 &self,
199 locked: &'a mut Locked<EbpfStateLock>,
200 current_task: &'a CurrentTask,
201 addr: &'a mut BpfSockAddr<'a>,
202 can_block: bool,
203 ) -> SockAddrProgramResult {
204 let mut run_context = EbpfRunContextImpl::new(locked, current_task);
205 match self.0.run_with_1_argument(&mut run_context, addr) {
206 0 if can_block => SockAddrProgramResult::Block,
208 1 => SockAddrProgramResult::Allow,
209 result => {
210 log_error!("eBPF program returned invalid result: {}", result);
213 SockAddrProgramResult::Allow
214 }
215 }
216 }
217}
218
219type AttachedSockAddrProgramCell = OrderedRwLock<Option<SockAddrProgram>, EbpfStateLock>;
220
221#[repr(C)]
223pub struct BpfSock<'a> {
224 value: bpf_sock,
226
227 socket: &'a ZxioBackedSocket,
228}
229
230impl<'a> Deref for BpfSock<'a> {
231 type Target = bpf_sock;
232 fn deref(&self) -> &Self::Target {
233 &self.value
234 }
235}
236
237impl<'a> DerefMut for BpfSock<'a> {
238 fn deref_mut(&mut self) -> &mut Self::Target {
239 &mut self.value
240 }
241}
242
243impl<'a> ProgramArgument for &'_ BpfSock<'a> {
244 fn get_type() -> &'static Type {
245 &*BPF_SOCK_TYPE
246 }
247}
248
249impl<'a, 'b> SocketCookieContext<&'a BpfSock<'a>> for EbpfRunContextImpl<'b> {
250 fn get_socket_cookie(&self, bpf_sock: &'a BpfSock<'a>) -> u64 {
251 let v = bpf_sock.socket.get_socket_cookie();
252 v.unwrap_or_else(|errno| {
253 log_error!("Failed to get socket cookie: {:?}", errno);
254 0
255 })
256 }
257}
258
259struct SockProgram(EbpfProgram<SockProgram>);
261
262impl EbpfProgramContext for SockProgram {
263 type RunContext<'a> = EbpfRunContextImpl<'a>;
264 type Packet<'a> = ();
265 type Arg1<'a> = &'a BpfSock<'a>;
266 type Arg2<'a> = ();
267 type Arg3<'a> = ();
268 type Arg4<'a> = ();
269 type Arg5<'a> = ();
270
271 type Map = PinnedMap;
272}
273
274ebpf_api::ebpf_program_context_type!(SockProgram, CgroupSockProgramContext);
275
276#[derive(Debug, PartialEq, Eq)]
277pub enum SockProgramResult {
278 Allow,
279 Block,
280}
281
282impl SockProgram {
283 fn run<'a>(
284 &self,
285 locked: &mut Locked<EbpfStateLock>,
286 current_task: &'a CurrentTask,
287 sock: &'a BpfSock<'a>,
288 ) -> SockProgramResult {
289 let mut run_context = EbpfRunContextImpl::new(locked, current_task);
290 if self.0.run_with_1_argument(&mut run_context, sock) == 0 {
291 SockProgramResult::Block
292 } else {
293 SockProgramResult::Allow
294 }
295 }
296}
297
298type AttachedSockProgramCell = OrderedRwLock<Option<SockProgram>, EbpfStateLock>;
299
300mod internal {
301 use ebpf::{ProgramArgument, Type};
302 use ebpf_api::BPF_SOCKOPT_TYPE;
303 use starnix_uapi::{bpf_sockopt, uaddr};
304 use std::ops::Deref;
305
306 #[repr(C)]
309 pub struct BpfSockOpt {
310 sockopt: bpf_sockopt,
311
312 value_buf: Vec<u8>,
318 }
319
320 impl BpfSockOpt {
321 pub fn new(level: u32, optname: u32, value_buf: Vec<u8>, optlen: u32, retval: i32) -> Self {
322 let mut sockopt = Self {
323 sockopt: bpf_sockopt {
324 level: level as i32,
325 optname: optname as i32,
326 optlen: optlen as i32,
327 retval: retval as i32,
328 ..Default::default()
329 },
330 value_buf,
331 };
332
333 unsafe {
335 sockopt.sockopt.__bindgen_anon_2.optval =
336 uaddr { addr: sockopt.value_buf.as_mut_ptr() as u64 };
337 sockopt.sockopt.__bindgen_anon_3.optval_end = uaddr {
338 addr: sockopt.value_buf.as_mut_ptr().add(sockopt.value_buf.len()) as u64,
339 };
340 }
341
342 sockopt
343 }
344
345 pub fn take_value(self) -> Vec<u8> {
348 self.value_buf
349 }
350 }
351
352 impl Deref for BpfSockOpt {
353 type Target = bpf_sockopt;
354 fn deref(&self) -> &Self::Target {
355 &self.sockopt
356 }
357 }
358
359 impl ProgramArgument for &'_ mut BpfSockOpt {
360 fn get_type() -> &'static Type {
361 &*BPF_SOCKOPT_TYPE
362 }
363 }
364}
365
366use internal::BpfSockOpt;
367
368struct SockOptProgram(EbpfProgram<SockOptProgram>);
370
371impl EbpfProgramContext for SockOptProgram {
372 type RunContext<'a> = EbpfRunContextImpl<'a>;
373 type Packet<'a> = ();
374 type Arg1<'a> = &'a mut BpfSockOpt;
375 type Arg2<'a> = ();
376 type Arg3<'a> = ();
377 type Arg4<'a> = ();
378 type Arg5<'a> = ();
379
380 type Map = PinnedMap;
381}
382
383ebpf_api::ebpf_program_context_type!(SockOptProgram, CgroupSockOptProgramContext);
384
385#[derive(Debug)]
386pub enum SetSockOptProgramResult {
387 Fail(Errno),
389
390 Allow(SockOptValue),
392
393 Bypass,
396}
397
398impl SockOptProgram {
399 fn run<'a>(
400 &self,
401 locked: &mut Locked<EbpfStateLock>,
402 current_task: &'a CurrentTask,
403 sockopt: &'a mut BpfSockOpt,
404 ) -> u64 {
405 let mut run_context = EbpfRunContextImpl::new(locked, current_task);
406 self.0.run_with_1_argument(&mut run_context, sockopt)
407 }
408}
409
410type AttachedSockOptProgramCell = OrderedRwLock<Option<SockOptProgram>, EbpfStateLock>;
411
412#[derive(Default)]
413pub struct CgroupEbpfProgramSet {
414 inet4_bind: AttachedSockAddrProgramCell,
415 inet6_bind: AttachedSockAddrProgramCell,
416 inet4_connect: AttachedSockAddrProgramCell,
417 inet6_connect: AttachedSockAddrProgramCell,
418 udp4_sendmsg: AttachedSockAddrProgramCell,
419 udp6_sendmsg: AttachedSockAddrProgramCell,
420 udp4_recvmsg: AttachedSockAddrProgramCell,
421 udp6_recvmsg: AttachedSockAddrProgramCell,
422 sock_create: AttachedSockProgramCell,
423 sock_release: AttachedSockProgramCell,
424 set_sockopt: AttachedSockOptProgramCell,
425 get_sockopt: AttachedSockOptProgramCell,
426}
427
428#[derive(Eq, PartialEq, Debug, Copy, Clone)]
429pub enum SockAddrOp {
430 Bind,
431 Connect,
432 UdpSendMsg,
433 UdpRecvMsg,
434}
435
436#[derive(Eq, PartialEq, Debug, Copy, Clone)]
437pub enum SockOp {
438 Create,
439 Release,
440}
441
442impl CgroupEbpfProgramSet {
443 fn get_sock_addr_program(
444 &self,
445 attach_type: AttachType,
446 ) -> Result<&AttachedSockAddrProgramCell, Errno> {
447 assert!(attach_type.is_cgroup());
448
449 match attach_type {
450 AttachType::CgroupInet4Bind => Ok(&self.inet4_bind),
451 AttachType::CgroupInet6Bind => Ok(&self.inet6_bind),
452 AttachType::CgroupInet4Connect => Ok(&self.inet4_connect),
453 AttachType::CgroupInet6Connect => Ok(&self.inet6_connect),
454 AttachType::CgroupUdp4Sendmsg => Ok(&self.udp4_sendmsg),
455 AttachType::CgroupUdp6Sendmsg => Ok(&self.udp6_sendmsg),
456 AttachType::CgroupUdp4Recvmsg => Ok(&self.udp4_recvmsg),
457 AttachType::CgroupUdp6Recvmsg => Ok(&self.udp6_recvmsg),
458 _ => error!(ENOTSUP),
459 }
460 }
461
462 fn get_sock_program(&self, attach_type: AttachType) -> Result<&AttachedSockProgramCell, Errno> {
463 assert!(attach_type.is_cgroup());
464
465 match attach_type {
466 AttachType::CgroupInetSockCreate => Ok(&self.sock_create),
467 AttachType::CgroupInetSockRelease => Ok(&self.sock_release),
468 _ => error!(ENOTSUP),
469 }
470 }
471
472 fn get_sock_opt_program(
473 &self,
474 attach_type: AttachType,
475 ) -> Result<&AttachedSockOptProgramCell, Errno> {
476 assert!(attach_type.is_cgroup());
477
478 match attach_type {
479 AttachType::CgroupSetsockopt => Ok(&self.set_sockopt),
480 AttachType::CgroupGetsockopt => Ok(&self.get_sockopt),
481 _ => error!(ENOTSUP),
482 }
483 }
484
485 pub fn run_sock_addr_prog(
488 &self,
489 locked: &mut Locked<FileOpsCore>,
490 current_task: &CurrentTask,
491 op: SockAddrOp,
492 domain: SocketDomain,
493 socket_type: SocketType,
494 protocol: SocketProtocol,
495 socket_address: &[u8],
496 socket: &ZxioBackedSocket,
497 ) -> Result<SockAddrProgramResult, Errno> {
498 let prog_cell = match (domain, op) {
499 (SocketDomain::Inet, SockAddrOp::Bind) => &self.inet4_bind,
500 (SocketDomain::Inet6, SockAddrOp::Bind) => &self.inet6_bind,
501 (SocketDomain::Inet, SockAddrOp::Connect) => &self.inet4_connect,
502 (SocketDomain::Inet6, SockAddrOp::Connect) => &self.inet6_connect,
503 (SocketDomain::Inet, SockAddrOp::UdpSendMsg) => &self.udp4_sendmsg,
504 (SocketDomain::Inet6, SockAddrOp::UdpSendMsg) => &self.udp6_sendmsg,
505 (SocketDomain::Inet, SockAddrOp::UdpRecvMsg) => &self.udp4_recvmsg,
506 (SocketDomain::Inet6, SockAddrOp::UdpRecvMsg) => &self.udp6_recvmsg,
507 _ => return Ok(SockAddrProgramResult::Allow),
508 };
509
510 let (prog_guard, locked) = prog_cell.read_and(locked);
511 let Some(prog) = prog_guard.as_ref() else {
512 return Ok(SockAddrProgramResult::Allow);
513 };
514
515 let mut bpf_sockaddr = BpfSockAddr { sock_addr: Default::default(), socket };
516 bpf_sockaddr.family = domain.as_raw().into();
517 bpf_sockaddr.type_ = socket_type.as_raw();
518 bpf_sockaddr.protocol = protocol.as_raw();
519
520 let (sa_family, _) = u16::read_from_prefix(socket_address).map_err(|_| errno!(EINVAL))?;
521
522 if domain.as_raw() != sa_family {
523 return error!(EAFNOSUPPORT);
524 }
525 bpf_sockaddr.user_family = sa_family.into();
526
527 match sa_family.into() {
528 linux_uapi::AF_INET => {
529 let (sockaddr, _) = linux_uapi::sockaddr_in::ref_from_prefix(socket_address)
530 .map_err(|_| errno!(EINVAL))?;
531 bpf_sockaddr.user_port = sockaddr.sin_port.into();
532 bpf_sockaddr.user_ip4 = sockaddr.sin_addr.s_addr;
533 }
534 linux_uapi::AF_INET6 => {
535 let sockaddr = linux_uapi::sockaddr_in6::ref_from_prefix(socket_address)
536 .map_err(|_| errno!(EINVAL))?
537 .0;
538 bpf_sockaddr.user_port = sockaddr.sin6_port.into();
539 bpf_sockaddr.user_ip6 = unsafe { sockaddr.sin6_addr.in6_u.u6_addr32 };
541 }
542 _ => return error!(EAFNOSUPPORT),
543 }
544
545 let can_block = op != SockAddrOp::UdpRecvMsg;
547 Ok(prog.run(locked, current_task, &mut bpf_sockaddr, can_block))
548 }
549
550 pub fn run_sock_prog(
551 &self,
552 locked: &mut Locked<FileOpsCore>,
553 current_task: &CurrentTask,
554 op: SockOp,
555 domain: SocketDomain,
556 socket_type: SocketType,
557 protocol: SocketProtocol,
558 socket: &ZxioBackedSocket,
559 ) -> SockProgramResult {
560 let prog_cell = match op {
561 SockOp::Create => &self.sock_create,
562 SockOp::Release => &self.sock_release,
563 };
564 let (prog_guard, locked) = prog_cell.read_and(locked);
565 let Some(prog) = prog_guard.as_ref() else {
566 return SockProgramResult::Allow;
567 };
568
569 let bpf_sock = BpfSock {
570 value: bpf_sock {
571 family: domain.as_raw().into(),
572 type_: socket_type.as_raw(),
573 protocol: protocol.as_raw(),
574 ..Default::default()
575 },
576 socket,
577 };
578
579 prog.run(locked, current_task, &bpf_sock)
580 }
581
582 pub fn run_getsockopt_prog(
583 &self,
584 locked: &mut Locked<FileOpsCore>,
585 current_task: &CurrentTask,
586 level: u32,
587 optname: u32,
588 optval: Vec<u8>,
589 optlen: usize,
590 error: Option<Errno>,
591 ) -> Result<(Vec<u8>, usize), Errno> {
592 let (prog_guard, locked) = self.get_sockopt.read_and(locked);
593 let Some(prog) = prog_guard.as_ref() else {
594 return error.map(|e| Err(e)).unwrap_or_else(|| Ok((optval, optlen)));
595 };
596
597 let retval = error.as_ref().map(|e| -(e.code.error_code() as i32)).unwrap_or(0);
598 let mut bpf_sockopt =
599 BpfSockOpt::new(level, optname, optval.clone(), optlen as u32, retval);
600
601 let result = prog.run(locked, current_task, &mut bpf_sockopt);
603
604 if bpf_sockopt.retval < 0 {
605 return Err(Errno::new(ErrnoCode::from_return_value(bpf_sockopt.retval as u64)));
606 }
607
608 match (result, bpf_sockopt.optlen) {
609 (0, _) => error!(EPERM),
611
612 (1, optlen) if optlen < 0 || (optlen as usize) > optval.len() => {
615 error!(EFAULT)
616 }
617
618 (1, 0) => Ok((optval, optlen)),
620
621 (1, new_optlen) => Ok((bpf_sockopt.take_value(), new_optlen as usize)),
624
625 (result, _) => {
626 log_error!("eBPF getsockopt program returned invalid result: {}", result);
629 Ok((optval, optlen))
630 }
631 }
632 }
633
634 pub fn run_setsockopt_prog(
635 &self,
636 locked: &mut Locked<FileOpsCore>,
637 current_task: &CurrentTask,
638 level: u32,
639 optname: u32,
640 value: SockOptValue,
641 ) -> SetSockOptProgramResult {
642 let (prog_guard, locked) = self.set_sockopt.read_and(locked);
643 let Some(prog) = prog_guard.as_ref() else {
644 return SetSockOptProgramResult::Allow(value);
645 };
646
647 let page_size = *PAGE_SIZE as usize;
648
649 let buffer = match value.read_bytes(current_task, page_size) {
652 Ok(buffer) => buffer,
653 Err(err) => return SetSockOptProgramResult::Fail(err),
654 };
655
656 let buffer_len = buffer.len();
657 let optlen = value.len();
658 let mut bpf_sockopt = BpfSockOpt::new(level, optname, buffer, optlen as u32, 0);
659 let result = prog.run(locked.cast_locked(), current_task, &mut bpf_sockopt);
660
661 match (result, bpf_sockopt.optlen) {
662 (0, _) => SetSockOptProgramResult::Fail(errno!(EPERM)),
664
665 (1, -1) => SetSockOptProgramResult::Bypass,
668
669 (1, new_optlen) if optlen > page_size && (new_optlen as usize) == optlen => {
673 SetSockOptProgramResult::Allow(value)
674 }
675
676 (1, optlen) if optlen < 0 || (optlen as usize) > buffer_len => {
679 SetSockOptProgramResult::Fail(errno!(EFAULT))
680 }
681
682 (1, 0) => SetSockOptProgramResult::Allow(value),
684
685 (1, optlen) => {
688 let mut value = bpf_sockopt.take_value();
689 value.resize(optlen as usize, 0);
690 SetSockOptProgramResult::Allow(value.into())
691 }
692
693 (result, _) => {
694 log_error!("eBPF setsockopt program returned invalid result: {}", result);
697 SetSockOptProgramResult::Allow(value)
698 }
699 }
700 }
701}
702
703fn attach_type_to_netstack_hook(attach_type: AttachType) -> Option<fnet_filter::SocketHook> {
704 let hook = match attach_type {
705 AttachType::CgroupInetEgress => fnet_filter::SocketHook::Egress,
706 AttachType::CgroupInetIngress => fnet_filter::SocketHook::Ingress,
707 _ => return None,
708 };
709 Some(hook)
710}
711
712#[derive(Copy, Clone, Debug, PartialEq, Eq)]
714enum AttachLocation {
715 Kernel,
717
718 Netstack,
720}
721
722impl TryFrom<AttachType> for AttachLocation {
723 type Error = Errno;
724
725 fn try_from(attach_type: AttachType) -> Result<Self, Self::Error> {
726 match attach_type {
727 AttachType::CgroupInet4Bind
728 | AttachType::CgroupInet6Bind
729 | AttachType::CgroupInet4Connect
730 | AttachType::CgroupInet6Connect
731 | AttachType::CgroupUdp4Sendmsg
732 | AttachType::CgroupUdp6Sendmsg
733 | AttachType::CgroupUdp4Recvmsg
734 | AttachType::CgroupUdp6Recvmsg
735 | AttachType::CgroupInetSockCreate
736 | AttachType::CgroupInetSockRelease
737 | AttachType::CgroupGetsockopt
738 | AttachType::CgroupSetsockopt => Ok(AttachLocation::Kernel),
739
740 AttachType::CgroupInetEgress | AttachType::CgroupInetIngress => {
741 Ok(AttachLocation::Netstack)
742 }
743
744 AttachType::CgroupDevice
745 | AttachType::CgroupInet4Getpeername
746 | AttachType::CgroupInet4Getsockname
747 | AttachType::CgroupInet4PostBind
748 | AttachType::CgroupInet6Getpeername
749 | AttachType::CgroupInet6Getsockname
750 | AttachType::CgroupInet6PostBind
751 | AttachType::CgroupSysctl
752 | AttachType::CgroupUnixConnect
753 | AttachType::CgroupUnixGetpeername
754 | AttachType::CgroupUnixGetsockname
755 | AttachType::CgroupUnixRecvmsg
756 | AttachType::CgroupUnixSendmsg
757 | AttachType::CgroupSockOps
758 | AttachType::SkSkbStreamParser
759 | AttachType::SkSkbStreamVerdict
760 | AttachType::SkMsgVerdict
761 | AttachType::LircMode2
762 | AttachType::FlowDissector
763 | AttachType::TraceRawTp
764 | AttachType::TraceFentry
765 | AttachType::TraceFexit
766 | AttachType::ModifyReturn
767 | AttachType::LsmMac
768 | AttachType::TraceIter
769 | AttachType::XdpDevmap
770 | AttachType::XdpCpumap
771 | AttachType::SkLookup
772 | AttachType::Xdp
773 | AttachType::SkSkbVerdict
774 | AttachType::SkReuseportSelect
775 | AttachType::SkReuseportSelectOrMigrate
776 | AttachType::PerfEvent
777 | AttachType::TraceKprobeMulti
778 | AttachType::LsmCgroup
779 | AttachType::StructOps
780 | AttachType::Netfilter
781 | AttachType::TcxIngress
782 | AttachType::TcxEgress
783 | AttachType::TraceUprobeMulti
784 | AttachType::NetkitPrimary
785 | AttachType::NetkitPeer
786 | AttachType::TraceKprobeSession => {
787 track_stub!(TODO("https://fxbug.dev/322873416"), "BPF_PROG_ATTACH", attach_type);
788 error!(ENOTSUP)
789 }
790
791 AttachType::Unspecified | AttachType::Invalid(_) => {
792 error!(EINVAL)
793 }
794 }
795 }
796}
797
798#[derive(Default)]
799pub struct EbpfAttachments {
800 root_cgroup: CgroupEbpfProgramSet,
801 socket_control: OnceLock<fnet_filter::SocketControlSynchronousProxy>,
802}
803
804impl EbpfAttachments {
805 pub fn root_cgroup(&self) -> &CgroupEbpfProgramSet {
806 &self.root_cgroup
807 }
808
809 fn socket_control(&self) -> &fnet_filter::SocketControlSynchronousProxy {
810 self.socket_control.get_or_init(|| {
811 connect_to_protocol_sync::<fnet_filter::SocketControlMarker>()
812 .expect("Failed to connect to fuchsia.net.filter.SocketControl.")
813 })
814 }
815
816 fn attach_prog(
817 &self,
818 locked: &mut Locked<Unlocked>,
819 current_task: &CurrentTask,
820 attach_type: AttachType,
821 target_fd: FdNumber,
822 program: ProgramHandle,
823 ) -> Result<SyscallResult, Errno> {
824 let location: AttachLocation = attach_type.try_into()?;
825 let program_type = attach_type.get_program_type();
826 match (location, program_type) {
827 (AttachLocation::Kernel, ProgramType::CgroupSockAddr) => {
828 check_root_cgroup_fd(locked, current_task, target_fd)?;
829
830 let linked_program = SockAddrProgram(program.link(attach_type.get_program_type())?);
831 *self.root_cgroup.get_sock_addr_program(attach_type)?.write(locked) =
832 Some(linked_program);
833
834 Ok(SUCCESS)
835 }
836
837 (AttachLocation::Kernel, ProgramType::CgroupSock) => {
838 check_root_cgroup_fd(locked, current_task, target_fd)?;
839
840 let linked_program = SockProgram(program.link(attach_type.get_program_type())?);
841 *self.root_cgroup.get_sock_program(attach_type)?.write(locked) =
842 Some(linked_program);
843
844 Ok(SUCCESS)
845 }
846
847 (AttachLocation::Kernel, ProgramType::CgroupSockopt) => {
848 check_root_cgroup_fd(locked, current_task, target_fd)?;
849
850 let linked_program = SockOptProgram(program.link(attach_type.get_program_type())?);
851 *self.root_cgroup.get_sock_opt_program(attach_type)?.write(locked) =
852 Some(linked_program);
853
854 Ok(SUCCESS)
855 }
856
857 (AttachLocation::Kernel, _) => {
858 unreachable!();
859 }
860
861 (AttachLocation::Netstack, _) => {
862 check_root_cgroup_fd(locked, current_task, target_fd)?;
863 self.attach_prog_in_netstack(attach_type, program)
864 }
865 }
866 }
867
868 fn detach_prog(
869 &self,
870 locked: &mut Locked<Unlocked>,
871 current_task: &CurrentTask,
872 attach_type: AttachType,
873 target_fd: FdNumber,
874 ) -> Result<SyscallResult, Errno> {
875 let location = attach_type.try_into()?;
876 let program_type = attach_type.get_program_type();
877 match (location, program_type) {
878 (AttachLocation::Kernel, ProgramType::CgroupSockAddr) => {
879 check_root_cgroup_fd(locked, current_task, target_fd)?;
880
881 let mut prog_guard =
882 self.root_cgroup.get_sock_addr_program(attach_type)?.write(locked);
883 if prog_guard.is_none() {
884 return error!(ENOENT);
885 }
886
887 *prog_guard = None;
888
889 Ok(SUCCESS)
890 }
891
892 (AttachLocation::Kernel, ProgramType::CgroupSock) => {
893 check_root_cgroup_fd(locked, current_task, target_fd)?;
894
895 let mut prog_guard = self.root_cgroup.get_sock_program(attach_type)?.write(locked);
896 if prog_guard.is_none() {
897 return error!(ENOENT);
898 }
899
900 *prog_guard = None;
901
902 Ok(SUCCESS)
903 }
904
905 (AttachLocation::Kernel, ProgramType::CgroupSockopt) => {
906 check_root_cgroup_fd(locked, current_task, target_fd)?;
907
908 let mut prog_guard =
909 self.root_cgroup.get_sock_opt_program(attach_type)?.write(locked);
910 if prog_guard.is_none() {
911 return error!(ENOENT);
912 }
913
914 *prog_guard = None;
915
916 Ok(SUCCESS)
917 }
918
919 (AttachLocation::Kernel, _) => {
920 unreachable!();
921 }
922
923 (AttachLocation::Netstack, _) => {
924 check_root_cgroup_fd(locked, current_task, target_fd)?;
925 self.detach_prog_in_netstack(attach_type)
926 }
927 }
928 }
929
930 fn attach_prog_in_netstack(
931 &self,
932 attach_type: AttachType,
933 program: ProgramHandle,
934 ) -> Result<SyscallResult, Errno> {
935 let hook = attach_type_to_netstack_hook(attach_type).ok_or_else(|| errno!(ENOTSUP))?;
936 let opts = fnet_filter::AttachEbpfProgramOptions {
937 hook: Some(hook),
938 program: Some((&**program).try_into()?),
939 ..Default::default()
940 };
941 self.socket_control()
942 .attach_ebpf_program(opts, zx::MonotonicInstant::INFINITE)
943 .map_err(|e| {
944 log_error!(
945 "failed to send fuchsia.net.filter/SocketControl.AttachEbpfProgram: {}",
946 e
947 );
948 errno!(EIO)
949 })?
950 .map_err(|e| {
951 use fnet_filter::SocketControlAttachEbpfProgramError as Error;
952 match e {
953 Error::NotSupported => errno!(ENOTSUP),
954 Error::LinkFailed => errno!(EINVAL),
955 Error::MapFailed => errno!(EIO),
956 Error::DuplicateAttachment => errno!(EEXIST),
957 }
958 })?;
959
960 Ok(SUCCESS)
961 }
962
963 fn detach_prog_in_netstack(&self, attach_type: AttachType) -> Result<SyscallResult, Errno> {
964 let hook = attach_type_to_netstack_hook(attach_type).ok_or_else(|| errno!(ENOTSUP))?;
965 self.socket_control()
966 .detach_ebpf_program(hook, zx::MonotonicInstant::INFINITE)
967 .map_err(|e| {
968 log_error!(
969 "failed to send fuchsia.net.filter/SocketControl.DetachEbpfProgram: {}",
970 e
971 );
972 errno!(EIO)
973 })?
974 .map_err(|e| {
975 use fnet_filter::SocketControlDetachEbpfProgramError as Error;
976 match e {
977 Error::NotFound => errno!(ENOENT),
978 }
979 })?;
980 Ok(SUCCESS)
981 }
982}