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