Browse Source

programs: add support for attaching and detaching TC programs

This change adds support for attaching TC programs directly from aya, without
having to use iproute2/tc.
Arnabjyoti Kalita 3 years ago
parent
commit
6974d349e8

+ 3 - 1
aya/include/linux_wrapper.h

@@ -4,8 +4,10 @@
 #include <linux/if_link.h>
 #include <linux/rtnetlink.h>
 #include <asm-generic/socket.h>
+#include <linux/pkt_sched.h>
+#include <linux/pkt_cls.h>
 
 /* workaround the fact that bindgen can't parse the IOC macros */
 int AYA_PERF_EVENT_IOC_ENABLE = PERF_EVENT_IOC_ENABLE;
 int AYA_PERF_EVENT_IOC_DISABLE = PERF_EVENT_IOC_DISABLE;
-int AYA_PERF_EVENT_IOC_SET_BPF = PERF_EVENT_IOC_SET_BPF;
+int AYA_PERF_EVENT_IOC_SET_BPF = PERF_EVENT_IOC_SET_BPF;

+ 151 - 61
aya/src/generated/linux_bindings_aarch64.rs

@@ -136,6 +136,16 @@ pub const XDP_FLAGS_MODES: u32 = 14;
 pub const XDP_FLAGS_MASK: u32 = 31;
 pub const SO_ATTACH_BPF: u32 = 50;
 pub const SO_DETACH_BPF: u32 = 27;
+pub const TC_H_MAJ_MASK: u32 = 4294901760;
+pub const TC_H_MIN_MASK: u32 = 65535;
+pub const TC_H_UNSPEC: u32 = 0;
+pub const TC_H_ROOT: u32 = 4294967295;
+pub const TC_H_INGRESS: u32 = 4294967281;
+pub const TC_H_CLSACT: u32 = 4294967281;
+pub const TC_H_MIN_PRIORITY: u32 = 65504;
+pub const TC_H_MIN_INGRESS: u32 = 65522;
+pub const TC_H_MIN_EGRESS: u32 = 65523;
+pub const TCA_BPF_FLAG_ACT_DIRECT: u32 = 1;
 pub type __u8 = ::std::os::raw::c_uchar;
 pub type __s16 = ::std::os::raw::c_short;
 pub type __u16 = ::std::os::raw::c_ushort;
@@ -600,6 +610,68 @@ pub struct bpf_attr__bindgen_ty_19 {
 }
 #[repr(C)]
 #[derive(Debug, Copy, Clone)]
