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