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