+pub struct bpf_prog_info {
+    pub type_: __u32,
+    pub id: __u32,
+    pub tag: [__u8; 8usize],
+    pub jited_prog_len: __u32,
+    pub xlated_prog_len: __u32,
+    pub jited_prog_insns: __u64,
+    pub xlated_prog_insns: __u64,
+    pub load_time: __u64,
+    pub created_by_uid: __u32,
+    pub nr_map_ids: __u32,
+    pub map_ids: __u64,
+    pub name: [::std::os::raw::c_char; 16usize],
+    pub ifindex: __u32,
+    pub _bitfield_align_1: [u8; 0],
+    pub _bitfield_1: __BindgenBitfieldUnit<[u8; 4usize]>,
+    pub netns_dev: __u64,
+    pub netns_ino: __u64,
+    pub nr_jited_ksyms: __u32,
+    pub nr_jited_func_lens: __u32,
+    pub jited_ksyms: __u64,
+    pub jited_func_lens: __u64,
+    pub btf_id: __u32,
+    pub func_info_rec_size: __u32,
+    pub func_info: __u64,
+    pub nr_func_info: __u32,
+    pub nr_line_info: __u32,
+    pub line_info: __u64,
+    pub jited_line_info: __u64,
+    pub nr_jited_line_info: __u32,
+    pub line_info_rec_size: __u32,
+    pub jited_line_info_rec_size: __u32,
+    pub nr_prog_tags: __u32,
+    pub prog_tags: __u64,
+    pub run_time_ns: __u64,
+    pub run_cnt: __u64,
+    pub recursion_misses: __u64,
+}
+impl bpf_prog_info {
+    #[inline]
+    pub fn gpl_compatible(&self) -> __u32 {
+        unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u32) }
+    }
+    #[inline]
+    pub fn set_gpl_compatible(&mut self, val: __u32) {
+        unsafe {
+            let val: u32 = ::std::mem::transmute(val);
+            self._bitfield_1.set(0usize, 1u8, val as u64)
+        }
+    }
+    #[inline]
+    pub fn new_bitfield_1(gpl_compatible: __u32) -> __BindgenBitfieldUnit<[u8; 4usize]> {
+        let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 4usize]> = Default::default();
+        __bindgen_bitfield_unit.set(0usize, 1u8, {
+            let gpl_compatible: u32 = unsafe { ::std::mem::transmute(gpl_compatible) };
+            gpl_compatible as u64
+        });
+        __bindgen_bitfield_unit
+    }
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
 pub struct btf_header {
     pub magic: __u16,
     pub version: __u8,
@@ -689,7 +761,7 @@ pub enum perf_sw_ids {
     PERF_COUNT_SW_BPF_OUTPUT = 10,
     PERF_COUNT_SW_MAX = 11,
 }
-#[repr(u64)]
+#[repr(u32)]
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub enum perf_event_sample_format {
     PERF_SAMPLE_IP = 1,
@@ -713,7 +785,6 @@ pub enum perf_event_sample_format {
     PERF_SAMPLE_REGS_INTR = 262144,
     PERF_SAMPLE_PHYS_ADDR = 524288,
     PERF_SAMPLE_MAX = 1048576,
-    __PERF_SAMPLE_CALLCHAIN_EARLY = 9223372036854775808,
 }
 #[repr(C)]
 #[derive(Copy, Clone)]
@@ -724,7 +795,7 @@ pub struct perf_event_attr {
     pub __bindgen_anon_1: perf_event_attr__bindgen_ty_1,
     pub sample_type: __u64,
     pub read_format: __u64,
-    pub _bitfield_align_1: [u32; 0],
+    pub _bitfield_align_1: [u64; 0],
     pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>,
     pub __bindgen_anon_2: perf_event_attr__bindgen_ty_2,
     pub bp_type: __u32,
@@ -757,8 +828,6 @@ pub union perf_event_attr__bindgen_ty_2 {
 #[derive(Copy, Clone)]
 pub union perf_event_attr__bindgen_ty_3 {
     pub bp_addr: __u64,
-    pub kprobe_func: __u64,
-    pub uprobe_path: __u64,
     pub config1: __u64,
     _bindgen_union_align: u64,
 }
@@ -766,8 +835,6 @@ pub union perf_event_attr__bindgen_ty_3 {
 #[derive(Copy, Clone)]
 pub union perf_event_attr__bindgen_ty_4 {
     pub bp_len: __u64,
-    pub kprobe_addr: __u64,
-    pub probe_offset: __u64,
     pub config2: __u64,
     _bindgen_union_align: u64,
 }
@@ -1081,47 +1148,14 @@ impl perf_event_attr {
         }
     }
     #[inline]
-    pub fn ksymbol(&self) -> __u64 {
-        unsafe { ::std::mem::transmute(self._bitfield_1.get(29usize, 1u8) as u64) }
-    }
-    #[inline]
-    pub fn set_ksymbol(&mut self, val: __u64) {
-        unsafe {
-            let val: u64 = ::std::mem::transmute(val);
-            self._bitfield_1.set(29usize, 1u8, val as u64)
-        }
-    }
-    #[inline]
-    pub fn bpf_event(&self) -> __u64 {
-        unsafe { ::std::mem::transmute(self._bitfield_1.get(30usize, 1u8) as u64) }
-    }
-    #[inline]
-    pub fn set_bpf_event(&mut self, val: __u64) {
-        unsafe {
-            let val: u64 = ::std::mem::transmute(val);
-            self._bitfield_1.set(30usize, 1u8, val as u64)
-        }
-    }
-    #[inline]
-    pub fn aux_output(&self) -> __u64 {
-        unsafe { ::std::mem::transmute(self._bitfield_1.get(31usize, 1u8) as u64) }
-    }
-    #[inline]
-    pub fn set_aux_output(&mut self, val: __u64) {
-        unsafe {
-            let val: u64 = ::std::mem::transmute(val);
-            self._bitfield_1.set(31usize, 1u8, val as u64)
-        }
-    }
-    #[inline]
     pub fn __reserved_1(&self) -> __u64 {
-        unsafe { ::std::mem::transmute(self._bitfield_1.get(32usize, 32u8) as u64) }
+        unsafe { ::std::mem::transmute(self._bitfield_1.get(29usize, 35u8) as u64) }
     }
     #[inline]
     pub fn set___reserved_1(&mut self, val: __u64) {
         unsafe {
             let val: u64 = ::std::mem::transmute(val);
-            self._bitfield_1.set(32usize, 32u8, val as u64)
+            self._bitfield_1.set(29usize, 35u8, val as u64)
         }
     }
     #[inline]
@@ -1154,9 +1188,6 @@ impl perf_event_attr {
         context_switch: __u64,
         write_backward: __u64,
         namespaces: __u64,
-        ksymbol: __u64,
-        bpf_event: __u64,
-        aux_output: __u64,
         __reserved_1: __u64,
     ) -> __BindgenBitfieldUnit<[u8; 8usize]> {
         let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default();
@@ -1274,19 +1305,7 @@ impl perf_event_attr {
             let namespaces: u64 = unsafe { ::std::mem::transmute(namespaces) };
             namespaces as u64
         });
-        __bindgen_bitfield_unit.set(29usize, 1u8, {
-            let ksymbol: u64 = unsafe { ::std::mem::transmute(ksymbol) };
-            ksymbol as u64
-        });
-        __bindgen_bitfield_unit.set(30usize, 1u8, {
-            let bpf_event: u64 = unsafe { ::std::mem::transmute(bpf_event) };
-            bpf_event as u64
-        });
-        __bindgen_bitfield_unit.set(31usize, 1u8, {
-            let aux_output: u64 = unsafe { ::std::mem::transmute(aux_output) };
-            aux_output as u64
-        });
-        __bindgen_bitfield_unit.set(32usize, 32u8, {
+        __bindgen_bitfield_unit.set(29usize, 35u8, {
             let __reserved_1: u64 = unsafe { ::std::mem::transmute(__reserved_1) };
             __reserved_1 as u64
         });
@@ -1465,9 +1484,7 @@ pub enum perf_event_type {
     PERF_RECORD_SWITCH = 14,
     PERF_RECORD_SWITCH_CPU_WIDE = 15,
     PERF_RECORD_NAMESPACES = 16,
-    PERF_RECORD_KSYMBOL = 17,
-    PERF_RECORD_BPF_EVENT = 18,
-    PERF_RECORD_MAX = 19,
+    PERF_RECORD_MAX = 17,
 }
 pub const IFLA_XDP_UNSPEC: _bindgen_ty_79 = _bindgen_ty_79::IFLA_XDP_UNSPEC;
 pub const IFLA_XDP_FD: _bindgen_ty_79 = _bindgen_ty_79::IFLA_XDP_FD;
@@ -1503,6 +1520,79 @@ pub struct ifinfomsg {
     pub ifi_flags: ::std::os::raw::c_uint,
     pub ifi_change: ::std::os::raw::c_uint,
 }
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct tcmsg {
+    pub tcm_family: ::std::os::raw::c_uchar,
+    pub tcm__pad1: ::std::os::raw::c_uchar,
+    pub tcm__pad2: ::std::os::raw::c_ushort,
+    pub tcm_ifindex: ::std::os::raw::c_int,
+    pub tcm_handle: __u32,
+    pub tcm_parent: __u32,
+    pub tcm_info: __u32,
+}
+pub const TCA_UNSPEC: _bindgen_ty_91 = _bindgen_ty_91::TCA_UNSPEC;
+pub const TCA_KIND: _bindgen_ty_91 = _bindgen_ty_91::TCA_KIND;
+pub const TCA_OPTIONS: _bindgen_ty_91 = _bindgen_ty_91::TCA_OPTIONS;
+pub const TCA_STATS: _bindgen_ty_91 = _bindgen_ty_91::TCA_STATS;
+pub const TCA_XSTATS: _bindgen_ty_91 = _bindgen_ty_91::TCA_XSTATS;
+pub const TCA_RATE: _bindgen_ty_91 = _bindgen_ty_91::TCA_RATE;
+pub const TCA_FCNT: _bindgen_ty_91 = _bindgen_ty_91::TCA_FCNT;
+pub const TCA_STATS2: _bindgen_ty_91 = _bindgen_ty_91::TCA_STATS2;
+pub const TCA_STAB: _bindgen_ty_91 = _bindgen_ty_91::TCA_STAB;
+pub const TCA_PAD: _bindgen_ty_91 = _bindgen_ty_91::TCA_PAD;
+pub const TCA_DUMP_INVISIBLE: _bindgen_ty_91 = _bindgen_ty_91::TCA_DUMP_INVISIBLE;
+pub const TCA_CHAIN: _bindgen_ty_91 = _bindgen_ty_91::TCA_CHAIN;
+pub const TCA_HW_OFFLOAD: _bindgen_ty_91 = _bindgen_ty_91::TCA_HW_OFFLOAD;
+pub const __TCA_MAX: _bindgen_ty_91 = _bindgen_ty_91::__TCA_MAX;
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum _bindgen_ty_91 {
+    TCA_UNSPEC = 0,
+    TCA_KIND = 1,
+    TCA_OPTIONS = 2,
+    TCA_STATS = 3,
+    TCA_XSTATS = 4,
+    TCA_RATE = 5,
+    TCA_FCNT = 6,
+    TCA_STATS2 = 7,
+    TCA_STAB = 8,
+    TCA_PAD = 9,
+    TCA_DUMP_INVISIBLE = 10,
+    TCA_CHAIN = 11,
+    TCA_HW_OFFLOAD = 12,
+    __TCA_MAX = 13,
+}
+pub const TCA_BPF_UNSPEC: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_UNSPEC;
+pub const TCA_BPF_ACT: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_ACT;
+pub const TCA_BPF_POLICE: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_POLICE;
+pub const TCA_BPF_CLASSID: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_CLASSID;
+pub const TCA_BPF_OPS_LEN: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_OPS_LEN;
+pub const TCA_BPF_OPS: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_OPS;
+pub const TCA_BPF_FD: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_FD;
+pub const TCA_BPF_NAME: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_NAME;
+pub const TCA_BPF_FLAGS: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_FLAGS;
+pub const TCA_BPF_FLAGS_GEN: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_FLAGS_GEN;
+pub const TCA_BPF_TAG: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_TAG;
+pub const TCA_BPF_ID: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_ID;
+pub const __TCA_BPF_MAX: _bindgen_ty_133 = _bindgen_ty_133::__TCA_BPF_MAX;
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum _bindgen_ty_133 {
+    TCA_BPF_UNSPEC = 0,
+    TCA_BPF_ACT = 1,
+    TCA_BPF_POLICE = 2,
+    TCA_BPF_CLASSID = 3,
+    TCA_BPF_OPS_LEN = 4,
+    TCA_BPF_OPS = 5,
+    TCA_BPF_FD = 6,
+    TCA_BPF_NAME = 7,
+    TCA_BPF_FLAGS = 8,
+    TCA_BPF_FLAGS_GEN = 9,
+    TCA_BPF_TAG = 10,
+    TCA_BPF_ID = 11,
+    __TCA_BPF_MAX = 12,
+}
 pub const AYA_PERF_EVENT_IOC_ENABLE: ::std::os::raw::c_int = 9216;
 pub const AYA_PERF_EVENT_IOC_DISABLE: ::std::os::raw::c_int = 9217;
 pub const AYA_PERF_EVENT_IOC_SET_BPF: ::std::os::raw::c_int = 1074013192;

+ 151 - 61
aya/src/generated/linux_bindings_x86_64.rs

@@ -136,6 +136,16 @@ pub const XDP_FLAGS_MODES: u32 = 14;
 pub const XDP_FLAGS_MASK: u32 = 31;
 pub const SO_ATTACH_BPF: u32 = 50;
 pub const SO_DETACH_BPF: u32 = 27;
+pub const TC_H_MAJ_MASK: u32 = 4294901760;
+pub const TC_H_MIN_MASK: u32 = 65535;
+pub const TC_H_UNSPEC: u32 = 0;
+pub const TC_H_ROOT: u32 = 4294967295;
+pub const TC_H_INGRESS: u32 = 4294967281;
+pub const TC_H_CLSACT: u32 = 4294967281;
+pub const TC_H_MIN_PRIORITY: u32 = 65504;
+pub const TC_H_MIN_INGRESS: u32 = 65522;
+pub const TC_H_MIN_EGRESS: u32 = 65523;
+pub const TCA_BPF_FLAG_ACT_DIRECT: u32 = 1;
 pub type __u8 = ::std::os::raw::c_uchar;
 pub type __s16 = ::std::os::raw::c_short;
 pub type __u16 = ::std::os::raw::c_ushort;
@@ -600,6 +610,68 @@ pub struct bpf_attr__bindgen_ty_19 {
 }
 #[repr(C)]
 #[derive(Debug, Copy, Clone)]
+pub struct bpf_prog_info {
+    pub type_: __u32,
+    pub id: __u32,
+    pub tag: [__u8; 8usize],
+    pub jited_prog_len: __u32,
+    pub xlated_prog_len: __u32,
+    pub jited_prog_insns: __u64,
+    pub xlated_prog_insns: __u64,
+    pub load_time: __u64,
+    pub created_by_uid: __u32,
+    pub nr_map_ids: __u32,
+    pub map_ids: __u64,
+    pub name: [::std::os::raw::c_char; 16usize],
+    pub ifindex: __u32,
+    pub _bitfield_align_1: [u8; 0],
+    pub _bitfield_1: __BindgenBitfieldUnit<[u8; 4usize]>,
+    pub netns_dev: __u64,
+    pub netns_ino: __u64,
+    pub nr_jited_ksyms: __u32,
+    pub nr_jited_func_lens: __u32,
+    pub jited_ksyms: __u64,
+    pub jited_func_lens: __u64,
+    pub btf_id: __u32,
+    pub func_info_rec_size: __u32,
+    pub func_info: __u64,
+    pub nr_func_info: __u32,
+    pub nr_line_info: __u32,
+    pub line_info: __u64,
+    pub jited_line_info: __u64,
+    pub nr_jited_line_info: __u32,
+    pub line_info_rec_size: __u32,
+    pub jited_line_info_rec_size: __u32,
+    pub nr_prog_tags: __u32,
+    pub prog_tags: __u64,
+    pub run_time_ns: __u64,
+    pub run_cnt: __u64,
+    pub recursion_misses: __u64,
+}
+impl bpf_prog_info {
+    #[inline]
+    pub fn gpl_compatible(&self) -> __u32 {
+        unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u32) }
+    }
+    #[inline]
+    pub fn set_gpl_compatible(&mut self, val: __u32) {
+        unsafe {
+            let val: u32 = ::std::mem::transmute(val);
+            self._bitfield_1.set(0usize, 1u8, val as u64)
+        }
+    }
+    #[inline]
+    pub fn new_bitfield_1(gpl_compatible: __u32) -> __BindgenBitfieldUnit<[u8; 4usize]> {
+        let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 4usize]> = Default::default();
+        __bindgen_bitfield_unit.set(0usize, 1u8, {
+            let gpl_compatible: u32 = unsafe { ::std::mem::transmute(gpl_compatible) };
+            gpl_compatible as u64
+        });
+        __bindgen_bitfield_unit
+    }
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
 pub struct btf_header {
     pub magic: __u16,
     pub version: __u8,
@@ -689,7 +761,7 @@ pub enum perf_sw_ids {
     PERF_COUNT_SW_BPF_OUTPUT = 10,
     PERF_COUNT_SW_MAX = 11,
 }
-#[repr(u64)]
+#[repr(u32)]
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub enum perf_event_sample_format {
     PERF_SAMPLE_IP = 1,
@@ -713,7 +785,6 @@ pub enum perf_event_sample_format {
     PERF_SAMPLE_REGS_INTR = 262144,
     PERF_SAMPLE_PHYS_ADDR = 524288,
     PERF_SAMPLE_MAX = 1048576,
-    __PERF_SAMPLE_CALLCHAIN_EARLY = 9223372036854775808,
 }
 #[repr(C)]
 #[derive(Copy, Clone)]
@@ -724,7 +795,7 @@ pub struct perf_event_attr {
     pub __bindgen_anon_1: perf_event_attr__bindgen_ty_1,
     pub sample_type: __u64,
     pub read_format: __u64,
-    pub _bitfield_align_1: [u32; 0],
+    pub _bitfield_align_1: [u64; 0],
     pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>,
     pub __bindgen_anon_2: perf_event_attr__bindgen_ty_2,
     pub bp_type: __u32,
@@ -757,8 +828,6 @@ pub union perf_event_attr__bindgen_ty_2 {
 #[derive(Copy, Clone)]
 pub union perf_event_attr__bindgen_ty_3 {
     pub bp_addr: __u64,
-    pub kprobe_func: __u64,
-    pub uprobe_path: __u64,
     pub config1: __u64,
     _bindgen_union_align: u64,
 }
@@ -766,8 +835,6 @@ pub union perf_event_attr__bindgen_ty_3 {
 #[derive(Copy, Clone)]
 pub union perf_event_attr__bindgen_ty_4 {
     pub bp_len: __u64,
-    pub kprobe_addr: __u64,
-    pub probe_offset: __u64,
     pub config2: __u64,
     _bindgen_union_align: u64,
 }
@@ -1081,47 +1148,14 @@ impl perf_event_attr {
         }
     }
     #[inline]
-    pub fn ksymbol(&self) -> __u64 {
-        unsafe { ::std::mem::transmute(self._bitfield_1.get(29usize, 1u8) as u64) }
-    }
-    #[inline]
-    pub fn set_ksymbol(&mut self, val: __u64) {
-        unsafe {
-            let val: u64 = ::std::mem::transmute(val);
-            self._bitfield_1.set(29usize, 1u8, val as u64)
-        }
-    }
-    #[inline]
-    pub fn bpf_event(&self) -> __u64 {
-        unsafe { ::std::mem::transmute(self._bitfield_1.get(30usize, 1u8) as u64) }
-    }
-    #[inline]
-    pub fn set_bpf_event(&mut self, val: __u64) {
-        unsafe {
-            let val: u64 = ::std::mem::transmute(val);
-            self._bitfield_1.set(30usize, 1u8, val as u64)
-        }
-    }
-    #[inline]
-    pub fn aux_output(&self) -> __u64 {
-        unsafe { ::std::mem::transmute(self._bitfield_1.get(31usize, 1u8) as u64) }
-    }
-    #[inline]
-    pub fn set_aux_output(&mut self, val: __u64) {
-        unsafe {
-            let val: u64 = ::std::mem::transmute(val);
-            self._bitfield_1.set(31usize, 1u8, val as u64)
-        }
-    }
-    #[inline]
     pub fn __reserved_1(&self) -> __u64 {
-        unsafe { ::std::mem::transmute(self._bitfield_1.get(32usize, 32u8) as u64) }
+        unsafe { ::std::mem::transmute(self._bitfield_1.get(29usize, 35u8) as u64) }
     }
     #[inline]
     pub fn set___reserved_1(&mut self, val: __u64) {
         unsafe {
             let val: u64 = ::std::mem::transmute(val);
-            self._bitfield_1.set(32usize, 32u8, val as u64)
+            self._bitfield_1.set(29usize, 35u8, val as u64)
         }
     }
     #[inline]
@@ -1154,9 +1188,6 @@ impl perf_event_attr {
         context_switch: __u64,
         write_backward: __u64,
         namespaces: __u64,
-        ksymbol: __u64,
-        bpf_event: __u64,
-        aux_output: __u64,
         __reserved_1: __u64,
     ) -> __BindgenBitfieldUnit<[u8; 8usize]> {
         let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default();
@@ -1274,19 +1305,7 @@ impl perf_event_attr {
             let namespaces: u64 = unsafe { ::std::mem::transmute(namespaces) };
             namespaces as u64
         });
-        __bindgen_bitfield_unit.set(29usize, 1u8, {
-            let ksymbol: u64 = unsafe { ::std::mem::transmute(ksymbol) };
-            ksymbol as u64
-        });
-        __bindgen_bitfield_unit.set(30usize, 1u8, {
-            let bpf_event: u64 = unsafe { ::std::mem::transmute(bpf_event) };
-            bpf_event as u64
-        });
-        __bindgen_bitfield_unit.set(31usize, 1u8, {
-            let aux_output: u64 = unsafe { ::std::mem::transmute(aux_output) };
-            aux_output as u64
-        });
-        __bindgen_bitfield_unit.set(32usize, 32u8, {
+        __bindgen_bitfield_unit.set(29usize, 35u8, {
             let __reserved_1: u64 = unsafe { ::std::mem::transmute(__reserved_1) };
             __reserved_1 as u64
         });
@@ -1465,9 +1484,7 @@ pub enum perf_event_type {
     PERF_RECORD_SWITCH = 14,
     PERF_RECORD_SWITCH_CPU_WIDE = 15,
     PERF_RECORD_NAMESPACES = 16,
-    PERF_RECORD_KSYMBOL = 17,
-    PERF_RECORD_BPF_EVENT = 18,
-    PERF_RECORD_MAX = 19,
+    PERF_RECORD_MAX = 17,
 }
 pub const IFLA_XDP_UNSPEC: _bindgen_ty_79 = _bindgen_ty_79::IFLA_XDP_UNSPEC;
 pub const IFLA_XDP_FD: _bindgen_ty_79 = _bindgen_ty_79::IFLA_XDP_FD;
@@ -1503,6 +1520,79 @@ pub struct ifinfomsg {
     pub ifi_flags: ::std::os::raw::c_uint,
     pub ifi_change: ::std::os::raw::c_uint,
 }
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct tcmsg {
+    pub tcm_family: ::std::os::raw::c_uchar,
+    pub tcm__pad1: ::std::os::raw::c_uchar,
+    pub tcm__pad2: ::std::os::raw::c_ushort,
+    pub tcm_ifindex: ::std::os::raw::c_int,
+    pub tcm_handle: __u32,
+    pub tcm_parent: __u32,
+    pub tcm_info: __u32,
+}
+pub const TCA_UNSPEC: _bindgen_ty_91 = _bindgen_ty_91::TCA_UNSPEC;
+pub const TCA_KIND: _bindgen_ty_91 = _bindgen_ty_91::TCA_KIND;
+pub const TCA_OPTIONS: _bindgen_ty_91 = _bindgen_ty_91::TCA_OPTIONS;
+pub const TCA_STATS: _bindgen_ty_91 = _bindgen_ty_91::TCA_STATS;
+pub const TCA_XSTATS: _bindgen_ty_91 = _bindgen_ty_91::TCA_XSTATS;
+pub const TCA_RATE: _bindgen_ty_91 = _bindgen_ty_91::TCA_RATE;
+pub const TCA_FCNT: _bindgen_ty_91 = _bindgen_ty_91::TCA_FCNT;
+pub const TCA_STATS2: _bindgen_ty_91 = _bindgen_ty_91::TCA_STATS2;
+pub const TCA_STAB: _bindgen_ty_91 = _bindgen_ty_91::TCA_STAB;
+pub const TCA_PAD: _bindgen_ty_91 = _bindgen_ty_91::TCA_PAD;
+pub const TCA_DUMP_INVISIBLE: _bindgen_ty_91 = _bindgen_ty_91::TCA_DUMP_INVISIBLE;
+pub const TCA_CHAIN: _bindgen_ty_91 = _bindgen_ty_91::TCA_CHAIN;
+pub const TCA_HW_OFFLOAD: _bindgen_ty_91 = _bindgen_ty_91::TCA_HW_OFFLOAD;
+pub const __TCA_MAX: _bindgen_ty_91 = _bindgen_ty_91::__TCA_MAX;
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum _bindgen_ty_91 {
+    TCA_UNSPEC = 0,
+    TCA_KIND = 1,
+    TCA_OPTIONS = 2,
+    TCA_STATS = 3,
+    TCA_XSTATS = 4,
+    TCA_RATE = 5,
+    TCA_FCNT = 6,
+    TCA_STATS2 = 7,
+    TCA_STAB = 8,
+    TCA_PAD = 9,
+    TCA_DUMP_INVISIBLE = 10,
+    TCA_CHAIN = 11,
+    TCA_HW_OFFLOAD = 12,
+    __TCA_MAX = 13,
+}
+pub const TCA_BPF_UNSPEC: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_UNSPEC;
+pub const TCA_BPF_ACT: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_ACT;
+pub const TCA_BPF_POLICE: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_POLICE;
+pub const TCA_BPF_CLASSID: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_CLASSID;
+pub const TCA_BPF_OPS_LEN: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_OPS_LEN;
+pub const TCA_BPF_OPS: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_OPS;
+pub const TCA_BPF_FD: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_FD;
+pub const TCA_BPF_NAME: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_NAME;
+pub const TCA_BPF_FLAGS: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_FLAGS;
+pub const TCA_BPF_FLAGS_GEN: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_FLAGS_GEN;
+pub const TCA_BPF_TAG: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_TAG;
+pub const TCA_BPF_ID: _bindgen_ty_133 = _bindgen_ty_133::TCA_BPF_ID;
+pub const __TCA_BPF_MAX: _bindgen_ty_133 = _bindgen_ty_133::__TCA_BPF_MAX;
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum _bindgen_ty_133 {
+    TCA_BPF_UNSPEC = 0,
+    TCA_BPF_ACT = 1,
+    TCA_BPF_POLICE = 2,
+    TCA_BPF_CLASSID = 3,
+    TCA_BPF_OPS_LEN = 4,
+    TCA_BPF_OPS = 5,
+    TCA_BPF_FD = 6,
+    TCA_BPF_NAME = 7,
+    TCA_BPF_FLAGS = 8,
+    TCA_BPF_FLAGS_GEN = 9,
+    TCA_BPF_TAG = 10,
+    TCA_BPF_ID = 11,
+    __TCA_BPF_MAX = 12,
+}
 pub const AYA_PERF_EVENT_IOC_ENABLE: ::std::os::raw::c_int = 9216;
 pub const AYA_PERF_EVENT_IOC_DISABLE: ::std::os::raw::c_int = 9217;
 pub const AYA_PERF_EVENT_IOC_SET_BPF: ::std::os::raw::c_int = 1074013192;

+ 0 - 42
aya/src/programs/cls.rs

@@ -1,42 +0,0 @@
-use crate::{
-    generated::bpf_prog_type::{BPF_PROG_TYPE_SCHED_ACT, BPF_PROG_TYPE_SCHED_CLS},
-    programs::{load_program, ProgramData, ProgramError},
-};
-
-#[derive(Debug)]
-pub struct SchedClassifier {
-    pub(crate) data: ProgramData,
-}
-
-impl SchedClassifier {
-    /// Loads the program inside the kernel.
-    ///
-    /// See also [`Program::load`](crate::programs::Program::load).
-    pub fn load(&mut self) -> Result<(), ProgramError> {
-        load_program(BPF_PROG_TYPE_SCHED_CLS, &mut self.data)
-    }
-
-    /// Returns the name of the program.
-    pub fn name(&self) -> String {
-        self.data.name.to_string()
-    }
-}
-
-#[derive(Debug)]
-pub struct SchedAction {
-    pub(crate) data: ProgramData,
-}
-
-impl SchedAction {
-    /// Loads the program inside the kernel.
-    ///
-    /// See also [`Program::load`](crate::programs::Program::load).
-    pub fn load(&mut self) -> Result<(), ProgramError> {
-        load_program(BPF_PROG_TYPE_SCHED_ACT, &mut self.data)
-    }
-
-    /// Returns the name of the program.
-    pub fn name(&self) -> String {
-        self.data.name.to_string()
-    }
-}

+ 5 - 2
aya/src/programs/mod.rs

@@ -44,7 +44,7 @@
 //! [`Bpf::program`]: crate::Bpf::program
 //! [`Bpf::program_mut`]: crate::Bpf::program_mut
 //! [maps]: crate::maps
-mod cls;
+mod tc;
 mod kprobe;
 mod perf_attach;
 mod probe;
@@ -60,7 +60,7 @@ use libc::{close, ENOSPC};
 use std::{cell::RefCell, cmp, convert::TryFrom, ffi::CStr, io, os::unix::io::RawFd, rc::Rc};
 use thiserror::Error;
 
-pub use cls::{SchedAction, SchedClassifier};
+pub use tc::{SchedClassifier, TcError, TcAttachPoint};
 pub use kprobe::{KProbe, KProbeError};
 use perf_attach::*;
 pub use probe::ProbeKind;
@@ -132,6 +132,9 @@ pub enum ProgramError {
 
     #[error(transparent)]
     XdpError(#[from] XdpError),
+
+    #[error(transparent)]
+    TcError(#[from] TcError),
 }
 
 pub trait ProgramFd {

+ 118 - 0
aya/src/programs/tc.rs

@@ -0,0 +1,118 @@
+use thiserror::Error;
+
+use std::{io, os::unix::io::RawFd};
+
+use crate::{
+    generated::{
+        TC_H_CLSACT, TC_H_MIN_INGRESS, TC_H_MIN_EGRESS,
+        bpf_prog_type::BPF_PROG_TYPE_SCHED_CLS,
+    },
+    programs::{Link, LinkRef, load_program, ProgramData, ProgramError},
+    sys::{netlink_qdisc_add_clsact, netlink_qdisc_attach, netlink_qdisc_detach},
+    util::{ifindex_from_ifname, tc_handler_make},
+};
+
+#[derive(Debug, Clone, Copy)]
+#[repr(u32)]
+pub enum TcAttachPoint {
+    Ingress = TC_H_MIN_INGRESS,
+    Egress = TC_H_MIN_EGRESS,
+    Custom,
+}
+
+#[derive(Debug)]
+pub struct SchedClassifier {
+    pub(crate) data: ProgramData,
+}
+
+#[derive(Debug, Error)]
+pub enum TcError {
+    #[error("netlink error while attaching ebpf program to tc")]
+    NetlinkError {
+        #[source]
+        io_error: io::Error,
+    },
+    #[error("the clsact qdisc is already attached")]
+    AlreadyAttached,
+}
+
+#[derive(Debug)]
+struct TcLink {
+    if_index: i32,
+    attach_point: TcAttachPoint,
+    prog_fd: Option<RawFd>,
+    priority: u32,
+}
+
+impl TcAttachPoint {
+    pub fn tcm_parent(&self, parent: u32) -> Result<u32, io::Error> {
+        match *self {
+            TcAttachPoint::Custom => {
+                if parent == 0 {
+                    return Err(io::Error::new(io::ErrorKind::Other, "Parent must be non-zero for Custom attach points"));
+                }
+                Ok(parent)
+            }
+            _ => Ok(tc_handler_make(TC_H_CLSACT, (*self).clone() as u32))
+        }
+    }
+}
+
+impl SchedClassifier {
+    /// Loads the program inside the kernel.
+    ///
+    /// See also [`Program::load`](crate::programs::Program::load).
+    pub fn load(&mut self) -> Result<(), ProgramError> {
+        load_program(BPF_PROG_TYPE_SCHED_CLS, &mut self.data)
+    }
+
+    /// Returns the name of the program.
+    pub fn name(&self) -> String {
+        self.data.name.to_string()
+    }
+
+    /// Attaches the program to the given `interface` and `attach-point`
+    pub fn attach(&mut self, interface: &str, attach_point: TcAttachPoint) -> Result<LinkRef, ProgramError> {
+        let prog_fd = self.data.fd_or_err()?;
+        let if_index = unsafe { ifindex_from_ifname(interface) }
+                         .map_err(|io_error| TcError::NetlinkError { io_error })?;
+        let prog_name = self.name();
+        let priority = unsafe { netlink_qdisc_attach(if_index as i32, &attach_point, prog_fd, &prog_name[..]) }
+            .map_err(|io_error| TcError::NetlinkError { io_error })?;
+        Ok(self.data.link(TcLink {
+            if_index: if_index as i32,
+            attach_point,
+            prog_fd: Some(prog_fd),
+            priority,
+        }))
+    }
+    
+    /// Add "clasct" qdisc to an interface
+    pub fn qdisc_add_clsact_to_interface(if_name: &str) -> Result<(), ProgramError> {
+        // unsafe wrapper
+        let if_index = unsafe { ifindex_from_ifname(if_name) }
+                           .map_err(|_| ProgramError::UnknownInterface {name: if_name.to_string()})?;
+        unsafe { netlink_qdisc_add_clsact(if_index as i32) }
+                           .map_err(|io_error| TcError::NetlinkError { io_error })?;
+        Ok(())
+    }
+}
+
+impl Drop for TcLink {
+    fn drop(&mut self) {
+        let _ = self.detach();
+    }
+}
+
+impl Link for TcLink {
+    fn detach(&mut self) -> Result<(), ProgramError> {
+        if let Some(_) = self.prog_fd.take() {
+            unsafe { netlink_qdisc_detach(self.if_index, &self.attach_point, self.priority) }
+                .map_err(|io_error| TcError::NetlinkError { io_error })?;
+            Ok(())
+        } else {
+            Err(ProgramError::AlreadyDetached)
+        }
+    }
+}
+

+ 257 - 11
aya/src/sys/netlink.rs

@@ -1,14 +1,28 @@
-use std::{io, mem, os::unix::io::RawFd, ptr};
+use std::{
+    io,
+    mem,
+    os::unix::io::RawFd, 
+    ptr,
+    slice,
+};
 
 use libc::{
     c_int, close, getsockname, nlattr, nlmsgerr, nlmsghdr, recv, send, setsockopt, sockaddr_nl,
     socket, AF_NETLINK, AF_UNSPEC, IFLA_XDP, NETLINK_ROUTE, NLA_ALIGNTO, NLA_F_NESTED, NLMSG_DONE,
-    NLMSG_ERROR, NLM_F_ACK, NLM_F_MULTI, NLM_F_REQUEST, RTM_SETLINK, SOCK_RAW, SOL_NETLINK,
+    NLMSG_ERROR, NLM_F_ACK, NLM_F_EXCL, NLM_F_ECHO, NLM_F_CREATE, NLM_F_MULTI, NLM_F_REQUEST, RTM_SETLINK, SOCK_RAW, 
+    SOL_NETLINK, RTM_NEWQDISC, RTM_NEWTFILTER, RTM_DELTFILTER, ETH_P_ALL,
 };
 
-use crate::generated::{
+use crate::{
+    generated::{
     _bindgen_ty_79::{IFLA_XDP_EXPECTED_FD, IFLA_XDP_FD, IFLA_XDP_FLAGS},
-    ifinfomsg, NLMSG_ALIGNTO, XDP_FLAGS_REPLACE,
+    _bindgen_ty_91::{TCA_KIND, TCA_OPTIONS},
+    _bindgen_ty_133::{TCA_BPF_FD, TCA_BPF_NAME, TCA_BPF_FLAGS},
+    ifinfomsg, tcmsg, NLMSG_ALIGNTO, XDP_FLAGS_REPLACE,
+    TC_H_UNSPEC, TCA_BPF_FLAG_ACT_DIRECT, TC_H_CLSACT, TC_H_INGRESS, TC_H_MAJ_MASK,
+    },
+    util::{htons, tc_handler_make},
+    programs::TcAttachPoint,
 };
 
 const NETLINK_EXT_ACK: c_int = 11;
@@ -21,7 +35,7 @@ pub(crate) unsafe fn netlink_set_xdp_fd(
     old_fd: Option<RawFd>,
     flags: u32,
 ) -> Result<(), io::Error> {
-    let sock = NetlinkSocket::open().unwrap();
+    let sock = NetlinkSocket::open()?;
 
     let seq = 1;
     // Safety: Request is POD so this is safe
@@ -116,6 +130,233 @@ pub(crate) unsafe fn netlink_set_xdp_fd(
     Ok(())
 }
 
+pub(crate) unsafe fn netlink_qdisc_add_clsact(
+    if_index: i32
+) -> Result<(), io::Error> {
+    let sock = NetlinkSocket::open()?;
+
+    let seq = 1;
+    let mut req = mem::zeroed::<QdiscRequest>();
+
+    req.header = nlmsghdr {
+        nlmsg_len: (mem::size_of::<nlmsghdr>() + mem::size_of::<tcmsg>()) as u32,
+        nlmsg_flags: (NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE) as u16,
+        nlmsg_type: RTM_NEWQDISC,
+        nlmsg_pid: 0,
+        nlmsg_seq: seq,
+    };
+    req.tc_info.tcm_family = AF_UNSPEC as u8;
+    req.tc_info.tcm_ifindex = if_index;
+    req.tc_info.tcm_handle = tc_handler_make(TC_H_CLSACT, TC_H_UNSPEC);
+    req.tc_info.tcm_parent = tc_handler_make(TC_H_CLSACT, TC_H_INGRESS);
+    req.tc_info.tcm_info = 0;
+
+    let attrs_addr = &req as *const _ as usize + req.header.nlmsg_len as usize;
+    let attrs_addr = align_to(attrs_addr, NLMSG_ALIGNTO as usize);
+    let nla_hdr_len = align_to(mem::size_of::<nlattr>(), NLA_ALIGNTO as usize);
+
+    // length of the root attribute
+    let mut nla_len = nla_hdr_len as u16;
+
+    let mut offset = attrs_addr as usize;
+    let attr = nlattr {
+        nla_type: TCA_KIND as u16,
+        // size of payload
+        nla_len: (nla_hdr_len + 7) as u16,
+    };
+
+    // write header
+    ptr::write(offset as *mut nlattr, attr);
+    offset += nla_hdr_len;
+    // write the "clsact" string
+    let buf = slice::from_raw_parts_mut(offset as *mut u8, 7);
+    buf.copy_from_slice(b"clsact\0");
+    nla_len += attr.nla_len;
+
+    req.header.nlmsg_len += align_to(nla_len as usize, NLA_ALIGNTO as usize) as u32;
+
+    if send(
+        sock.sock,
+        &req as *const _ as *const _,
+        req.header.nlmsg_len as usize,
+        0,
+    ) < 0
+    {
+        return Err(io::Error::last_os_error())?;
+    }
+    sock.recv()?;
+
+    Ok(())
+}
+
+pub(crate) unsafe fn netlink_qdisc_detach(
+    if_index: i32,
+    attach_point: &TcAttachPoint,
+    priority: u32,
+) -> Result<(), io::Error> {
+        let sock = NetlinkSocket::open()?;
+    let seq = 1;
+    let mut req = mem::zeroed::<QdiscRequest>();
+
+    req.header = nlmsghdr{
+        nlmsg_len: (mem::size_of::<nlmsghdr>() + mem::size_of::<tcmsg>()) as u32,
+        nlmsg_flags: (NLM_F_REQUEST | NLM_F_ACK) as u16,
+        nlmsg_type: RTM_DELTFILTER,
+        nlmsg_pid: 0,
+        nlmsg_seq: seq,
+    };
+
+    req.tc_info.tcm_family = AF_UNSPEC as u8;
+    req.tc_info.tcm_handle = 0; // auto-assigned, if not provided
+    req.tc_info.tcm_info = tc_handler_make(priority << 16, htons(ETH_P_ALL as u16) as u32);
+    req.tc_info.tcm_parent = attach_point.tcm_parent(0)?;
+    req.tc_info.tcm_ifindex = if_index;
+
+    if send(
+        sock.sock,
+        &req as *const _ as *const _,
+        req.header.nlmsg_len as usize,
+        0,
+    ) < 0
+    {
+        return Err(io::Error::last_os_error())?;
+    }
+
+    sock.recv()?;
+
+    Ok(())
+}
+
+pub(crate) unsafe fn netlink_qdisc_attach(
+    if_index: i32,
+    attach_point: &TcAttachPoint,
+    prog_fd: RawFd,
+    prog_name: &str,
+) -> Result<u32, io::Error> {
+    let sock = NetlinkSocket::open()?;
+    let seq = 1;
+    let priority = 0;
+    let mut req = mem::zeroed::<QdiscRequest>();
+
+    req.header = nlmsghdr{
+        nlmsg_len: (mem::size_of::<nlmsghdr>() + mem::size_of::<tcmsg>()) as u32,
+        nlmsg_flags: (NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE | NLM_F_ECHO) as u16,
+        nlmsg_type: RTM_NEWTFILTER,
+        nlmsg_pid: 0,
+        nlmsg_seq: seq,
+    };
+    req.tc_info.tcm_family = AF_UNSPEC as u8;
+    req.tc_info.tcm_handle = 0;  // auto-assigned, if not provided
+    req.tc_info.tcm_ifindex = if_index;
+    req.tc_info.tcm_parent = attach_point.tcm_parent(0)?;
+
+    req.tc_info.tcm_info = tc_handler_make(priority << 16, htons(ETH_P_ALL as u16) as u32);
+
+    let attrs_addr = &req as *const _ as usize + req.header.nlmsg_len as usize;
+    let attrs_addr = align_to(attrs_addr, NLMSG_ALIGNTO as usize);
+    let nla_hdr_len = align_to(mem::size_of::<nlattr>(), NLA_ALIGNTO as usize);
+
+    let mut nla_len = nla_hdr_len as u16;
+
+    let mut offset = attrs_addr as usize;
+
+    let attr = nlattr {
+        nla_type: TCA_KIND as u16,
+        nla_len: (nla_hdr_len + 4) as u16,
+    };
+
+    // write header
+    ptr::write(offset as *mut nlattr, attr);
+    offset += nla_hdr_len;
+
+    // now write the actual "bpf" string
+    let buf = slice::from_raw_parts_mut(offset as *mut u8, 4);
+    buf.copy_from_slice(b"bpf\0");
+
+    offset += 4;
+    nla_len += attr.nla_len;
+
+    let nested_tca_options_start = nla_len;
+    let nested_attr_offset = offset;
+    // now write the nested portion
+    
+    let mut nested_attr = nlattr {
+        nla_type: TCA_OPTIONS as u16 | NLA_F_NESTED as u16,
+        nla_len: nla_hdr_len as u16,  // no data
+    };
+
+    offset += nla_hdr_len;
+    nla_len += attr.nla_len;
+    // add program fd and name.
+
+    let attr = nlattr {
+        nla_type: TCA_BPF_FD as u16,
+        nla_len: (nla_hdr_len + mem::size_of::<RawFd>()) as u16,
+    };
+    ptr::write(offset as *mut nlattr, attr);
+    offset += nla_hdr_len;
+
+    ptr::write(offset as *mut RawFd, prog_fd);
+    offset += mem::size_of::<i32>();
+    nla_len += attr.nla_len;
+
+    let prog_name_null = prog_name.to_string() + "\0";
+    let prog_name_len = prog_name_null.len();
+
+    let attr = nlattr {
+        nla_type: TCA_BPF_NAME as u16,
+        nla_len: (nla_hdr_len + prog_name_len) as u16,
+    };
+
+    ptr::write(offset as *mut nlattr, attr);
+    offset += nla_hdr_len;
+
+    let buf = slice::from_raw_parts_mut(offset as *mut u8, prog_name_len);
+    buf.copy_from_slice(prog_name_null.as_bytes());
+
+    offset += prog_name_len;
+    nla_len += attr.nla_len;
+
+    // write bpf flags for direct action, direct action is the default
+    let bpf_flags = TCA_BPF_FLAG_ACT_DIRECT;
+    let attr = nlattr {
+        nla_type: TCA_BPF_FLAGS as u16,
+        nla_len: (nla_hdr_len + mem::size_of::<u32>()) as u16,
+    };
+    ptr::write(offset as *mut nlattr, attr);
+    offset += nla_hdr_len;
+
+    ptr::write(offset as *mut u32, bpf_flags);
+    nla_len += attr.nla_len;
+
+    // now write the NESTED nlattr 
+    nested_attr.nla_len = nla_len - nested_tca_options_start;
+    ptr::write(nested_attr_offset as *mut nlattr, nested_attr);
+    req.header.nlmsg_len += align_to(nla_len as usize, NLA_ALIGNTO as usize) as u32;
+
+    if send(
+        sock.sock,
+        &req as *const _ as *const _,
+        req.header.nlmsg_len as usize,
+        0,
+    ) < 0
+    {
+        return Err(io::Error::last_os_error())?;
+    }
+
+    let reply_msg = sock.recv()?;
+    let mut tcinfo = 0;
+    for reply in &reply_msg {
+        if reply.header.nlmsg_type == RTM_NEWTFILTER {
+            let _tcmsg = reply._data.as_ptr() as *const tcmsg;
+            tcinfo = (*_tcmsg).tcm_info;
+            break;
+        }
+    }
+    let priority = ((tcinfo & TC_H_MAJ_MASK) >> 16) as u32;
+    Ok(priority)
+}
+
 #[repr(C)]
 struct Request {
     header: nlmsghdr,
@@ -123,6 +364,12 @@ struct Request {
     attrs: [u8; 64],
 }
 
+#[repr(C)]
+struct QdiscRequest {
+    header: nlmsghdr,
+    tc_info: tcmsg,
+}
+
 struct NetlinkSocket {
     sock: RawFd,
     _nl_pid: u32,
@@ -164,9 +411,9 @@ impl NetlinkSocket {
         })
     }
 
-    fn recv(&self) -> Result<(), io::Error> {
+    fn recv(&self) -> Result<Vec<NetlinkMessage>, io::Error> {
         let mut buf = [0u8; 4096];
-
+        let mut messages = Vec::new();
         let mut multipart = true;
         while multipart {
             multipart = false;
@@ -184,9 +431,7 @@ impl NetlinkSocket {
             while offset < len {
                 let message = NetlinkMessage::read(&buf[offset..])?;
                 offset += align_to(message.header.nlmsg_len as usize, NLMSG_ALIGNTO as usize);
-
                 multipart = message.header.nlmsg_flags & NLM_F_MULTI as u16 != 0;
-
                 match message.header.nlmsg_type as i32 {
                     NLMSG_ERROR => {
                         let err = message.error.unwrap();
@@ -197,12 +442,12 @@ impl NetlinkSocket {
                         return Err(io::Error::from_raw_os_error(-err.error));
                     }
                     NLMSG_DONE => break,
-                    _ => {}
+                    _ => messages.push(message),
                 }
             }
         }
 
-        Ok(())
+        Ok(messages)
     }
 }
 
@@ -258,3 +503,4 @@ impl Drop for NetlinkSocket {
 fn align_to(v: usize, align: usize) -> usize {
     (v + (align - 1)) & !(align - 1)
 }
+

+ 34 - 0
aya/src/util.rs

@@ -2,10 +2,18 @@
 use std::{
     collections::BTreeMap,
     fs::{self, File},
+    ffi::CString,
+    os::raw::c_char,
     io::{self, BufReader},
     str::FromStr,
 };
 
+use crate::{
+    generated::{TC_H_MAJ_MASK, TC_H_MIN_MASK},
+};
+
+use libc::if_nametoindex;
+
 use io::BufRead;
 
 const ONLINE_CPUS: &str = "/sys/devices/system/cpu/online";
@@ -26,6 +34,10 @@ pub fn nr_cpus() -> Result<usize, io::Error> {
     Ok(possible_cpus()?.len())
 }
 
+pub(crate) fn tc_handler_make(major: u32, minor: u32) -> u32 {
+    (major & TC_H_MAJ_MASK) | (minor & TC_H_MIN_MASK)
+}
+
 pub(crate) fn possible_cpus() -> Result<Vec<u32>, io::Error> {
     let data = fs::read_to_string(POSSIBLE_CPUS)?;
     parse_cpu_ranges(data.trim()).map_err(|_| {
@@ -57,6 +69,28 @@ fn parse_cpu_ranges(data: &str) -> Result<Vec<u32>, ()> {
     Ok(cpus)
 }
 
+/// Gets interface index from interface name.
+pub unsafe fn ifindex_from_ifname(if_name: &str) -> Result<u32, io::Error> {
+    let c_str_if_name = CString::new(if_name)?;
+    let c_if_name: *const c_char = c_str_if_name.as_ptr() as *const c_char;
+    // unsafe libc wrapper
+    let if_index = if_nametoindex(c_if_name);
+    if if_index ==  0 {
+        return Err(io::Error::last_os_error());
+    }
+    Ok(if_index)
+}
+
+
+/// htons and ntohs util functions
+pub fn htons(u: u16) -> u16 {
+        u.to_be()
+}
+
+pub fn ntohs(u: u16) -> u16 {
+        u16::from_be(u)
+}
+
 /// Loads kernel symbols from `/proc/kallsyms`.
 ///
 /// The symbols can be passed to [`StackTrace::resolve`](crate::maps::stack_trace::StackTrace::resolve).

+ 1 - 1
bpf/aya-bpf/src/maps/hash_map.rs

@@ -4,7 +4,7 @@ use aya_bpf_cty::{c_long, c_void};
 
 use crate::{
     bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_HASH},
-    helpers::{bpf_map_lookup_elem, bpf_map_update_elem},
+    helpers::{bpf_map_lookup_elem, bpf_map_update_elem, bpf_map_delete_elem},
 };
 
 #[repr(transparent)]

+ 17 - 0
xtask/src/codegen/aya.rs

@@ -59,6 +59,7 @@ fn codegen_bindings(opts: &Options) -> Result<(), anyhow::Error> {
         "bpf_map_type",
         "bpf_prog_type",
         "bpf_attach_type",
+        "bpf_prog_info",
         // BTF
         "btf_header",
         "btf_ext_info",
@@ -80,6 +81,7 @@ fn codegen_bindings(opts: &Options) -> Result<(), anyhow::Error> {
         "perf_event_type",
         // NETLINK
         "ifinfomsg",
+        "tcmsg",
     ];
 
     let vars = [
@@ -112,7 +114,22 @@ fn codegen_bindings(opts: &Options) -> Result<(), anyhow::Error> {
         // NETLINK
         "NLMSG_ALIGNTO",
         "IFLA_XDP_FD",
+        "TCA_KIND",
+        "TCA_OPTIONS",
+        "TCA_BPF_FD",
+        "TCA_BPF_NAME",
+        "TCA_BPF_FLAGS",
+        "TCA_BPF_FLAG_ACT_DIRECT",
         "XDP_FLAGS_.*",
+        "TC_H_MAJ_MASK",
+        "TC_H_MIN_MASK",
+        "TC_H_UNSPEC",
+        "TC_H_ROOT",
+        "TC_H_INGRESS",
+        "TC_H_CLSACT",
+        "TC_H_MIN_PRIORITY",
+        "TC_H_MIN_INGRESS",
+        "TC_H_MIN_EGRESS",
     ];
 
     let dir = PathBuf::from("aya");