Skip to content

Commit

Permalink
[CWS] add kernel bpf filter for raw packet (#30288)
Browse files Browse the repository at this point in the history
  • Loading branch information
safchain authored Nov 25, 2024
1 parent 5d4eb27 commit cfaf5ee
Show file tree
Hide file tree
Showing 32 changed files with 805 additions and 143 deletions.
1 change: 1 addition & 0 deletions LICENSE-3rdparty.csv
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,7 @@ core,github.com/cilium/ebpf/perf,MIT,"Copyright (c) 2017 Nathan Sweet | Copyrigh
core,github.com/cilium/ebpf/ringbuf,MIT,"Copyright (c) 2017 Nathan Sweet | Copyright (c) 2018, 2019 Cloudflare | Copyright (c) 2019 Authors of Cilium"
core,github.com/cilium/ebpf/rlimit,MIT,"Copyright (c) 2017 Nathan Sweet | Copyright (c) 2018, 2019 Cloudflare | Copyright (c) 2019 Authors of Cilium"
core,github.com/clbanning/mxj,MIT,Copyright (c) 2012-2016 Charles Banning <[email protected]>. All rights reserved | Copyright 2009 The Go Authors. All rights reserved
core,github.com/cloudflare/cbpfc,BSD-3-Clause,"Copyright (c) 2019, Cloudflare. All rights reserved"
core,github.com/cloudflare/circl/dh/x25519,BSD-3-Clause,Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2019 Cloudflare. All rights reserved
core,github.com/cloudflare/circl/dh/x448,BSD-3-Clause,Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2019 Cloudflare. All rights reserved
core,github.com/cloudflare/circl/ecc/goldilocks,BSD-3-Clause,Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2019 Cloudflare. All rights reserved
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ require (
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/cors v1.11.1 // indirect
github.com/safchain/baloum v0.0.0-20221229104256-b1fc8f70a86b
github.com/safchain/baloum v0.0.0-20241120122234-f22c9bd19f3b
github.com/saracen/walker v0.1.3 // indirect
github.com/sassoftware/go-rpmutils v0.3.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
Expand Down Expand Up @@ -604,6 +604,7 @@ require (
github.com/DataDog/datadog-agent/pkg/util/defaultpaths v0.0.0-00010101000000-000000000000
github.com/DataDog/datadog-agent/pkg/util/utilizationtracker v0.0.0
github.com/NVIDIA/go-nvml v0.12.4-0
github.com/cloudflare/cbpfc v0.0.0-20240920015331-ff978e94500b
github.com/containerd/containerd/api v1.8.0
github.com/containerd/errdefs v1.0.0
github.com/distribution/reference v0.6.0
Expand Down
6 changes: 4 additions & 2 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions pkg/security/ebpf/c/include/constants/custom.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@ enum DENTRY_ERPC_RESOLUTION_CODE

enum TC_TAIL_CALL_KEYS
{
UNKNOWN,
DNS_REQUEST,
DNS_REQUEST = 1,
DNS_REQUEST_PARSER,
IMDS_REQUEST,
RAW_PACKET,
};

enum TC_RAWPACKET_KEYS {
RAW_PACKET_FILTER,
// reserved keys for raw packet filter tail calls
};

#define DNS_MAX_LENGTH 256
Expand Down
5 changes: 0 additions & 5 deletions pkg/security/ebpf/c/include/helpers/network.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,6 @@ __attribute__((always_inline)) void fill_network_context(struct network_context_
fill_network_device_context(&net_ctx->device, skb, pkt);
}

__attribute__((always_inline)) void tail_call_to_classifier(struct __sk_buff *skb, int classifier_id) {
bpf_tail_call_compat(skb, &classifier_router, classifier_id);
}

__attribute__((always_inline)) void parse_tuple(struct nf_conntrack_tuple *tuple, struct flow_t *flow) {
flow->sport = tuple->src.u.all;
flow->dport = tuple->dst.u.all;
Expand All @@ -94,7 +90,6 @@ __attribute__((always_inline)) void parse_tuple(struct nf_conntrack_tuple *tuple
bpf_probe_read(&flow->daddr, sizeof(flow->daddr), &tuple->dst.u3.all);
}


__attribute__((always_inline)) struct packet_t * parse_packet(struct __sk_buff *skb, int direction) {
struct cursor c = {};
tc_cursor_init(&c, skb);
Expand Down
4 changes: 2 additions & 2 deletions pkg/security/ebpf/c/include/hooks/network/dns.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ int classifier_dns_request(struct __sk_buff *skb) {
evt->id = htons(header.id);

// tail call to the dns request parser
tail_call_to_classifier(skb, DNS_REQUEST_PARSER);
bpf_tail_call_compat(skb, &classifier_router, DNS_REQUEST_PARSER);

// tail call failed, ignore packet
return ACT_OK;
Expand Down Expand Up @@ -116,7 +116,7 @@ int classifier_dns_request_parser(struct __sk_buff *skb) {
send_event_with_size_ptr(skb, EVENT_DNS, evt, offsetof(struct dns_event_t, name) + qname_length);

if (!is_dns_request_parsing_done(skb, pkt)) {
tail_call_to_classifier(skb, DNS_REQUEST_PARSER);
bpf_tail_call_compat(skb, &classifier_router, DNS_REQUEST_PARSER);
}

return ACT_OK;
Expand Down
55 changes: 19 additions & 36 deletions pkg/security/ebpf/c/include/hooks/network/raw.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,61 +4,44 @@
#include "helpers/network.h"
#include "perf_ring.h"

__attribute__((always_inline)) struct raw_packet_t *get_raw_packet_event() {
__attribute__((always_inline)) struct raw_packet_event_t *get_raw_packet_event() {
u32 key = 0;
return bpf_map_lookup_elem(&raw_packets, &key);
return bpf_map_lookup_elem(&raw_packet_event, &key);
}

SEC("classifier/raw_packet")
int classifier_raw_packet(struct __sk_buff *skb) {
SEC("classifier/raw_packet_sender")
int classifier_raw_packet_sender(struct __sk_buff *skb) {
struct packet_t *pkt = get_packet();
if (pkt == NULL) {
// should never happen
return ACT_OK;
}

struct raw_packet_t *evt = get_raw_packet_event();
if ((evt == NULL) || (skb == NULL)) {
struct raw_packet_event_t *evt = get_raw_packet_event();
if (evt == NULL || skb == NULL || evt->len == 0) {
// should never happen
return ACT_OK;
}

bpf_skb_pull_data(skb, 0);
// process context
fill_network_process_context(&evt->process, pkt);

u32 len = *(u32 *)(skb + offsetof(struct __sk_buff, len));
if (len > sizeof(evt->data)) {
len = sizeof(evt->data);
struct proc_cache_t *entry = get_proc_cache(evt->process.pid);
if (entry == NULL) {
evt->container.container_id[0] = 0;
} else {
copy_container_id_no_tracing(entry->container.container_id, &evt->container.container_id);
}

// NOTE(safchain) inline asm because clang isn't generating the proper instructions for :
// if (len == 0) return ACT_OK;
/*asm ("r4 = %[len]\n"
"if r4 > 0 goto + 2\n"
"r0 = 0\n"
"exit\n" :: [len]"r"((u64)len));*/

if (len > 1) {
if (bpf_skb_load_bytes(skb, 0, evt->data, len) < 0) {
return ACT_OK;
}
evt->len = skb->len;

// process context
fill_network_process_context(&evt->process, pkt);

struct proc_cache_t *entry = get_proc_cache(evt->process.pid);
if (entry == NULL) {
evt->container.container_id[0] = 0;
} else {
copy_container_id_no_tracing(entry->container.container_id, &evt->container.container_id);
}
fill_network_device_context(&evt->device, skb, pkt);

fill_network_device_context(&evt->device, skb, pkt);

u32 size = offsetof(struct raw_packet_t, data) + len;
send_event_with_size_ptr(skb, EVENT_RAW_PACKET, evt, size);
u32 len = evt->len;
if (len > sizeof(evt->data)) {
len = sizeof(evt->data);
}

send_event_with_size_ptr(skb, EVENT_RAW_PACKET, evt, offsetof(struct raw_packet_event_t, data) + len);

return ACT_OK;
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/security/ebpf/c/include/hooks/network/router.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ __attribute__((always_inline)) int route_pkt(struct __sk_buff *skb, struct packe
// route DNS requests
if (is_event_enabled(EVENT_DNS)) {
if (pkt->l4_protocol == IPPROTO_UDP && pkt->translated_ns_flow.flow.dport == htons(53)) {
tail_call_to_classifier(skb, DNS_REQUEST);
bpf_tail_call_compat(skb, &classifier_router, DNS_REQUEST);
}
}

// route IMDS requests
if (is_event_enabled(EVENT_IMDS)) {
if (pkt->l4_protocol == IPPROTO_TCP && ((pkt->ns_flow.flow.saddr[0] & 0xFFFFFFFF) == get_imds_ip() || (pkt->ns_flow.flow.daddr[0] & 0xFFFFFFFF) == get_imds_ip())) {
tail_call_to_classifier(skb, IMDS_REQUEST);
bpf_tail_call_compat(skb, &classifier_router, IMDS_REQUEST);
}
}

Expand Down
39 changes: 37 additions & 2 deletions pkg/security/ebpf/c/include/hooks/network/tc.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,45 @@ int classifier_egress(struct __sk_buff *skb) {
return route_pkt(skb, pkt, EGRESS);
};

__attribute__((always_inline)) int prepare_raw_packet_event(struct __sk_buff *skb) {
struct raw_packet_event_t *evt = get_raw_packet_event();
if (evt == NULL) {
// should never happen
return ACT_OK;
}

bpf_skb_pull_data(skb, 0);

u32 len = *(u32 *)(skb + offsetof(struct __sk_buff, len));
if (len > sizeof(evt->data)) {
len = sizeof(evt->data);
}

if (len > 1) {
if (bpf_skb_load_bytes(skb, 0, evt->data, len) < 0) {
return ACT_OK;
}
evt->len = skb->len;
} else {
evt->len = 0;
}

return ACT_OK;
}


SEC("classifier/ingress")
int classifier_raw_packet_ingress(struct __sk_buff *skb) {
struct packet_t *pkt = parse_packet(skb, INGRESS);
if (!pkt) {
return ACT_OK;
}

tail_call_to_classifier(skb, RAW_PACKET_FILTER);
if (prepare_raw_packet_event(skb) != ACT_OK) {
return ACT_OK;
}

bpf_tail_call_compat(skb, &raw_packet_classifier_router, RAW_PACKET_FILTER);

return ACT_OK;
};
Expand All @@ -45,7 +76,11 @@ int classifier_raw_packet_egress(struct __sk_buff *skb) {
return ACT_OK;
}

tail_call_to_classifier(skb, RAW_PACKET_FILTER);
if (prepare_raw_packet_event(skb) != ACT_OK) {
return ACT_OK;
}

bpf_tail_call_compat(skb, &raw_packet_classifier_router, RAW_PACKET_FILTER);

return ACT_OK;
};
Expand Down
5 changes: 3 additions & 2 deletions pkg/security/ebpf/c/include/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,15 @@ BPF_PERCPU_ARRAY_MAP(packets, struct packet_t, 1)
BPF_PERCPU_ARRAY_MAP(selinux_write_buffer, struct selinux_write_buffer_t, 1)
BPF_PERCPU_ARRAY_MAP(is_new_kthread, u32, 1)
BPF_PERCPU_ARRAY_MAP(syscalls_stats, struct syscalls_stats_t, EVENT_MAX)
BPF_PERCPU_ARRAY_MAP(raw_packets, struct raw_packet_t, 1)
BPF_PERCPU_ARRAY_MAP(raw_packet_event, struct raw_packet_event_t, 1)

BPF_PROG_ARRAY(args_envs_progs, 3)
BPF_PROG_ARRAY(dentry_resolver_kprobe_or_fentry_callbacks, EVENT_MAX)
BPF_PROG_ARRAY(dentry_resolver_tracepoint_callbacks, EVENT_MAX)
BPF_PROG_ARRAY(dentry_resolver_kprobe_or_fentry_progs, 6)
BPF_PROG_ARRAY(dentry_resolver_tracepoint_progs, 3)
BPF_PROG_ARRAY(classifier_router, 100)
BPF_PROG_ARRAY(classifier_router, 10)
BPF_PROG_ARRAY(sys_exit_progs, 64)
BPF_PROG_ARRAY(raw_packet_classifier_router, 32)

#endif
2 changes: 1 addition & 1 deletion pkg/security/ebpf/c/include/structs/network.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ struct network_context_t {
u16 l4_protocol;
};

struct raw_packet_t {
struct raw_packet_event_t {
struct kevent_t event;
struct process_context_t process;
struct span_context_t span;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ int test_ad_ratelimiter_basic() {
assert_zero(activity_dump_rate_limiter_allow(&config, cookie, now, 0),
"event allowed which should not be");
}
return 0;
return 1;
}

SEC("test/ad_ratelimiter_basic_half")
Expand Down Expand Up @@ -73,7 +73,7 @@ int test_ad_ratelimiter_basic_half() {
assert_zero(activity_dump_rate_limiter_allow(&config, cookie, now, 0),
"event allowed which should not be");
}
return 0;
return 1;
}

__attribute__((always_inline)) int test_ad_ratelimiter_variable_droprate(int algo) {
Expand Down Expand Up @@ -106,7 +106,7 @@ __attribute__((always_inline)) int test_ad_ratelimiter_variable_droprate(int alg
assert_greater_than(total_allowed, AD_RL_TEST_RATE * 3 / 4, "nope");
assert_lesser_than(total_allowed, AD_RL_TEST_RATE / 10, "nope");
}
return 0;
return 1;
}

SEC("test/ad_ratelimiter_decreasing_droprate")
Expand Down
1 change: 1 addition & 0 deletions pkg/security/ebpf/c/include/tests/baloum.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ static int (*baloum_call)(struct baloum_ctx *ctx, const char *section) = (void *
static int (*baloum_strcmp)(const char *s1, const char *s2) = (void *)0xfffd;
static int (*baloum_memcmp)(const void *b1, const void *b2, __u32 size) = (void *)0xfffc;
static int (*baloum_sleep)(__u64 ns) = (void *)0xfffb;
static int (*baloum_memcpy)(const void *b1, const void *b2, __u32 size) = (void *)0xfffa;

#define assert_memcmp(b1, b2, s, msg) \
if (baloum_memcmp(b1, b2, s) != 0) { \
Expand Down
8 changes: 4 additions & 4 deletions pkg/security/ebpf/c/include/tests/discarders_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ int test_discarders_event_mask() {
ret = _is_discarded_by_inode(EVENT_CHMOD, mount_id, inode);
assert_not_zero(ret, "inode should be discarded");

return 0;
return 1;
}

SEC("test/discarders_retention")
Expand Down Expand Up @@ -93,7 +93,7 @@ int test_discarders_retention() {
ret = _is_discarded_by_inode(EVENT_OPEN, mount_id, inode);
assert_not_zero(ret, "inode should be discarded");

return 0;
return 1;
}

SEC("test/discarders_revision")
Expand Down Expand Up @@ -142,7 +142,7 @@ int test_discarders_revision() {
ret = _is_discarded_by_inode(EVENT_OPEN, mount_id1, inode1);
assert_not_zero(ret, "inode should be discarded");

return 0;
return 1;
}

SEC("test/discarders_mount_revision")
Expand Down Expand Up @@ -183,7 +183,7 @@ int test_discarders_mount_revision() {
ret = _is_discarded_by_inode(EVENT_OPEN, mount_id1, inode1);
assert_not_zero(ret, "inode should be discarded");

return 0;
return 1;
}

#endif
30 changes: 30 additions & 0 deletions pkg/security/ebpf/c/include/tests/raw_packet_test.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef _RAW_PACKET_TEST_H
#define _RAW_PACKET_TEST_H

#include "helpers/network.h"
#include "baloum.h"

SEC("test/raw_packet_tail_calls")
int raw_packet_tail_calls(struct __sk_buff *skb) {
struct raw_packet_event_t *evt = get_raw_packet_event();
assert_not_null(evt, "unable to get raw packet event")

// tcp dst port 5555 and tcp[tcpflags] == tcp-syn
unsigned char data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x10,
0x00, 0x30, 0xf4, 0xa2, 0x40, 0x00, 0x40, 0x06,
0x48, 0x13, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
0x00, 0x01, 0xa2, 0x36, 0x15, 0xb3, 0x1c, 0x5b,
0x89, 0x33, 0x00, 0x00, 0x00, 0x00, 0x70, 0x02,
0xff, 0xd7, 0xfe, 0x24, 0x00, 0x00, 0x02, 0x04,
0xff, 0xd7, 0x01, 0x03, 0x03, 0x07
};
baloum_memcpy(evt->data, data, sizeof(data));

bpf_tail_call_compat(skb, &raw_packet_classifier_router, RAW_PACKET_FILTER);

return 1;
}

#endif
1 change: 1 addition & 0 deletions pkg/security/ebpf/c/include/tests/tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@

#include "discarders_test.h"
#include "activity_dump_ratelimiter_test.h"
#include "raw_packet_test.h"

#endif
Loading

0 comments on commit cfaf5ee

Please sign in to comment.