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::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    // 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    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    // SAFETY: reading i32 field from a union is always safe.
109    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    // SAFETY: reading i32 field from a union is always safe.
127    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// Wrapper for `bpf_sock_addr` used to implement `ProgramArgument` trait.
134#[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
170// Context for eBPF programs of type BPF_PROG_TYPE_CGROUP_SOCKADDR.
171struct 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            // UDP_RECVMSG programs are not allowed to block the packet.
203            0 if can_block => SockAddrProgramResult::Block,
204            1 => SockAddrProgramResult::Allow,
205            result => {
206                // TODO(https://fxbug.dev/413490751): Change this to panic once
207                // result validation is implemented in the eBPF verifier.
208                log_error!("eBPF program returned invalid result: {}", result);
209                SockAddrProgramResult::Allow
210            }
211        }
212    }
213}
214
215type AttachedSockAddrProgramCell = LockDepRwLock<Option<SockAddrProgram>, EbpfStateLock>;
216
217// Wrapper for `bpf_sock` used to implement `ProgramArgument` trait.
218#[repr(C)]
219pub struct BpfSock<'a> {
220    // Must be first field.
221    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
275// Context for eBPF programs of type BPF_PROG_TYPE_CGROUP_SOCK.
276struct 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    // Wrapper for `bpf_sockopt` that implements `ProgramArgument` trait.
320    #[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    /// Wrapper for `bpf_sockopt` that keeps a buffer for the `optval`.
331    pub struct BpfSockOptWithValue {
332        sockopt: BpfSockOpt,
333
334        // Buffer used to store the option value. A pointer to the buffer
335        // contents is stored in `sockopt`. `Vec::as_mut_ptr()` guarantees that
336        // the pointer remains valid only as long as the `Vec` is not modified,
337        // so this field should not be updated directly. `take_value()` can be
338        // used to extract the value when `BpfSockOpt` is no longer needed.
339        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            // SAFETY: Setting buffer bounds in unions is safe.
363            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        // Returns the value. Consumes `self` since it's not safe to use again
382        // after the value buffer is moved.
383        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
398// Context for eBPF programs of type BPF_PROG_TYPE_CGROUP_SOCKOPT.
399struct SockOptProgram(EbpfProgram<SockOptProgram>);
400
401// RunContext for eBPF programs of type BPF_PROG_TYPE_CGROUP_SOCKOPT.
402pub struct SockOptEbpfRunContextImpl<'a> {
403    ebpf_run_context: EbpfRunContextImpl<'a>,
404
405    // Pointer to the BpfSockOpt passed to the program. Used for
406    // `bpf_set_retval` and `bpf_get_retval`.
407    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 the syscall.
469    Fail(Errno),
470
471    /// Proceed with the specified option value.
472    Allow(SockOptValue),
473
474    /// Return to userspace without invoking the underlying implementation of
475    /// setsockopt.
476    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    // Executes eBPF program for the operation `op`. `socket_address` contains
563    // socket address as a `sockaddr` struct.
564    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                // SAFETY: reading an array of u32 from a union is safe.
618                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        // UDP recvmsg programs are not allowed to filter packets.
627        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        // Run the program.
691        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                // TODO(https://fxbug.dev/413490751): Change this to panic once
701                // result validation is implemented in the verifier.
702                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            // Fail if the program set an invalid `optlen`.
715            Err(_) => error!(EFAULT),
716            Ok(new_optlen) if new_optlen > value_buf.len() => error!(EFAULT),
717
718            // If `optlen` is set to 0 then proceed with the original value.
719            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        // Read only the first page from the user-specified buffer in case it's
741        // larger than that.
742        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                // TODO(https://fxbug.dev/413490751): Change this to panic once
763                // result validation is implemented in the verifier.
764                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            // `setsockopt` programs can bypass the platform implementation by
777            // setting `optlen` to -1.
778            -1 => SetSockOptProgramResult::Bypass,
779
780            // If the original value is larger than a page and the program
781            // didn't change `optlen` then return the original value. This
782            // allows to avoid `EFAULT` below with a no-op program.
783            new_optlen if optlen > page_size && (new_optlen as usize) == optlen => {
784                SetSockOptProgramResult::Allow(value)
785            }
786
787            // Fail if the program has set an invalid `optlen` (except for the
788            // case handled above).
789            optlen if optlen < 0 || (optlen as usize) > buffer_len => {
790                SetSockOptProgramResult::Fail(errno!(EFAULT))
791            }
792
793            // If `optlen` is set to 0 then proceed with the original value.
794            0 => SetSockOptProgramResult::Allow(value),
795
796            // Return value from `bpf_sockbuf` - it may be different from the
797            // original value.
798            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// Defined a location where eBPF programs can be attached.
817#[derive(Copy, Clone, Debug, PartialEq, Eq)]
818enum AttachLocation {
819    // Attached in Starnix kernel.
820    Kernel,
821
822    // Attached in Netstack.
823    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        // The following program types cannot be attached with
911        // `bpf(BPF_PROG_ATTACH)` yet.
912        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}