From 27baf2917e8d69a80f5baca2394019cf79c6bb19 Mon Sep 17 00:00:00 2001
From: "Mohamed S. Mahmoud" <mmahmoud@redhat.com>
Date: Sat, 24 Jun 2023 09:33:46 -0400
Subject: [PATCH] NETOBSERV-1061: Add TCP drop and DNS tracking hooks (#115)

* Add TCP drop hook and update flows metrics

Signed-off-by: msherif1234 <mmahmoud@redhat.com>

* update agent e2e manifest to mount kernel debug volume

Signed-off-by: msherif1234 <mmahmoud@redhat.com>

* Add light weight DNS tracker hook

Signed-off-by: msherif1234 <mmahmoud@redhat.com>

* rename tcpdrop fields to reflect its the latest drop
fix lint errors
flatten icmp block

Signed-off-by: msherif1234 <mmahmoud@redhat.com>

---------

Signed-off-by: msherif1234 <mmahmoud@redhat.com>
---
 bpf/configs.h                                 |   9 +
 bpf/dns_tracker.h                             | 103 ++++
 bpf/flow.h                                    |  22 +
 bpf/flows.c                                   | 223 +------
 bpf/maps_definition.h                         |  21 +
 bpf/tcp_drops.h                               |  88 +++
 bpf/utils.h                                   | 295 +++++++++
 e2e/cluster/base/04-agent.yml                 |   9 +
 e2e/ipfix/manifests/30-agent.yml              |   9 +
 e2e/kafka/manifests/30-agent.yml              |   9 +
 .../server/flowlogs-dump-collector.go         |  20 +-
 pkg/agent/agent.go                            |   2 +-
 pkg/agent/config.go                           |   4 +
 pkg/ebpf/bpf_bpfeb.go                         |  23 +
 pkg/ebpf/bpf_bpfeb.o                          | Bin 27208 -> 67240 bytes
 pkg/ebpf/bpf_bpfel.go                         |  23 +
 pkg/ebpf/bpf_bpfel.o                          | Bin 27280 -> 67888 bytes
 pkg/ebpf/tracer.go                            |  67 +-
 pkg/exporter/kafka_proto_test.go              |   2 +-
 pkg/exporter/proto.go                         |  80 ++-
 pkg/flow/account.go                           |   2 +-
 pkg/flow/account_test.go                      |  72 ++-
 pkg/flow/record.go                            |  24 +-
 pkg/flow/record_test.go                       |  25 +-
 pkg/flow/tracer_map.go                        |   2 +-
 pkg/grpc/grpc_test.go                         |  35 +-
 pkg/pbflow/flow.pb.go                         | 283 +++++----
 proto/flow.proto                              |  17 +-
 vendor/github.com/cilium/ebpf/link/cgroup.go  | 165 +++++
 vendor/github.com/cilium/ebpf/link/doc.go     |   2 +
 vendor/github.com/cilium/ebpf/link/iter.go    |  85 +++
 vendor/github.com/cilium/ebpf/link/kprobe.go  | 574 ++++++++++++++++++
 .../cilium/ebpf/link/kprobe_multi.go          | 180 ++++++
 vendor/github.com/cilium/ebpf/link/link.go    | 315 ++++++++++
 vendor/github.com/cilium/ebpf/link/netns.go   |  36 ++
 .../github.com/cilium/ebpf/link/perf_event.go | 434 +++++++++++++
 .../github.com/cilium/ebpf/link/platform.go   |  25 +
 vendor/github.com/cilium/ebpf/link/program.go |  76 +++
 vendor/github.com/cilium/ebpf/link/query.go   |  63 ++
 .../cilium/ebpf/link/raw_tracepoint.go        |  87 +++
 .../cilium/ebpf/link/socket_filter.go         |  40 ++
 .../github.com/cilium/ebpf/link/syscalls.go   | 123 ++++
 .../github.com/cilium/ebpf/link/tracepoint.go |  77 +++
 vendor/github.com/cilium/ebpf/link/tracing.go | 150 +++++
 vendor/github.com/cilium/ebpf/link/uprobe.go  | 359 +++++++++++
 vendor/github.com/cilium/ebpf/link/xdp.go     |  54 ++
 vendor/modules.txt                            |   1 +
 47 files changed, 3891 insertions(+), 424 deletions(-)
 create mode 100644 bpf/configs.h
 create mode 100644 bpf/dns_tracker.h
 create mode 100644 bpf/maps_definition.h
 create mode 100644 bpf/tcp_drops.h
 create mode 100644 bpf/utils.h
 create mode 100644 vendor/github.com/cilium/ebpf/link/cgroup.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/doc.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/iter.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/kprobe.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/kprobe_multi.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/link.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/netns.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/perf_event.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/platform.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/program.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/query.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/raw_tracepoint.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/socket_filter.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/syscalls.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/tracepoint.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/tracing.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/uprobe.go
 create mode 100644 vendor/github.com/cilium/ebpf/link/xdp.go

diff --git a/bpf/configs.h b/bpf/configs.h
new file mode 100644
index 000000000..208faea1f
--- /dev/null
+++ b/bpf/configs.h
@@ -0,0 +1,9 @@
+
+#ifndef __CONFIGS_H__
+#define __CONFIGS_H__
+
+// Constant definitions, to be overridden by the invoker
+volatile const u32 sampling = 0;
+volatile const u8 trace_messages = 0;
+
+#endif //__CONFIGS_H__
diff --git a/bpf/dns_tracker.h b/bpf/dns_tracker.h
new file mode 100644
index 000000000..f3aa795f3
--- /dev/null
+++ b/bpf/dns_tracker.h
@@ -0,0 +1,103 @@
+/*
+    light weight DNS tracker using trace points.
+*/
+
+#ifndef __DNS_TRACKER_H__
+#define __DNS_TRACKER_H__
+#include "utils.h"
+
+#define DNS_PORT        53
+#define DNS_QR_FLAG     0x8000
+#define UDP_MAXMSG      512
+
+struct dns_header {
+    u16 id;
+    u16 flags;
+    u16 qdcount;
+    u16 ancount;
+    u16 nscount;
+    u16 arcount;
+};
+
+static inline void find_or_create_dns_flow(flow_id *id, struct dns_header *dns, int len, int dir, u16 flags) {
+    flow_metrics *aggregate_flow = bpf_map_lookup_elem(&aggregated_flows, id);
+    u64 current_time = bpf_ktime_get_ns();
+    // net_dev_queue trace point hook will run before TC hooks, so the flow shouldn't exists, if it does
+    // that indicates we have a stale DNS query/response or in the middle of TCP flow so we will do nothing
+    if (aggregate_flow == NULL) {
+        // there is no matching flows so lets create new one and add the drops
+         flow_metrics new_flow;
+         __builtin_memset(&new_flow, 0, sizeof(new_flow));
+         new_flow.start_mono_time_ts = current_time;
+         new_flow.end_mono_time_ts = current_time;
+         new_flow.packets = 1;
+         new_flow.bytes = len;
+         new_flow.flags = flags;
+         new_flow.dns_record.id = bpf_ntohs(dns->id);
+         new_flow.dns_record.flags = bpf_ntohs(dns->flags);
+        if (dir == EGRESS) {
+            new_flow.dns_record.req_mono_time_ts = current_time;
+        } else {
+            new_flow.dns_record.rsp_mono_time_ts = current_time;
+        }
+        bpf_map_update_elem(&aggregated_flows, id, &new_flow, BPF_ANY);
+    }
+}
+
+static inline int trace_dns(struct sk_buff *skb) {
+    flow_id id;
+    u8 protocol = 0;
+    u16 family = 0,flags = 0, len = 0;
+
+    __builtin_memset(&id, 0, sizeof(id));
+
+    id.if_index = skb->skb_iif;
+
+    // read L2 info
+    set_key_with_l2_info(skb, &id, &family);
+
+    // read L3 info
+    set_key_with_l3_info(skb, family, &id, &protocol);
+
+    switch (protocol) {
+    case IPPROTO_UDP:
+        len = set_key_with_udp_info(skb, &id, IPPROTO_UDP);
+        // make sure udp payload doesn't exceed max msg size
+        if (len - sizeof(struct udphdr) > UDP_MAXMSG) {
+            return -1;
+        }
+        // set the length to udp hdr size as it will be used below to locate dns header
+        len = sizeof(struct udphdr);
+        break;
+    case IPPROTO_TCP:
+        len = set_key_with_tcp_info(skb, &id, IPPROTO_TCP, &flags);
+        break;
+    default:
+        return -1;
+    }
+
+    // check for DNS packets
+    if (id.dst_port == DNS_PORT || id.src_port == DNS_PORT) {
+        struct dns_header dns;
+        bpf_probe_read(&dns, sizeof(dns), (struct dns_header *)(skb->head + skb->transport_header + len));
+        if ((bpf_ntohs(dns.flags) & DNS_QR_FLAG) == 0) { /* dns query */
+            id.direction = EGRESS;
+        } else { /* dns response */
+            id.direction = INGRESS;
+        } // end of dns response
+        find_or_create_dns_flow(&id, &dns, skb->len, id.direction, flags);
+    } // end of dns port check
+
+    return 0;
+}
+
+SEC("tracepoint/net/net_dev_queue")
+int trace_net_packets(struct trace_event_raw_net_dev_template *args) {
+    struct sk_buff skb;
+
+    __builtin_memset(&skb, 0, sizeof(skb));
+    bpf_probe_read(&skb, sizeof(struct sk_buff), args->skbaddr);
+    return trace_dns(&skb);
+}
+
+#endif // __DNS_TRACKER_H__
diff --git a/bpf/flow.h b/bpf/flow.h
index 366a686b7..ddb306f8e 100644
--- a/bpf/flow.h
+++ b/bpf/flow.h
@@ -10,6 +10,8 @@ typedef __u16 u16;
 typedef __u32 u32;
 typedef __u64 u64;
 
+#define AF_INET  2
+#define AF_INET6 10
 #define ETH_ALEN 6
 #define ETH_P_IP 0x0800
 #define ETH_P_IPV6 0x86DD
@@ -30,8 +32,24 @@ typedef struct flow_metrics_t {
     // 0 otherwise
     // https://chromium.googlesource.com/chromiumos/docs/+/master/constants/errnos.md
     u8 errno;
+    struct tcp_drops_t {
+        u32 packets;
+        u64 bytes;
+        u16 latest_flags;
+        u8 latest_state;
+        u32 latest_drop_cause;
+    } __attribute__((packed)) tcp_drops;
+    struct dns_record_t {
+        u16 id;
+        u16 flags;
+        u64 req_mono_time_ts;
+        u64 rsp_mono_time_ts;
+    } __attribute__((packed)) dns_record;
 } __attribute__((packed)) flow_metrics;
 
+// Force emitting struct tcp_drops into the ELF.
+const struct tcp_drops_t *unused0 __attribute__((unused));
+
 // Force emitting struct flow_metrics into the ELF.
 const struct flow_metrics_t *unused1 __attribute__((unused));
 
@@ -71,4 +89,8 @@ typedef struct flow_record_t {
 
 // Force emitting struct flow_record into the ELF.
 const struct flow_record_t *unused3 __attribute__((unused));
+
+// Force emitting struct dns_record into the ELF.
+const struct dns_record_t *unused4 __attribute__((unused));
+
 #endif
diff --git a/bpf/flows.c b/bpf/flows.c
index ba934557f..5219b808d 100644
--- a/bpf/flows.c
+++ b/bpf/flows.c
@@ -13,226 +13,10 @@
             until an entry is available.
         4) When hash collision is detected, we send the new entry to userpace via ringbuffer.
 */
-#include <vmlinux.h>
-#include <bpf_helpers.h>
+#include "utils.h"
+#include "tcp_drops.h"
+#include "dns_tracker.h"
 
-#include "flow.h"
-#define DISCARD 1
-#define SUBMIT 0
-
-// according to field 61 in https://www.iana.org/assignments/ipfix/ipfix.xhtml
-#define INGRESS 0
-#define EGRESS 1
-
-// Flags according to RFC 9293 & https://www.iana.org/assignments/ipfix/ipfix.xhtml
-#define FIN_FLAG 0x01
-#define SYN_FLAG 0x02
-#define RST_FLAG 0x04
-#define PSH_FLAG 0x08
-#define ACK_FLAG 0x10
-#define URG_FLAG 0x20
-#define ECE_FLAG 0x40
-#define CWR_FLAG 0x80
-// Custom flags exported
-#define SYN_ACK_FLAG 0x100
-#define FIN_ACK_FLAG 0x200
-#define RST_ACK_FLAG 0x400
-
-#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
-	__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define bpf_ntohs(x)		__builtin_bswap16(x)
-#define bpf_htons(x)		__builtin_bswap16(x)
-#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
-	__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-#define bpf_ntohs(x)		(x)
-#define bpf_htons(x)		(x)
-#else
-# error "Endianness detection needs to be set up for your compiler?!"
-#endif
-
-// Common Ringbuffer as a conduit for ingress/egress flows to userspace
-struct {
-    __uint(type, BPF_MAP_TYPE_RINGBUF);
-    __uint(max_entries, 1 << 24);
-} direct_flows SEC(".maps");
-
-// Key: the flow identifier. Value: the flow metrics for that identifier.
-struct {
-    __uint(type, BPF_MAP_TYPE_HASH);
-    __type(key, flow_id);
-    __type(value, flow_metrics);
-    __uint(max_entries, 1 << 24);
-    __uint(map_flags, BPF_F_NO_PREALLOC);
-} aggregated_flows SEC(".maps");
-
-// Constant definitions, to be overridden by the invoker
-volatile const u32 sampling = 0;
-volatile const u8 trace_messages = 0;
-
-const u8 ip4in6[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
-
-// sets the TCP header flags for connection information
-static inline void set_flags(struct tcphdr *th, u16 *flags) {
-    //If both ACK and SYN are set, then it is server -> client communication during 3-way handshake. 
-    if (th->ack && th->syn) {
-        *flags |= SYN_ACK_FLAG;
-    } else if (th->ack && th->fin ) {
-        // If both ACK and FIN are set, then it is graceful termination from server.
-        *flags |= FIN_ACK_FLAG;
-    } else if (th->ack && th->rst ) {
-        // If both ACK and RST are set, then it is abrupt connection termination. 
-        *flags |= RST_ACK_FLAG;
-    } else if (th->fin) {
-        *flags |= FIN_FLAG;
-    } else if (th->syn) {
-        *flags |= SYN_FLAG;
-    } else if (th->ack) {
-        *flags |= ACK_FLAG;
-    } else if (th->rst) {
-        *flags |= RST_FLAG;
-    } else if (th->psh) {
-        *flags |= PSH_FLAG;
-    } else if (th->urg) {
-        *flags |= URG_FLAG;
-    } else if (th->ece) {
-        *flags |= ECE_FLAG;
-    } else if (th->cwr) {
-        *flags |= CWR_FLAG;
-    }
-}
-
-// L4_info structure contains L4 headers parsed information.
-struct l4_info_t {
-    // TCP/UDP/SCTP source port in host byte order
-    u16 src_port;
-    // TCP/UDP/SCTP destination port in host byte order
-    u16 dst_port;
-    // ICMPv4/ICMPv6 type value
-    u8 icmp_type;
-    // ICMPv4/ICMPv6 code value
-    u8 icmp_code;
-    // TCP flags
-    u16 flags;
-};
-
-// Extract L4 info for the supported protocols
-static inline void fill_l4info(void *l4_hdr_start, void *data_end, u8 protocol,
-                               struct l4_info_t *l4_info) {
-	switch (protocol) {
-    case IPPROTO_TCP: {
-        struct tcphdr *tcp = l4_hdr_start;
-        if ((void *)tcp + sizeof(*tcp) <= data_end) {
-            l4_info->src_port = bpf_ntohs(tcp->source);
-            l4_info->dst_port = bpf_ntohs(tcp->dest);
-            set_flags(tcp, &l4_info->flags);
-        }
-    } break;
-    case IPPROTO_UDP: {
-        struct udphdr *udp = l4_hdr_start;
-        if ((void *)udp + sizeof(*udp) <= data_end) {
-            l4_info->src_port = bpf_ntohs(udp->source);
-            l4_info->dst_port = bpf_ntohs(udp->dest);
-        }
-    } break;
-    case IPPROTO_SCTP: {
-        struct sctphdr *sctph = l4_hdr_start;
-        if ((void *)sctph + sizeof(*sctph) <= data_end) {
-            l4_info->src_port = bpf_ntohs(sctph->source);
-            l4_info->dst_port = bpf_ntohs(sctph->dest);
-        }
-    } break;
-    case IPPROTO_ICMP: {
-        struct icmphdr *icmph = l4_hdr_start;
-        if ((void *)icmph + sizeof(*icmph) <= data_end) {
-            l4_info->icmp_type = icmph->type;
-            l4_info->icmp_code = icmph->code;
-        }
-    } break;
-    case IPPROTO_ICMPV6: {
-        struct icmp6hdr *icmp6h = l4_hdr_start;
-         if ((void *)icmp6h + sizeof(*icmp6h) <= data_end) {
-            l4_info->icmp_type = icmp6h->icmp6_type;
-            l4_info->icmp_code = icmp6h->icmp6_code;
-        }
-    } break;
-    default:
-        break;
-    }
-}
-
-// sets flow fields from IPv4 header information
-static inline int fill_iphdr(struct iphdr *ip, void *data_end, flow_id *id, u16 *flags) {
-    struct l4_info_t l4_info;
-    void *l4_hdr_start;
-
-    l4_hdr_start = (void *)ip + sizeof(*ip);
-    if (l4_hdr_start > data_end) {
-        return DISCARD;
-    }
-    __builtin_memset(&l4_info, 0, sizeof(l4_info));
-    __builtin_memcpy(id->src_ip, ip4in6, sizeof(ip4in6));
-    __builtin_memcpy(id->dst_ip, ip4in6, sizeof(ip4in6));
-    __builtin_memcpy(id->src_ip + sizeof(ip4in6), &ip->saddr, sizeof(ip->saddr));
-    __builtin_memcpy(id->dst_ip + sizeof(ip4in6), &ip->daddr, sizeof(ip->daddr));
-    id->transport_protocol = ip->protocol;
-    fill_l4info(l4_hdr_start, data_end, ip->protocol, &l4_info);
-    id->src_port = l4_info.src_port;
-    id->dst_port = l4_info.dst_port;
-    id->icmp_type = l4_info.icmp_type;
-    id->icmp_code = l4_info.icmp_code;
-    *flags = l4_info.flags;
-
-    return SUBMIT;
-}
-
-// sets flow fields from IPv6 header information
-static inline int fill_ip6hdr(struct ipv6hdr *ip, void *data_end, flow_id *id, u16 *flags) {
-    struct l4_info_t l4_info;
-    void *l4_hdr_start;
-
-    l4_hdr_start = (void *)ip + sizeof(*ip);
-    if (l4_hdr_start > data_end) {
-        return DISCARD;
-    }
-    __builtin_memset(&l4_info, 0, sizeof(l4_info));
-    __builtin_memcpy(id->src_ip, ip->saddr.in6_u.u6_addr8, 16);
-    __builtin_memcpy(id->dst_ip, ip->daddr.in6_u.u6_addr8, 16);
-    id->transport_protocol = ip->nexthdr;
-    fill_l4info(l4_hdr_start, data_end, ip->nexthdr, &l4_info);
-    id->src_port = l4_info.src_port;
-    id->dst_port = l4_info.dst_port;
-    id->icmp_type = l4_info.icmp_type;
-    id->icmp_code = l4_info.icmp_code;
-    *flags = l4_info.flags;
-
-    return SUBMIT;
-}
-// sets flow fields from Ethernet header information
-static inline int fill_ethhdr(struct ethhdr *eth, void *data_end, flow_id *id, u16 *flags) {
-    if ((void *)eth + sizeof(*eth) > data_end) {
-        return DISCARD;
-    }
-    __builtin_memcpy(id->dst_mac, eth->h_dest, ETH_ALEN);
-    __builtin_memcpy(id->src_mac, eth->h_source, ETH_ALEN);
-    id->eth_protocol = bpf_ntohs(eth->h_proto);
-
-    if (id->eth_protocol == ETH_P_IP) {
-        struct iphdr *ip = (void *)eth + sizeof(*eth);
-        return fill_iphdr(ip, data_end, id, flags);
-    } else if (id->eth_protocol == ETH_P_IPV6) {
-        struct ipv6hdr *ip6 = (void *)eth + sizeof(*eth);
-        return fill_ip6hdr(ip6, data_end, id, flags);
-    } else {
-        // TODO : Need to implement other specific ethertypes if needed
-        // For now other parts of flow id remain zero
-        __builtin_memset(&(id->src_ip), 0, sizeof(struct in6_addr));
-        __builtin_memset(&(id->dst_ip), 0, sizeof(struct in6_addr));
-        id->transport_protocol = 0;
-        id->src_port = 0;
-        id->dst_port = 0;
-    }
-    return SUBMIT;
-}
 
 static inline int flow_monitor(struct __sk_buff *skb, u8 direction) {
     // If sampling is defined, will only parse 1 out of "sampling" flows
@@ -317,4 +101,5 @@ SEC("tc_egress")
 int egress_flow_parse(struct __sk_buff *skb) {
     return flow_monitor(skb, EGRESS);
 }
+
 char _license[] SEC("license") = "GPL";
diff --git a/bpf/maps_definition.h b/bpf/maps_definition.h
new file mode 100644
index 000000000..8bd0d0120
--- /dev/null
+++ b/bpf/maps_definition.h
@@ -0,0 +1,21 @@
+#ifndef __MAPS_DEFINITION_H__
+#define __MAPS_DEFINITION_H__
+
+#include <vmlinux.h>
+
+// Common Ringbuffer as a conduit for ingress/egress flows to userspace
+struct {
+    __uint(type, BPF_MAP_TYPE_RINGBUF);
+    __uint(max_entries, 1 << 24);
+} direct_flows SEC(".maps");
+
+// Key: the flow identifier. Value: the flow metrics for that identifier.
+struct {
+    __uint(type, BPF_MAP_TYPE_HASH);
+    __type(key, flow_id);
+    __type(value, flow_metrics);
+    __uint(max_entries, 1 << 24);
+    __uint(map_flags, BPF_F_NO_PREALLOC);
+} aggregated_flows SEC(".maps");
+
+#endif //__MAPS_DEFINITION_H__
diff --git a/bpf/tcp_drops.h b/bpf/tcp_drops.h
new file mode 100644
index 000000000..bbd84b547
--- /dev/null
+++ b/bpf/tcp_drops.h
@@ -0,0 +1,88 @@
+/*
+    TCPDrops using trace points.
+*/
+
+#ifndef __TCP_DROPS_H__
+#define __TCP_DROPS_H__
+
+#include "utils.h"
+
+static inline int trace_tcp_drop(void *ctx, struct sock *sk,
+                                 struct sk_buff *skb,
+                                 enum skb_drop_reason reason) {
+    if (sk == NULL)
+        return 0;
+
+    flow_id id;
+    __builtin_memset(&id, 0, sizeof(id));
+
+    u8 state = 0, protocol = 0;
+    u16 family = 0,flags = 0;
+
+    // pull in details from the packet headers and the sock struct
+    bpf_probe_read(&state, sizeof(u8), (u8 *)&sk->__sk_common.skc_state);
+
+    id.if_index = skb->skb_iif;
+
+    // read L2 info
+    set_key_with_l2_info(skb, &id, &family);
+
+    // read L3 info
+    set_key_with_l3_info(skb, family, &id, &protocol);
+
+    // We only support TCP drops for any other protocol just return w/o doing anything
+    if (protocol != IPPROTO_TCP) {
+        return 0;
+    }
+
+    // read L4 info
+    set_key_with_tcp_info(skb, &id, protocol, &flags);
+
+    long ret = 0;
+    for (direction_t dir = INGRESS; dir < MAX_DIRECTION; dir++) {
+        id.direction = dir;
+        ret = tcp_drop_lookup_and_update_flow(skb, &id, state, flags, reason);
+        if (ret == 0) {
+            return 0;
+        }
+    }
+    // there is no matching flows so lets create new one and add the drops
+    u64 current_time = bpf_ktime_get_ns();
+    id.direction = INGRESS;
+    flow_metrics new_flow = {
+        .start_mono_time_ts = current_time,
+        .end_mono_time_ts = current_time,
+        .flags = flags,
+        .tcp_drops.packets = 1,
+        .tcp_drops.bytes = skb->len,
+        .tcp_drops.latest_state = state,
+        .tcp_drops.latest_flags = flags,
+        .tcp_drops.latest_drop_cause = reason,
+    };
+    ret = bpf_map_update_elem(&aggregated_flows, &id, &new_flow, BPF_ANY);
+    if (trace_messages && ret != 0) {
+        bpf_printk("error tcp drop creating new flow %d\n", ret);
+    }
+
+    return ret;
+}
+
+SEC("tracepoint/skb/kfree_skb")
+int kfree_skb(struct trace_event_raw_kfree_skb *args) {
+    struct sk_buff skb;
+    __builtin_memset(&skb, 0, sizeof(skb));
+
+    bpf_probe_read(&skb, sizeof(struct sk_buff), args->skbaddr);
+    struct sock *sk = skb.sk;
+    enum skb_drop_reason reason = args->reason;
+
+    // SKB_NOT_DROPPED_YET,
+    // SKB_CONSUMED,
+    // SKB_DROP_REASON_NOT_SPECIFIED,
+    if (reason > SKB_DROP_REASON_NOT_SPECIFIED) {
+        return trace_tcp_drop(args, sk, &skb, reason);
+    }
+    return 0;
+}
+
+#endif //__TCP_DROPS_H__
diff --git a/bpf/utils.h b/bpf/utils.h
new file mode 100644
index 000000000..5338f1b72
--- /dev/null
+++ b/bpf/utils.h
@@ -0,0 +1,295 @@
+#ifndef __UTILS_H__
+#define __UTILS_H__
+
+#include <vmlinux.h>
+#include <bpf_helpers.h>
+
+#include "flow.h"
+#include "maps_definition.h"
+#include "configs.h"
+
+#define DISCARD 1
+#define SUBMIT 0
+
+// according to field 61 in https://www.iana.org/assignments/ipfix/ipfix.xhtml
+typedef enum {
+    INGRESS         = 0,
+    EGRESS          = 1,
+    MAX_DIRECTION   = 2,
+} direction_t;
+
+// L4_info structure contains L4 headers parsed information.
+struct l4_info_t {
+    // TCP/UDP/SCTP source port in host byte order
+    u16 src_port;
+    // TCP/UDP/SCTP destination port in host byte order
+    u16 dst_port;
+    // ICMPv4/ICMPv6 type value
+    u8 icmp_type;
+    // ICMPv4/ICMPv6 code value
+    u8 icmp_code;
+    // TCP flags
+    u16 flags;
+};
+
+const u8 ip4in6[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
+
+// Flags according to RFC 9293 & https://www.iana.org/assignments/ipfix/ipfix.xhtml
+#define FIN_FLAG 0x01
+#define SYN_FLAG 0x02
+#define RST_FLAG 0x04
+#define PSH_FLAG 0x08
+#define ACK_FLAG 0x10
+#define URG_FLAG 0x20
+#define ECE_FLAG 0x40
+#define CWR_FLAG 0x80
+// Custom flags exported
+#define SYN_ACK_FLAG 0x100
+#define FIN_ACK_FLAG 0x200
+#define RST_ACK_FLAG 0x400
+
+#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+	__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define bpf_ntohs(x)		__builtin_bswap16(x)
+#define bpf_htons(x)		__builtin_bswap16(x)
+#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
+	__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define bpf_ntohs(x)		(x)
+#define bpf_htons(x)		(x)
+#else
+# error "Endianness detection needs to be set up for your compiler?!"
+#endif
+
+
+// sets the TCP header flags for connection information
+static inline void set_flags(struct tcphdr *th, u16 *flags) {
+    //If both ACK and SYN are set, then it is server -> client communication during 3-way handshake.
+    if (th->ack && th->syn) {
+        *flags |= SYN_ACK_FLAG;
+    } else if (th->ack && th->fin ) {
+        // If both ACK and FIN are set, then it is graceful termination from server.
+        *flags |= FIN_ACK_FLAG;
+    } else if (th->ack && th->rst ) {
+        // If both ACK and RST are set, then it is abrupt connection termination.
+        *flags |= RST_ACK_FLAG;
+    } else if (th->fin) {
+        *flags |= FIN_FLAG;
+    } else if (th->syn) {
+        *flags |= SYN_FLAG;
+    } else if (th->ack) {
+        *flags |= ACK_FLAG;
+    } else if (th->rst) {
+        *flags |= RST_FLAG;
+    } else if (th->psh) {
+        *flags |= PSH_FLAG;
+    } else if (th->urg) {
+        *flags |= URG_FLAG;
+    } else if (th->ece) {
+        *flags |= ECE_FLAG;
+    } else if (th->cwr) {
+        *flags |= CWR_FLAG;
+    }
+}
+
+// Extract L4 info for the supported protocols
+static inline void fill_l4info(void *l4_hdr_start, void *data_end, u8 protocol,
+                               struct l4_info_t *l4_info) {
+	switch (protocol) {
+    case IPPROTO_TCP: {
+        struct tcphdr *tcp = l4_hdr_start;
+        if ((void *)tcp + sizeof(*tcp) <= data_end) {
+            l4_info->src_port = bpf_ntohs(tcp->source);
+            l4_info->dst_port = bpf_ntohs(tcp->dest);
+            set_flags(tcp, &l4_info->flags);
+        }
+    } break;
+    case IPPROTO_UDP: {
+        struct udphdr *udp = l4_hdr_start;
+        if ((void *)udp + sizeof(*udp) <= data_end) {
+            l4_info->src_port = bpf_ntohs(udp->source);
+            l4_info->dst_port = bpf_ntohs(udp->dest);
+        }
+    } break;
+    case IPPROTO_SCTP: {
+        struct sctphdr *sctph = l4_hdr_start;
+        if ((void *)sctph + sizeof(*sctph) <= data_end) {
+            l4_info->src_port = bpf_ntohs(sctph->source);
+            l4_info->dst_port = bpf_ntohs(sctph->dest);
+        }
+    } break;
+    case IPPROTO_ICMP: {
+        struct icmphdr *icmph = l4_hdr_start;
+        if ((void *)icmph + sizeof(*icmph) <= data_end) {
+            l4_info->icmp_type = icmph->type;
+            l4_info->icmp_code = icmph->code;
+        }
+    } break;
+    case IPPROTO_ICMPV6: {
+        struct icmp6hdr *icmp6h = l4_hdr_start;
+         if ((void *)icmp6h + sizeof(*icmp6h) <= data_end) {
+            l4_info->icmp_type = icmp6h->icmp6_type;
+            l4_info->icmp_code = icmp6h->icmp6_code;
+        }
+    } break;
+    default:
+        break;
+    }
+}
+
+// sets flow fields from IPv4 header information
+static inline int fill_iphdr(struct iphdr *ip, void *data_end, flow_id *id, u16 *flags) {
+    struct l4_info_t l4_info;
+    void *l4_hdr_start;
+
+    l4_hdr_start = (void *)ip + sizeof(*ip);
+    if (l4_hdr_start > data_end) {
+        return DISCARD;
+    }
+    __builtin_memset(&l4_info, 0, sizeof(l4_info));
+    __builtin_memcpy(id->src_ip, ip4in6, sizeof(ip4in6));
+    __builtin_memcpy(id->dst_ip, ip4in6, sizeof(ip4in6));
+    __builtin_memcpy(id->src_ip + sizeof(ip4in6), &ip->saddr, sizeof(ip->saddr));
+    __builtin_memcpy(id->dst_ip + sizeof(ip4in6), &ip->daddr, sizeof(ip->daddr));
+    id->transport_protocol = ip->protocol;
+    fill_l4info(l4_hdr_start, data_end, ip->protocol, &l4_info);
+    id->src_port = l4_info.src_port;
+    id->dst_port = l4_info.dst_port;
+    id->icmp_type = l4_info.icmp_type;
+    id->icmp_code = l4_info.icmp_code;
+    *flags = l4_info.flags;
+
+    return SUBMIT;
+}
+
+// sets flow fields from IPv6 header information
+static inline int fill_ip6hdr(struct ipv6hdr *ip, void *data_end, flow_id *id, u16 *flags) {
+    struct l4_info_t l4_info;
+    void *l4_hdr_start;
+
+    l4_hdr_start = (void *)ip + sizeof(*ip);
+    if (l4_hdr_start > data_end) {
+        return DISCARD;
+    }
+    __builtin_memset(&l4_info, 0, sizeof(l4_info));
+    __builtin_memcpy(id->src_ip, ip->saddr.in6_u.u6_addr8, IP_MAX_LEN);
+    __builtin_memcpy(id->dst_ip, ip->daddr.in6_u.u6_addr8, IP_MAX_LEN);
+    id->transport_protocol = ip->nexthdr;
+    fill_l4info(l4_hdr_start, data_end, ip->nexthdr, &l4_info);
+    id->src_port = l4_info.src_port;
+    id->dst_port = l4_info.dst_port;
+    id->icmp_type = l4_info.icmp_type;
+    id->icmp_code = l4_info.icmp_code;
+    *flags = l4_info.flags;
+
+    return SUBMIT;
+}
+
+// sets flow fields from Ethernet header information
+static inline int fill_ethhdr(struct ethhdr *eth, void *data_end, flow_id *id, u16 *flags) {
+    if ((void *)eth + sizeof(*eth) > data_end) {
+        return DISCARD;
+    }
+    __builtin_memcpy(id->dst_mac, eth->h_dest, ETH_ALEN);
+    __builtin_memcpy(id->src_mac, eth->h_source, ETH_ALEN);
+    id->eth_protocol = bpf_ntohs(eth->h_proto);
+
+    if (id->eth_protocol == ETH_P_IP) {
+        struct iphdr *ip = (void *)eth + sizeof(*eth);
+        return fill_iphdr(ip, data_end, id, flags);
+    } else if (id->eth_protocol == ETH_P_IPV6) {
+        struct ipv6hdr *ip6 = (void *)eth + sizeof(*eth);
+        return fill_ip6hdr(ip6, data_end, id, flags);
+    } else {
+        // TODO : Need to implement other specific ethertypes if needed
+        // For now other parts of flow id remain zero
+        __builtin_memset(&(id->src_ip), 0, sizeof(struct in6_addr));
+        __builtin_memset(&(id->dst_ip), 0, sizeof(struct in6_addr));
+        id->transport_protocol = 0;
+        id->src_port = 0;
+        id->dst_port = 0;
+    }
+    return SUBMIT;
+}
+
+static inline void set_key_with_l2_info(struct sk_buff *skb, flow_id *id, u16 *family) {
+     struct ethhdr eth;
+     __builtin_memset(&eth, 0, sizeof(eth));
+     bpf_probe_read(&eth, sizeof(eth), (struct ethhdr *)(skb->head + skb->mac_header));
+     id->eth_protocol = bpf_ntohs(eth.h_proto);
+     __builtin_memcpy(id->dst_mac, eth.h_dest, ETH_ALEN);
+     __builtin_memcpy(id->src_mac, eth.h_source, ETH_ALEN);
+    if (id->eth_protocol == ETH_P_IP) {
+        *family = AF_INET;
+    } else if (id->eth_protocol == ETH_P_IPV6) {
+        *family = AF_INET6;
+    }
+ }
+
+static inline void set_key_with_l3_info(struct sk_buff *skb, u16 family, flow_id *id, u8 *protocol) {
+     if (family == AF_INET) {
+         struct iphdr ip;
+         __builtin_memset(&ip, 0, sizeof(ip));
+         bpf_probe_read(&ip, sizeof(ip), (struct iphdr *)(skb->head + skb->network_header));
+         __builtin_memcpy(id->src_ip, ip4in6, sizeof(ip4in6));
+         __builtin_memcpy(id->dst_ip, ip4in6, sizeof(ip4in6));
+         __builtin_memcpy(id->src_ip + sizeof(ip4in6), &ip.saddr, sizeof(ip.saddr));
+         __builtin_memcpy(id->dst_ip + sizeof(ip4in6), &ip.daddr, sizeof(ip.daddr));
+         *protocol = ip.protocol;
+     } else if (family == AF_INET6) {
+         struct ipv6hdr ip;
+         __builtin_memset(&ip, 0, sizeof(ip));
+         bpf_probe_read(&ip, sizeof(ip), (struct ipv6hdr *)(skb->head + skb->network_header));
+         __builtin_memcpy(id->src_ip, ip.saddr.in6_u.u6_addr8, IP_MAX_LEN);
+         __builtin_memcpy(id->dst_ip, ip.daddr.in6_u.u6_addr8, IP_MAX_LEN);
+         *protocol = ip.nexthdr;
+     }
+ }
+
+static inline int set_key_with_tcp_info(struct sk_buff *skb, flow_id *id, u8 protocol, u16 *flags) {
+     u16 sport = 0,dport = 0;
+     struct tcphdr tcp;
+
+     __builtin_memset(&tcp, 0, sizeof(tcp));
+     bpf_probe_read(&tcp, sizeof(tcp), (struct tcphdr *)(skb->head + skb->transport_header));
+     sport = bpf_ntohs(tcp.source);
+     dport = bpf_ntohs(tcp.dest);
+     set_flags(&tcp, flags);
+     id->src_port = sport;
+     id->dst_port = dport;
+     id->transport_protocol = protocol;
+     return tcp.doff * sizeof(u32);
+ }
+
+static inline int set_key_with_udp_info(struct sk_buff *skb, flow_id *id, u8 protocol) {
+     u16 sport = 0,dport = 0;
+     struct udphdr udp;
+
+     __builtin_memset(&udp, 0, sizeof(udp));
+     bpf_probe_read(&udp, sizeof(udp), (struct udp *)(skb->head + skb->transport_header));
+     sport = bpf_ntohs(udp.source);
+     dport = bpf_ntohs(udp.dest);
+     id->src_port = sport;
+     id->dst_port = dport;
+     id->transport_protocol = protocol;
+     return bpf_ntohs(udp.len);
+ }
+
+static inline long tcp_drop_lookup_and_update_flow(struct sk_buff *skb, flow_id *id, u8 state, u16 flags,
+                                            enum skb_drop_reason reason) {
+     flow_metrics *aggregate_flow = bpf_map_lookup_elem(&aggregated_flows, id);
+     if (aggregate_flow != NULL) {
+         aggregate_flow->tcp_drops.packets += 1;
+         aggregate_flow->tcp_drops.bytes += skb->len;
+         aggregate_flow->tcp_drops.latest_state = state;
+         aggregate_flow->tcp_drops.latest_flags = flags;
+         aggregate_flow->tcp_drops.latest_drop_cause = reason;
+         long ret = bpf_map_update_elem(&aggregated_flows, id, aggregate_flow, BPF_ANY);
+         if (trace_messages && ret != 0) {
+             bpf_printk("error tcp drop updating flow %d\n", ret);
+         }
+         return 0;
+      }
+      return -1;
+ }
+
+#endif // __UTILS_H__
diff --git a/e2e/cluster/base/04-agent.yml b/e2e/cluster/base/04-agent.yml
index 19a0ff521..7771b9f5d 100644
--- a/e2e/cluster/base/04-agent.yml
+++ b/e2e/cluster/base/04-agent.yml
@@ -32,3 +32,12 @@ spec:
                 fieldPath: status.hostIP
           - name: FLOWS_TARGET_PORT
             value: "9999"
+        volumeMounts:
+            - name: bpf-kernel-debug
+              mountPath: /sys/kernel/debug
+              mountPropagation: Bidirectional
+      volumes:
+        - name: bpf-kernel-debug
+          hostPath:
+            path: /sys/kernel/debug
+            type: Directory
diff --git a/e2e/ipfix/manifests/30-agent.yml b/e2e/ipfix/manifests/30-agent.yml
index d6e50d848..8a0b09fc3 100644
--- a/e2e/ipfix/manifests/30-agent.yml
+++ b/e2e/ipfix/manifests/30-agent.yml
@@ -34,3 +34,12 @@ spec:
                 fieldPath: status.hostIP
           - name: FLOWS_TARGET_PORT
             value: "9999"
+        volumeMounts:
+            - name: bpf-kernel-debug
+              mountPath: /sys/kernel/debug
+              mountPropagation: Bidirectional
+      volumes:
+        - name: bpf-kernel-debug
+          hostPath:
+            path: /sys/kernel/debug
+            type: Directory
diff --git a/e2e/kafka/manifests/30-agent.yml b/e2e/kafka/manifests/30-agent.yml
index e8ac40835..6602d8574 100644
--- a/e2e/kafka/manifests/30-agent.yml
+++ b/e2e/kafka/manifests/30-agent.yml
@@ -30,3 +30,12 @@ spec:
             value: 200ms
           - name: LOG_LEVEL
             value: debug
+        volumeMounts:
+            - name: bpf-kernel-debug
+              mountPath: /sys/kernel/debug
+              mountPropagation: Bidirectional
+      volumes:
+        - name: bpf-kernel-debug
+          hostPath:
+            path: /sys/kernel/debug
+            type: Directory
diff --git a/examples/flowlogs-dump/server/flowlogs-dump-collector.go b/examples/flowlogs-dump/server/flowlogs-dump-collector.go
index 2728e0b3b..d04af1c02 100644
--- a/examples/flowlogs-dump/server/flowlogs-dump-collector.go
+++ b/examples/flowlogs-dump/server/flowlogs-dump-collector.go
@@ -72,7 +72,7 @@ func main() {
 	for records := range receivedRecords {
 		for _, record := range records.Entries {
 			if record.EthProtocol == ipv6 {
-				log.Printf("%s: %v %s IP %s:%d > %s:%d: protocol:%s type: %d code: %d dir:%d bytes:%d packets:%d flags:%d ends: %v\n",
+				log.Printf("%s: %v %s IP %s:%d > %s:%d: protocol:%s type: %d code: %d dir:%d bytes:%d packets:%d flags:%d ends: %v dnsId: %d dnsFlags: 0x%04x dnsReq: %v dnsRsp: %v\n",
 					ipProto[record.EthProtocol],
 					record.TimeFlowStart.AsTime().Local().Format("15:04:05.000000"),
 					record.Interface,
@@ -81,16 +81,20 @@ func main() {
 					net.IP(record.Network.GetDstAddr().GetIpv6()).To16(),
 					record.Transport.DstPort,
 					protocolByNumber[record.Transport.Protocol],
-					record.Icmp.IcmpType,
-					record.Icmp.IcmpCode,
+					record.IcmpType,
+					record.IcmpCode,
 					record.Direction,
 					record.Bytes,
 					record.Packets,
 					record.Flags,
 					record.TimeFlowEnd.AsTime().Local().Format("15:04:05.000000"),
+					record.GetDnsId(),
+					record.GetDnsFlags(),
+					record.GetTimeDnsReq(),
+					record.GetTimeDnsRsp(),
 				)
 			} else {
-				log.Printf("%s: %v %s IP %s:%d > %s:%d: protocol:%s type: %d code: %d dir:%d bytes:%d packets:%d flags:%d ends: %v\n",
+				log.Printf("%s: %v %s IP %s:%d > %s:%d: protocol:%s type: %d code: %d dir:%d bytes:%d packets:%d flags:%d ends: %v dnsId: %d dnsFlags: 0x%04x dnsReq: %v dnsRsp: %v\n",
 					ipProto[record.EthProtocol],
 					record.TimeFlowStart.AsTime().Local().Format("15:04:05.000000"),
 					record.Interface,
@@ -99,13 +103,17 @@ func main() {
 					ipIntToNetIP(record.Network.GetDstAddr().GetIpv4()).String(),
 					record.Transport.DstPort,
 					protocolByNumber[record.Transport.Protocol],
-					record.Icmp.IcmpType,
-					record.Icmp.IcmpCode,
+					record.IcmpType,
+					record.IcmpCode,
 					record.Direction,
 					record.Bytes,
 					record.Packets,
 					record.Flags,
 					record.TimeFlowEnd.AsTime().Local().Format("15:04:05.000000"),
+					record.GetDnsId(),
+					record.GetDnsFlags(),
+					record.GetTimeDnsReq(),
+					record.GetTimeDnsRsp(),
 				)
 			}
 		}
diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go
index efd44c821..2553feef6 100644
--- a/pkg/agent/agent.go
+++ b/pkg/agent/agent.go
@@ -122,7 +122,7 @@ func FlowsAgent(cfg *Config) (*Flows, error) {
 		debug = true
 	}
 
-	fetcher, err := ebpf.NewFlowFetcher(debug, cfg.Sampling, cfg.CacheMaxFlows, ingress, egress)
+	fetcher, err := ebpf.NewFlowFetcher(debug, cfg.Sampling, cfg.CacheMaxFlows, ingress, egress, cfg.EnableTCPDrops, cfg.EnableDNSTracking)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/agent/config.go b/pkg/agent/config.go
index 6dfb49b2d..7cc146295 100644
--- a/pkg/agent/config.go
+++ b/pkg/agent/config.go
@@ -138,4 +138,8 @@ type Config struct {
 	ProfilePort int `env:"PROFILE_PORT"`
 	// EnableGC enables golang garbage collection run at the end of every map eviction, default is true
 	EnableGC bool `env:"ENABLE_GARBAGE_COLLECTION" envDefault:"true"`
+	// EnableTcpDrops enable TCP drops eBPF hook to account for tcp dropped flows
+	EnableTCPDrops bool `env:"ENABLE_TCP_DROPS" envDefault:"false"`
+	// EnableDNSTracking enable DNS tracking eBPF hook to track dns query/response flows
+	EnableDNSTracking bool `env:"ENABLE_DNS_TRACKING" envDefault:"false"`
 }
diff --git a/pkg/ebpf/bpf_bpfeb.go b/pkg/ebpf/bpf_bpfeb.go
index 575ad0681..c1037c2d1 100644
--- a/pkg/ebpf/bpf_bpfeb.go
+++ b/pkg/ebpf/bpf_bpfeb.go
@@ -13,6 +13,13 @@ import (
 	"github.com/cilium/ebpf"
 )
 
+type BpfDnsRecordT struct {
+	Id            uint16
+	Flags         uint16
+	ReqMonoTimeTs uint64
+	RspMonoTimeTs uint64
+}
+
 type BpfFlowId BpfFlowIdT
 
 type BpfFlowIdT struct {
@@ -39,6 +46,8 @@ type BpfFlowMetricsT struct {
 	EndMonoTimeTs   uint64
 	Flags           uint16
 	Errno           uint8
+	TcpDrops        BpfTcpDropsT
+	DnsRecord       BpfDnsRecordT
 }
 
 type BpfFlowRecordT struct {
@@ -46,6 +55,14 @@ type BpfFlowRecordT struct {
 	Metrics BpfFlowMetrics
 }
 
+type BpfTcpDropsT struct {
+	Packets         uint32
+	Bytes           uint64
+	LatestFlags     uint16
+	LatestState     uint8
+	LatestDropCause uint32
+}
+
 // LoadBpf returns the embedded CollectionSpec for Bpf.
 func LoadBpf() (*ebpf.CollectionSpec, error) {
 	reader := bytes.NewReader(_BpfBytes)
@@ -89,6 +106,8 @@ type BpfSpecs struct {
 type BpfProgramSpecs struct {
 	EgressFlowParse  *ebpf.ProgramSpec `ebpf:"egress_flow_parse"`
 	IngressFlowParse *ebpf.ProgramSpec `ebpf:"ingress_flow_parse"`
+	KfreeSkb         *ebpf.ProgramSpec `ebpf:"kfree_skb"`
+	TraceNetPackets  *ebpf.ProgramSpec `ebpf:"trace_net_packets"`
 }
 
 // BpfMapSpecs contains maps before they are loaded into the kernel.
@@ -135,12 +154,16 @@ func (m *BpfMaps) Close() error {
 type BpfPrograms struct {
 	EgressFlowParse  *ebpf.Program `ebpf:"egress_flow_parse"`
 	IngressFlowParse *ebpf.Program `ebpf:"ingress_flow_parse"`
+	KfreeSkb         *ebpf.Program `ebpf:"kfree_skb"`
+	TraceNetPackets  *ebpf.Program `ebpf:"trace_net_packets"`
 }
 
 func (p *BpfPrograms) Close() error {
 	return _BpfClose(
 		p.EgressFlowParse,
 		p.IngressFlowParse,
+		p.KfreeSkb,
+		p.TraceNetPackets,
 	)
 }
 
diff --git a/pkg/ebpf/bpf_bpfeb.o b/pkg/ebpf/bpf_bpfeb.o
index f3dd6b1ea09dea6751857a74838cac9ded0401a6..cdaf6f13945bac619aaad7889be15ae739665777 100644
GIT binary patch
literal 67240
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
aEkWXF_%bNpgOW8U9f9<~_I1GgF987UZa`ZA

literal 27208
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

diff --git a/pkg/ebpf/bpf_bpfel.go b/pkg/ebpf/bpf_bpfel.go
index bae6823f0..4aa8bfd8c 100644
--- a/pkg/ebpf/bpf_bpfel.go
+++ b/pkg/ebpf/bpf_bpfel.go
@@ -13,6 +13,13 @@ import (
 	"github.com/cilium/ebpf"
 )
 
+type BpfDnsRecordT struct {
+	Id            uint16
+	Flags         uint16
+	ReqMonoTimeTs uint64
+	RspMonoTimeTs uint64
+}
+
 type BpfFlowId BpfFlowIdT
 
 type BpfFlowIdT struct {
@@ -39,6 +46,8 @@ type BpfFlowMetricsT struct {
 	EndMonoTimeTs   uint64
 	Flags           uint16
 	Errno           uint8
+	TcpDrops        BpfTcpDropsT
+	DnsRecord       BpfDnsRecordT
 }
 
 type BpfFlowRecordT struct {
@@ -46,6 +55,14 @@ type BpfFlowRecordT struct {
 	Metrics BpfFlowMetrics
 }
 
+type BpfTcpDropsT struct {
+	Packets         uint32
+	Bytes           uint64
+	LatestFlags     uint16
+	LatestState     uint8
+	LatestDropCause uint32
+}
+
 // LoadBpf returns the embedded CollectionSpec for Bpf.
 func LoadBpf() (*ebpf.CollectionSpec, error) {
 	reader := bytes.NewReader(_BpfBytes)
@@ -89,6 +106,8 @@ type BpfSpecs struct {
 type BpfProgramSpecs struct {
 	EgressFlowParse  *ebpf.ProgramSpec `ebpf:"egress_flow_parse"`
 	IngressFlowParse *ebpf.ProgramSpec `ebpf:"ingress_flow_parse"`
+	KfreeSkb         *ebpf.ProgramSpec `ebpf:"kfree_skb"`
+	TraceNetPackets  *ebpf.ProgramSpec `ebpf:"trace_net_packets"`
 }
 
 // BpfMapSpecs contains maps before they are loaded into the kernel.
@@ -135,12 +154,16 @@ func (m *BpfMaps) Close() error {
 type BpfPrograms struct {
 	EgressFlowParse  *ebpf.Program `ebpf:"egress_flow_parse"`
 	IngressFlowParse *ebpf.Program `ebpf:"ingress_flow_parse"`
+	KfreeSkb         *ebpf.Program `ebpf:"kfree_skb"`
+	TraceNetPackets  *ebpf.Program `ebpf:"trace_net_packets"`
 }
 
 func (p *BpfPrograms) Close() error {
 	return _BpfClose(
 		p.EgressFlowParse,
 		p.IngressFlowParse,
+		p.KfreeSkb,
+		p.TraceNetPackets,
 	)
 }
 
diff --git a/pkg/ebpf/bpf_bpfel.o b/pkg/ebpf/bpf_bpfel.o
index b6095610414e142b585363b87c85ed31fa4fe8f5..df2199eef85efc66c71fe22bac3642b760bfa572 100644
GIT binary patch
literal 67888
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
U2otuB2&B&(8rL8h=x`7N0D<kP;s5{u

literal 27280
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

diff --git a/pkg/ebpf/tracer.go b/pkg/ebpf/tracer.go
index 2253c1f4b..29eba8bf1 100644
--- a/pkg/ebpf/tracer.go
+++ b/pkg/ebpf/tracer.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/btf"
+	"github.com/cilium/ebpf/link"
 	"github.com/cilium/ebpf/ringbuf"
 	"github.com/cilium/ebpf/rlimit"
 	"github.com/netobserv/netobserv-ebpf-agent/pkg/ifaces"
@@ -17,7 +18,7 @@ import (
 )
 
 // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
-//go:generate bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS -type flow_metrics_t -type flow_id_t -type flow_record_t Bpf ../../bpf/flows.c -- -I../../bpf/headers
+//go:generate bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS -type flow_metrics_t -type flow_id_t -type flow_record_t -type tcp_drops_t -type dns_record_t Bpf ../../bpf/flows.c -- -I../../bpf/headers
 
 const (
 	qdiscType = "clsact"
@@ -34,20 +35,22 @@ var log = logrus.WithField("component", "ebpf.FlowFetcher")
 // and to flows that are forwarded by the kernel via ringbuffer because could not be aggregated
 // in the map
 type FlowFetcher struct {
-	objects        *BpfObjects
-	qdiscs         map[ifaces.Interface]*netlink.GenericQdisc
-	egressFilters  map[ifaces.Interface]*netlink.BpfFilter
-	ingressFilters map[ifaces.Interface]*netlink.BpfFilter
-	ringbufReader  *ringbuf.Reader
-	cacheMaxSize   int
-	enableIngress  bool
-	enableEgress   bool
+	objects              *BpfObjects
+	qdiscs               map[ifaces.Interface]*netlink.GenericQdisc
+	egressFilters        map[ifaces.Interface]*netlink.BpfFilter
+	ingressFilters       map[ifaces.Interface]*netlink.BpfFilter
+	ringbufReader        *ringbuf.Reader
+	cacheMaxSize         int
+	enableIngress        bool
+	enableEgress         bool
+	tcpDropsTracePoint   link.Link
+	dnsTrackerTracePoint link.Link
 }
 
 func NewFlowFetcher(
 	traceMessages bool,
 	sampling, cacheMaxSize int,
-	ingress, egress bool,
+	ingress, egress, tcpDrops, dnsTracker bool,
 ) (*FlowFetcher, error) {
 	if err := rlimit.RemoveMemlock(); err != nil {
 		log.WithError(err).
@@ -73,6 +76,7 @@ func NewFlowFetcher(
 	}); err != nil {
 		return nil, fmt.Errorf("rewriting BPF constants definition: %w", err)
 	}
+
 	if err := spec.LoadAndAssign(&objects, nil); err != nil {
 		var ve *ebpf.VerifierError
 		if errors.As(err, &ve) {
@@ -88,20 +92,39 @@ func NewFlowFetcher(
 	 * for more details.
 	 */
 	btf.FlushKernelSpec()
+
+	var tcpDropsLink link.Link
+	if tcpDrops {
+		tcpDropsLink, err = link.Tracepoint("skb", "kfree_skb", objects.KfreeSkb, nil)
+		if err != nil {
+			return nil, fmt.Errorf("failed to attach the BPF program to kfree_skb tracepoint: %w", err)
+		}
+	}
+
+	var dnsTrackerLink link.Link
+	if dnsTracker {
+		dnsTrackerLink, err = link.Tracepoint("net", "net_dev_queue", objects.TraceNetPackets, nil)
+		if err != nil {
+			return nil, fmt.Errorf("failed to attach the BPF program to trace_net_packets: %w", err)
+		}
+	}
+
 	// read events from igress+egress ringbuffer
 	flows, err := ringbuf.NewReader(objects.DirectFlows)
 	if err != nil {
 		return nil, fmt.Errorf("accessing to ringbuffer: %w", err)
 	}
 	return &FlowFetcher{
-		objects:        &objects,
-		ringbufReader:  flows,
-		egressFilters:  map[ifaces.Interface]*netlink.BpfFilter{},
-		ingressFilters: map[ifaces.Interface]*netlink.BpfFilter{},
-		qdiscs:         map[ifaces.Interface]*netlink.GenericQdisc{},
-		cacheMaxSize:   cacheMaxSize,
-		enableIngress:  ingress,
-		enableEgress:   egress,
+		objects:              &objects,
+		ringbufReader:        flows,
+		egressFilters:        map[ifaces.Interface]*netlink.BpfFilter{},
+		ingressFilters:       map[ifaces.Interface]*netlink.BpfFilter{},
+		qdiscs:               map[ifaces.Interface]*netlink.GenericQdisc{},
+		cacheMaxSize:         cacheMaxSize,
+		enableIngress:        ingress,
+		enableEgress:         egress,
+		tcpDropsTracePoint:   tcpDropsLink,
+		dnsTrackerTracePoint: dnsTrackerLink,
 	}, nil
 }
 
@@ -217,6 +240,14 @@ func (m *FlowFetcher) Close() error {
 	log.Debug("unregistering eBPF objects")
 
 	var errs []error
+
+	if m.tcpDropsTracePoint != nil {
+		m.tcpDropsTracePoint.Close()
+	}
+
+	if m.dnsTrackerTracePoint != nil {
+		m.dnsTrackerTracePoint.Close()
+	}
 	// m.ringbufReader.Read is a blocking operation, so we need to close the ring buffer
 	// from another goroutine to avoid the system not being able to exit if there
 	// isn't traffic in a given interface
diff --git a/pkg/exporter/kafka_proto_test.go b/pkg/exporter/kafka_proto_test.go
index d07028c80..9bdb0b2b8 100644
--- a/pkg/exporter/kafka_proto_test.go
+++ b/pkg/exporter/kafka_proto_test.go
@@ -65,7 +65,7 @@ func TestProtoConversion(t *testing.T) {
 	assert.EqualValues(t, 4321, r.Transport.SrcPort)
 	assert.EqualValues(t, 1234, r.Transport.DstPort)
 	assert.EqualValues(t, 210, r.Transport.Protocol)
-	assert.EqualValues(t, 8, r.Icmp.IcmpType)
+	assert.EqualValues(t, 8, r.IcmpType)
 	assert.Equal(t, record.TimeFlowStart.UnixMilli(), r.TimeFlowStart.AsTime().UnixMilli())
 	assert.Equal(t, record.TimeFlowEnd.UnixMilli(), r.TimeFlowEnd.AsTime().UnixMilli())
 	assert.EqualValues(t, 789, r.Bytes)
diff --git a/pkg/exporter/proto.go b/pkg/exporter/proto.go
index 94f9bcea0..df460b1ba 100644
--- a/pkg/exporter/proto.go
+++ b/pkg/exporter/proto.go
@@ -38,7 +38,7 @@ func flowToPB(record *flow.Record) *pbflow.Record {
 }
 
 func v4FlowToPB(fr *flow.Record) *pbflow.Record {
-	return &pbflow.Record{
+	var pbflowRecord = pbflow.Record{
 		EthProtocol: uint32(fr.Id.EthProtocol),
 		Direction:   pbflow.Direction(fr.Id.Direction),
 		DataLink: &pbflow.DataLink{
@@ -54,11 +54,9 @@ func v4FlowToPB(fr *flow.Record) *pbflow.Record {
 			SrcPort:  uint32(fr.Id.SrcPort),
 			DstPort:  uint32(fr.Id.DstPort),
 		},
-		Icmp: &pbflow.Icmp{
-			IcmpType: uint32(fr.Id.IcmpType),
-			IcmpCode: uint32(fr.Id.IcmpCode),
-		},
-		Bytes: fr.Metrics.Bytes,
+		IcmpType: uint32(fr.Id.IcmpType),
+		IcmpCode: uint32(fr.Id.IcmpCode),
+		Bytes:    fr.Metrics.Bytes,
 		TimeFlowStart: &timestamppb.Timestamp{
 			Seconds: fr.TimeFlowStart.Unix(),
 			Nanos:   int32(fr.TimeFlowStart.Nanosecond()),
@@ -67,16 +65,36 @@ func v4FlowToPB(fr *flow.Record) *pbflow.Record {
 			Seconds: fr.TimeFlowEnd.Unix(),
 			Nanos:   int32(fr.TimeFlowEnd.Nanosecond()),
 		},
-		Packets:   uint64(fr.Metrics.Packets),
-		Duplicate: fr.Duplicate,
-		AgentIp:   agentIP(fr.AgentIP),
-		Flags:     uint32(fr.Metrics.Flags),
-		Interface: string(fr.Interface),
+		Packets:                uint64(fr.Metrics.Packets),
+		Duplicate:              fr.Duplicate,
+		AgentIp:                agentIP(fr.AgentIP),
+		Flags:                  uint32(fr.Metrics.Flags),
+		Interface:              string(fr.Interface),
+		TcpDropBytes:           fr.Metrics.TcpDrops.Bytes,
+		TcpDropPackets:         uint64(fr.Metrics.TcpDrops.Packets),
+		TcpDropLatestFlags:     uint32(fr.Metrics.TcpDrops.LatestFlags),
+		TcpDropLatestState:     uint32(fr.Metrics.TcpDrops.LatestState),
+		TcpDropLatestDropCause: fr.Metrics.TcpDrops.LatestDropCause,
+		DnsId:                  uint32(fr.Metrics.DnsRecord.Id),
+		DnsFlags:               uint32(fr.Metrics.DnsRecord.Flags),
+	}
+	if fr.Metrics.DnsRecord.ReqMonoTimeTs != 0 {
+		pbflowRecord.TimeDnsReq = &timestamppb.Timestamp{
+			Seconds: fr.TimeDNSRequest.Unix(),
+			Nanos:   int32(fr.TimeDNSRequest.Nanosecond()),
+		}
+	}
+	if fr.Metrics.DnsRecord.RspMonoTimeTs != 0 {
+		pbflowRecord.TimeDnsRsp = &timestamppb.Timestamp{
+			Seconds: fr.TimeDNSResponse.Unix(),
+			Nanos:   int32(fr.TimeDNSResponse.Nanosecond()),
+		}
 	}
+	return &pbflowRecord
 }
 
 func v6FlowToPB(fr *flow.Record) *pbflow.Record {
-	return &pbflow.Record{
+	var pbflowRecord = pbflow.Record{
 		EthProtocol: uint32(fr.Id.EthProtocol),
 		Direction:   pbflow.Direction(fr.Id.Direction),
 		DataLink: &pbflow.DataLink{
@@ -92,11 +110,9 @@ func v6FlowToPB(fr *flow.Record) *pbflow.Record {
 			SrcPort:  uint32(fr.Id.SrcPort),
 			DstPort:  uint32(fr.Id.DstPort),
 		},
-		Icmp: &pbflow.Icmp{
-			IcmpType: uint32(fr.Id.IcmpType),
-			IcmpCode: uint32(fr.Id.IcmpCode),
-		},
-		Bytes: fr.Metrics.Bytes,
+		IcmpType: uint32(fr.Id.IcmpType),
+		IcmpCode: uint32(fr.Id.IcmpCode),
+		Bytes:    fr.Metrics.Bytes,
 		TimeFlowStart: &timestamppb.Timestamp{
 			Seconds: fr.TimeFlowStart.Unix(),
 			Nanos:   int32(fr.TimeFlowStart.Nanosecond()),
@@ -105,12 +121,32 @@ func v6FlowToPB(fr *flow.Record) *pbflow.Record {
 			Seconds: fr.TimeFlowEnd.Unix(),
 			Nanos:   int32(fr.TimeFlowEnd.Nanosecond()),
 		},
-		Packets:   uint64(fr.Metrics.Packets),
-		Flags:     uint32(fr.Metrics.Flags),
-		Interface: fr.Interface,
-		Duplicate: fr.Duplicate,
-		AgentIp:   agentIP(fr.AgentIP),
+		Packets:                uint64(fr.Metrics.Packets),
+		Flags:                  uint32(fr.Metrics.Flags),
+		Interface:              fr.Interface,
+		Duplicate:              fr.Duplicate,
+		AgentIp:                agentIP(fr.AgentIP),
+		TcpDropBytes:           fr.Metrics.TcpDrops.Bytes,
+		TcpDropPackets:         uint64(fr.Metrics.TcpDrops.Packets),
+		TcpDropLatestFlags:     uint32(fr.Metrics.TcpDrops.LatestFlags),
+		TcpDropLatestState:     uint32(fr.Metrics.TcpDrops.LatestState),
+		TcpDropLatestDropCause: fr.Metrics.TcpDrops.LatestDropCause,
+		DnsId:                  uint32(fr.Metrics.DnsRecord.Id),
+		DnsFlags:               uint32(fr.Metrics.DnsRecord.Flags),
+	}
+	if fr.Metrics.DnsRecord.ReqMonoTimeTs != 0 {
+		pbflowRecord.TimeDnsReq = &timestamppb.Timestamp{
+			Seconds: fr.TimeDNSRequest.Unix(),
+			Nanos:   int32(fr.TimeDNSRequest.Nanosecond()),
+		}
+	}
+	if fr.Metrics.DnsRecord.RspMonoTimeTs != 0 {
+		pbflowRecord.TimeDnsRsp = &timestamppb.Timestamp{
+			Seconds: fr.TimeDNSResponse.Unix(),
+			Nanos:   int32(fr.TimeDNSResponse.Nanosecond()),
+		}
 	}
+	return &pbflowRecord
 }
 
 // Mac bytes are encoded in the same order as in the array. This is, a Mac
diff --git a/pkg/flow/account.go b/pkg/flow/account.go
index a38b8140a..b88840e04 100644
--- a/pkg/flow/account.go
+++ b/pkg/flow/account.go
@@ -87,7 +87,7 @@ func (c *Accounter) evict(entries map[ebpf.BpfFlowId]*ebpf.BpfFlowMetrics, evict
 	monotonicNow := uint64(c.monoClock())
 	records := make([]*Record, 0, len(entries))
 	for key, metrics := range entries {
-		records = append(records, NewRecord(key, *metrics, now, monotonicNow))
+		records = append(records, NewRecord(key, metrics, now, monotonicNow))
 	}
 	alog.WithField("numEntries", len(records)).Debug("records evicted from userspace accounter")
 	evictor <- records
diff --git a/pkg/flow/account_test.go b/pkg/flow/account_test.go
index c53df299c..211348fa7 100644
--- a/pkg/flow/account_test.go
+++ b/pkg/flow/account_test.go
@@ -64,18 +64,30 @@ func TestEvict_MaxEntries(t *testing.T) {
 		Id: k1,
 		Metrics: ebpf.BpfFlowMetrics{
 			Bytes: 123, Packets: 1, StartMonoTimeTs: 123, EndMonoTimeTs: 123, Flags: 1,
+			DnsRecord: ebpf.BpfDnsRecordT{
+				ReqMonoTimeTs: 123,
+				RspMonoTimeTs: 0,
+			},
 		},
 	}
 	inputs <- &RawRecord{
 		Id: k2,
 		Metrics: ebpf.BpfFlowMetrics{
 			Bytes: 456, Packets: 1, StartMonoTimeTs: 456, EndMonoTimeTs: 456, Flags: 1,
+			DnsRecord: ebpf.BpfDnsRecordT{
+				ReqMonoTimeTs: 456,
+				RspMonoTimeTs: 0,
+			},
 		},
 	}
 	inputs <- &RawRecord{
 		Id: k1,
 		Metrics: ebpf.BpfFlowMetrics{
 			Bytes: 321, Packets: 1, StartMonoTimeTs: 789, EndMonoTimeTs: 789, Flags: 1,
+			DnsRecord: ebpf.BpfDnsRecordT{
+				ReqMonoTimeTs: 789,
+				RspMonoTimeTs: 789,
+			},
 		},
 	}
 	requireNoEviction(t, evictor)
@@ -85,6 +97,10 @@ func TestEvict_MaxEntries(t *testing.T) {
 		Id: k3,
 		Metrics: ebpf.BpfFlowMetrics{
 			Bytes: 111, Packets: 1, StartMonoTimeTs: 888, EndMonoTimeTs: 888, Flags: 1,
+			DnsRecord: ebpf.BpfDnsRecordT{
+				ReqMonoTimeTs: 888,
+				RspMonoTimeTs: 888,
+			},
 		},
 	}
 
@@ -105,20 +121,30 @@ func TestEvict_MaxEntries(t *testing.T) {
 				Id: k1,
 				Metrics: ebpf.BpfFlowMetrics{
 					Bytes: 123, Packets: 1, StartMonoTimeTs: 123, EndMonoTimeTs: 123, Flags: 1,
+					DnsRecord: ebpf.BpfDnsRecordT{
+						ReqMonoTimeTs: 123,
+						RspMonoTimeTs: 0,
+					},
 				},
 			},
-			TimeFlowStart: now.Add(-(1000 - 123) * time.Nanosecond),
-			TimeFlowEnd:   now.Add(-(1000 - 123) * time.Nanosecond),
+			TimeFlowStart:  now.Add(-(1000 - 123) * time.Nanosecond),
+			TimeFlowEnd:    now.Add(-(1000 - 123) * time.Nanosecond),
+			TimeDNSRequest: now.Add(-(1000 - 123) * time.Nanosecond),
 		},
 		k2: {
 			RawRecord: RawRecord{
 				Id: k2,
 				Metrics: ebpf.BpfFlowMetrics{
 					Bytes: 456, Packets: 1, StartMonoTimeTs: 456, EndMonoTimeTs: 456, Flags: 1,
+					DnsRecord: ebpf.BpfDnsRecordT{
+						ReqMonoTimeTs: 456,
+						RspMonoTimeTs: 0,
+					},
 				},
 			},
-			TimeFlowStart: now.Add(-(1000 - 456) * time.Nanosecond),
-			TimeFlowEnd:   now.Add(-(1000 - 456) * time.Nanosecond),
+			TimeFlowStart:  now.Add(-(1000 - 456) * time.Nanosecond),
+			TimeFlowEnd:    now.Add(-(1000 - 456) * time.Nanosecond),
+			TimeDNSRequest: now.Add(-(1000 - 456) * time.Nanosecond),
 		},
 	}, received)
 }
@@ -141,18 +167,30 @@ func TestEvict_Period(t *testing.T) {
 		Id: k1,
 		Metrics: ebpf.BpfFlowMetrics{
 			Bytes: 10, Packets: 1, StartMonoTimeTs: 123, EndMonoTimeTs: 123, Flags: 1,
+			DnsRecord: ebpf.BpfDnsRecordT{
+				ReqMonoTimeTs: 123,
+				RspMonoTimeTs: 0,
+			},
 		},
 	}
 	inputs <- &RawRecord{
 		Id: k1,
 		Metrics: ebpf.BpfFlowMetrics{
 			Bytes: 10, Packets: 1, StartMonoTimeTs: 456, EndMonoTimeTs: 456, Flags: 1,
+			DnsRecord: ebpf.BpfDnsRecordT{
+				ReqMonoTimeTs: 456,
+				RspMonoTimeTs: 0,
+			},
 		},
 	}
 	inputs <- &RawRecord{
 		Id: k1,
 		Metrics: ebpf.BpfFlowMetrics{
 			Bytes: 10, Packets: 1, StartMonoTimeTs: 789, EndMonoTimeTs: 789, Flags: 1,
+			DnsRecord: ebpf.BpfDnsRecordT{
+				ReqMonoTimeTs: 789,
+				RspMonoTimeTs: 0,
+			},
 		},
 	}
 	// Forcing at least one eviction here
@@ -161,12 +199,20 @@ func TestEvict_Period(t *testing.T) {
 		Id: k1,
 		Metrics: ebpf.BpfFlowMetrics{
 			Bytes: 10, Packets: 1, StartMonoTimeTs: 1123, EndMonoTimeTs: 1123, Flags: 1,
+			DnsRecord: ebpf.BpfDnsRecordT{
+				ReqMonoTimeTs: 1123,
+				RspMonoTimeTs: 0,
+			},
 		},
 	}
 	inputs <- &RawRecord{
 		Id: k1,
 		Metrics: ebpf.BpfFlowMetrics{
 			Bytes: 10, Packets: 1, StartMonoTimeTs: 1456, EndMonoTimeTs: 1456, Flags: 1,
+			DnsRecord: ebpf.BpfDnsRecordT{
+				ReqMonoTimeTs: 1456,
+				RspMonoTimeTs: 0,
+			},
 		},
 	}
 
@@ -183,10 +229,15 @@ func TestEvict_Period(t *testing.T) {
 				StartMonoTimeTs: 123,
 				EndMonoTimeTs:   123,
 				Flags:           1,
+				DnsRecord: ebpf.BpfDnsRecordT{
+					ReqMonoTimeTs: 123,
+					RspMonoTimeTs: 0,
+				},
 			},
 		},
-		TimeFlowStart: now.Add(-1000 + 123),
-		TimeFlowEnd:   now.Add(-1000 + 123),
+		TimeFlowStart:  now.Add(-1000 + 123),
+		TimeFlowEnd:    now.Add(-1000 + 123),
+		TimeDNSRequest: now.Add(-1000 + 123),
 	}, *records[0])
 	records = receiveTimeout(t, evictor)
 	require.Len(t, records, 1)
@@ -199,10 +250,15 @@ func TestEvict_Period(t *testing.T) {
 				StartMonoTimeTs: 1123,
 				EndMonoTimeTs:   1123,
 				Flags:           1,
+				DnsRecord: ebpf.BpfDnsRecordT{
+					ReqMonoTimeTs: 1123,
+					RspMonoTimeTs: 0,
+				},
 			},
 		},
-		TimeFlowStart: now.Add(-1000 + 1123),
-		TimeFlowEnd:   now.Add(-1000 + 1123),
+		TimeFlowStart:  now.Add(-1000 + 1123),
+		TimeFlowEnd:    now.Add(-1000 + 1123),
+		TimeDNSRequest: now.Add(-1000 + 1123),
 	}, *records[0])
 
 	// no more flows are evicted
diff --git a/pkg/flow/record.go b/pkg/flow/record.go
index 60dc0148a..d8313b1cd 100644
--- a/pkg/flow/record.go
+++ b/pkg/flow/record.go
@@ -37,9 +37,11 @@ type RawRecord ebpf.BpfFlowRecordT
 type Record struct {
 	RawRecord
 	// TODO: redundant field from RecordMetrics. Reorganize structs
-	TimeFlowStart time.Time
-	TimeFlowEnd   time.Time
-	Interface     string
+	TimeFlowStart   time.Time
+	TimeFlowEnd     time.Time
+	TimeDNSRequest  time.Time
+	TimeDNSResponse time.Time
+	Interface       string
 	// Duplicate tells whether this flow has another duplicate so it has to be excluded from
 	// any metrics' aggregation (e.g. bytes/second rates between two pods).
 	// The reason for this field is that the same flow can be observed from multiple interfaces,
@@ -54,20 +56,30 @@ type Record struct {
 
 func NewRecord(
 	key ebpf.BpfFlowId,
-	metrics ebpf.BpfFlowMetrics,
+	metrics *ebpf.BpfFlowMetrics,
 	currentTime time.Time,
 	monotonicCurrentTime uint64,
 ) *Record {
 	startDelta := time.Duration(monotonicCurrentTime - metrics.StartMonoTimeTs)
 	endDelta := time.Duration(monotonicCurrentTime - metrics.EndMonoTimeTs)
-	return &Record{
+	var reqDNS, rspDNS time.Duration
+	var record = Record{
 		RawRecord: RawRecord{
 			Id:      key,
-			Metrics: metrics,
+			Metrics: *metrics,
 		},
 		TimeFlowStart: currentTime.Add(-startDelta),
 		TimeFlowEnd:   currentTime.Add(-endDelta),
 	}
+	if metrics.DnsRecord.ReqMonoTimeTs != 0 {
+		reqDNS = time.Duration(monotonicCurrentTime - metrics.DnsRecord.ReqMonoTimeTs)
+		record.TimeDNSRequest = currentTime.Add(-reqDNS)
+	}
+	if metrics.DnsRecord.RspMonoTimeTs != 0 {
+		rspDNS = time.Duration(monotonicCurrentTime - metrics.DnsRecord.RspMonoTimeTs)
+		record.TimeDNSResponse = currentTime.Add(-rspDNS)
+	}
+	return &record
 }
 
 // IP returns the net.IP equivalent object
diff --git a/pkg/flow/record_test.go b/pkg/flow/record_test.go
index 8f1fc59b0..fc6b0abed 100644
--- a/pkg/flow/record_test.go
+++ b/pkg/flow/record_test.go
@@ -32,7 +32,17 @@ func TestRecordBinaryEncoding(t *testing.T) {
 		0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, // u64 flow_end_time
 		0x13, 0x14, //flags
 		0x33, // u8 errno
-
+		// tcp_drops structure
+		0x10, 0x11, 0x12, 0x13, // u32 packets
+		0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, // u64 bytes
+		0x1c, 0x1d, //flags
+		0x1e,          // state
+		0x11, 0, 0, 0, //case
+		// dns_record structure
+		01, 00, // id
+		0x80, 00, // flags
+		0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // req ts
+		0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, // rsp ts
 	}))
 	require.NoError(t, err)
 
@@ -58,6 +68,19 @@ func TestRecordBinaryEncoding(t *testing.T) {
 			EndMonoTimeTs:   0x1a19181716151413,
 			Flags:           0x1413,
 			Errno:           0x33,
+			TcpDrops: ebpf.BpfTcpDropsT{
+				Packets:         0x13121110,
+				Bytes:           0x1b1a191817161514,
+				LatestFlags:     0x1d1c,
+				LatestState:     0x1e,
+				LatestDropCause: 0x11,
+			},
+			DnsRecord: ebpf.BpfDnsRecordT{
+				Id:            0x0001,
+				Flags:         0x0080,
+				ReqMonoTimeTs: 0x1817161514131211,
+				RspMonoTimeTs: 0x2827262524232221,
+			},
 		},
 	}, *fr)
 	// assert that IP addresses are interpreted as IPv4 addresses
diff --git a/pkg/flow/tracer_map.go b/pkg/flow/tracer_map.go
index e067e34e0..8a0118893 100644
--- a/pkg/flow/tracer_map.go
+++ b/pkg/flow/tracer_map.go
@@ -104,7 +104,7 @@ func (m *MapTracer) evictFlows(ctx context.Context, enableGC bool, forwardFlows
 		}
 		forwardingFlows = append(forwardingFlows, NewRecord(
 			flowKey,
-			*aggregatedMetrics,
+			aggregatedMetrics,
 			currentTime,
 			uint64(monotonicTimeNow),
 		))
diff --git a/pkg/grpc/grpc_test.go b/pkg/grpc/grpc_test.go
index efdd355bf..4328dc174 100644
--- a/pkg/grpc/grpc_test.go
+++ b/pkg/grpc/grpc_test.go
@@ -139,9 +139,10 @@ func BenchmarkIPv4GRPCCommunication(b *testing.B) {
 	client := cc.Client()
 
 	f := &pbflow.Record{
-		EthProtocol:   2048,
-		Bytes:         456,
-		Flags:         1,
+		EthProtocol: 2048,
+		Bytes:       456,
+		Flags:       1,
+
 		Direction:     pbflow.Direction_EGRESS,
 		TimeFlowStart: timestamppb.Now(),
 		TimeFlowEnd:   timestamppb.Now(),
@@ -162,10 +163,15 @@ func BenchmarkIPv4GRPCCommunication(b *testing.B) {
 			SrcPort:  23000,
 			DstPort:  443,
 		},
-		Icmp: &pbflow.Icmp{
-			IcmpType: 8,
-			IcmpCode: 10,
-		},
+		IcmpType:               8,
+		IcmpCode:               10,
+		TcpDropBytes:           100,
+		TcpDropPackets:         1,
+		TcpDropLatestFlags:     1,
+		TcpDropLatestState:     2,
+		TcpDropLatestDropCause: 3,
+		TimeDnsReq:             timestamppb.Now(),
+		TimeDnsRsp:             timestamppb.Now(),
 	}
 	records := &pbflow.Records{}
 	for i := 0; i < 100; i++ {
@@ -215,10 +221,17 @@ func BenchmarkIPv6GRPCCommunication(b *testing.B) {
 			SrcPort:  23000,
 			DstPort:  443,
 		},
-		Icmp: &pbflow.Icmp{
-			IcmpType: 8,
-			IcmpCode: 10,
-		},
+		IcmpType:               8,
+		IcmpCode:               10,
+		TcpDropBytes:           100,
+		TcpDropPackets:         1,
+		TcpDropLatestFlags:     1,
+		TcpDropLatestState:     2,
+		TcpDropLatestDropCause: 3,
+		DnsId:                  1,
+		DnsFlags:               100,
+		TimeDnsReq:             timestamppb.Now(),
+		TimeDnsRsp:             timestamppb.Now(),
 	}
 	records := &pbflow.Records{}
 	for i := 0; i < 100; i++ {
diff --git a/pkg/pbflow/flow.pb.go b/pkg/pbflow/flow.pb.go
index af4bee082..60076f902 100644
--- a/pkg/pbflow/flow.pb.go
+++ b/pkg/pbflow/flow.pb.go
@@ -177,9 +177,19 @@ type Record struct {
 	// From all the duplicate flows, one will set this value to false and the rest will be true.
 	Duplicate bool `protobuf:"varint,11,opt,name=duplicate,proto3" json:"duplicate,omitempty"`
 	// Agent IP address to help identifying the source of the flow
-	AgentIp *IP    `protobuf:"bytes,12,opt,name=agent_ip,json=agentIp,proto3" json:"agent_ip,omitempty"`
-	Flags   uint32 `protobuf:"varint,13,opt,name=flags,proto3" json:"flags,omitempty"`
-	Icmp    *Icmp  `protobuf:"bytes,14,opt,name=icmp,proto3" json:"icmp,omitempty"`
+	AgentIp                *IP                    `protobuf:"bytes,12,opt,name=agent_ip,json=agentIp,proto3" json:"agent_ip,omitempty"`
+	Flags                  uint32                 `protobuf:"varint,13,opt,name=flags,proto3" json:"flags,omitempty"`
+	IcmpType               uint32                 `protobuf:"varint,14,opt,name=icmp_type,json=icmpType,proto3" json:"icmp_type,omitempty"`
+	IcmpCode               uint32                 `protobuf:"varint,15,opt,name=icmp_code,json=icmpCode,proto3" json:"icmp_code,omitempty"`
+	TcpDropBytes           uint64                 `protobuf:"varint,16,opt,name=tcp_drop_bytes,json=tcpDropBytes,proto3" json:"tcp_drop_bytes,omitempty"`
+	TcpDropPackets         uint64                 `protobuf:"varint,17,opt,name=tcp_drop_packets,json=tcpDropPackets,proto3" json:"tcp_drop_packets,omitempty"`
+	TcpDropLatestFlags     uint32                 `protobuf:"varint,18,opt,name=tcp_drop_latest_flags,json=tcpDropLatestFlags,proto3" json:"tcp_drop_latest_flags,omitempty"`
+	TcpDropLatestState     uint32                 `protobuf:"varint,19,opt,name=tcp_drop_latest_state,json=tcpDropLatestState,proto3" json:"tcp_drop_latest_state,omitempty"`
+	TcpDropLatestDropCause uint32                 `protobuf:"varint,20,opt,name=tcp_drop_latest_drop_cause,json=tcpDropLatestDropCause,proto3" json:"tcp_drop_latest_drop_cause,omitempty"`
+	DnsId                  uint32                 `protobuf:"varint,21,opt,name=dns_id,json=dnsId,proto3" json:"dns_id,omitempty"`
+	DnsFlags               uint32                 `protobuf:"varint,22,opt,name=dns_flags,json=dnsFlags,proto3" json:"dns_flags,omitempty"`
+	TimeDnsReq             *timestamppb.Timestamp `protobuf:"bytes,23,opt,name=time_dns_req,json=timeDnsReq,proto3" json:"time_dns_req,omitempty"`
+	TimeDnsRsp             *timestamppb.Timestamp `protobuf:"bytes,24,opt,name=time_dns_rsp,json=timeDnsRsp,proto3" json:"time_dns_rsp,omitempty"`
 }
 
 func (x *Record) Reset() {
@@ -305,9 +315,79 @@ func (x *Record) GetFlags() uint32 {
 	return 0
 }
 
-func (x *Record) GetIcmp() *Icmp {
+func (x *Record) GetIcmpType() uint32 {
 	if x != nil {
-		return x.Icmp
+		return x.IcmpType
+	}
+	return 0
+}
+
+func (x *Record) GetIcmpCode() uint32 {
+	if x != nil {
+		return x.IcmpCode
+	}
+	return 0
+}
+
+func (x *Record) GetTcpDropBytes() uint64 {
+	if x != nil {
+		return x.TcpDropBytes
+	}
+	return 0
+}
+
+func (x *Record) GetTcpDropPackets() uint64 {
+	if x != nil {
+		return x.TcpDropPackets
+	}
+	return 0
+}
+
+func (x *Record) GetTcpDropLatestFlags() uint32 {
+	if x != nil {
+		return x.TcpDropLatestFlags
+	}
+	return 0
+}
+
+func (x *Record) GetTcpDropLatestState() uint32 {
+	if x != nil {
+		return x.TcpDropLatestState
+	}
+	return 0
+}
+
+func (x *Record) GetTcpDropLatestDropCause() uint32 {
+	if x != nil {
+		return x.TcpDropLatestDropCause
+	}
+	return 0
+}
+
+func (x *Record) GetDnsId() uint32 {
+	if x != nil {
+		return x.DnsId
+	}
+	return 0
+}
+
+func (x *Record) GetDnsFlags() uint32 {
+	if x != nil {
+		return x.DnsFlags
+	}
+	return 0
+}
+
+func (x *Record) GetTimeDnsReq() *timestamppb.Timestamp {
+	if x != nil {
+		return x.TimeDnsReq
+	}
+	return nil
+}
+
+func (x *Record) GetTimeDnsRsp() *timestamppb.Timestamp {
+	if x != nil {
+		return x.TimeDnsRsp
 	}
 	return nil
 }
@@ -567,61 +647,6 @@ func (x *Transport) GetProtocol() uint32 {
 	return 0
 }
 
-type Icmp struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	IcmpType uint32 `protobuf:"varint,1,opt,name=icmp_type,json=icmpType,proto3" json:"icmp_type,omitempty"`
-	IcmpCode uint32 `protobuf:"varint,2,opt,name=icmp_code,json=icmpCode,proto3" json:"icmp_code,omitempty"`
-}
-
-func (x *Icmp) Reset() {
-	*x = Icmp{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_proto_flow_proto_msgTypes[7]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Icmp) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Icmp) ProtoMessage() {}
-
-func (x *Icmp) ProtoReflect() protoreflect.Message {
-	mi := &file_proto_flow_proto_msgTypes[7]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use Icmp.ProtoReflect.Descriptor instead.
-func (*Icmp) Descriptor() ([]byte, []int) {
-	return file_proto_flow_proto_rawDescGZIP(), []int{7}
-}
-
-func (x *Icmp) GetIcmpType() uint32 {
-	if x != nil {
-		return x.IcmpType
-	}
-	return 0
-}
-
-func (x *Icmp) GetIcmpCode() uint32 {
-	if x != nil {
-		return x.IcmpCode
-	}
-	return 0
-}
-
 var File_proto_flow_proto protoreflect.FileDescriptor
 
 var file_proto_flow_proto_rawDesc = []byte{
@@ -633,7 +658,7 @@ var file_proto_flow_proto_rawDesc = []byte{
 	0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72,
 	0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x66, 0x6c,
 	0x6f, 0x77, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69,
-	0x65, 0x73, 0x22, 0xb6, 0x04, 0x0a, 0x06, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a,
+	0x65, 0x73, 0x22, 0xf0, 0x07, 0x0a, 0x06, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a,
 	0x0c, 0x65, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20,
 	0x01, 0x28, 0x0d, 0x52, 0x0b, 0x65, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
 	0x12, 0x2f, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20,
@@ -666,40 +691,64 @@ var file_proto_flow_proto_rawDesc = []byte{
 	0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a,
 	0x2e, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x49, 0x50, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e,
 	0x74, 0x49, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x0d, 0x20, 0x01,
-	0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x20, 0x0a, 0x04, 0x69, 0x63, 0x6d,
-	0x70, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77,
-	0x2e, 0x49, 0x63, 0x6d, 0x70, 0x52, 0x04, 0x69, 0x63, 0x6d, 0x70, 0x22, 0x3c, 0x0a, 0x08, 0x44,
-	0x61, 0x74, 0x61, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x72, 0x63, 0x5f, 0x6d,
-	0x61, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x72, 0x63, 0x4d, 0x61, 0x63,
-	0x12, 0x17, 0x0a, 0x07, 0x64, 0x73, 0x74, 0x5f, 0x6d, 0x61, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x06, 0x64, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x22, 0x57, 0x0a, 0x07, 0x4e, 0x65, 0x74,
-	0x77, 0x6f, 0x72, 0x6b, 0x12, 0x25, 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e,
-	0x49, 0x50, 0x52, 0x07, 0x73, 0x72, 0x63, 0x41, 0x64, 0x64, 0x72, 0x12, 0x25, 0x0a, 0x08, 0x64,
-	0x73, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e,
-	0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x49, 0x50, 0x52, 0x07, 0x64, 0x73, 0x74, 0x41, 0x64,
-	0x64, 0x72, 0x22, 0x3d, 0x0a, 0x02, 0x49, 0x50, 0x12, 0x14, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x07, 0x48, 0x00, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x14,
-	0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04,
-	0x69, 0x70, 0x76, 0x36, 0x42, 0x0b, 0x0a, 0x09, 0x69, 0x70, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c,
-	0x79, 0x22, 0x5d, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x19,
-	0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
-	0x52, 0x07, 0x73, 0x72, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x73, 0x74,
-	0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x64, 0x73, 0x74,
-	0x50, 0x6f, 0x72, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
-	0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
-	0x22, 0x40, 0x0a, 0x04, 0x49, 0x63, 0x6d, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70,
-	0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x63, 0x6d,
-	0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x63, 0x6f,
-	0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x43, 0x6f,
-	0x64, 0x65, 0x2a, 0x24, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
-	0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06,
-	0x45, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x01, 0x32, 0x3e, 0x0a, 0x09, 0x43, 0x6f, 0x6c, 0x6c,
-	0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x0f, 0x2e,
-	0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x16,
-	0x2e, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f,
-	0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x70, 0x62,
-	0x66, 0x6c, 0x6f, 0x77, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x63, 0x6d,
+	0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x63,
+	0x6d, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x63,
+	0x6f, 0x64, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x43,
+	0x6f, 0x64, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x63, 0x70, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x5f,
+	0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x63, 0x70,
+	0x44, 0x72, 0x6f, 0x70, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x63, 0x70,
+	0x5f, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x11, 0x20,
+	0x01, 0x28, 0x04, 0x52, 0x0e, 0x74, 0x63, 0x70, 0x44, 0x72, 0x6f, 0x70, 0x50, 0x61, 0x63, 0x6b,
+	0x65, 0x74, 0x73, 0x12, 0x31, 0x0a, 0x15, 0x74, 0x63, 0x70, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x5f,
+	0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x12, 0x20, 0x01,
+	0x28, 0x0d, 0x52, 0x12, 0x74, 0x63, 0x70, 0x44, 0x72, 0x6f, 0x70, 0x4c, 0x61, 0x74, 0x65, 0x73,
+	0x74, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x31, 0x0a, 0x15, 0x74, 0x63, 0x70, 0x5f, 0x64, 0x72,
+	0x6f, 0x70, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18,
+	0x13, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x74, 0x63, 0x70, 0x44, 0x72, 0x6f, 0x70, 0x4c, 0x61,
+	0x74, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x1a, 0x74, 0x63, 0x70,
+	0x5f, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x64, 0x72, 0x6f,
+	0x70, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x74,
+	0x63, 0x70, 0x44, 0x72, 0x6f, 0x70, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x44, 0x72, 0x6f, 0x70,
+	0x43, 0x61, 0x75, 0x73, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18,
+	0x15, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x64, 0x6e, 0x73, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09,
+	0x64, 0x6e, 0x73, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52,
+	0x08, 0x64, 0x6e, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x3c, 0x0a, 0x0c, 0x74, 0x69, 0x6d,
+	0x65, 0x5f, 0x64, 0x6e, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+	0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x74, 0x69, 0x6d,
+	0x65, 0x44, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x12, 0x3c, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f,
+	0x64, 0x6e, 0x73, 0x5f, 0x72, 0x73, 0x70, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
+	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+	0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x44,
+	0x6e, 0x73, 0x52, 0x73, 0x70, 0x22, 0x3c, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x4c, 0x69, 0x6e,
+	0x6b, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x72, 0x63, 0x5f, 0x6d, 0x61, 0x63, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x04, 0x52, 0x06, 0x73, 0x72, 0x63, 0x4d, 0x61, 0x63, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x73,
+	0x74, 0x5f, 0x6d, 0x61, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x64, 0x73, 0x74,
+	0x4d, 0x61, 0x63, 0x22, 0x57, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x25,
+	0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x0a, 0x2e, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x49, 0x50, 0x52, 0x07, 0x73, 0x72,
+	0x63, 0x41, 0x64, 0x64, 0x72, 0x12, 0x25, 0x0a, 0x08, 0x64, 0x73, 0x74, 0x5f, 0x61, 0x64, 0x64,
+	0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77,
+	0x2e, 0x49, 0x50, 0x52, 0x07, 0x64, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x22, 0x3d, 0x0a, 0x02,
+	0x49, 0x50, 0x12, 0x14, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x01, 0x20, 0x01, 0x28, 0x07,
+	0x48, 0x00, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x14, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x42, 0x0b,
+	0x0a, 0x09, 0x69, 0x70, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x22, 0x5d, 0x0a, 0x09, 0x54,
+	0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f,
+	0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x72, 0x63, 0x50,
+	0x6f, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x73, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x64, 0x73, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1a,
+	0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d,
+	0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2a, 0x24, 0x0a, 0x09, 0x44, 0x69,
+	0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x47, 0x52, 0x45,
+	0x53, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x01,
+	0x32, 0x3e, 0x0a, 0x09, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a,
+	0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x0f, 0x2e, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x52,
+	0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x16, 0x2e, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x2e,
+	0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00,
+	0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x70, 0x62, 0x66, 0x6c, 0x6f, 0x77, 0x62, 0x06, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -715,7 +764,7 @@ func file_proto_flow_proto_rawDescGZIP() []byte {
 }
 
 var file_proto_flow_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_proto_flow_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
+var file_proto_flow_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
 var file_proto_flow_proto_goTypes = []interface{}{
 	(Direction)(0),                // 0: pbflow.Direction
 	(*CollectorReply)(nil),        // 1: pbflow.CollectorReply
@@ -725,28 +774,28 @@ var file_proto_flow_proto_goTypes = []interface{}{
 	(*Network)(nil),               // 5: pbflow.Network
 	(*IP)(nil),                    // 6: pbflow.IP
 	(*Transport)(nil),             // 7: pbflow.Transport
-	(*Icmp)(nil),                  // 8: pbflow.Icmp
-	(*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp
+	(*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp
 }
 var file_proto_flow_proto_depIdxs = []int32{
 	3,  // 0: pbflow.Records.entries:type_name -> pbflow.Record
 	0,  // 1: pbflow.Record.direction:type_name -> pbflow.Direction
-	9,  // 2: pbflow.Record.time_flow_start:type_name -> google.protobuf.Timestamp
-	9,  // 3: pbflow.Record.time_flow_end:type_name -> google.protobuf.Timestamp
+	8,  // 2: pbflow.Record.time_flow_start:type_name -> google.protobuf.Timestamp
+	8,  // 3: pbflow.Record.time_flow_end:type_name -> google.protobuf.Timestamp
 	4,  // 4: pbflow.Record.data_link:type_name -> pbflow.DataLink
 	5,  // 5: pbflow.Record.network:type_name -> pbflow.Network
 	7,  // 6: pbflow.Record.transport:type_name -> pbflow.Transport
 	6,  // 7: pbflow.Record.agent_ip:type_name -> pbflow.IP
-	8,  // 8: pbflow.Record.icmp:type_name -> pbflow.Icmp
-	6,  // 9: pbflow.Network.src_addr:type_name -> pbflow.IP
-	6,  // 10: pbflow.Network.dst_addr:type_name -> pbflow.IP
-	2,  // 11: pbflow.Collector.Send:input_type -> pbflow.Records
-	1,  // 12: pbflow.Collector.Send:output_type -> pbflow.CollectorReply
-	12, // [12:13] is the sub-list for method output_type
-	11, // [11:12] is the sub-list for method input_type
-	11, // [11:11] is the sub-list for extension type_name
-	11, // [11:11] is the sub-list for extension extendee
-	0,  // [0:11] is the sub-list for field type_name
+	8,  // 8: pbflow.Record.time_dns_req:type_name -> google.protobuf.Timestamp
+	8,  // 9: pbflow.Record.time_dns_rsp:type_name -> google.protobuf.Timestamp
+	6,  // 10: pbflow.Network.src_addr:type_name -> pbflow.IP
+	6,  // 11: pbflow.Network.dst_addr:type_name -> pbflow.IP
+	2,  // 12: pbflow.Collector.Send:input_type -> pbflow.Records
+	1,  // 13: pbflow.Collector.Send:output_type -> pbflow.CollectorReply
+	13, // [13:14] is the sub-list for method output_type
+	12, // [12:13] is the sub-list for method input_type
+	12, // [12:12] is the sub-list for extension type_name
+	12, // [12:12] is the sub-list for extension extendee
+	0,  // [0:12] is the sub-list for field type_name
 }
 
 func init() { file_proto_flow_proto_init() }
@@ -839,18 +888,6 @@ func file_proto_flow_proto_init() {
 				return nil
 			}
 		}
-		file_proto_flow_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Icmp); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
 	}
 	file_proto_flow_proto_msgTypes[5].OneofWrappers = []interface{}{
 		(*IP_Ipv4)(nil),
@@ -862,7 +899,7 @@ func file_proto_flow_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_proto_flow_proto_rawDesc,
 			NumEnums:      1,
-			NumMessages:   8,
+			NumMessages:   7,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/proto/flow.proto b/proto/flow.proto
index c7b459a1d..fb854059f 100644
--- a/proto/flow.proto
+++ b/proto/flow.proto
@@ -41,7 +41,17 @@ message Record {
   // Agent IP address to help identifying the source of the flow
   IP agent_ip = 12;
   uint32 flags = 13;
-  Icmp   icmp = 14;
+  uint32 icmp_type = 14;
+  uint32 icmp_code = 15;
+  uint64 tcp_drop_bytes = 16;
+  uint64 tcp_drop_packets = 17;
+  uint32 tcp_drop_latest_flags = 18;
+  uint32 tcp_drop_latest_state = 19;
+  uint32 tcp_drop_latest_drop_cause = 20;
+  uint32 dns_id = 21;
+  uint32 dns_flags = 22;
+  google.protobuf.Timestamp time_dns_req = 23;
+  google.protobuf.Timestamp time_dns_rsp = 24;
 }
 
 message DataLink {
@@ -69,11 +79,6 @@ message Transport {
   uint32 protocol = 3;
 }
 
-message Icmp {
-  uint32 icmp_type = 1;
-  uint32 icmp_code = 2;
-}
-
 // as defined by field 61 in
 // https://www.iana.org/assignments/ipfix/ipfix.xhtml
 enum Direction {
diff --git a/vendor/github.com/cilium/ebpf/link/cgroup.go b/vendor/github.com/cilium/ebpf/link/cgroup.go
new file mode 100644
index 000000000..bfad1cced
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/cgroup.go
@@ -0,0 +1,165 @@
+package link
+
+import (
+	"errors"
+	"fmt"
+	"os"
+
+	"github.com/cilium/ebpf"
+)
+
+type cgroupAttachFlags uint32
+
+// cgroup attach flags
+const (
+	flagAllowOverride cgroupAttachFlags = 1 << iota
+	flagAllowMulti
+	flagReplace
+)
+
+type CgroupOptions struct {
+	// Path to a cgroupv2 folder.
+	Path string
+	// One of the AttachCgroup* constants
+	Attach ebpf.AttachType
+	// Program must be of type CGroup*, and the attach type must match Attach.
+	Program *ebpf.Program
+}
+
+// AttachCgroup links a BPF program to a cgroup.
+func AttachCgroup(opts CgroupOptions) (Link, error) {
+	cgroup, err := os.Open(opts.Path)
+	if err != nil {
+		return nil, fmt.Errorf("can't open cgroup: %s", err)
+	}
+
+	clone, err := opts.Program.Clone()
+	if err != nil {
+		cgroup.Close()
+		return nil, err
+	}
+
+	var cg Link
+	cg, err = newLinkCgroup(cgroup, opts.Attach, clone)
+	if errors.Is(err, ErrNotSupported) {
+		cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowMulti)
+	}
+	if errors.Is(err, ErrNotSupported) {
+		cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowOverride)
+	}
+	if err != nil {
+		cgroup.Close()
+		clone.Close()
+		return nil, err
+	}
+
+	return cg, nil
+}
+
+type progAttachCgroup struct {
+	cgroup     *os.File
+	current    *ebpf.Program
+	attachType ebpf.AttachType
+	flags      cgroupAttachFlags
+}
+
+var _ Link = (*progAttachCgroup)(nil)
+
+func (cg *progAttachCgroup) isLink() {}
+
+func newProgAttachCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program, flags cgroupAttachFlags) (*progAttachCgroup, error) {
+	if flags&flagAllowMulti > 0 {
+		if err := haveProgAttachReplace(); err != nil {
+			return nil, fmt.Errorf("can't support multiple programs: %w", err)
+		}
+	}
+
+	err := RawAttachProgram(RawAttachProgramOptions{
+		Target:  int(cgroup.Fd()),
+		Program: prog,
+		Flags:   uint32(flags),
+		Attach:  attach,
+	})
+	if err != nil {
+		return nil, fmt.Errorf("cgroup: %w", err)
+	}
+
+	return &progAttachCgroup{cgroup, prog, attach, flags}, nil
+}
+
+func (cg *progAttachCgroup) Close() error {
+	defer cg.cgroup.Close()
+	defer cg.current.Close()
+
+	err := RawDetachProgram(RawDetachProgramOptions{
+		Target:  int(cg.cgroup.Fd()),
+		Program: cg.current,
+		Attach:  cg.attachType,
+	})
+	if err != nil {
+		return fmt.Errorf("close cgroup: %s", err)
+	}
+	return nil
+}
+
+func (cg *progAttachCgroup) Update(prog *ebpf.Program) error {
+	new, err := prog.Clone()
+	if err != nil {
+		return err
+	}
+
+	args := RawAttachProgramOptions{
+		Target:  int(cg.cgroup.Fd()),
+		Program: prog,
+		Attach:  cg.attachType,
+		Flags:   uint32(cg.flags),
+	}
+
+	if cg.flags&flagAllowMulti > 0 {
+		// Atomically replacing multiple programs requires at least
+		// 5.5 (commit 7dd68b3279f17921 "bpf: Support replacing cgroup-bpf
+		// program in MULTI mode")
+		args.Flags |= uint32(flagReplace)
+		args.Replace = cg.current
+	}
+
+	if err := RawAttachProgram(args); err != nil {
+		new.Close()
+		return fmt.Errorf("can't update cgroup: %s", err)
+	}
+
+	cg.current.Close()
+	cg.current = new
+	return nil
+}
+
+func (cg *progAttachCgroup) Pin(string) error {
+	return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported)
+}
+
+func (cg *progAttachCgroup) Unpin() error {
+	return fmt.Errorf("can't unpin cgroup: %w", ErrNotSupported)
+}
+
+func (cg *progAttachCgroup) Info() (*Info, error) {
+	return nil, fmt.Errorf("can't get cgroup info: %w", ErrNotSupported)
+}
+
+type linkCgroup struct {
+	RawLink
+}
+
+var _ Link = (*linkCgroup)(nil)
+
+func newLinkCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program) (*linkCgroup, error) {
+	link, err := AttachRawLink(RawLinkOptions{
+		Target:  int(cgroup.Fd()),
+		Program: prog,
+		Attach:  attach,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return &linkCgroup{*link}, err
+}
diff --git a/vendor/github.com/cilium/ebpf/link/doc.go b/vendor/github.com/cilium/ebpf/link/doc.go
new file mode 100644
index 000000000..2bde35ed7
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/doc.go
@@ -0,0 +1,2 @@
+// Package link allows attaching eBPF programs to various kernel hooks.
+package link
diff --git a/vendor/github.com/cilium/ebpf/link/iter.go b/vendor/github.com/cilium/ebpf/link/iter.go
new file mode 100644
index 000000000..d2b32ef33
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/iter.go
@@ -0,0 +1,85 @@
+package link
+
+import (
+	"fmt"
+	"io"
+	"unsafe"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/internal/sys"
+)
+
+type IterOptions struct {
+	// Program must be of type Tracing with attach type
+	// AttachTraceIter. The kind of iterator to attach to is
+	// determined at load time via the AttachTo field.
+	//
+	// AttachTo requires the kernel to include BTF of itself,
+	// and it to be compiled with a recent pahole (>= 1.16).
+	Program *ebpf.Program
+
+	// Map specifies the target map for bpf_map_elem and sockmap iterators.
+	// It may be nil.
+	Map *ebpf.Map
+}
+
+// AttachIter attaches a BPF seq_file iterator.
+func AttachIter(opts IterOptions) (*Iter, error) {
+	if err := haveBPFLink(); err != nil {
+		return nil, err
+	}
+
+	progFd := opts.Program.FD()
+	if progFd < 0 {
+		return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd)
+	}
+
+	var info bpfIterLinkInfoMap
+	if opts.Map != nil {
+		mapFd := opts.Map.FD()
+		if mapFd < 0 {
+			return nil, fmt.Errorf("invalid map: %w", sys.ErrClosedFd)
+		}
+		info.map_fd = uint32(mapFd)
+	}
+
+	attr := sys.LinkCreateIterAttr{
+		ProgFd:      uint32(progFd),
+		AttachType:  sys.AttachType(ebpf.AttachTraceIter),
+		IterInfo:    sys.NewPointer(unsafe.Pointer(&info)),
+		IterInfoLen: uint32(unsafe.Sizeof(info)),
+	}
+
+	fd, err := sys.LinkCreateIter(&attr)
+	if err != nil {
+		return nil, fmt.Errorf("can't link iterator: %w", err)
+	}
+
+	return &Iter{RawLink{fd, ""}}, err
+}
+
+// Iter represents an attached bpf_iter.
+type Iter struct {
+	RawLink
+}
+
+// Open creates a new instance of the iterator.
+//
+// Reading from the returned reader triggers the BPF program.
+func (it *Iter) Open() (io.ReadCloser, error) {
+	attr := &sys.IterCreateAttr{
+		LinkFd: it.fd.Uint(),
+	}
+
+	fd, err := sys.IterCreate(attr)
+	if err != nil {
+		return nil, fmt.Errorf("can't create iterator: %w", err)
+	}
+
+	return fd.File("bpf_iter"), nil
+}
+
+// union bpf_iter_link_info.map
+type bpfIterLinkInfoMap struct {
+	map_fd uint32
+}
diff --git a/vendor/github.com/cilium/ebpf/link/kprobe.go b/vendor/github.com/cilium/ebpf/link/kprobe.go
new file mode 100644
index 000000000..9ce7eb4a4
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/kprobe.go
@@ -0,0 +1,574 @@
+package link
+
+import (
+	"crypto/rand"
+	"errors"
+	"fmt"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"syscall"
+	"unsafe"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/internal/sys"
+	"github.com/cilium/ebpf/internal/unix"
+)
+
+var (
+	kprobeEventsPath = filepath.Join(tracefsPath, "kprobe_events")
+)
+
+type probeType uint8
+
+type probeArgs struct {
+	symbol, group, path          string
+	offset, refCtrOffset, cookie uint64
+	pid, retprobeMaxActive       int
+	ret                          bool
+}
+
+// KprobeOptions defines additional parameters that will be used
+// when loading Kprobes.
+type KprobeOptions struct {
+	// Arbitrary value that can be fetched from an eBPF program
+	// via `bpf_get_attach_cookie()`.
+	//
+	// Needs kernel 5.15+.
+	Cookie uint64
+	// Offset of the kprobe relative to the traced symbol.
+	// Can be used to insert kprobes at arbitrary offsets in kernel functions,
+	// e.g. in places where functions have been inlined.
+	Offset uint64
+	// Increase the maximum number of concurrent invocations of a kretprobe.
+	// Required when tracing some long running functions in the kernel.
+	//
+	// Deprecated: this setting forces the use of an outdated kernel API and is not portable
+	// across kernel versions.
+	RetprobeMaxActive int
+}
+
+const (
+	kprobeType probeType = iota
+	uprobeType
+)
+
+func (pt probeType) String() string {
+	if pt == kprobeType {
+		return "kprobe"
+	}
+	return "uprobe"
+}
+
+func (pt probeType) EventsPath() string {
+	if pt == kprobeType {
+		return kprobeEventsPath
+	}
+	return uprobeEventsPath
+}
+
+func (pt probeType) PerfEventType(ret bool) perfEventType {
+	if pt == kprobeType {
+		if ret {
+			return kretprobeEvent
+		}
+		return kprobeEvent
+	}
+	if ret {
+		return uretprobeEvent
+	}
+	return uprobeEvent
+}
+
+// Kprobe attaches the given eBPF program to a perf event that fires when the
+// given kernel symbol starts executing. See /proc/kallsyms for available
+// symbols. For example, printk():
+//
+//	kp, err := Kprobe("printk", prog, nil)
+//
+// Losing the reference to the resulting Link (kp) will close the Kprobe
+// and prevent further execution of prog. The Link must be Closed during
+// program shutdown to avoid leaking system resources.
+func Kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) {
+	k, err := kprobe(symbol, prog, opts, false)
+	if err != nil {
+		return nil, err
+	}
+
+	lnk, err := attachPerfEvent(k, prog)
+	if err != nil {
+		k.Close()
+		return nil, err
+	}
+
+	return lnk, nil
+}
+
+// Kretprobe attaches the given eBPF program to a perf event that fires right
+// before the given kernel symbol exits, with the function stack left intact.
+// See /proc/kallsyms for available symbols. For example, printk():
+//
+//	kp, err := Kretprobe("printk", prog, nil)
+//
+// Losing the reference to the resulting Link (kp) will close the Kretprobe
+// and prevent further execution of prog. The Link must be Closed during
+// program shutdown to avoid leaking system resources.
+//
+// On kernels 5.10 and earlier, setting a kretprobe on a nonexistent symbol
+// incorrectly returns unix.EINVAL instead of os.ErrNotExist.
+func Kretprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) {
+	k, err := kprobe(symbol, prog, opts, true)
+	if err != nil {
+		return nil, err
+	}
+
+	lnk, err := attachPerfEvent(k, prog)
+	if err != nil {
+		k.Close()
+		return nil, err
+	}
+
+	return lnk, nil
+}
+
+// isValidKprobeSymbol implements the equivalent of a regex match
+// against "^[a-zA-Z_][0-9a-zA-Z_.]*$".
+func isValidKprobeSymbol(s string) bool {
+	if len(s) < 1 {
+		return false
+	}
+
+	for i, c := range []byte(s) {
+		switch {
+		case c >= 'a' && c <= 'z':
+		case c >= 'A' && c <= 'Z':
+		case c == '_':
+		case i > 0 && c >= '0' && c <= '9':
+
+		// Allow `.` in symbol name. GCC-compiled kernel may change symbol name
+		// to have a `.isra.$n` suffix, like `udp_send_skb.isra.52`.
+		// See: https://gcc.gnu.org/gcc-10/changes.html
+		case i > 0 && c == '.':
+
+		default:
+			return false
+		}
+	}
+
+	return true
+}
+
+// kprobe opens a perf event on the given symbol and attaches prog to it.
+// If ret is true, create a kretprobe.
+func kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (*perfEvent, error) {
+	if symbol == "" {
+		return nil, fmt.Errorf("symbol name cannot be empty: %w", errInvalidInput)
+	}
+	if prog == nil {
+		return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput)
+	}
+	if !isValidKprobeSymbol(symbol) {
+		return nil, fmt.Errorf("symbol '%s' must be a valid symbol in /proc/kallsyms: %w", symbol, errInvalidInput)
+	}
+	if prog.Type() != ebpf.Kprobe {
+		return nil, fmt.Errorf("eBPF program type %s is not a Kprobe: %w", prog.Type(), errInvalidInput)
+	}
+
+	args := probeArgs{
+		pid:    perfAllThreads,
+		symbol: symbol,
+		ret:    ret,
+	}
+
+	if opts != nil {
+		args.retprobeMaxActive = opts.RetprobeMaxActive
+		args.cookie = opts.Cookie
+		args.offset = opts.Offset
+	}
+
+	// Use kprobe PMU if the kernel has it available.
+	tp, err := pmuKprobe(args)
+	if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {
+		args.symbol = platformPrefix(symbol)
+		tp, err = pmuKprobe(args)
+	}
+	if err == nil {
+		return tp, nil
+	}
+	if err != nil && !errors.Is(err, ErrNotSupported) {
+		return nil, fmt.Errorf("creating perf_kprobe PMU: %w", err)
+	}
+
+	// Use tracefs if kprobe PMU is missing.
+	args.symbol = symbol
+	tp, err = tracefsKprobe(args)
+	if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {
+		args.symbol = platformPrefix(symbol)
+		tp, err = tracefsKprobe(args)
+	}
+	if err != nil {
+		return nil, fmt.Errorf("creating trace event '%s' in tracefs: %w", symbol, err)
+	}
+
+	return tp, nil
+}
+
+// pmuKprobe opens a perf event based on the kprobe PMU.
+// Returns os.ErrNotExist if the given symbol does not exist in the kernel.
+func pmuKprobe(args probeArgs) (*perfEvent, error) {
+	return pmuProbe(kprobeType, args)
+}
+
+// pmuProbe opens a perf event based on a Performance Monitoring Unit.
+//
+// Requires at least a 4.17 kernel.
+// e12f03d7031a "perf/core: Implement the 'perf_kprobe' PMU"
+// 33ea4b24277b "perf/core: Implement the 'perf_uprobe' PMU"
+//
+// Returns ErrNotSupported if the kernel doesn't support perf_[k,u]probe PMU
+func pmuProbe(typ probeType, args probeArgs) (*perfEvent, error) {
+	// Getting the PMU type will fail if the kernel doesn't support
+	// the perf_[k,u]probe PMU.
+	et, err := readUint64FromFileOnce("%d\n", "/sys/bus/event_source/devices", typ.String(), "type")
+	if errors.Is(err, os.ErrNotExist) {
+		return nil, fmt.Errorf("%s: %w", typ, ErrNotSupported)
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	// Use tracefs if we want to set kretprobe's retprobeMaxActive.
+	if args.retprobeMaxActive != 0 {
+		return nil, fmt.Errorf("pmu probe: non-zero retprobeMaxActive: %w", ErrNotSupported)
+	}
+
+	var config uint64
+	if args.ret {
+		bit, err := readUint64FromFileOnce("config:%d\n", "/sys/bus/event_source/devices", typ.String(), "/format/retprobe")
+		if err != nil {
+			return nil, err
+		}
+		config |= 1 << bit
+	}
+
+	var (
+		attr  unix.PerfEventAttr
+		sp    unsafe.Pointer
+		token string
+	)
+	switch typ {
+	case kprobeType:
+		// Create a pointer to a NUL-terminated string for the kernel.
+		sp, err = unsafeStringPtr(args.symbol)
+		if err != nil {
+			return nil, err
+		}
+
+		token = kprobeToken(args)
+
+		attr = unix.PerfEventAttr{
+			// The minimum size required for PMU kprobes is PERF_ATTR_SIZE_VER1,
+			// since it added the config2 (Ext2) field. Use Ext2 as probe_offset.
+			Size:   unix.PERF_ATTR_SIZE_VER1,
+			Type:   uint32(et),          // PMU event type read from sysfs
+			Ext1:   uint64(uintptr(sp)), // Kernel symbol to trace
+			Ext2:   args.offset,         // Kernel symbol offset
+			Config: config,              // Retprobe flag
+		}
+	case uprobeType:
+		sp, err = unsafeStringPtr(args.path)
+		if err != nil {
+			return nil, err
+		}
+
+		if args.refCtrOffset != 0 {
+			config |= args.refCtrOffset << uprobeRefCtrOffsetShift
+		}
+
+		token = uprobeToken(args)
+
+		attr = unix.PerfEventAttr{
+			// The minimum size required for PMU uprobes is PERF_ATTR_SIZE_VER1,
+			// since it added the config2 (Ext2) field. The Size field controls the
+			// size of the internal buffer the kernel allocates for reading the
+			// perf_event_attr argument from userspace.
+			Size:   unix.PERF_ATTR_SIZE_VER1,
+			Type:   uint32(et),          // PMU event type read from sysfs
+			Ext1:   uint64(uintptr(sp)), // Uprobe path
+			Ext2:   args.offset,         // Uprobe offset
+			Config: config,              // RefCtrOffset, Retprobe flag
+		}
+	}
+
+	rawFd, err := unix.PerfEventOpen(&attr, args.pid, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
+
+	// On some old kernels, kprobe PMU doesn't allow `.` in symbol names and
+	// return -EINVAL. Return ErrNotSupported to allow falling back to tracefs.
+	// https://github.com/torvalds/linux/blob/94710cac0ef4/kernel/trace/trace_kprobe.c#L340-L343
+	if errors.Is(err, unix.EINVAL) && strings.Contains(args.symbol, ".") {
+		return nil, fmt.Errorf("token %s: older kernels don't accept dots: %w", token, ErrNotSupported)
+	}
+	// Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL
+	// when trying to create a retprobe for a missing symbol.
+	if errors.Is(err, os.ErrNotExist) {
+		return nil, fmt.Errorf("token %s: not found: %w", token, err)
+	}
+	// Since commit ab105a4fb894, EILSEQ is returned when a kprobe sym+offset is resolved
+	// to an invalid insn boundary. The exact conditions that trigger this error are
+	// arch specific however.
+	if errors.Is(err, unix.EILSEQ) {
+		return nil, fmt.Errorf("token %s: bad insn boundary: %w", token, os.ErrNotExist)
+	}
+	// Since at least commit cb9a19fe4aa51, ENOTSUPP is returned
+	// when attempting to set a uprobe on a trap instruction.
+	if errors.Is(err, sys.ENOTSUPP) {
+		return nil, fmt.Errorf("token %s: failed setting uprobe on offset %#x (possible trap insn): %w", token, args.offset, err)
+	}
+
+	if err != nil {
+		return nil, fmt.Errorf("token %s: opening perf event: %w", token, err)
+	}
+
+	// Ensure the string pointer is not collected before PerfEventOpen returns.
+	runtime.KeepAlive(sp)
+
+	fd, err := sys.NewFD(rawFd)
+	if err != nil {
+		return nil, err
+	}
+
+	// Kernel has perf_[k,u]probe PMU available, initialize perf event.
+	return &perfEvent{
+		typ:    typ.PerfEventType(args.ret),
+		name:   args.symbol,
+		pmuID:  et,
+		cookie: args.cookie,
+		fd:     fd,
+	}, nil
+}
+
+// tracefsKprobe creates a Kprobe tracefs entry.
+func tracefsKprobe(args probeArgs) (*perfEvent, error) {
+	return tracefsProbe(kprobeType, args)
+}
+
+// tracefsProbe creates a trace event by writing an entry to <tracefs>/[k,u]probe_events.
+// A new trace event group name is generated on every call to support creating
+// multiple trace events for the same kernel or userspace symbol.
+// Path and offset are only set in the case of uprobe(s) and are used to set
+// the executable/library path on the filesystem and the offset where the probe is inserted.
+// A perf event is then opened on the newly-created trace event and returned to the caller.
+func tracefsProbe(typ probeType, args probeArgs) (*perfEvent, error) {
+	// Generate a random string for each trace event we attempt to create.
+	// This value is used as the 'group' token in tracefs to allow creating
+	// multiple kprobe trace events with the same name.
+	group, err := randomGroup("ebpf")
+	if err != nil {
+		return nil, fmt.Errorf("randomizing group name: %w", err)
+	}
+	args.group = group
+
+	// Create the [k,u]probe trace event using tracefs.
+	tid, err := createTraceFSProbeEvent(typ, args)
+	if err != nil {
+		return nil, fmt.Errorf("creating probe entry on tracefs: %w", err)
+	}
+
+	// Kprobes are ephemeral tracepoints and share the same perf event type.
+	fd, err := openTracepointPerfEvent(tid, args.pid)
+	if err != nil {
+		// Make sure we clean up the created tracefs event when we return error.
+		// If a livepatch handler is already active on the symbol, the write to
+		// tracefs will succeed, a trace event will show up, but creating the
+		// perf event will fail with EBUSY.
+		_ = closeTraceFSProbeEvent(typ, args.group, args.symbol)
+		return nil, err
+	}
+
+	return &perfEvent{
+		typ:       typ.PerfEventType(args.ret),
+		group:     group,
+		name:      args.symbol,
+		tracefsID: tid,
+		cookie:    args.cookie,
+		fd:        fd,
+	}, nil
+}
+
+var errInvalidMaxActive = errors.New("can only set maxactive on kretprobes")
+
+// createTraceFSProbeEvent creates a new ephemeral trace event.
+//
+// Returns os.ErrNotExist if symbol is not a valid
+// kernel symbol, or if it is not traceable with kprobes. Returns os.ErrExist
+// if a probe with the same group and symbol already exists. Returns an error if
+// args.retprobeMaxActive is used on non kprobe types. Returns ErrNotSupported if
+// the kernel is too old to support kretprobe maxactive.
+func createTraceFSProbeEvent(typ probeType, args probeArgs) (uint64, error) {
+	// Before attempting to create a trace event through tracefs,
+	// check if an event with the same group and name already exists.
+	// Kernels 4.x and earlier don't return os.ErrExist on writing a duplicate
+	// entry, so we need to rely on reads for detecting uniqueness.
+	_, err := getTraceEventID(args.group, args.symbol)
+	if err == nil {
+		return 0, fmt.Errorf("trace event %s/%s: %w", args.group, args.symbol, os.ErrExist)
+	}
+	if err != nil && !errors.Is(err, os.ErrNotExist) {
+		return 0, fmt.Errorf("checking trace event %s/%s: %w", args.group, args.symbol, err)
+	}
+
+	// Open the kprobe_events file in tracefs.
+	f, err := os.OpenFile(typ.EventsPath(), os.O_APPEND|os.O_WRONLY, 0666)
+	if err != nil {
+		return 0, fmt.Errorf("error opening '%s': %w", typ.EventsPath(), err)
+	}
+	defer f.Close()
+
+	var pe, token string
+	switch typ {
+	case kprobeType:
+		// The kprobe_events syntax is as follows (see Documentation/trace/kprobetrace.txt):
+		// p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe
+		// r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe
+		// -:[GRP/]EVENT                                        : Clear a probe
+		//
+		// Some examples:
+		// r:ebpf_1234/r_my_kretprobe nf_conntrack_destroy
+		// p:ebpf_5678/p_my_kprobe __x64_sys_execve
+		//
+		// Leaving the kretprobe's MAXACTIVE set to 0 (or absent) will make the
+		// kernel default to NR_CPUS. This is desired in most eBPF cases since
+		// subsampling or rate limiting logic can be more accurately implemented in
+		// the eBPF program itself.
+		// See Documentation/kprobes.txt for more details.
+		if args.retprobeMaxActive != 0 && !args.ret {
+			return 0, errInvalidMaxActive
+		}
+		token = kprobeToken(args)
+		pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.ret, args.retprobeMaxActive), args.group, sanitizeSymbol(args.symbol), token)
+	case uprobeType:
+		// The uprobe_events syntax is as follows:
+		// p[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a probe
+		// r[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a return probe
+		// -:[GRP/]EVENT                           : Clear a probe
+		//
+		// Some examples:
+		// r:ebpf_1234/readline /bin/bash:0x12345
+		// p:ebpf_5678/main_mySymbol /bin/mybin:0x12345(0x123)
+		//
+		// See Documentation/trace/uprobetracer.txt for more details.
+		if args.retprobeMaxActive != 0 {
+			return 0, errInvalidMaxActive
+		}
+		token = uprobeToken(args)
+		pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.ret, 0), args.group, args.symbol, token)
+	}
+	_, err = f.WriteString(pe)
+
+	// Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL
+	// when trying to create a retprobe for a missing symbol.
+	if errors.Is(err, os.ErrNotExist) {
+		return 0, fmt.Errorf("token %s: not found: %w", token, err)
+	}
+	// Since commit ab105a4fb894, EILSEQ is returned when a kprobe sym+offset is resolved
+	// to an invalid insn boundary. The exact conditions that trigger this error are
+	// arch specific however.
+	if errors.Is(err, syscall.EILSEQ) {
+		return 0, fmt.Errorf("token %s: bad insn boundary: %w", token, os.ErrNotExist)
+	}
+	// ERANGE is returned when the `SYM[+offs]` token is too big and cannot
+	// be resolved.
+	if errors.Is(err, syscall.ERANGE) {
+		return 0, fmt.Errorf("token %s: offset too big: %w", token, os.ErrNotExist)
+	}
+
+	if err != nil {
+		return 0, fmt.Errorf("token %s: writing '%s': %w", token, pe, err)
+	}
+
+	// Get the newly-created trace event's id.
+	tid, err := getTraceEventID(args.group, args.symbol)
+	if args.retprobeMaxActive != 0 && errors.Is(err, os.ErrNotExist) {
+		// Kernels < 4.12 don't support maxactive and therefore auto generate
+		// group and event names from the symbol and offset. The symbol is used
+		// without any sanitization.
+		// See https://elixir.bootlin.com/linux/v4.10/source/kernel/trace/trace_kprobe.c#L712
+		event := fmt.Sprintf("kprobes/r_%s_%d", args.symbol, args.offset)
+		if err := removeTraceFSProbeEvent(typ, event); err != nil {
+			return 0, fmt.Errorf("failed to remove spurious maxactive event: %s", err)
+		}
+		return 0, fmt.Errorf("create trace event with non-default maxactive: %w", ErrNotSupported)
+	}
+	if err != nil {
+		return 0, fmt.Errorf("get trace event id: %w", err)
+	}
+
+	return tid, nil
+}
+
+// closeTraceFSProbeEvent removes the [k,u]probe with the given type, group and symbol
+// from <tracefs>/[k,u]probe_events.
+func closeTraceFSProbeEvent(typ probeType, group, symbol string) error {
+	pe := fmt.Sprintf("%s/%s", group, sanitizeSymbol(symbol))
+	return removeTraceFSProbeEvent(typ, pe)
+}
+
+func removeTraceFSProbeEvent(typ probeType, pe string) error {
+	f, err := os.OpenFile(typ.EventsPath(), os.O_APPEND|os.O_WRONLY, 0666)
+	if err != nil {
+		return fmt.Errorf("error opening %s: %w", typ.EventsPath(), err)
+	}
+	defer f.Close()
+
+	// See [k,u]probe_events syntax above. The probe type does not need to be specified
+	// for removals.
+	if _, err = f.WriteString("-:" + pe); err != nil {
+		return fmt.Errorf("remove event %q from %s: %w", pe, typ.EventsPath(), err)
+	}
+
+	return nil
+}
+
+// randomGroup generates a pseudorandom string for use as a tracefs group name.
+// Returns an error when the output string would exceed 63 characters (kernel
+// limitation), when rand.Read() fails or when prefix contains characters not
+// allowed by isValidTraceID.
+func randomGroup(prefix string) (string, error) {
+	if !isValidTraceID(prefix) {
+		return "", fmt.Errorf("prefix '%s' must be alphanumeric or underscore: %w", prefix, errInvalidInput)
+	}
+
+	b := make([]byte, 8)
+	if _, err := rand.Read(b); err != nil {
+		return "", fmt.Errorf("reading random bytes: %w", err)
+	}
+
+	group := fmt.Sprintf("%s_%x", prefix, b)
+	if len(group) > 63 {
+		return "", fmt.Errorf("group name '%s' cannot be longer than 63 characters: %w", group, errInvalidInput)
+	}
+
+	return group, nil
+}
+
+func probePrefix(ret bool, maxActive int) string {
+	if ret {
+		if maxActive > 0 {
+			return fmt.Sprintf("r%d", maxActive)
+		}
+		return "r"
+	}
+	return "p"
+}
+
+// kprobeToken creates the SYM[+offs] token for the tracefs api.
+func kprobeToken(args probeArgs) string {
+	po := args.symbol
+
+	if args.offset != 0 {
+		po += fmt.Sprintf("+%#x", args.offset)
+	}
+
+	return po
+}
diff --git a/vendor/github.com/cilium/ebpf/link/kprobe_multi.go b/vendor/github.com/cilium/ebpf/link/kprobe_multi.go
new file mode 100644
index 000000000..151f47d66
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/kprobe_multi.go
@@ -0,0 +1,180 @@
+package link
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"unsafe"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/asm"
+	"github.com/cilium/ebpf/internal"
+	"github.com/cilium/ebpf/internal/sys"
+	"github.com/cilium/ebpf/internal/unix"
+)
+
+// KprobeMultiOptions defines additional parameters that will be used
+// when opening a KprobeMulti Link.
+type KprobeMultiOptions struct {
+	// Symbols takes a list of kernel symbol names to attach an ebpf program to.
+	//
+	// Mutually exclusive with Addresses.
+	Symbols []string
+
+	// Addresses takes a list of kernel symbol addresses in case they can not
+	// be referred to by name.
+	//
+	// Note that only start addresses can be specified, since the fprobe API
+	// limits the attach point to the function entry or return.
+	//
+	// Mutually exclusive with Symbols.
+	Addresses []uint64
+
+	// Cookies specifies arbitrary values that can be fetched from an eBPF
+	// program via `bpf_get_attach_cookie()`.
+	//
+	// If set, its length should be equal to the length of Symbols or Addresses.
+	// Each Cookie is assigned to the Symbol or Address specified at the
+	// corresponding slice index.
+	Cookies []uint64
+}
+
+// KprobeMulti attaches the given eBPF program to the entry point of a given set
+// of kernel symbols.
+//
+// The difference with Kprobe() is that multi-kprobe accomplishes this in a
+// single system call, making it significantly faster than attaching many
+// probes one at a time.
+//
+// Requires at least Linux 5.18.
+func KprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {
+	return kprobeMulti(prog, opts, 0)
+}
+
+// KretprobeMulti attaches the given eBPF program to the return point of a given
+// set of kernel symbols.
+//
+// The difference with Kretprobe() is that multi-kprobe accomplishes this in a
+// single system call, making it significantly faster than attaching many
+// probes one at a time.
+//
+// Requires at least Linux 5.18.
+func KretprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {
+	return kprobeMulti(prog, opts, unix.BPF_F_KPROBE_MULTI_RETURN)
+}
+
+func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Link, error) {
+	if prog == nil {
+		return nil, errors.New("cannot attach a nil program")
+	}
+
+	syms := uint32(len(opts.Symbols))
+	addrs := uint32(len(opts.Addresses))
+	cookies := uint32(len(opts.Cookies))
+
+	if syms == 0 && addrs == 0 {
+		return nil, fmt.Errorf("one of Symbols or Addresses is required: %w", errInvalidInput)
+	}
+	if syms != 0 && addrs != 0 {
+		return nil, fmt.Errorf("Symbols and Addresses are mutually exclusive: %w", errInvalidInput)
+	}
+	if cookies > 0 && cookies != syms && cookies != addrs {
+		return nil, fmt.Errorf("Cookies must be exactly Symbols or Addresses in length: %w", errInvalidInput)
+	}
+
+	if err := haveBPFLinkKprobeMulti(); err != nil {
+		return nil, err
+	}
+
+	attr := &sys.LinkCreateKprobeMultiAttr{
+		ProgFd:           uint32(prog.FD()),
+		AttachType:       sys.BPF_TRACE_KPROBE_MULTI,
+		KprobeMultiFlags: flags,
+	}
+
+	switch {
+	case syms != 0:
+		attr.Count = syms
+		attr.Syms = sys.NewStringSlicePointer(opts.Symbols)
+
+	case addrs != 0:
+		attr.Count = addrs
+		attr.Addrs = sys.NewPointer(unsafe.Pointer(&opts.Addresses[0]))
+	}
+
+	if cookies != 0 {
+		attr.Cookies = sys.NewPointer(unsafe.Pointer(&opts.Cookies[0]))
+	}
+
+	fd, err := sys.LinkCreateKprobeMulti(attr)
+	if errors.Is(err, unix.ESRCH) {
+		return nil, fmt.Errorf("couldn't find one or more symbols: %w", os.ErrNotExist)
+	}
+	if errors.Is(err, unix.EINVAL) {
+		return nil, fmt.Errorf("%w (missing kernel symbol or prog's AttachType not AttachTraceKprobeMulti?)", err)
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	return &kprobeMultiLink{RawLink{fd, ""}}, nil
+}
+
+type kprobeMultiLink struct {
+	RawLink
+}
+
+var _ Link = (*kprobeMultiLink)(nil)
+
+func (kml *kprobeMultiLink) Update(prog *ebpf.Program) error {
+	return fmt.Errorf("update kprobe_multi: %w", ErrNotSupported)
+}
+
+func (kml *kprobeMultiLink) Pin(string) error {
+	return fmt.Errorf("pin kprobe_multi: %w", ErrNotSupported)
+}
+
+func (kml *kprobeMultiLink) Unpin() error {
+	return fmt.Errorf("unpin kprobe_multi: %w", ErrNotSupported)
+}
+
+var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", "5.18", func() error {
+	prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
+		Name: "probe_kpm_link",
+		Type: ebpf.Kprobe,
+		Instructions: asm.Instructions{
+			asm.Mov.Imm(asm.R0, 0),
+			asm.Return(),
+		},
+		AttachType: ebpf.AttachTraceKprobeMulti,
+		License:    "MIT",
+	})
+	if errors.Is(err, unix.E2BIG) {
+		// Kernel doesn't support AttachType field.
+		return internal.ErrNotSupported
+	}
+	if err != nil {
+		return err
+	}
+	defer prog.Close()
+
+	fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{
+		ProgFd:     uint32(prog.FD()),
+		AttachType: sys.BPF_TRACE_KPROBE_MULTI,
+		Count:      1,
+		Syms:       sys.NewStringSlicePointer([]string{"vprintk"}),
+	})
+	switch {
+	case errors.Is(err, unix.EINVAL):
+		return internal.ErrNotSupported
+	// If CONFIG_FPROBE isn't set.
+	case errors.Is(err, unix.EOPNOTSUPP):
+		return internal.ErrNotSupported
+	case err != nil:
+		return err
+	}
+
+	fd.Close()
+
+	return nil
+})
diff --git a/vendor/github.com/cilium/ebpf/link/link.go b/vendor/github.com/cilium/ebpf/link/link.go
new file mode 100644
index 000000000..d4eeb92de
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/link.go
@@ -0,0 +1,315 @@
+package link
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/btf"
+	"github.com/cilium/ebpf/internal"
+	"github.com/cilium/ebpf/internal/sys"
+)
+
+var ErrNotSupported = internal.ErrNotSupported
+
+// Link represents a Program attached to a BPF hook.
+type Link interface {
+	// Replace the current program with a new program.
+	//
+	// Passing a nil program is an error. May return an error wrapping ErrNotSupported.
+	Update(*ebpf.Program) error
+
+	// Persist a link by pinning it into a bpffs.
+	//
+	// May return an error wrapping ErrNotSupported.
+	Pin(string) error
+
+	// Undo a previous call to Pin.
+	//
+	// May return an error wrapping ErrNotSupported.
+	Unpin() error
+
+	// Close frees resources.
+	//
+	// The link will be broken unless it has been successfully pinned.
+	// A link may continue past the lifetime of the process if Close is
+	// not called.
+	Close() error
+
+	// Info returns metadata on a link.
+	//
+	// May return an error wrapping ErrNotSupported.
+	Info() (*Info, error)
+
+	// Prevent external users from implementing this interface.
+	isLink()
+}
+
+// LoadPinnedLink loads a link that was persisted into a bpffs.
+func LoadPinnedLink(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {
+	raw, err := loadPinnedRawLink(fileName, opts)
+	if err != nil {
+		return nil, err
+	}
+
+	return wrapRawLink(raw)
+}
+
+// wrap a RawLink in a more specific type if possible.
+//
+// The function takes ownership of raw and closes it on error.
+func wrapRawLink(raw *RawLink) (Link, error) {
+	info, err := raw.Info()
+	if err != nil {
+		raw.Close()
+		return nil, err
+	}
+
+	switch info.Type {
+	case RawTracepointType:
+		return &rawTracepoint{*raw}, nil
+	case TracingType:
+		return &tracing{*raw}, nil
+	case CgroupType:
+		return &linkCgroup{*raw}, nil
+	case IterType:
+		return &Iter{*raw}, nil
+	case NetNsType:
+		return &NetNsLink{*raw}, nil
+	default:
+		return raw, nil
+	}
+}
+
+// ID uniquely identifies a BPF link.
+type ID = sys.LinkID
+
+// RawLinkOptions control the creation of a raw link.
+type RawLinkOptions struct {
+	// File descriptor to attach to. This differs for each attach type.
+	Target int
+	// Program to attach.
+	Program *ebpf.Program
+	// Attach must match the attach type of Program.
+	Attach ebpf.AttachType
+	// BTF is the BTF of the attachment target.
+	BTF btf.TypeID
+	// Flags control the attach behaviour.
+	Flags uint32
+}
+
+// Info contains metadata on a link.
+type Info struct {
+	Type    Type
+	ID      ID
+	Program ebpf.ProgramID
+	extra   interface{}
+}
+
+type TracingInfo sys.TracingLinkInfo
+type CgroupInfo sys.CgroupLinkInfo
+type NetNsInfo sys.NetNsLinkInfo
+type XDPInfo sys.XDPLinkInfo
+
+// Tracing returns tracing type-specific link info.
+//
+// Returns nil if the type-specific link info isn't available.
+func (r Info) Tracing() *TracingInfo {
+	e, _ := r.extra.(*TracingInfo)
+	return e
+}
+
+// Cgroup returns cgroup type-specific link info.
+//
+// Returns nil if the type-specific link info isn't available.
+func (r Info) Cgroup() *CgroupInfo {
+	e, _ := r.extra.(*CgroupInfo)
+	return e
+}
+
+// NetNs returns netns type-specific link info.
+//
+// Returns nil if the type-specific link info isn't available.
+func (r Info) NetNs() *NetNsInfo {
+	e, _ := r.extra.(*NetNsInfo)
+	return e
+}
+
+// ExtraNetNs returns XDP type-specific link info.
+//
+// Returns nil if the type-specific link info isn't available.
+func (r Info) XDP() *XDPInfo {
+	e, _ := r.extra.(*XDPInfo)
+	return e
+}
+
+// RawLink is the low-level API to bpf_link.
+//
+// You should consider using the higher level interfaces in this
+// package instead.
+type RawLink struct {
+	fd         *sys.FD
+	pinnedPath string
+}
+
+// AttachRawLink creates a raw link.
+func AttachRawLink(opts RawLinkOptions) (*RawLink, error) {
+	if err := haveBPFLink(); err != nil {
+		return nil, err
+	}
+
+	if opts.Target < 0 {
+		return nil, fmt.Errorf("invalid target: %s", sys.ErrClosedFd)
+	}
+
+	progFd := opts.Program.FD()
+	if progFd < 0 {
+		return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd)
+	}
+
+	attr := sys.LinkCreateAttr{
+		TargetFd:    uint32(opts.Target),
+		ProgFd:      uint32(progFd),
+		AttachType:  sys.AttachType(opts.Attach),
+		TargetBtfId: uint32(opts.BTF),
+		Flags:       opts.Flags,
+	}
+	fd, err := sys.LinkCreate(&attr)
+	if err != nil {
+		return nil, fmt.Errorf("create link: %w", err)
+	}
+
+	return &RawLink{fd, ""}, nil
+}
+
+func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) {
+	fd, err := sys.ObjGet(&sys.ObjGetAttr{
+		Pathname:  sys.NewStringPointer(fileName),
+		FileFlags: opts.Marshal(),
+	})
+	if err != nil {
+		return nil, fmt.Errorf("load pinned link: %w", err)
+	}
+
+	return &RawLink{fd, fileName}, nil
+}
+
+func (l *RawLink) isLink() {}
+
+// FD returns the raw file descriptor.
+func (l *RawLink) FD() int {
+	return l.fd.Int()
+}
+
+// Close breaks the link.
+//
+// Use Pin if you want to make the link persistent.
+func (l *RawLink) Close() error {
+	return l.fd.Close()
+}
+
+// Pin persists a link past the lifetime of the process.
+//
+// Calling Close on a pinned Link will not break the link
+// until the pin is removed.
+func (l *RawLink) Pin(fileName string) error {
+	if err := internal.Pin(l.pinnedPath, fileName, l.fd); err != nil {
+		return err
+	}
+	l.pinnedPath = fileName
+	return nil
+}
+
+// Unpin implements the Link interface.
+func (l *RawLink) Unpin() error {
+	if err := internal.Unpin(l.pinnedPath); err != nil {
+		return err
+	}
+	l.pinnedPath = ""
+	return nil
+}
+
+// IsPinned returns true if the Link has a non-empty pinned path.
+func (l *RawLink) IsPinned() bool {
+	return l.pinnedPath != ""
+}
+
+// Update implements the Link interface.
+func (l *RawLink) Update(new *ebpf.Program) error {
+	return l.UpdateArgs(RawLinkUpdateOptions{
+		New: new,
+	})
+}
+
+// RawLinkUpdateOptions control the behaviour of RawLink.UpdateArgs.
+type RawLinkUpdateOptions struct {
+	New   *ebpf.Program
+	Old   *ebpf.Program
+	Flags uint32
+}
+
+// UpdateArgs updates a link based on args.
+func (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error {
+	newFd := opts.New.FD()
+	if newFd < 0 {
+		return fmt.Errorf("invalid program: %s", sys.ErrClosedFd)
+	}
+
+	var oldFd int
+	if opts.Old != nil {
+		oldFd = opts.Old.FD()
+		if oldFd < 0 {
+			return fmt.Errorf("invalid replacement program: %s", sys.ErrClosedFd)
+		}
+	}
+
+	attr := sys.LinkUpdateAttr{
+		LinkFd:    l.fd.Uint(),
+		NewProgFd: uint32(newFd),
+		OldProgFd: uint32(oldFd),
+		Flags:     opts.Flags,
+	}
+	return sys.LinkUpdate(&attr)
+}
+
+// Info returns metadata about the link.
+func (l *RawLink) Info() (*Info, error) {
+	var info sys.LinkInfo
+
+	if err := sys.ObjInfo(l.fd, &info); err != nil {
+		return nil, fmt.Errorf("link info: %s", err)
+	}
+
+	var extra interface{}
+	switch info.Type {
+	case CgroupType:
+		extra = &CgroupInfo{}
+	case NetNsType:
+		extra = &NetNsInfo{}
+	case TracingType:
+		extra = &TracingInfo{}
+	case XDPType:
+		extra = &XDPInfo{}
+	case RawTracepointType, IterType,
+		PerfEventType, KprobeMultiType:
+		// Extra metadata not supported.
+	default:
+		return nil, fmt.Errorf("unknown link info type: %d", info.Type)
+	}
+
+	if extra != nil {
+		buf := bytes.NewReader(info.Extra[:])
+		err := binary.Read(buf, internal.NativeEndian, extra)
+		if err != nil {
+			return nil, fmt.Errorf("cannot read extra link info: %w", err)
+		}
+	}
+
+	return &Info{
+		info.Type,
+		info.Id,
+		ebpf.ProgramID(info.ProgId),
+		extra,
+	}, nil
+}
diff --git a/vendor/github.com/cilium/ebpf/link/netns.go b/vendor/github.com/cilium/ebpf/link/netns.go
new file mode 100644
index 000000000..344ecced6
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/netns.go
@@ -0,0 +1,36 @@
+package link
+
+import (
+	"fmt"
+
+	"github.com/cilium/ebpf"
+)
+
+// NetNsLink is a program attached to a network namespace.
+type NetNsLink struct {
+	RawLink
+}
+
+// AttachNetNs attaches a program to a network namespace.
+func AttachNetNs(ns int, prog *ebpf.Program) (*NetNsLink, error) {
+	var attach ebpf.AttachType
+	switch t := prog.Type(); t {
+	case ebpf.FlowDissector:
+		attach = ebpf.AttachFlowDissector
+	case ebpf.SkLookup:
+		attach = ebpf.AttachSkLookup
+	default:
+		return nil, fmt.Errorf("can't attach %v to network namespace", t)
+	}
+
+	link, err := AttachRawLink(RawLinkOptions{
+		Target:  ns,
+		Program: prog,
+		Attach:  attach,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return &NetNsLink{*link}, nil
+}
diff --git a/vendor/github.com/cilium/ebpf/link/perf_event.go b/vendor/github.com/cilium/ebpf/link/perf_event.go
new file mode 100644
index 000000000..61f80627a
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/perf_event.go
@@ -0,0 +1,434 @@
+package link
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"sync"
+	"unsafe"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/asm"
+	"github.com/cilium/ebpf/internal"
+	"github.com/cilium/ebpf/internal/sys"
+	"github.com/cilium/ebpf/internal/unix"
+)
+
+// Getting the terminology right is usually the hardest part. For posterity and
+// for staying sane during implementation:
+//
+// - trace event: Representation of a kernel runtime hook. Filesystem entries
+//   under <tracefs>/events. Can be tracepoints (static), kprobes or uprobes.
+//   Can be instantiated into perf events (see below).
+// - tracepoint: A predetermined hook point in the kernel. Exposed as trace
+//   events in (sub)directories under <tracefs>/events. Cannot be closed or
+//   removed, they are static.
+// - k(ret)probe: Ephemeral trace events based on entry or exit points of
+//   exported kernel symbols. kprobe-based (tracefs) trace events can be
+//   created system-wide by writing to the <tracefs>/kprobe_events file, or
+//   they can be scoped to the current process by creating PMU perf events.
+// - u(ret)probe: Ephemeral trace events based on user provides ELF binaries
+//   and offsets. uprobe-based (tracefs) trace events can be
+//   created system-wide by writing to the <tracefs>/uprobe_events file, or
+//   they can be scoped to the current process by creating PMU perf events.
+// - perf event: An object instantiated based on an existing trace event or
+//   kernel symbol. Referred to by fd in userspace.
+//   Exactly one eBPF program can be attached to a perf event. Multiple perf
+//   events can be created from a single trace event. Closing a perf event
+//   stops any further invocations of the attached eBPF program.
+
+var (
+	tracefsPath = "/sys/kernel/debug/tracing"
+
+	errInvalidInput = errors.New("invalid input")
+)
+
+const (
+	perfAllThreads = -1
+)
+
+type perfEventType uint8
+
+const (
+	tracepointEvent perfEventType = iota
+	kprobeEvent
+	kretprobeEvent
+	uprobeEvent
+	uretprobeEvent
+)
+
+// A perfEvent represents a perf event kernel object. Exactly one eBPF program
+// can be attached to it. It is created based on a tracefs trace event or a
+// Performance Monitoring Unit (PMU).
+type perfEvent struct {
+	// The event type determines the types of programs that can be attached.
+	typ perfEventType
+
+	// Group and name of the tracepoint/kprobe/uprobe.
+	group string
+	name  string
+
+	// PMU event ID read from sysfs. Valid IDs are non-zero.
+	pmuID uint64
+	// ID of the trace event read from tracefs. Valid IDs are non-zero.
+	tracefsID uint64
+
+	// User provided arbitrary value.
+	cookie uint64
+
+	// This is the perf event FD.
+	fd *sys.FD
+}
+
+func (pe *perfEvent) Close() error {
+	if err := pe.fd.Close(); err != nil {
+		return fmt.Errorf("closing perf event fd: %w", err)
+	}
+
+	switch pe.typ {
+	case kprobeEvent, kretprobeEvent:
+		// Clean up kprobe tracefs entry.
+		if pe.tracefsID != 0 {
+			return closeTraceFSProbeEvent(kprobeType, pe.group, pe.name)
+		}
+	case uprobeEvent, uretprobeEvent:
+		// Clean up uprobe tracefs entry.
+		if pe.tracefsID != 0 {
+			return closeTraceFSProbeEvent(uprobeType, pe.group, pe.name)
+		}
+	case tracepointEvent:
+		// Tracepoint trace events don't hold any extra resources.
+		return nil
+	}
+
+	return nil
+}
+
+// perfEventLink represents a bpf perf link.
+type perfEventLink struct {
+	RawLink
+	pe *perfEvent
+}
+
+func (pl *perfEventLink) isLink() {}
+
+// Pinning requires the underlying perf event FD to stay open.
+//
+// | PerfEvent FD | BpfLink FD | Works |
+// |--------------|------------|-------|
+// | Open         | Open       | Yes   |
+// | Closed       | Open       | No    |
+// | Open         | Closed     | No (Pin() -> EINVAL) |
+// | Closed       | Closed     | No (Pin() -> EINVAL) |
+//
+// There is currently no pretty way to recover the perf event FD
+// when loading a pinned link, so leave as not supported for now.
+func (pl *perfEventLink) Pin(string) error {
+	return fmt.Errorf("perf event link pin: %w", ErrNotSupported)
+}
+
+func (pl *perfEventLink) Unpin() error {
+	return fmt.Errorf("perf event link unpin: %w", ErrNotSupported)
+}
+
+func (pl *perfEventLink) Close() error {
+	if err := pl.pe.Close(); err != nil {
+		return fmt.Errorf("perf event link close: %w", err)
+	}
+	return pl.fd.Close()
+}
+
+func (pl *perfEventLink) Update(prog *ebpf.Program) error {
+	return fmt.Errorf("perf event link update: %w", ErrNotSupported)
+}
+
+// perfEventIoctl implements Link and handles the perf event lifecycle
+// via ioctl().
+type perfEventIoctl struct {
+	*perfEvent
+}
+
+func (pi *perfEventIoctl) isLink() {}
+
+// Since 4.15 (e87c6bc3852b "bpf: permit multiple bpf attachments for a single perf event"),
+// calling PERF_EVENT_IOC_SET_BPF appends the given program to a prog_array
+// owned by the perf event, which means multiple programs can be attached
+// simultaneously.
+//
+// Before 4.15, calling PERF_EVENT_IOC_SET_BPF more than once on a perf event
+// returns EEXIST.
+//
+// Detaching a program from a perf event is currently not possible, so a
+// program replacement mechanism cannot be implemented for perf events.
+func (pi *perfEventIoctl) Update(prog *ebpf.Program) error {
+	return fmt.Errorf("perf event ioctl update: %w", ErrNotSupported)
+}
+
+func (pi *perfEventIoctl) Pin(string) error {
+	return fmt.Errorf("perf event ioctl pin: %w", ErrNotSupported)
+}
+
+func (pi *perfEventIoctl) Unpin() error {
+	return fmt.Errorf("perf event ioctl unpin: %w", ErrNotSupported)
+}
+
+func (pi *perfEventIoctl) Info() (*Info, error) {
+	return nil, fmt.Errorf("perf event ioctl info: %w", ErrNotSupported)
+}
+
+// attach the given eBPF prog to the perf event stored in pe.
+// pe must contain a valid perf event fd.
+// prog's type must match the program type stored in pe.
+func attachPerfEvent(pe *perfEvent, prog *ebpf.Program) (Link, error) {
+	if prog == nil {
+		return nil, errors.New("cannot attach a nil program")
+	}
+	if prog.FD() < 0 {
+		return nil, fmt.Errorf("invalid program: %w", sys.ErrClosedFd)
+	}
+
+	switch pe.typ {
+	case kprobeEvent, kretprobeEvent, uprobeEvent, uretprobeEvent:
+		if t := prog.Type(); t != ebpf.Kprobe {
+			return nil, fmt.Errorf("invalid program type (expected %s): %s", ebpf.Kprobe, t)
+		}
+	case tracepointEvent:
+		if t := prog.Type(); t != ebpf.TracePoint {
+			return nil, fmt.Errorf("invalid program type (expected %s): %s", ebpf.TracePoint, t)
+		}
+	default:
+		return nil, fmt.Errorf("unknown perf event type: %d", pe.typ)
+	}
+
+	if err := haveBPFLinkPerfEvent(); err == nil {
+		return attachPerfEventLink(pe, prog)
+	}
+	return attachPerfEventIoctl(pe, prog)
+}
+
+func attachPerfEventIoctl(pe *perfEvent, prog *ebpf.Program) (*perfEventIoctl, error) {
+	if pe.cookie != 0 {
+		return nil, fmt.Errorf("cookies are not supported: %w", ErrNotSupported)
+	}
+
+	// Assign the eBPF program to the perf event.
+	err := unix.IoctlSetInt(pe.fd.Int(), unix.PERF_EVENT_IOC_SET_BPF, prog.FD())
+	if err != nil {
+		return nil, fmt.Errorf("setting perf event bpf program: %w", err)
+	}
+
+	// PERF_EVENT_IOC_ENABLE and _DISABLE ignore their given values.
+	if err := unix.IoctlSetInt(pe.fd.Int(), unix.PERF_EVENT_IOC_ENABLE, 0); err != nil {
+		return nil, fmt.Errorf("enable perf event: %s", err)
+	}
+
+	pi := &perfEventIoctl{pe}
+
+	// Close the perf event when its reference is lost to avoid leaking system resources.
+	runtime.SetFinalizer(pi, (*perfEventIoctl).Close)
+	return pi, nil
+}
+
+// Use the bpf api to attach the perf event (BPF_LINK_TYPE_PERF_EVENT, 5.15+).
+//
+// https://github.com/torvalds/linux/commit/b89fbfbb854c9afc3047e8273cc3a694650b802e
+func attachPerfEventLink(pe *perfEvent, prog *ebpf.Program) (*perfEventLink, error) {
+	fd, err := sys.LinkCreatePerfEvent(&sys.LinkCreatePerfEventAttr{
+		ProgFd:     uint32(prog.FD()),
+		TargetFd:   pe.fd.Uint(),
+		AttachType: sys.BPF_PERF_EVENT,
+		BpfCookie:  pe.cookie,
+	})
+	if err != nil {
+		return nil, fmt.Errorf("cannot create bpf perf link: %v", err)
+	}
+
+	pl := &perfEventLink{RawLink{fd: fd}, pe}
+
+	// Close the perf event when its reference is lost to avoid leaking system resources.
+	runtime.SetFinalizer(pl, (*perfEventLink).Close)
+	return pl, nil
+}
+
+// unsafeStringPtr returns an unsafe.Pointer to a NUL-terminated copy of str.
+func unsafeStringPtr(str string) (unsafe.Pointer, error) {
+	p, err := unix.BytePtrFromString(str)
+	if err != nil {
+		return nil, err
+	}
+	return unsafe.Pointer(p), nil
+}
+
+// getTraceEventID reads a trace event's ID from tracefs given its group and name.
+// The kernel requires group and name to be alphanumeric or underscore.
+//
+// name automatically has its invalid symbols converted to underscores so the caller
+// can pass a raw symbol name, e.g. a kernel symbol containing dots.
+func getTraceEventID(group, name string) (uint64, error) {
+	name = sanitizeSymbol(name)
+	path, err := sanitizePath(tracefsPath, "events", group, name, "id")
+	if err != nil {
+		return 0, err
+	}
+	tid, err := readUint64FromFile("%d\n", path)
+	if errors.Is(err, os.ErrNotExist) {
+		return 0, err
+	}
+	if err != nil {
+		return 0, fmt.Errorf("reading trace event ID of %s/%s: %w", group, name, err)
+	}
+
+	return tid, nil
+}
+
+// openTracepointPerfEvent opens a tracepoint-type perf event. System-wide
+// [k,u]probes created by writing to <tracefs>/[k,u]probe_events are tracepoints
+// behind the scenes, and can be attached to using these perf events.
+func openTracepointPerfEvent(tid uint64, pid int) (*sys.FD, error) {
+	attr := unix.PerfEventAttr{
+		Type:        unix.PERF_TYPE_TRACEPOINT,
+		Config:      tid,
+		Sample_type: unix.PERF_SAMPLE_RAW,
+		Sample:      1,
+		Wakeup:      1,
+	}
+
+	fd, err := unix.PerfEventOpen(&attr, pid, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
+	if err != nil {
+		return nil, fmt.Errorf("opening tracepoint perf event: %w", err)
+	}
+
+	return sys.NewFD(fd)
+}
+
+func sanitizePath(base string, path ...string) (string, error) {
+	l := filepath.Join(path...)
+	p := filepath.Join(base, l)
+	if !strings.HasPrefix(p, base) {
+		return "", fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, errInvalidInput)
+	}
+	return p, nil
+}
+
+// readUint64FromFile reads a uint64 from a file.
+//
+// format specifies the contents of the file in fmt.Scanf syntax.
+func readUint64FromFile(format string, path ...string) (uint64, error) {
+	filename := filepath.Join(path...)
+	data, err := os.ReadFile(filename)
+	if err != nil {
+		return 0, fmt.Errorf("reading file %q: %w", filename, err)
+	}
+
+	var value uint64
+	n, err := fmt.Fscanf(bytes.NewReader(data), format, &value)
+	if err != nil {
+		return 0, fmt.Errorf("parsing file %q: %w", filename, err)
+	}
+	if n != 1 {
+		return 0, fmt.Errorf("parsing file %q: expected 1 item, got %d", filename, n)
+	}
+
+	return value, nil
+}
+
+type uint64FromFileKey struct {
+	format, path string
+}
+
+var uint64FromFileCache = struct {
+	sync.RWMutex
+	values map[uint64FromFileKey]uint64
+}{
+	values: map[uint64FromFileKey]uint64{},
+}
+
+// readUint64FromFileOnce is like readUint64FromFile but memoizes the result.
+func readUint64FromFileOnce(format string, path ...string) (uint64, error) {
+	filename := filepath.Join(path...)
+	key := uint64FromFileKey{format, filename}
+
+	uint64FromFileCache.RLock()
+	if value, ok := uint64FromFileCache.values[key]; ok {
+		uint64FromFileCache.RUnlock()
+		return value, nil
+	}
+	uint64FromFileCache.RUnlock()
+
+	value, err := readUint64FromFile(format, filename)
+	if err != nil {
+		return 0, err
+	}
+
+	uint64FromFileCache.Lock()
+	defer uint64FromFileCache.Unlock()
+
+	if value, ok := uint64FromFileCache.values[key]; ok {
+		// Someone else got here before us, use what is cached.
+		return value, nil
+	}
+
+	uint64FromFileCache.values[key] = value
+	return value, nil
+}
+
+// Probe BPF perf link.
+//
+// https://elixir.bootlin.com/linux/v5.16.8/source/kernel/bpf/syscall.c#L4307
+// https://github.com/torvalds/linux/commit/b89fbfbb854c9afc3047e8273cc3a694650b802e
+var haveBPFLinkPerfEvent = internal.NewFeatureTest("bpf_link_perf_event", "5.15", func() error {
+	prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
+		Name: "probe_bpf_perf_link",
+		Type: ebpf.Kprobe,
+		Instructions: asm.Instructions{
+			asm.Mov.Imm(asm.R0, 0),
+			asm.Return(),
+		},
+		License: "MIT",
+	})
+	if err != nil {
+		return err
+	}
+	defer prog.Close()
+
+	_, err = sys.LinkCreatePerfEvent(&sys.LinkCreatePerfEventAttr{
+		ProgFd:     uint32(prog.FD()),
+		AttachType: sys.BPF_PERF_EVENT,
+	})
+	if errors.Is(err, unix.EINVAL) {
+		return internal.ErrNotSupported
+	}
+	if errors.Is(err, unix.EBADF) {
+		return nil
+	}
+	return err
+})
+
+// isValidTraceID implements the equivalent of a regex match
+// against "^[a-zA-Z_][0-9a-zA-Z_]*$".
+//
+// Trace event groups, names and kernel symbols must adhere to this set
+// of characters. Non-empty, first character must not be a number, all
+// characters must be alphanumeric or underscore.
+func isValidTraceID(s string) bool {
+	if len(s) < 1 {
+		return false
+	}
+	for i, c := range []byte(s) {
+		switch {
+		case c >= 'a' && c <= 'z':
+		case c >= 'A' && c <= 'Z':
+		case c == '_':
+		case i > 0 && c >= '0' && c <= '9':
+
+		default:
+			return false
+		}
+	}
+
+	return true
+}
diff --git a/vendor/github.com/cilium/ebpf/link/platform.go b/vendor/github.com/cilium/ebpf/link/platform.go
new file mode 100644
index 000000000..eb6f7b7a3
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/platform.go
@@ -0,0 +1,25 @@
+package link
+
+import (
+	"fmt"
+	"runtime"
+)
+
+func platformPrefix(symbol string) string {
+
+	prefix := runtime.GOARCH
+
+	// per https://github.com/golang/go/blob/master/src/go/build/syslist.go
+	switch prefix {
+	case "386":
+		prefix = "ia32"
+	case "amd64", "amd64p32":
+		prefix = "x64"
+	case "arm64", "arm64be":
+		prefix = "arm64"
+	default:
+		return symbol
+	}
+
+	return fmt.Sprintf("__%s_%s", prefix, symbol)
+}
diff --git a/vendor/github.com/cilium/ebpf/link/program.go b/vendor/github.com/cilium/ebpf/link/program.go
new file mode 100644
index 000000000..ea3181737
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/program.go
@@ -0,0 +1,76 @@
+package link
+
+import (
+	"fmt"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/internal/sys"
+)
+
+type RawAttachProgramOptions struct {
+	// File descriptor to attach to. This differs for each attach type.
+	Target int
+	// Program to attach.
+	Program *ebpf.Program
+	// Program to replace (cgroups).
+	Replace *ebpf.Program
+	// Attach must match the attach type of Program (and Replace).
+	Attach ebpf.AttachType
+	// Flags control the attach behaviour. This differs for each attach type.
+	Flags uint32
+}
+
+// RawAttachProgram is a low level wrapper around BPF_PROG_ATTACH.
+//
+// You should use one of the higher level abstractions available in this
+// package if possible.
+func RawAttachProgram(opts RawAttachProgramOptions) error {
+	if err := haveProgAttach(); err != nil {
+		return err
+	}
+
+	var replaceFd uint32
+	if opts.Replace != nil {
+		replaceFd = uint32(opts.Replace.FD())
+	}
+
+	attr := sys.ProgAttachAttr{
+		TargetFd:     uint32(opts.Target),
+		AttachBpfFd:  uint32(opts.Program.FD()),
+		ReplaceBpfFd: replaceFd,
+		AttachType:   uint32(opts.Attach),
+		AttachFlags:  uint32(opts.Flags),
+	}
+
+	if err := sys.ProgAttach(&attr); err != nil {
+		return fmt.Errorf("can't attach program: %w", err)
+	}
+	return nil
+}
+
+type RawDetachProgramOptions struct {
+	Target  int
+	Program *ebpf.Program
+	Attach  ebpf.AttachType
+}
+
+// RawDetachProgram is a low level wrapper around BPF_PROG_DETACH.
+//
+// You should use one of the higher level abstractions available in this
+// package if possible.
+func RawDetachProgram(opts RawDetachProgramOptions) error {
+	if err := haveProgAttach(); err != nil {
+		return err
+	}
+
+	attr := sys.ProgDetachAttr{
+		TargetFd:    uint32(opts.Target),
+		AttachBpfFd: uint32(opts.Program.FD()),
+		AttachType:  uint32(opts.Attach),
+	}
+	if err := sys.ProgDetach(&attr); err != nil {
+		return fmt.Errorf("can't detach program: %w", err)
+	}
+
+	return nil
+}
diff --git a/vendor/github.com/cilium/ebpf/link/query.go b/vendor/github.com/cilium/ebpf/link/query.go
new file mode 100644
index 000000000..8c882414d
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/query.go
@@ -0,0 +1,63 @@
+package link
+
+import (
+	"fmt"
+	"os"
+	"unsafe"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/internal/sys"
+)
+
+// QueryOptions defines additional parameters when querying for programs.
+type QueryOptions struct {
+	// Path can be a path to a cgroup, netns or LIRC2 device
+	Path string
+	// Attach specifies the AttachType of the programs queried for
+	Attach ebpf.AttachType
+	// QueryFlags are flags for BPF_PROG_QUERY, e.g. BPF_F_QUERY_EFFECTIVE
+	QueryFlags uint32
+}
+
+// QueryPrograms retrieves ProgramIDs associated with the AttachType.
+//
+// It only returns IDs of programs that were attached using PROG_ATTACH and not bpf_link.
+// Returns (nil, nil) if there are no programs attached to the queried kernel resource.
+// Calling QueryPrograms on a kernel missing PROG_QUERY will result in ErrNotSupported.
+func QueryPrograms(opts QueryOptions) ([]ebpf.ProgramID, error) {
+	if haveProgQuery() != nil {
+		return nil, fmt.Errorf("can't query program IDs: %w", ErrNotSupported)
+	}
+
+	f, err := os.Open(opts.Path)
+	if err != nil {
+		return nil, fmt.Errorf("can't open file: %s", err)
+	}
+	defer f.Close()
+
+	// query the number of programs to allocate correct slice size
+	attr := sys.ProgQueryAttr{
+		TargetFd:   uint32(f.Fd()),
+		AttachType: sys.AttachType(opts.Attach),
+		QueryFlags: opts.QueryFlags,
+	}
+	if err := sys.ProgQuery(&attr); err != nil {
+		return nil, fmt.Errorf("can't query program count: %w", err)
+	}
+
+	// return nil if no progs are attached
+	if attr.ProgCount == 0 {
+		return nil, nil
+	}
+
+	// we have at least one prog, so we query again
+	progIds := make([]ebpf.ProgramID, attr.ProgCount)
+	attr.ProgIds = sys.NewPointer(unsafe.Pointer(&progIds[0]))
+	attr.ProgCount = uint32(len(progIds))
+	if err := sys.ProgQuery(&attr); err != nil {
+		return nil, fmt.Errorf("can't query program IDs: %w", err)
+	}
+
+	return progIds, nil
+
+}
diff --git a/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go b/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go
new file mode 100644
index 000000000..925e621cb
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go
@@ -0,0 +1,87 @@
+package link
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/internal/sys"
+)
+
+type RawTracepointOptions struct {
+	// Tracepoint name.
+	Name string
+	// Program must be of type RawTracepoint*
+	Program *ebpf.Program
+}
+
+// AttachRawTracepoint links a BPF program to a raw_tracepoint.
+//
+// Requires at least Linux 4.17.
+func AttachRawTracepoint(opts RawTracepointOptions) (Link, error) {
+	if t := opts.Program.Type(); t != ebpf.RawTracepoint && t != ebpf.RawTracepointWritable {
+		return nil, fmt.Errorf("invalid program type %s, expected RawTracepoint(Writable)", t)
+	}
+	if opts.Program.FD() < 0 {
+		return nil, fmt.Errorf("invalid program: %w", sys.ErrClosedFd)
+	}
+
+	fd, err := sys.RawTracepointOpen(&sys.RawTracepointOpenAttr{
+		Name:   sys.NewStringPointer(opts.Name),
+		ProgFd: uint32(opts.Program.FD()),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	err = haveBPFLink()
+	if errors.Is(err, ErrNotSupported) {
+		// Prior to commit 70ed506c3bbc ("bpf: Introduce pinnable bpf_link abstraction")
+		// raw_tracepoints are just a plain fd.
+		return &simpleRawTracepoint{fd}, nil
+	}
+
+	if err != nil {
+		return nil, err
+	}
+
+	return &rawTracepoint{RawLink{fd: fd}}, nil
+}
+
+type simpleRawTracepoint struct {
+	fd *sys.FD
+}
+
+var _ Link = (*simpleRawTracepoint)(nil)
+
+func (frt *simpleRawTracepoint) isLink() {}
+
+func (frt *simpleRawTracepoint) Close() error {
+	return frt.fd.Close()
+}
+
+func (frt *simpleRawTracepoint) Update(_ *ebpf.Program) error {
+	return fmt.Errorf("update raw_tracepoint: %w", ErrNotSupported)
+}
+
+func (frt *simpleRawTracepoint) Pin(string) error {
+	return fmt.Errorf("pin raw_tracepoint: %w", ErrNotSupported)
+}
+
+func (frt *simpleRawTracepoint) Unpin() error {
+	return fmt.Errorf("unpin raw_tracepoint: %w", ErrNotSupported)
+}
+
+func (frt *simpleRawTracepoint) Info() (*Info, error) {
+	return nil, fmt.Errorf("can't get raw_tracepoint info: %w", ErrNotSupported)
+}
+
+type rawTracepoint struct {
+	RawLink
+}
+
+var _ Link = (*rawTracepoint)(nil)
+
+func (rt *rawTracepoint) Update(_ *ebpf.Program) error {
+	return fmt.Errorf("update raw_tracepoint: %w", ErrNotSupported)
+}
diff --git a/vendor/github.com/cilium/ebpf/link/socket_filter.go b/vendor/github.com/cilium/ebpf/link/socket_filter.go
new file mode 100644
index 000000000..94f3958cc
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/socket_filter.go
@@ -0,0 +1,40 @@
+package link
+
+import (
+	"syscall"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/internal/unix"
+)
+
+// AttachSocketFilter attaches a SocketFilter BPF program to a socket.
+func AttachSocketFilter(conn syscall.Conn, program *ebpf.Program) error {
+	rawConn, err := conn.SyscallConn()
+	if err != nil {
+		return err
+	}
+	var ssoErr error
+	err = rawConn.Control(func(fd uintptr) {
+		ssoErr = syscall.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_ATTACH_BPF, program.FD())
+	})
+	if ssoErr != nil {
+		return ssoErr
+	}
+	return err
+}
+
+// DetachSocketFilter detaches a SocketFilter BPF program from a socket.
+func DetachSocketFilter(conn syscall.Conn) error {
+	rawConn, err := conn.SyscallConn()
+	if err != nil {
+		return err
+	}
+	var ssoErr error
+	err = rawConn.Control(func(fd uintptr) {
+		ssoErr = syscall.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_DETACH_BPF, 0)
+	})
+	if ssoErr != nil {
+		return ssoErr
+	}
+	return err
+}
diff --git a/vendor/github.com/cilium/ebpf/link/syscalls.go b/vendor/github.com/cilium/ebpf/link/syscalls.go
new file mode 100644
index 000000000..38f7ae9b7
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/syscalls.go
@@ -0,0 +1,123 @@
+package link
+
+import (
+	"errors"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/asm"
+	"github.com/cilium/ebpf/internal"
+	"github.com/cilium/ebpf/internal/sys"
+	"github.com/cilium/ebpf/internal/unix"
+)
+
+// Type is the kind of link.
+type Type = sys.LinkType
+
+// Valid link types.
+const (
+	UnspecifiedType   = sys.BPF_LINK_TYPE_UNSPEC
+	RawTracepointType = sys.BPF_LINK_TYPE_RAW_TRACEPOINT
+	TracingType       = sys.BPF_LINK_TYPE_TRACING
+	CgroupType        = sys.BPF_LINK_TYPE_CGROUP
+	IterType          = sys.BPF_LINK_TYPE_ITER
+	NetNsType         = sys.BPF_LINK_TYPE_NETNS
+	XDPType           = sys.BPF_LINK_TYPE_XDP
+	PerfEventType     = sys.BPF_LINK_TYPE_PERF_EVENT
+	KprobeMultiType   = sys.BPF_LINK_TYPE_KPROBE_MULTI
+)
+
+var haveProgAttach = internal.NewFeatureTest("BPF_PROG_ATTACH", "4.10", func() error {
+	prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
+		Type:    ebpf.CGroupSKB,
+		License: "MIT",
+		Instructions: asm.Instructions{
+			asm.Mov.Imm(asm.R0, 0),
+			asm.Return(),
+		},
+	})
+	if err != nil {
+		return internal.ErrNotSupported
+	}
+
+	// BPF_PROG_ATTACH was introduced at the same time as CGgroupSKB,
+	// so being able to load the program is enough to infer that we
+	// have the syscall.
+	prog.Close()
+	return nil
+})
+
+var haveProgAttachReplace = internal.NewFeatureTest("BPF_PROG_ATTACH atomic replacement", "5.5", func() error {
+	if err := haveProgAttach(); err != nil {
+		return err
+	}
+
+	prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
+		Type:       ebpf.CGroupSKB,
+		AttachType: ebpf.AttachCGroupInetIngress,
+		License:    "MIT",
+		Instructions: asm.Instructions{
+			asm.Mov.Imm(asm.R0, 0),
+			asm.Return(),
+		},
+	})
+	if err != nil {
+		return internal.ErrNotSupported
+	}
+	defer prog.Close()
+
+	// We know that we have BPF_PROG_ATTACH since we can load CGroupSKB programs.
+	// If passing BPF_F_REPLACE gives us EINVAL we know that the feature isn't
+	// present.
+	attr := sys.ProgAttachAttr{
+		// We rely on this being checked after attachFlags.
+		TargetFd:    ^uint32(0),
+		AttachBpfFd: uint32(prog.FD()),
+		AttachType:  uint32(ebpf.AttachCGroupInetIngress),
+		AttachFlags: uint32(flagReplace),
+	}
+
+	err = sys.ProgAttach(&attr)
+	if errors.Is(err, unix.EINVAL) {
+		return internal.ErrNotSupported
+	}
+	if errors.Is(err, unix.EBADF) {
+		return nil
+	}
+	return err
+})
+
+var haveBPFLink = internal.NewFeatureTest("bpf_link", "5.7", func() error {
+	attr := sys.LinkCreateAttr{
+		// This is a hopefully invalid file descriptor, which triggers EBADF.
+		TargetFd:   ^uint32(0),
+		ProgFd:     ^uint32(0),
+		AttachType: sys.AttachType(ebpf.AttachCGroupInetIngress),
+	}
+	_, err := sys.LinkCreate(&attr)
+	if errors.Is(err, unix.EINVAL) {
+		return internal.ErrNotSupported
+	}
+	if errors.Is(err, unix.EBADF) {
+		return nil
+	}
+	return err
+})
+
+var haveProgQuery = internal.NewFeatureTest("BPF_PROG_QUERY", "4.15", func() error {
+	attr := sys.ProgQueryAttr{
+		// We rely on this being checked during the syscall.
+		// With an otherwise correct payload we expect EBADF here
+		// as an indication that the feature is present.
+		TargetFd:   ^uint32(0),
+		AttachType: sys.AttachType(ebpf.AttachCGroupInetIngress),
+	}
+
+	err := sys.ProgQuery(&attr)
+	if errors.Is(err, unix.EINVAL) {
+		return internal.ErrNotSupported
+	}
+	if errors.Is(err, unix.EBADF) {
+		return nil
+	}
+	return err
+})
diff --git a/vendor/github.com/cilium/ebpf/link/tracepoint.go b/vendor/github.com/cilium/ebpf/link/tracepoint.go
new file mode 100644
index 000000000..a59ef9d1c
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/tracepoint.go
@@ -0,0 +1,77 @@
+package link
+
+import (
+	"fmt"
+
+	"github.com/cilium/ebpf"
+)
+
+// TracepointOptions defines additional parameters that will be used
+// when loading Tracepoints.
+type TracepointOptions struct {
+	// Arbitrary value that can be fetched from an eBPF program
+	// via `bpf_get_attach_cookie()`.
+	//
+	// Needs kernel 5.15+.
+	Cookie uint64
+}
+
+// Tracepoint attaches the given eBPF program to the tracepoint with the given
+// group and name. See /sys/kernel/debug/tracing/events to find available
+// tracepoints. The top-level directory is the group, the event's subdirectory
+// is the name. Example:
+//
+//	tp, err := Tracepoint("syscalls", "sys_enter_fork", prog, nil)
+//
+// Losing the reference to the resulting Link (tp) will close the Tracepoint
+// and prevent further execution of prog. The Link must be Closed during
+// program shutdown to avoid leaking system resources.
+//
+// Note that attaching eBPF programs to syscalls (sys_enter_*/sys_exit_*) is
+// only possible as of kernel 4.14 (commit cf5f5ce).
+func Tracepoint(group, name string, prog *ebpf.Program, opts *TracepointOptions) (Link, error) {
+	if group == "" || name == "" {
+		return nil, fmt.Errorf("group and name cannot be empty: %w", errInvalidInput)
+	}
+	if prog == nil {
+		return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput)
+	}
+	if !isValidTraceID(group) || !isValidTraceID(name) {
+		return nil, fmt.Errorf("group and name '%s/%s' must be alphanumeric or underscore: %w", group, name, errInvalidInput)
+	}
+	if prog.Type() != ebpf.TracePoint {
+		return nil, fmt.Errorf("eBPF program type %s is not a Tracepoint: %w", prog.Type(), errInvalidInput)
+	}
+
+	tid, err := getTraceEventID(group, name)
+	if err != nil {
+		return nil, err
+	}
+
+	fd, err := openTracepointPerfEvent(tid, perfAllThreads)
+	if err != nil {
+		return nil, err
+	}
+
+	var cookie uint64
+	if opts != nil {
+		cookie = opts.Cookie
+	}
+
+	pe := &perfEvent{
+		typ:       tracepointEvent,
+		group:     group,
+		name:      name,
+		tracefsID: tid,
+		cookie:    cookie,
+		fd:        fd,
+	}
+
+	lnk, err := attachPerfEvent(pe, prog)
+	if err != nil {
+		pe.Close()
+		return nil, err
+	}
+
+	return lnk, nil
+}
diff --git a/vendor/github.com/cilium/ebpf/link/tracing.go b/vendor/github.com/cilium/ebpf/link/tracing.go
new file mode 100644
index 000000000..e26cc9149
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/tracing.go
@@ -0,0 +1,150 @@
+package link
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/btf"
+	"github.com/cilium/ebpf/internal/sys"
+)
+
+type tracing struct {
+	RawLink
+}
+
+func (f *tracing) Update(new *ebpf.Program) error {
+	return fmt.Errorf("tracing update: %w", ErrNotSupported)
+}
+
+// AttachFreplace attaches the given eBPF program to the function it replaces.
+//
+// The program and name can either be provided at link time, or can be provided
+// at program load time. If they were provided at load time, they should be nil
+// and empty respectively here, as they will be ignored by the kernel.
+// Examples:
+//
+//	AttachFreplace(dispatcher, "function", replacement)
+//	AttachFreplace(nil, "", replacement)
+func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (Link, error) {
+	if (name == "") != (targetProg == nil) {
+		return nil, fmt.Errorf("must provide both or neither of name and targetProg: %w", errInvalidInput)
+	}
+	if prog == nil {
+		return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput)
+	}
+	if prog.Type() != ebpf.Extension {
+		return nil, fmt.Errorf("eBPF program type %s is not an Extension: %w", prog.Type(), errInvalidInput)
+	}
+
+	var (
+		target int
+		typeID btf.TypeID
+	)
+	if targetProg != nil {
+		btfHandle, err := targetProg.Handle()
+		if err != nil {
+			return nil, err
+		}
+		defer btfHandle.Close()
+
+		spec, err := btfHandle.Spec()
+		if err != nil {
+			return nil, err
+		}
+
+		var function *btf.Func
+		if err := spec.TypeByName(name, &function); err != nil {
+			return nil, err
+		}
+
+		target = targetProg.FD()
+		typeID, err = spec.TypeID(function)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	link, err := AttachRawLink(RawLinkOptions{
+		Target:  target,
+		Program: prog,
+		Attach:  ebpf.AttachNone,
+		BTF:     typeID,
+	})
+	if errors.Is(err, sys.ENOTSUPP) {
+		// This may be returned by bpf_tracing_prog_attach via bpf_arch_text_poke.
+		return nil, fmt.Errorf("create raw tracepoint: %w", ErrNotSupported)
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	return &tracing{*link}, nil
+}
+
+type TracingOptions struct {
+	// Program must be of type Tracing with attach type
+	// AttachTraceFEntry/AttachTraceFExit/AttachModifyReturn or
+	// AttachTraceRawTp.
+	Program *ebpf.Program
+}
+
+type LSMOptions struct {
+	// Program must be of type LSM with attach type
+	// AttachLSMMac.
+	Program *ebpf.Program
+}
+
+// attachBTFID links all BPF program types (Tracing/LSM) that they attach to a btf_id.
+func attachBTFID(program *ebpf.Program) (Link, error) {
+	if program.FD() < 0 {
+		return nil, fmt.Errorf("invalid program %w", sys.ErrClosedFd)
+	}
+
+	fd, err := sys.RawTracepointOpen(&sys.RawTracepointOpenAttr{
+		ProgFd: uint32(program.FD()),
+	})
+	if errors.Is(err, sys.ENOTSUPP) {
+		// This may be returned by bpf_tracing_prog_attach via bpf_arch_text_poke.
+		return nil, fmt.Errorf("create raw tracepoint: %w", ErrNotSupported)
+	}
+	if err != nil {
+		return nil, fmt.Errorf("create raw tracepoint: %w", err)
+	}
+
+	raw := RawLink{fd: fd}
+	info, err := raw.Info()
+	if err != nil {
+		raw.Close()
+		return nil, err
+	}
+
+	if info.Type == RawTracepointType {
+		// Sadness upon sadness: a Tracing program with AttachRawTp returns
+		// a raw_tracepoint link. Other types return a tracing link.
+		return &rawTracepoint{raw}, nil
+	}
+
+	return &tracing{RawLink: RawLink{fd: fd}}, nil
+}
+
+// AttachTracing links a tracing (fentry/fexit/fmod_ret) BPF program or
+// a BTF-powered raw tracepoint (tp_btf) BPF Program to a BPF hook defined
+// in kernel modules.
+func AttachTracing(opts TracingOptions) (Link, error) {
+	if t := opts.Program.Type(); t != ebpf.Tracing {
+		return nil, fmt.Errorf("invalid program type %s, expected Tracing", t)
+	}
+
+	return attachBTFID(opts.Program)
+}
+
+// AttachLSM links a Linux security module (LSM) BPF Program to a BPF
+// hook defined in kernel modules.
+func AttachLSM(opts LSMOptions) (Link, error) {
+	if t := opts.Program.Type(); t != ebpf.LSM {
+		return nil, fmt.Errorf("invalid program type %s, expected LSM", t)
+	}
+
+	return attachBTFID(opts.Program)
+}
diff --git a/vendor/github.com/cilium/ebpf/link/uprobe.go b/vendor/github.com/cilium/ebpf/link/uprobe.go
new file mode 100644
index 000000000..aa1ad9bbe
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/uprobe.go
@@ -0,0 +1,359 @@
+package link
+
+import (
+	"debug/elf"
+	"errors"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/internal"
+)
+
+var (
+	uprobeEventsPath = filepath.Join(tracefsPath, "uprobe_events")
+
+	uprobeRefCtrOffsetPMUPath = "/sys/bus/event_source/devices/uprobe/format/ref_ctr_offset"
+	// elixir.bootlin.com/linux/v5.15-rc7/source/kernel/events/core.c#L9799
+	uprobeRefCtrOffsetShift = 32
+	haveRefCtrOffsetPMU     = internal.NewFeatureTest("RefCtrOffsetPMU", "4.20", func() error {
+		_, err := os.Stat(uprobeRefCtrOffsetPMUPath)
+		if err != nil {
+			return internal.ErrNotSupported
+		}
+		return nil
+	})
+
+	// ErrNoSymbol indicates that the given symbol was not found
+	// in the ELF symbols table.
+	ErrNoSymbol = errors.New("not found")
+)
+
+// Executable defines an executable program on the filesystem.
+type Executable struct {
+	// Path of the executable on the filesystem.
+	path string
+	// Parsed ELF and dynamic symbols' addresses.
+	addresses map[string]uint64
+}
+
+// UprobeOptions defines additional parameters that will be used
+// when loading Uprobes.
+type UprobeOptions struct {
+	// Symbol address. Must be provided in case of external symbols (shared libs).
+	// If set, overrides the address eventually parsed from the executable.
+	Address uint64
+	// The offset relative to given symbol. Useful when tracing an arbitrary point
+	// inside the frame of given symbol.
+	//
+	// Note: this field changed from being an absolute offset to being relative
+	// to Address.
+	Offset uint64
+	// Only set the uprobe on the given process ID. Useful when tracing
+	// shared library calls or programs that have many running instances.
+	PID int
+	// Automatically manage SDT reference counts (semaphores).
+	//
+	// If this field is set, the Kernel will increment/decrement the
+	// semaphore located in the process memory at the provided address on
+	// probe attach/detach.
+	//
+	// See also:
+	// sourceware.org/systemtap/wiki/UserSpaceProbeImplementation (Semaphore Handling)
+	// github.com/torvalds/linux/commit/1cc33161a83d
+	// github.com/torvalds/linux/commit/a6ca88b241d5
+	RefCtrOffset uint64
+	// Arbitrary value that can be fetched from an eBPF program
+	// via `bpf_get_attach_cookie()`.
+	//
+	// Needs kernel 5.15+.
+	Cookie uint64
+}
+
+// To open a new Executable, use:
+//
+//	OpenExecutable("/bin/bash")
+//
+// The returned value can then be used to open Uprobe(s).
+func OpenExecutable(path string) (*Executable, error) {
+	if path == "" {
+		return nil, fmt.Errorf("path cannot be empty")
+	}
+
+	f, err := os.Open(path)
+	if err != nil {
+		return nil, fmt.Errorf("open file '%s': %w", path, err)
+	}
+	defer f.Close()
+
+	se, err := internal.NewSafeELFFile(f)
+	if err != nil {
+		return nil, fmt.Errorf("parse ELF file: %w", err)
+	}
+
+	if se.Type != elf.ET_EXEC && se.Type != elf.ET_DYN {
+		// ELF is not an executable or a shared object.
+		return nil, errors.New("the given file is not an executable or a shared object")
+	}
+
+	ex := Executable{
+		path:      path,
+		addresses: make(map[string]uint64),
+	}
+
+	if err := ex.load(se); err != nil {
+		return nil, err
+	}
+
+	return &ex, nil
+}
+
+func (ex *Executable) load(f *internal.SafeELFFile) error {
+	syms, err := f.Symbols()
+	if err != nil && !errors.Is(err, elf.ErrNoSymbols) {
+		return err
+	}
+
+	dynsyms, err := f.DynamicSymbols()
+	if err != nil && !errors.Is(err, elf.ErrNoSymbols) {
+		return err
+	}
+
+	syms = append(syms, dynsyms...)
+
+	for _, s := range syms {
+		if elf.ST_TYPE(s.Info) != elf.STT_FUNC {
+			// Symbol not associated with a function or other executable code.
+			continue
+		}
+
+		address := s.Value
+
+		// Loop over ELF segments.
+		for _, prog := range f.Progs {
+			// Skip uninteresting segments.
+			if prog.Type != elf.PT_LOAD || (prog.Flags&elf.PF_X) == 0 {
+				continue
+			}
+
+			if prog.Vaddr <= s.Value && s.Value < (prog.Vaddr+prog.Memsz) {
+				// If the symbol value is contained in the segment, calculate
+				// the symbol offset.
+				//
+				// fn symbol offset = fn symbol VA - .text VA + .text offset
+				//
+				// stackoverflow.com/a/40249502
+				address = s.Value - prog.Vaddr + prog.Off
+				break
+			}
+		}
+
+		ex.addresses[s.Name] = address
+	}
+
+	return nil
+}
+
+// address calculates the address of a symbol in the executable.
+//
+// opts must not be nil.
+func (ex *Executable) address(symbol string, opts *UprobeOptions) (uint64, error) {
+	if opts.Address > 0 {
+		return opts.Address + opts.Offset, nil
+	}
+
+	address, ok := ex.addresses[symbol]
+	if !ok {
+		return 0, fmt.Errorf("symbol %s: %w", symbol, ErrNoSymbol)
+	}
+
+	// Symbols with location 0 from section undef are shared library calls and
+	// are relocated before the binary is executed. Dynamic linking is not
+	// implemented by the library, so mark this as unsupported for now.
+	//
+	// Since only offset values are stored and not elf.Symbol, if the value is 0,
+	// assume it's an external symbol.
+	if address == 0 {
+		return 0, fmt.Errorf("cannot resolve %s library call '%s': %w "+
+			"(consider providing UprobeOptions.Address)", ex.path, symbol, ErrNotSupported)
+	}
+
+	return address + opts.Offset, nil
+}
+
+// Uprobe attaches the given eBPF program to a perf event that fires when the
+// given symbol starts executing in the given Executable.
+// For example, /bin/bash::main():
+//
+//	ex, _ = OpenExecutable("/bin/bash")
+//	ex.Uprobe("main", prog, nil)
+//
+// When using symbols which belongs to shared libraries,
+// an offset must be provided via options:
+//
+//	up, err := ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123})
+//
+// Note: Setting the Offset field in the options supersedes the symbol's offset.
+//
+// Losing the reference to the resulting Link (up) will close the Uprobe
+// and prevent further execution of prog. The Link must be Closed during
+// program shutdown to avoid leaking system resources.
+//
+// Functions provided by shared libraries can currently not be traced and
+// will result in an ErrNotSupported.
+func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) {
+	u, err := ex.uprobe(symbol, prog, opts, false)
+	if err != nil {
+		return nil, err
+	}
+
+	lnk, err := attachPerfEvent(u, prog)
+	if err != nil {
+		u.Close()
+		return nil, err
+	}
+
+	return lnk, nil
+}
+
+// Uretprobe attaches the given eBPF program to a perf event that fires right
+// before the given symbol exits. For example, /bin/bash::main():
+//
+//	ex, _ = OpenExecutable("/bin/bash")
+//	ex.Uretprobe("main", prog, nil)
+//
+// When using symbols which belongs to shared libraries,
+// an offset must be provided via options:
+//
+//	up, err := ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123})
+//
+// Note: Setting the Offset field in the options supersedes the symbol's offset.
+//
+// Losing the reference to the resulting Link (up) will close the Uprobe
+// and prevent further execution of prog. The Link must be Closed during
+// program shutdown to avoid leaking system resources.
+//
+// Functions provided by shared libraries can currently not be traced and
+// will result in an ErrNotSupported.
+func (ex *Executable) Uretprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) {
+	u, err := ex.uprobe(symbol, prog, opts, true)
+	if err != nil {
+		return nil, err
+	}
+
+	lnk, err := attachPerfEvent(u, prog)
+	if err != nil {
+		u.Close()
+		return nil, err
+	}
+
+	return lnk, nil
+}
+
+// uprobe opens a perf event for the given binary/symbol and attaches prog to it.
+// If ret is true, create a uretprobe.
+func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions, ret bool) (*perfEvent, error) {
+	if prog == nil {
+		return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput)
+	}
+	if prog.Type() != ebpf.Kprobe {
+		return nil, fmt.Errorf("eBPF program type %s is not Kprobe: %w", prog.Type(), errInvalidInput)
+	}
+	if opts == nil {
+		opts = &UprobeOptions{}
+	}
+
+	offset, err := ex.address(symbol, opts)
+	if err != nil {
+		return nil, err
+	}
+
+	pid := opts.PID
+	if pid == 0 {
+		pid = perfAllThreads
+	}
+
+	if opts.RefCtrOffset != 0 {
+		if err := haveRefCtrOffsetPMU(); err != nil {
+			return nil, fmt.Errorf("uprobe ref_ctr_offset: %w", err)
+		}
+	}
+
+	args := probeArgs{
+		symbol:       symbol,
+		path:         ex.path,
+		offset:       offset,
+		pid:          pid,
+		refCtrOffset: opts.RefCtrOffset,
+		ret:          ret,
+		cookie:       opts.Cookie,
+	}
+
+	// Use uprobe PMU if the kernel has it available.
+	tp, err := pmuUprobe(args)
+	if err == nil {
+		return tp, nil
+	}
+	if err != nil && !errors.Is(err, ErrNotSupported) {
+		return nil, fmt.Errorf("creating perf_uprobe PMU: %w", err)
+	}
+
+	// Use tracefs if uprobe PMU is missing.
+	args.symbol = sanitizeSymbol(symbol)
+	tp, err = tracefsUprobe(args)
+	if err != nil {
+		return nil, fmt.Errorf("creating trace event '%s:%s' in tracefs: %w", ex.path, symbol, err)
+	}
+
+	return tp, nil
+}
+
+// pmuUprobe opens a perf event based on the uprobe PMU.
+func pmuUprobe(args probeArgs) (*perfEvent, error) {
+	return pmuProbe(uprobeType, args)
+}
+
+// tracefsUprobe creates a Uprobe tracefs entry.
+func tracefsUprobe(args probeArgs) (*perfEvent, error) {
+	return tracefsProbe(uprobeType, args)
+}
+
+// sanitizeSymbol replaces every invalid character for the tracefs api with an underscore.
+// It is equivalent to calling regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString("_").
+func sanitizeSymbol(s string) string {
+	var b strings.Builder
+	b.Grow(len(s))
+	var skip bool
+	for _, c := range []byte(s) {
+		switch {
+		case c >= 'a' && c <= 'z',
+			c >= 'A' && c <= 'Z',
+			c >= '0' && c <= '9':
+			skip = false
+			b.WriteByte(c)
+
+		default:
+			if !skip {
+				b.WriteByte('_')
+				skip = true
+			}
+		}
+	}
+
+	return b.String()
+}
+
+// uprobeToken creates the PATH:OFFSET(REF_CTR_OFFSET) token for the tracefs api.
+func uprobeToken(args probeArgs) string {
+	po := fmt.Sprintf("%s:%#x", args.path, args.offset)
+
+	if args.refCtrOffset != 0 {
+		// This is not documented in Documentation/trace/uprobetracer.txt.
+		// elixir.bootlin.com/linux/v5.15-rc7/source/kernel/trace/trace.c#L5564
+		po += fmt.Sprintf("(%#x)", args.refCtrOffset)
+	}
+
+	return po
+}
diff --git a/vendor/github.com/cilium/ebpf/link/xdp.go b/vendor/github.com/cilium/ebpf/link/xdp.go
new file mode 100644
index 000000000..aa8dd3a4c
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/xdp.go
@@ -0,0 +1,54 @@
+package link
+
+import (
+	"fmt"
+
+	"github.com/cilium/ebpf"
+)
+
+// XDPAttachFlags represents how XDP program will be attached to interface.
+type XDPAttachFlags uint32
+
+const (
+	// XDPGenericMode (SKB) links XDP BPF program for drivers which do
+	// not yet support native XDP.
+	XDPGenericMode XDPAttachFlags = 1 << (iota + 1)
+	// XDPDriverMode links XDP BPF program into the driver’s receive path.
+	XDPDriverMode
+	// XDPOffloadMode offloads the entire XDP BPF program into hardware.
+	XDPOffloadMode
+)
+
+type XDPOptions struct {
+	// Program must be an XDP BPF program.
+	Program *ebpf.Program
+
+	// Interface is the interface index to attach program to.
+	Interface int
+
+	// Flags is one of XDPAttachFlags (optional).
+	//
+	// Only one XDP mode should be set, without flag defaults
+	// to driver/generic mode (best effort).
+	Flags XDPAttachFlags
+}
+
+// AttachXDP links an XDP BPF program to an XDP hook.
+func AttachXDP(opts XDPOptions) (Link, error) {
+	if t := opts.Program.Type(); t != ebpf.XDP {
+		return nil, fmt.Errorf("invalid program type %s, expected XDP", t)
+	}
+
+	if opts.Interface < 1 {
+		return nil, fmt.Errorf("invalid interface index: %d", opts.Interface)
+	}
+
+	rawLink, err := AttachRawLink(RawLinkOptions{
+		Program: opts.Program,
+		Attach:  ebpf.AttachXDP,
+		Target:  opts.Interface,
+		Flags:   uint32(opts.Flags),
+	})
+
+	return rawLink, err
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 2d580fafb..f7eac7119 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -16,6 +16,7 @@ github.com/cilium/ebpf/internal
 github.com/cilium/ebpf/internal/epoll
 github.com/cilium/ebpf/internal/sys
 github.com/cilium/ebpf/internal/unix
+github.com/cilium/ebpf/link
 github.com/cilium/ebpf/ringbuf
 github.com/cilium/ebpf/rlimit
 # github.com/davecgh/go-spew v1.1.1
-- 
GitLab