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, SocketDomain, SocketProtocol, SocketType, ZxioBackedSocket,
17};
18use ebpf::{BpfValue, EbpfProgram, EbpfProgramContext, EbpfPtr, ProgramArgument, Type};
19use ebpf_api::{
20    AttachType, BPF_SOCK_ADDR_TYPE, BPF_SOCK_TYPE, BpfSockContext, CgroupSockAddrProgramContext,
21    CgroupSockOptProgramContext, CgroupSockProgramContext, CurrentTaskContext, Map, MapValueRef,
22    MapsContext, PinnedMap, ProgramType, ReturnValueContext, SocketRef,
23};
24use fidl_fuchsia_net_filter as fnet_filter;
25use fuchsia_component::client::connect_to_protocol_sync;
26use linux_uapi::{bpf_sockopt, uaddr};
27use starnix_logging::{log_error, log_warn, track_stub};
28use starnix_sync::{EbpfStateLock, LockDepRwLock};
29use starnix_syscalls::{SUCCESS, SyscallResult};
30use starnix_uapi::auth::{CAP_NET_ADMIN, CAP_SYS_ADMIN, Capabilities};
31use starnix_uapi::errors::{Errno, ErrnoCode, is_error_return_value};
32use starnix_uapi::{
33    CGROUP2_SUPER_MAGIC, bpf_attr__bindgen_ty_6, bpf_sock, bpf_sock_addr, errno, error, gid_t,
34    pid_t, uid_t,
35};
36use std::ops::{Deref, DerefMut};
37use std::sync::{Arc, OnceLock};
38use zerocopy::FromBytes;
39
40pub type BpfAttachAttr = bpf_attr__bindgen_ty_6;
41
42fn check_root_cgroup_fd(current_task: &CurrentTask, cgroup_fd: FdNumber) -> Result<(), Errno> {
43    let file = current_task.files().get(cgroup_fd)?;
44
45    // Check that `cgroup_fd` is from the CGROUP2 file system.
46    let is_cgroup = file.node().fs().statfs(current_task)?.f_type == CGROUP2_SUPER_MAGIC as i64;
47    if !is_cgroup {
48        log_warn!("bpf_prog_attach(BPF_PROG_ATTACH) is called with an invalid cgroup2 FD.");
49        return error!(EINVAL);
50    }
51
52    // Currently cgroup attachments are supported only for the root cgroup.
53    // TODO(https://fxbug.dev//388077431) Allow attachments to any cgroup once cgroup
54    // hierarchy is moved to starnix_core.
55    let is_root = file
56        .node()
57        .fs()
58        .maybe_root()
59        .map(|root| Arc::ptr_eq(&root.node, file.node()))
60        .unwrap_or(false);
61    if !is_root {
62        log_warn!("bpf_prog_attach(BPF_PROG_ATTACH) is supported only for root cgroup.");
63        return error!(EINVAL);
64    }
65
66    Ok(())
67}
68
69pub fn bpf_prog_attach(
70    current_task: &CurrentTask,
71    attr: BpfAttachAttr,
72) -> Result<SyscallResult, Errno> {
73    // SAFETY: reading i32 field from a union is always safe.
74    let bpf_fd = FdNumber::from_raw(attr.attach_bpf_fd as i32);
75    let object = get_bpf_object(current_task, bpf_fd)?;
76    if matches!(object, BpfHandle::ProgramStub(_)) {
77        log_warn!("Stub program. Faking successful attach");
78        return Ok(SUCCESS);
79    }
80    let program = object.as_program()?.clone();
81
82    if !security::is_task_capable_noaudit(current_task, CAP_SYS_ADMIN) {
83        let required_caps = get_capability_for_program(program.info.program_type)?;
84        security::check_task_capable(current_task, required_caps)?;
85    }
86
87    let attach_type = AttachType::from(attr.attach_type);
88    let program_type = program.info.program_type;
89    if attach_type.get_program_type() != program_type {
90        log_warn!(
91            "bpf_prog_attach(BPF_PROG_ATTACH): program not compatible with attach_type \
92                   attach_type: {attach_type:?}, program_type: {program_type:?}"
93        );
94        return error!(EINVAL);
95    }
96
97    if !attach_type.is_compatible_with_expected_attach_type(program.info.expected_attach_type) {
98        log_warn!(
99            "bpf_prog_attach(BPF_PROG_ATTACH): expected_attach_type didn't match attach_type \
100                   expected_attach_type: {:?}, attach_type: {:?}",
101            program.info.expected_attach_type,
102            attach_type
103        );
104        return error!(EINVAL);
105    }
106
107    // SAFETY: reading i32 field from a union is always safe.
108    let target_fd = unsafe { attr.__bindgen_anon_1.target_fd };
109    let target_fd = FdNumber::from_raw(target_fd as i32);
110
111    current_task.kernel().ebpf_state.attachments.attach_prog(
112        current_task,
113        attach_type,
114        target_fd,
115        program,
116    )
117}
118
119pub fn bpf_prog_detach(
120    current_task: &CurrentTask,
121    attr: BpfAttachAttr,
122) -> Result<SyscallResult, Errno> {
123    let attach_type = AttachType::from(attr.attach_type);
124
125    // SAFETY: reading i32 field from a union is always safe.
126    let target_fd = unsafe { attr.__bindgen_anon_1.target_fd };
127    let target_fd = FdNumber::from_raw(target_fd as i32);
128
129    current_task.kernel().ebpf_state.attachments.detach_prog(current_task, attach_type, target_fd)
130}
131
132// Wrapper for `bpf_sock_addr` used to implement `ProgramArgument` trait.
133#[repr(C)]
134pub struct BpfSockAddr<'a> {
135    sock_addr: bpf_sock_addr,
136
137    bpf_sock: &'a BpfSock<'a>,
138}
139
140impl<'a> Deref for BpfSockAddr<'a> {
141    type Target = bpf_sock_addr;
142    fn deref(&self) -> &Self::Target {
143        &self.sock_addr
144    }
145}
146
147impl<'a> DerefMut for BpfSockAddr<'a> {
148    fn deref_mut(&mut self) -> &mut Self::Target {
149        &mut self.sock_addr
150    }
151}
152
153impl<'a> ProgramArgument for &'_ mut BpfSockAddr<'a> {
154    fn get_type() -> &'static Type {
155        &*BPF_SOCK_ADDR_TYPE
156    }
157}
158
159impl<'a, 'b> SocketRef for &'a mut BpfSockAddr<'a> {
160    fn get_socket_cookie(&self) -> Option<u64> {
161        self.bpf_sock.get_socket_cookie()
162    }
163
164    fn get_socket_uid(&self) -> Option<uid_t> {
165        self.bpf_sock.get_socket_uid()
166    }
167}
168
169// Context for eBPF programs of type BPF_PROG_TYPE_CGROUP_SOCKADDR.
170struct SockAddrProgram(EbpfProgram<SockAddrProgram>);
171
172impl EbpfProgramContext for SockAddrProgram {
173    type RunContext<'a> = EbpfRunContextImpl<'a>;
174    type Packet<'a> = ();
175    type Arg1<'a> = &'a mut BpfSockAddr<'a>;
176    type Arg2<'a> = ();
177    type Arg3<'a> = ();
178    type Arg4<'a> = ();
179    type Arg5<'a> = ();
180
181    type Map = PinnedMap;
182}
183
184ebpf_api::ebpf_program_context_type!(SockAddrProgram, CgroupSockAddrProgramContext);
185
186#[derive(Debug, PartialEq, Eq)]
187pub enum SockAddrProgramResult {
188    Allow,
189    Block,
190}
191
192impl SockAddrProgram {
193    fn run<'a>(
194        &self,
195        current_task: &'a CurrentTask,
196        addr: &'a mut BpfSockAddr<'a>,
197        can_block: bool,
198    ) -> SockAddrProgramResult {
199        let mut run_context = EbpfRunContextImpl::new(current_task);
200        match self.0.run_with_1_argument(&mut run_context, addr) {
201            // UDP_RECVMSG programs are not allowed to block the packet.
202            0 if can_block => SockAddrProgramResult::Block,
203            1 => SockAddrProgramResult::Allow,
204            result => {
205                // TODO(https://fxbug.dev/413490751): Change this to panic once
206                // result validation is implemented in the eBPF verifier.
207                log_error!("eBPF program returned invalid result: {}", result);
208                SockAddrProgramResult::Allow
209            }
210        }
211    }
212}
213
214type AttachedSockAddrProgramCell = LockDepRwLock<Option<SockAddrProgram>, EbpfStateLock>;
215
216// Wrapper for `bpf_sock` used to implement `ProgramArgument` trait.
217#[repr(C)]
218pub struct BpfSock<'a> {
219    // Must be first field.
220    value: bpf_sock,
221
222    socket: Option<&'a ZxioBackedSocket>,
223}
224
225impl<'a> BpfSock<'a> {
226    fn from_socket(socket: &'a Socket) -> Self {
227        Self {
228            value: bpf_sock {
229                family: socket.domain.as_raw().into(),
230                type_: socket.socket_type.as_raw(),
231                protocol: socket.protocol.as_raw(),
232                ..Default::default()
233            },
234            socket: socket.downcast_socket(),
235        }
236    }
237}
238
239impl<'a> Deref for BpfSock<'a> {
240    type Target = bpf_sock;
241    fn deref(&self) -> &Self::Target {
242        &self.value
243    }
244}
245
246impl<'a> DerefMut for BpfSock<'a> {
247    fn deref_mut(&mut self) -> &mut Self::Target {
248        &mut self.value
249    }
250}
251
252impl<'a> ProgramArgument for &'_ BpfSock<'a> {
253    fn get_type() -> &'static Type {
254        &*BPF_SOCK_TYPE
255    }
256}
257
258impl<'a> SocketRef for &'_ BpfSock<'a> {
259    fn get_socket_cookie(&self) -> Option<u64> {
260        self.socket.and_then(|socket| {
261            socket
262                .get_socket_cookie()
263                .inspect_err(|errno| log_error!("Failed to get socket cookie: {:?}", errno))
264                .ok()
265        })
266    }
267
268    fn get_socket_uid(&self) -> Option<uid_t> {
269        self.socket.map(|socket| socket.uid())
270    }
271}
272
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                ..Default::default()
653            },
654            socket: Some(socket),
655        };
656
657        prog.run(current_task, &bpf_sock)
658    }
659
660    pub fn run_getsockopt_prog(
661        &self,
662        current_task: &CurrentTask,
663        level: u32,
664        optname: u32,
665        value_buf: Vec<u8>,
666        optlen: usize,
667        error: Option<Errno>,
668        socket: &Socket,
669    ) -> Result<(Vec<u8>, usize), Errno> {
670        let prog_guard = self.get_sockopt.read();
671        let Some(prog) = prog_guard.as_ref() else {
672            return error.map(|e| Err(e)).unwrap_or_else(|| Ok((value_buf, optlen)));
673        };
674
675        let bpf_sock = BpfSock::from_socket(socket);
676
677        let retval = error.as_ref().map(|e| -(e.code.error_code() as i32)).unwrap_or(0);
678        let mut bpf_sockopt = BpfSockOptWithValue::new(
679            level,
680            optname,
681            value_buf.clone(),
682            optlen as u32,
683            retval,
684            &bpf_sock,
685        );
686
687        // Run the program.
688        let result = prog.run(current_task, &mut bpf_sockopt);
689
690        let retval = bpf_sockopt.retval;
691
692        let retval = match result {
693            0 if is_error_return_value(retval) => retval,
694            0 => -(linux_uapi::EPERM as i32),
695            1 => retval,
696            _ => {
697                // TODO(https://fxbug.dev/413490751): Change this to panic once
698                // result validation is implemented in the verifier.
699                log_error!("eBPF getsockopt program returned invalid result: {}", result);
700                retval
701            }
702        };
703
704        if retval < 0 {
705            return Err(Errno::new(ErrnoCode::from_error_code(-retval as i16)));
706        }
707
708        let new_optlen = bpf_sockopt.optlen;
709
710        match usize::try_from(new_optlen) {
711            // Fail if the program set an invalid `optlen`.
712            Err(_) => error!(EFAULT),
713            Ok(new_optlen) if new_optlen > value_buf.len() => error!(EFAULT),
714
715            // If `optlen` is set to 0 then proceed with the original value.
716            Ok(0) => Ok((value_buf, optlen)),
717
718            Ok(new_optlen) => Ok((bpf_sockopt.take_value(), new_optlen)),
719        }
720    }
721
722    pub fn run_setsockopt_prog(
723        &self,
724        current_task: &CurrentTask,
725        level: u32,
726        optname: u32,
727        value: SockOptValue,
728        socket: &Socket,
729    ) -> SetSockOptProgramResult {
730        let prog_guard = self.set_sockopt.read();
731        let Some(prog) = prog_guard.as_ref() else {
732            return SetSockOptProgramResult::Allow(value);
733        };
734
735        let page_size = *PAGE_SIZE as usize;
736
737        // Read only the first page from the user-specified buffer in case it's
738        // larger than that.
739        let buffer = match value.read_bytes(current_task, page_size) {
740            Ok(buffer) => buffer,
741            Err(err) => return SetSockOptProgramResult::Fail(err),
742        };
743
744        let bpf_sock = BpfSock::from_socket(socket);
745
746        let buffer_len = buffer.len();
747        let optlen = value.len();
748        let mut bpf_sockopt =
749            BpfSockOptWithValue::new(level, optname, buffer, optlen as u32, 0, &bpf_sock);
750        let result = prog.run(current_task, &mut bpf_sockopt);
751
752        let retval = bpf_sockopt.retval;
753
754        let retval = match result {
755            0 if is_error_return_value(retval) => retval,
756            0 => -(linux_uapi::EPERM as i32),
757            1 => retval,
758            _ => {
759                // TODO(https://fxbug.dev/413490751): Change this to panic once
760                // result validation is implemented in the verifier.
761                log_error!("eBPF getsockopt program returned invalid result: {}", result);
762                retval
763            }
764        };
765
766        if retval < 0 {
767            return SetSockOptProgramResult::Fail(Errno::new(ErrnoCode::from_error_code(
768                -retval as i16,
769            )));
770        }
771
772        match bpf_sockopt.optlen {
773            // `setsockopt` programs can bypass the platform implementation by
774            // setting `optlen` to -1.
775            -1 => SetSockOptProgramResult::Bypass,
776
777            // If the original value is larger than a page and the program
778            // didn't change `optlen` then return the original value. This
779            // allows to avoid `EFAULT` below with a no-op program.
780            new_optlen if optlen > page_size && (new_optlen as usize) == optlen => {
781                SetSockOptProgramResult::Allow(value)
782            }
783
784            // Fail if the program has set an invalid `optlen` (except for the
785            // case handled above).
786            optlen if optlen < 0 || (optlen as usize) > buffer_len => {
787                SetSockOptProgramResult::Fail(errno!(EFAULT))
788            }
789
790            // If `optlen` is set to 0 then proceed with the original value.
791            0 => SetSockOptProgramResult::Allow(value),
792
793            // Return value from `bpf_sockbuf` - it may be different from the
794            // original value.
795            optlen => {
796                let mut value = bpf_sockopt.take_value();
797                value.resize(optlen as usize, 0);
798                SetSockOptProgramResult::Allow(value.into())
799            }
800        }
801    }
802}
803
804fn attach_type_to_netstack_hook(attach_type: AttachType) -> Option<fnet_filter::SocketHook> {
805    let hook = match attach_type {
806        AttachType::CgroupInetEgress => fnet_filter::SocketHook::Egress,
807        AttachType::CgroupInetIngress => fnet_filter::SocketHook::Ingress,
808        _ => return None,
809    };
810    Some(hook)
811}
812
813// Defined a location where eBPF programs can be attached.
814#[derive(Copy, Clone, Debug, PartialEq, Eq)]
815enum AttachLocation {
816    // Attached in Starnix kernel.
817    Kernel,
818
819    // Attached in Netstack.
820    Netstack,
821}
822
823impl TryFrom<AttachType> for AttachLocation {
824    type Error = Errno;
825
826    fn try_from(attach_type: AttachType) -> Result<Self, Self::Error> {
827        match attach_type {
828            AttachType::CgroupInet4Bind
829            | AttachType::CgroupInet6Bind
830            | AttachType::CgroupInet4Connect
831            | AttachType::CgroupInet6Connect
832            | AttachType::CgroupUdp4Sendmsg
833            | AttachType::CgroupUdp6Sendmsg
834            | AttachType::CgroupUdp4Recvmsg
835            | AttachType::CgroupUdp6Recvmsg
836            | AttachType::CgroupInetSockCreate
837            | AttachType::CgroupInetSockRelease
838            | AttachType::CgroupGetsockopt
839            | AttachType::CgroupSetsockopt => Ok(AttachLocation::Kernel),
840
841            AttachType::CgroupInetEgress | AttachType::CgroupInetIngress => {
842                Ok(AttachLocation::Netstack)
843            }
844
845            AttachType::CgroupDevice
846            | AttachType::CgroupInet4Getpeername
847            | AttachType::CgroupInet4Getsockname
848            | AttachType::CgroupInet4PostBind
849            | AttachType::CgroupInet6Getpeername
850            | AttachType::CgroupInet6Getsockname
851            | AttachType::CgroupInet6PostBind
852            | AttachType::CgroupSysctl
853            | AttachType::CgroupUnixConnect
854            | AttachType::CgroupUnixGetpeername
855            | AttachType::CgroupUnixGetsockname
856            | AttachType::CgroupUnixRecvmsg
857            | AttachType::CgroupUnixSendmsg
858            | AttachType::CgroupSockOps
859            | AttachType::SkSkbStreamParser
860            | AttachType::SkSkbStreamVerdict
861            | AttachType::SkMsgVerdict
862            | AttachType::LircMode2
863            | AttachType::FlowDissector
864            | AttachType::TraceRawTp
865            | AttachType::TraceFentry
866            | AttachType::TraceFexit
867            | AttachType::ModifyReturn
868            | AttachType::LsmMac
869            | AttachType::TraceIter
870            | AttachType::XdpDevmap
871            | AttachType::XdpCpumap
872            | AttachType::SkLookup
873            | AttachType::Xdp
874            | AttachType::SkSkbVerdict
875            | AttachType::SkReuseportSelect
876            | AttachType::SkReuseportSelectOrMigrate
877            | AttachType::PerfEvent
878            | AttachType::TraceKprobeMulti
879            | AttachType::LsmCgroup
880            | AttachType::StructOps
881            | AttachType::Netfilter
882            | AttachType::TcxIngress
883            | AttachType::TcxEgress
884            | AttachType::TraceUprobeMulti
885            | AttachType::NetkitPrimary
886            | AttachType::NetkitPeer
887            | AttachType::TraceKprobeSession => {
888                track_stub!(TODO("https://fxbug.dev/322873416"), "BPF_PROG_ATTACH", attach_type);
889                error!(ENOTSUP)
890            }
891
892            AttachType::Unspecified | AttachType::Invalid(_) => {
893                error!(EINVAL)
894            }
895        }
896    }
897}
898
899fn get_capability_for_program(program_type: ProgramType) -> Result<Capabilities, Errno> {
900    match program_type {
901        ProgramType::CgroupSkb
902        | ProgramType::CgroupSock
903        | ProgramType::CgroupSockAddr
904        | ProgramType::CgroupSockopt
905        | ProgramType::CgroupSysctl => Ok(CAP_NET_ADMIN),
906
907        // The following program types cannot be attached with
908        // `bpf(BPF_PROG_ATTACH)` yet.
909        ProgramType::CgroupDevice
910        | ProgramType::Ext
911        | ProgramType::FlowDissector
912        | ProgramType::Kprobe
913        | ProgramType::LircMode2
914        | ProgramType::Lsm
915        | ProgramType::LwtIn
916        | ProgramType::LwtOut
917        | ProgramType::LwtSeg6Local
918        | ProgramType::LwtXmit
919        | ProgramType::Netfilter
920        | ProgramType::PerfEvent
921        | ProgramType::RawTracepoint
922        | ProgramType::RawTracepointWritable
923        | ProgramType::SchedAct
924        | ProgramType::SchedCls
925        | ProgramType::SkLookup
926        | ProgramType::SkMsg
927        | ProgramType::SkReuseport
928        | ProgramType::SkSkb
929        | ProgramType::SocketFilter
930        | ProgramType::SockOps
931        | ProgramType::StructOps
932        | ProgramType::Syscall
933        | ProgramType::Tracepoint
934        | ProgramType::Tracing
935        | ProgramType::Unspec
936        | ProgramType::Xdp
937        | ProgramType::Fuse => error!(ENOTSUP),
938    }
939}
940
941#[derive(Default)]
942pub struct EbpfAttachments {
943    root_cgroup: CgroupEbpfProgramSet,
944    socket_control: OnceLock<fnet_filter::SocketControlSynchronousProxy>,
945}
946
947impl EbpfAttachments {
948    pub fn root_cgroup(&self) -> &CgroupEbpfProgramSet {
949        &self.root_cgroup
950    }
951
952    fn socket_control(&self) -> &fnet_filter::SocketControlSynchronousProxy {
953        self.socket_control.get_or_init(|| {
954            connect_to_protocol_sync::<fnet_filter::SocketControlMarker>()
955                .expect("Failed to connect to fuchsia.net.filter.SocketControl.")
956        })
957    }
958
959    fn attach_prog(
960        &self,
961        current_task: &CurrentTask,
962        attach_type: AttachType,
963        target_fd: FdNumber,
964        program: ProgramHandle,
965    ) -> Result<SyscallResult, Errno> {
966        let location: AttachLocation = attach_type.try_into()?;
967        let program_type = attach_type.get_program_type();
968        match (location, program_type) {
969            (AttachLocation::Kernel, ProgramType::CgroupSockAddr) => {
970                check_root_cgroup_fd(current_task, target_fd)?;
971
972                let linked_program = SockAddrProgram(program.link(attach_type.get_program_type())?);
973                *self.root_cgroup.get_sock_addr_program(attach_type)?.write() =
974                    Some(linked_program);
975
976                Ok(SUCCESS)
977            }
978
979            (AttachLocation::Kernel, ProgramType::CgroupSock) => {
980                check_root_cgroup_fd(current_task, target_fd)?;
981
982                let linked_program = SockProgram(program.link(attach_type.get_program_type())?);
983                *self.root_cgroup.get_sock_program(attach_type)?.write() = Some(linked_program);
984
985                Ok(SUCCESS)
986            }
987
988            (AttachLocation::Kernel, ProgramType::CgroupSockopt) => {
989                check_root_cgroup_fd(current_task, target_fd)?;
990
991                let linked_program = SockOptProgram(program.link(attach_type.get_program_type())?);
992                *self.root_cgroup.get_sock_opt_program(attach_type)?.write() = Some(linked_program);
993
994                Ok(SUCCESS)
995            }
996
997            (AttachLocation::Kernel, _) => {
998                unreachable!();
999            }
1000
1001            (AttachLocation::Netstack, _) => {
1002                check_root_cgroup_fd(current_task, target_fd)?;
1003                self.attach_prog_in_netstack(attach_type, program)
1004            }
1005        }
1006    }
1007
1008    fn detach_prog(
1009        &self,
1010        current_task: &CurrentTask,
1011        attach_type: AttachType,
1012        target_fd: FdNumber,
1013    ) -> Result<SyscallResult, Errno> {
1014        let location = attach_type.try_into()?;
1015        let program_type = attach_type.get_program_type();
1016        match (location, program_type) {
1017            (AttachLocation::Kernel, ProgramType::CgroupSockAddr) => {
1018                check_root_cgroup_fd(current_task, target_fd)?;
1019
1020                let mut prog_guard = self.root_cgroup.get_sock_addr_program(attach_type)?.write();
1021                if prog_guard.is_none() {
1022                    return error!(ENOENT);
1023                }
1024
1025                *prog_guard = None;
1026
1027                Ok(SUCCESS)
1028            }
1029
1030            (AttachLocation::Kernel, ProgramType::CgroupSock) => {
1031                check_root_cgroup_fd(current_task, target_fd)?;
1032
1033                let mut prog_guard = self.root_cgroup.get_sock_program(attach_type)?.write();
1034                if prog_guard.is_none() {
1035                    return error!(ENOENT);
1036                }
1037
1038                *prog_guard = None;
1039
1040                Ok(SUCCESS)
1041            }
1042
1043            (AttachLocation::Kernel, ProgramType::CgroupSockopt) => {
1044                check_root_cgroup_fd(current_task, target_fd)?;
1045
1046                let mut prog_guard = self.root_cgroup.get_sock_opt_program(attach_type)?.write();
1047                if prog_guard.is_none() {
1048                    return error!(ENOENT);
1049                }
1050
1051                *prog_guard = None;
1052
1053                Ok(SUCCESS)
1054            }
1055
1056            (AttachLocation::Kernel, _) => {
1057                unreachable!();
1058            }
1059
1060            (AttachLocation::Netstack, _) => {
1061                check_root_cgroup_fd(current_task, target_fd)?;
1062                self.detach_prog_in_netstack(attach_type)
1063            }
1064        }
1065    }
1066
1067    fn attach_prog_in_netstack(
1068        &self,
1069        attach_type: AttachType,
1070        program: ProgramHandle,
1071    ) -> Result<SyscallResult, Errno> {
1072        let hook = attach_type_to_netstack_hook(attach_type).ok_or_else(|| errno!(ENOTSUP))?;
1073        let opts = fnet_filter::AttachEbpfProgramOptions {
1074            hook: Some(hook),
1075            program: Some((&**program).try_into()?),
1076            ..Default::default()
1077        };
1078        self.socket_control()
1079            .attach_ebpf_program(opts, zx::MonotonicInstant::INFINITE)
1080            .map_err(|e| {
1081                log_error!(
1082                    "failed to send fuchsia.net.filter/SocketControl.AttachEbpfProgram: {}",
1083                    e
1084                );
1085                errno!(EIO)
1086            })?
1087            .map_err(|e| {
1088                use fnet_filter::SocketControlAttachEbpfProgramError as Error;
1089                match e {
1090                    Error::NotSupported => errno!(ENOTSUP),
1091                    Error::LinkFailed => errno!(EINVAL),
1092                    Error::MapFailed => errno!(EIO),
1093                    Error::DuplicateAttachment => errno!(EEXIST),
1094                }
1095            })?;
1096
1097        Ok(SUCCESS)
1098    }
1099
1100    fn detach_prog_in_netstack(&self, attach_type: AttachType) -> Result<SyscallResult, Errno> {
1101        let hook = attach_type_to_netstack_hook(attach_type).ok_or_else(|| errno!(ENOTSUP))?;
1102        self.socket_control()
1103            .detach_ebpf_program(hook, zx::MonotonicInstant::INFINITE)
1104            .map_err(|e| {
1105                log_error!(
1106                    "failed to send fuchsia.net.filter/SocketControl.DetachEbpfProgram: {}",
1107                    e
1108                );
1109                errno!(EIO)
1110            })?
1111            .map_err(|e| {
1112                use fnet_filter::SocketControlDetachEbpfProgramError as Error;
1113                match e {
1114                    Error::NotFound => errno!(ENOENT),
1115                }
1116            })?;
1117        Ok(SUCCESS)
1118    }
1119}