Skip to main content

starnix_core/bpf/
attachments.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// TODO(https://github.com/rust-lang/rust/issues/39371): remove
6#![allow(non_upper_case_globals)]
7
8use crate::bpf::check_cap_or_sys_admin;
9use crate::bpf::context::EbpfRunContextImpl;
10use crate::bpf::fs::{BpfHandle, get_bpf_object};
11use crate::bpf::program::ProgramHandle;
12use crate::mm::PAGE_SIZE;
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, 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    // Check that `cgroup_fd` is from the CGROUP2 file system.
47    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    // Currently cgroup attachments are supported only for the root cgroup.
54    // TODO(https://fxbug.dev//388077431) Allow attachments to any cgroup once cgroup
55    // hierarchy is moved to starnix_core.
56    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    // SAFETY: reading i32 field from a union is always safe.
75    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    let required_caps = get_capability_for_program(program.info.program_type)?;
84    check_cap_or_sys_admin(current_task, required_caps)?;
85
86    let attach_type = AttachType::from(attr.attach_type);
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    // SAFETY: reading i32 field from a union is always safe.
107    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        current_task,
112        attach_type,
113        target_fd,
114        program,
115    )
116}
117
118pub fn bpf_prog_detach(
119    current_task: &CurrentTask,
120    attr: BpfAttachAttr,
121) -> Result<SyscallResult, Errno> {
122    let attach_type = AttachType::from(attr.attach_type);
123
124    // SAFETY: reading i32 field from a union is always safe.
125    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(current_task, attach_type, target_fd)
129}
130
131// Wrapper for `bpf_sock_addr` used to implement `ProgramArgument` trait.
132#[repr(C)]
133pub struct BpfSockAddr<'a> {
134    sock_addr: bpf_sock_addr,
135
136    bpf_sock: &'a BpfSock<'a>,
137}
138
139impl<'a> Deref for BpfSockAddr<'a> {
140    type Target = bpf_sock_addr;
141    fn deref(&self) -> &Self::Target {
142        &self.sock_addr
143    }
144}
145
146impl<'a> DerefMut for BpfSockAddr<'a> {
147    fn deref_mut(&mut self) -> &mut Self::Target {
148        &mut self.sock_addr
149    }
150}
151
152impl<'a> ProgramArgument for &'_ mut BpfSockAddr<'a> {
153    fn get_type() -> &'static Type {
154        &*BPF_SOCK_ADDR_TYPE
155    }
156}
157
158impl<'a, 'b> SocketRef for &'a mut BpfSockAddr<'a> {
159    fn get_socket_cookie(&self) -> Option<u64> {
160        self.bpf_sock.get_socket_cookie()
161    }
162
163    fn get_socket_uid(&self) -> Option<uid_t> {
164        self.bpf_sock.get_socket_uid()
165    }
166}
167
168// Context for eBPF programs of type BPF_PROG_TYPE_CGROUP_SOCKADDR.
169struct SockAddrProgram(EbpfProgram<SockAddrProgram>);
170
171impl EbpfProgramContext for SockAddrProgram {
172    type RunContext<'a> = EbpfRunContextImpl<'a>;
173    type Packet<'a> = ();
174    type Arg1<'a> = &'a mut BpfSockAddr<'a>;
175    type Arg2<'a> = ();
176    type Arg3<'a> = ();
177    type Arg4<'a> = ();
178    type Arg5<'a> = ();
179
180    type Map = PinnedMap;
181}
182
183ebpf_api::ebpf_program_context_type!(SockAddrProgram, CgroupSockAddrProgramContext);
184
185#[derive(Debug, PartialEq, Eq)]
186pub enum SockAddrProgramResult {
187    Allow,
188    Block,
189}
190
191impl SockAddrProgram {
192    fn run<'a>(
193        &self,
194        current_task: &'a CurrentTask,
195        addr: &'a mut BpfSockAddr<'a>,
196        can_block: bool,
197    ) -> SockAddrProgramResult {
198        let mut run_context = EbpfRunContextImpl::new(current_task);
199        match self.0.run_with_1_argument(&mut run_context, addr) {
200            // UDP_RECVMSG programs are not allowed to block the packet.
201            0 if can_block => SockAddrProgramResult::Block,
202            1 => SockAddrProgramResult::Allow,
203            result => {
204                // TODO(https://fxbug.dev/413490751): Change this to panic once
205                // result validation is implemented in the eBPF verifier.
206                log_error!("eBPF program returned invalid result: {}", result);
207                SockAddrProgramResult::Allow
208            }
209        }
210    }
211}
212
213type AttachedSockAddrProgramCell = LockDepRwLock<Option<SockAddrProgram>, EbpfStateLock>;
214
215// Wrapper for `bpf_sock` used to implement `ProgramArgument` trait.
216#[repr(C)]
217pub struct BpfSock<'a> {
218    // Must be first field.
219    value: bpf_sock,
220
221    socket: Option<&'a ZxioBackedSocket>,
222}
223
224impl<'a> BpfSock<'a> {
225    fn from_socket(socket: &'a Socket) -> Self {
226        Self {
227            value: bpf_sock {
228                family: socket.domain.as_raw().into(),
229                type_: socket.socket_type.as_raw(),
230                protocol: socket.protocol.as_raw(),
231                state: socket.bpf_state().into(),
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
273// Context for eBPF programs of type BPF_PROG_TYPE_CGROUP_SOCK.
274struct 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    // Wrapper for `bpf_sockopt` that implements `ProgramArgument` trait.
318    #[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    /// Wrapper for `bpf_sockopt` that keeps a buffer for the `optval`.
329    pub struct BpfSockOptWithValue {
330        sockopt: BpfSockOpt,
331
332        // Buffer used to store the option value. A pointer to the buffer
333        // contents is stored in `sockopt`. `Vec::as_mut_ptr()` guarantees that
334        // the pointer remains valid only as long as the `Vec` is not modified,
335        // so this field should not be updated directly. `take_value()` can be
336        // used to extract the value when `BpfSockOpt` is no longer needed.
337        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            // SAFETY: Setting buffer bounds in unions is safe.
361            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        // Returns the value. Consumes `self` since it's not safe to use again
380        // after the value buffer is moved.
381        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
396// Context for eBPF programs of type BPF_PROG_TYPE_CGROUP_SOCKOPT.
397struct SockOptProgram(EbpfProgram<SockOptProgram>);
398
399// RunContext for eBPF programs of type BPF_PROG_TYPE_CGROUP_SOCKOPT.
400pub struct SockOptEbpfRunContextImpl<'a> {
401    ebpf_run_context: EbpfRunContextImpl<'a>,
402
403    // Pointer to the BpfSockOpt passed to the program. Used for
404    // `bpf_set_retval` and `bpf_get_retval`.
405    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 the syscall.
467    Fail(Errno),
468
469    /// Proceed with the specified option value.
470    Allow(SockOptValue),
471
472    /// Return to userspace without invoking the underlying implementation of
473    /// setsockopt.
474    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    // Executes eBPF program for the operation `op`. `socket_address` contains
561    // socket address as a `sockaddr` struct.
562    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                // SAFETY: reading an array of u32 from a union is safe.
616                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        // UDP recvmsg programs are not allowed to filter packets.
625        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                state: SocketBpfState::Close.into(),
653                ..Default::default()
654            },
655            socket: Some(socket),
656        };
657
658        prog.run(current_task, &bpf_sock)
659    }
660
661    pub fn run_getsockopt_prog(
662        &self,
663        current_task: &CurrentTask,
664        level: u32,
665        optname: u32,
666        value_buf: Vec<u8>,
667        optlen: usize,
668        error: Option<Errno>,
669        socket: &Socket,
670    ) -> Result<(Vec<u8>, usize), Errno> {
671        let prog_guard = self.get_sockopt.read();
672        let Some(prog) = prog_guard.as_ref() else {
673            return error.map(|e| Err(e)).unwrap_or_else(|| Ok((value_buf, optlen)));
674        };
675
676        let bpf_sock = BpfSock::from_socket(socket);
677
678        let retval = error.as_ref().map(|e| -(e.code.error_code() as i32)).unwrap_or(0);
679        let mut bpf_sockopt = BpfSockOptWithValue::new(
680            level,
681            optname,
682            value_buf.clone(),
683            optlen as u32,
684            retval,
685            &bpf_sock,
686        );
687
688        // Run the program.
689        let result = prog.run(current_task, &mut bpf_sockopt);
690
691        let retval = bpf_sockopt.retval;
692
693        let retval = match result {
694            0 if is_error_return_value(retval) => retval,
695            0 => -(linux_uapi::EPERM as i32),
696            1 => retval,
697            _ => {
698                // TODO(https://fxbug.dev/413490751): Change this to panic once
699                // result validation is implemented in the verifier.
700                log_error!("eBPF getsockopt program returned invalid result: {}", result);
701                retval
702            }
703        };
704
705        if retval < 0 {
706            return Err(Errno::new(ErrnoCode::from_error_code(-retval as i16)));
707        }
708
709        let new_optlen = bpf_sockopt.optlen;
710
711        match usize::try_from(new_optlen) {
712            // Fail if the program set an invalid `optlen`.
713            Err(_) => error!(EFAULT),
714            Ok(new_optlen) if new_optlen > value_buf.len() => error!(EFAULT),
715
716            // If `optlen` is set to 0 then proceed with the original value.
717            Ok(0) => Ok((value_buf, optlen)),
718
719            Ok(new_optlen) => Ok((bpf_sockopt.take_value(), new_optlen)),
720        }
721    }
722
723    pub fn run_setsockopt_prog(
724        &self,
725        current_task: &CurrentTask,
726        level: u32,
727        optname: u32,
728        value: SockOptValue,
729        socket: &Socket,
730    ) -> SetSockOptProgramResult {
731        let prog_guard = self.set_sockopt.read();
732        let Some(prog) = prog_guard.as_ref() else {
733            return SetSockOptProgramResult::Allow(value);
734        };
735
736        let page_size = *PAGE_SIZE as usize;
737
738        // Read only the first page from the user-specified buffer in case it's
739        // larger than that.
740        let buffer = match value.read_bytes(current_task, page_size) {
741            Ok(buffer) => buffer,
742            Err(err) => return SetSockOptProgramResult::Fail(err),
743        };
744
745        let bpf_sock = BpfSock::from_socket(socket);
746
747        let buffer_len = buffer.len();
748        let optlen = value.len();
749        let mut bpf_sockopt =
750            BpfSockOptWithValue::new(level, optname, buffer, optlen as u32, 0, &bpf_sock);
751        let result = prog.run(current_task, &mut bpf_sockopt);
752
753        let retval = bpf_sockopt.retval;
754
755        let retval = match result {
756            0 if is_error_return_value(retval) => retval,
757            0 => -(linux_uapi::EPERM as i32),
758            1 => retval,
759            _ => {
760                // TODO(https://fxbug.dev/413490751): Change this to panic once
761                // result validation is implemented in the verifier.
762                log_error!("eBPF getsockopt program returned invalid result: {}", result);
763                retval
764            }
765        };
766
767        if retval < 0 {
768            return SetSockOptProgramResult::Fail(Errno::new(ErrnoCode::from_error_code(
769                -retval as i16,
770            )));
771        }
772
773        match bpf_sockopt.optlen {
774            // `setsockopt` programs can bypass the platform implementation by
775            // setting `optlen` to -1.
776            -1 => SetSockOptProgramResult::Bypass,
777
778            // If the original value is larger than a page and the program
779            // didn't change `optlen` then return the original value. This
780            // allows to avoid `EFAULT` below with a no-op program.
781            new_optlen if optlen > page_size && (new_optlen as usize) == optlen => {
782                SetSockOptProgramResult::Allow(value)
783            }
784
785            // Fail if the program has set an invalid `optlen` (except for the
786            // case handled above).
787            optlen if optlen < 0 || (optlen as usize) > buffer_len => {
788                SetSockOptProgramResult::Fail(errno!(EFAULT))
789            }
790
791            // If `optlen` is set to 0 then proceed with the original value.
792            0 => SetSockOptProgramResult::Allow(value),
793
794            // Return value from `bpf_sockbuf` - it may be different from the
795            // original value.
796            optlen => {
797                let mut value = bpf_sockopt.take_value();
798                value.resize(optlen as usize, 0);
799                SetSockOptProgramResult::Allow(value.into())
800            }
801        }
802    }
803}
804
805fn attach_type_to_netstack_hook(attach_type: AttachType) -> Option<fnet_filter::SocketHook> {
806    let hook = match attach_type {
807        AttachType::CgroupInetEgress => fnet_filter::SocketHook::Egress,
808        AttachType::CgroupInetIngress => fnet_filter::SocketHook::Ingress,
809        _ => return None,
810    };
811    Some(hook)
812}
813
814// Defined a location where eBPF programs can be attached.
815#[derive(Copy, Clone, Debug, PartialEq, Eq)]
816enum AttachLocation {
817    // Attached in Starnix kernel.
818    Kernel,
819
820    // Attached in Netstack.
821    Netstack,
822}
823
824impl TryFrom<AttachType> for AttachLocation {
825    type Error = Errno;
826
827    fn try_from(attach_type: AttachType) -> Result<Self, Self::Error> {
828        match attach_type {
829            AttachType::CgroupInet4Bind
830            | AttachType::CgroupInet6Bind
831            | AttachType::CgroupInet4Connect
832            | AttachType::CgroupInet6Connect
833            | AttachType::CgroupUdp4Sendmsg
834            | AttachType::CgroupUdp6Sendmsg
835            | AttachType::CgroupUdp4Recvmsg
836            | AttachType::CgroupUdp6Recvmsg
837            | AttachType::CgroupInetSockCreate
838            | AttachType::CgroupInetSockRelease
839            | AttachType::CgroupGetsockopt
840            | AttachType::CgroupSetsockopt => Ok(AttachLocation::Kernel),
841
842            AttachType::CgroupInetEgress | AttachType::CgroupInetIngress => {
843                Ok(AttachLocation::Netstack)
844            }
845
846            AttachType::CgroupDevice
847            | AttachType::CgroupInet4Getpeername
848            | AttachType::CgroupInet4Getsockname
849            | AttachType::CgroupInet4PostBind
850            | AttachType::CgroupInet6Getpeername
851            | AttachType::CgroupInet6Getsockname
852            | AttachType::CgroupInet6PostBind
853            | AttachType::CgroupSysctl
854            | AttachType::CgroupUnixConnect
855            | AttachType::CgroupUnixGetpeername
856            | AttachType::CgroupUnixGetsockname
857            | AttachType::CgroupUnixRecvmsg
858            | AttachType::CgroupUnixSendmsg
859            | AttachType::CgroupSockOps
860            | AttachType::SkSkbStreamParser
861            | AttachType::SkSkbStreamVerdict
862            | AttachType::SkMsgVerdict
863            | AttachType::LircMode2
864            | AttachType::FlowDissector
865            | AttachType::TraceRawTp
866            | AttachType::TraceFentry
867            | AttachType::TraceFexit
868            | AttachType::ModifyReturn
869            | AttachType::LsmMac
870            | AttachType::TraceIter
871            | AttachType::XdpDevmap
872            | AttachType::XdpCpumap
873            | AttachType::SkLookup
874            | AttachType::Xdp
875            | AttachType::SkSkbVerdict
876            | AttachType::SkReuseportSelect
877            | AttachType::SkReuseportSelectOrMigrate
878            | AttachType::PerfEvent
879            | AttachType::TraceKprobeMulti
880            | AttachType::LsmCgroup
881            | AttachType::StructOps
882            | AttachType::Netfilter
883            | AttachType::TcxIngress
884            | AttachType::TcxEgress
885            | AttachType::TraceUprobeMulti
886            | AttachType::NetkitPrimary
887            | AttachType::NetkitPeer
888            | AttachType::TraceKprobeSession => {
889                track_stub!(TODO("https://fxbug.dev/322873416"), "BPF_PROG_ATTACH", attach_type);
890                error!(ENOTSUP)
891            }
892
893            AttachType::Unspecified | AttachType::Invalid(_) => {
894                error!(EINVAL)
895            }
896        }
897    }
898}
899
900fn get_capability_for_program(program_type: ProgramType) -> Result<Capabilities, Errno> {
901    match program_type {
902        ProgramType::CgroupSkb
903        | ProgramType::CgroupSock
904        | ProgramType::CgroupSockAddr
905        | ProgramType::CgroupSockopt
906        | ProgramType::CgroupSysctl => Ok(CAP_NET_ADMIN),
907
908        // The following program types cannot be attached with
909        // `bpf(BPF_PROG_ATTACH)` yet.
910        ProgramType::CgroupDevice
911        | ProgramType::Ext
912        | ProgramType::FlowDissector
913        | ProgramType::Kprobe
914        | ProgramType::LircMode2
915        | ProgramType::Lsm
916        | ProgramType::LwtIn
917        | ProgramType::LwtOut
918        | ProgramType::LwtSeg6Local
919        | ProgramType::LwtXmit
920        | ProgramType::Netfilter
921        | ProgramType::PerfEvent
922        | ProgramType::RawTracepoint
923        | ProgramType::RawTracepointWritable
924        | ProgramType::SchedAct
925        | ProgramType::SchedCls
926        | ProgramType::SkLookup
927        | ProgramType::SkMsg
928        | ProgramType::SkReuseport
929        | ProgramType::SkSkb
930        | ProgramType::SocketFilter
931        | ProgramType::SockOps
932        | ProgramType::StructOps
933        | ProgramType::Syscall
934        | ProgramType::Tracepoint
935        | ProgramType::Tracing
936        | ProgramType::Unspec
937        | ProgramType::Xdp => 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}