1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//! Features for miscellaneous eBPF subsystem properties
use bpf_rs::{
    insns::{alu64_imm, exit, jmp32_imm, jmp_imm, mov64_imm, AluOp, JmpOp, Register},
    libbpf_sys::{bpf_insn, bpf_prog_load, BPF_MAXINSNS},
    ProgramLicense, ProgramType,
};
use nix::{
    errno::{errno, Errno},
    unistd,
};
use std::ptr;

#[cfg(feature="serde")]
use serde::Serialize;

#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Misc {
    pub large_insn_limit: bool,
    pub bounded_loops: bool,
    pub isa_v2_ext: bool,
    pub isa_v3_ext: bool,
}

impl Misc {
    fn load_insns(insns: Vec<bpf_insn>) -> bool {
        Errno::clear();
        let fd = unsafe {
            bpf_prog_load(
                ProgramType::SocketFilter.into(),
                ptr::null(),
                ProgramLicense::GPL.as_ptr(),
                insns.as_ptr(),
                u64::try_from(insns.len()).unwrap_or(0u64),
                ptr::null(),
            )
        };

        let success = fd >= 0 || errno() == 0;

        if fd >= 0 {
            let _ = unistd::close(fd);
        }

        success
    }

    fn probe_large_insn_limit() -> bool {
        let max_insns = usize::try_from(BPF_MAXINSNS).unwrap();
        let mut large_insn_prog = vec![mov64_imm(Register::R0, 1); max_insns + 1];
        large_insn_prog[max_insns] = exit();
        Self::load_insns(large_insn_prog)
    }

    fn probe_bounded_loops() -> bool {
        let insns = vec![
            mov64_imm(Register::R0, 10),
            alu64_imm(AluOp::SUB, Register::R0, 1),
            jmp_imm(JmpOp::JNE, Register::R0, 0, -2),
            exit(),
        ];
        Self::load_insns(insns)
    }

    fn probe_isa_v2() -> bool {
        let insns = vec![
            mov64_imm(Register::R0, 0),
            jmp_imm(JmpOp::JLT, Register::R0, 0, 1),
            mov64_imm(Register::R0, 1),
            exit(),
        ];
        Self::load_insns(insns)
    }

    fn probe_isa_v3() -> bool {
        let insns = vec![
            mov64_imm(Register::R0, 0),
            jmp32_imm(JmpOp::JLT, Register::R0, 0, 1),
            mov64_imm(Register::R0, 1),
            exit(),
        ];
        Self::load_insns(insns)
    }
}

pub fn features() -> Misc {
    Misc {
        large_insn_limit: Misc::probe_large_insn_limit(),
        bounded_loops: Misc::probe_bounded_loops(),
        isa_v2_ext: Misc::probe_isa_v2(),
        isa_v3_ext: Misc::probe_isa_v3(),
    }
}