Skip to content

Commit 1ab0912

Browse files
Add UDP probes in anticipation of DNS monitoring (#206)
* add upd send/recv msg probes * format * first pass at DNS packet fetching * use skb functions * Revert "use skb functions" This reverts commit fbf1608. * try to make recvmsg portable * only use kprobes for udp methods, pray this works * verifier logic has changed? * testing skb methods * clean up skb code * format * still testing * typo * still cleaning up test code * cleanup * rename enums * format * cleanup, use proper skbuff offsets * use varlen for packet body * use json for output * further cleanup * cleanup * skip peeked calls in kprobe * change DNS max packet size * add counter for headlen==0 events * cleanup, error handling * add new counter for sk_buff failures * format.. * update name * update docs, var name * tiny rewording * add tests, clean up json * use host command * use path.. * use arg array * use bash for test * use bash for test * use sh * test with a special binary... * format * clean up * format... * set correct read len for outgoing packets * revert changes to utils * use better udp send * format * clean up --------- Co-authored-by: Christiano Haesbaert <haesbaert@elastic.co>
1 parent 86558ea commit 1ab0912

File tree

8 files changed

+314
-10
lines changed

8 files changed

+314
-10
lines changed

GPL/Events/EbpfEventProto.h

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
#define EBPF_EVENTPROBE_EBPFEVENTPROTO_H
1212

1313
#define TASK_COMM_LEN 16
14+
// The theoretical max size of DNS packets over UDP is 512.
15+
// Like so many things in DNS this number probaby isn't 100% accurate.
16+
// DNS extensions in RFC2671 and RFC6891 mean the actual size can be larger.
17+
#define MAX_DNS_PACKET 1500
1418

1519
#ifndef __KERNEL__
1620
#include <stdint.h>
@@ -40,6 +44,7 @@ enum ebpf_event_type {
4044
EBPF_EVENT_PROCESS_SHMGET = (1 << 17),
4145
EBPF_EVENT_PROCESS_PTRACE = (1 << 18),
4246
EBPF_EVENT_PROCESS_LOAD_MODULE = (1 << 19),
47+
EBPF_EVENT_NETWORK_DNS_PKT = (1 << 20),
4348
};
4449

4550
struct ebpf_event_header {
@@ -66,6 +71,7 @@ enum ebpf_varlen_field_type {
6671
EBPF_VL_FIELD_SYMLINK_TARGET_PATH,
6772
EBPF_VL_FIELD_MOD_VERSION,
6873
EBPF_VL_FIELD_MOD_SRCVERSION,
74+
EBPF_VL_FIELD_DNS_BODY,
6975
};
7076

7177
// Convenience macro to iterate all the variable length fields in an event
@@ -341,13 +347,19 @@ struct ebpf_process_load_module_event {
341347

342348
enum ebpf_net_info_transport {
343349
EBPF_NETWORK_EVENT_TRANSPORT_TCP = 1,
350+
EBPF_NETWORK_EVENT_TRANSPORT_UDP = 2,
344351
};
345352

346353
enum ebpf_net_info_af {
347354
EBPF_NETWORK_EVENT_AF_INET = 1,
348355
EBPF_NETWORK_EVENT_AF_INET6 = 2,
349356
};
350357

358+
enum ebpf_net_udp_info {
359+
EBPF_NETWORK_EVENT_SKB_CONSUME_UDP = 1,
360+
EBPF_NETWORK_EVENT_IP_SEND_UDP = 2,
361+
};
362+
351363
struct ebpf_net_info_tcp_close {
352364
uint64_t bytes_sent;
353365
uint64_t bytes_received;
@@ -379,10 +391,22 @@ struct ebpf_net_event {
379391
char comm[TASK_COMM_LEN];
380392
} __attribute__((packed));
381393

394+
struct ebpf_dns_event {
395+
struct ebpf_event_header hdr;
396+
struct ebpf_pid_info pids;
397+
struct ebpf_net_info net;
398+
char comm[TASK_COMM_LEN];
399+
enum ebpf_net_udp_info udp_evt;
400+
uint64_t original_len;
401+
// Variable length fields: dns body
402+
struct ebpf_varlen_fields_start vl_fields;
403+
} __attribute__((packed));
404+
382405
// Basic event statistics
383406
struct ebpf_event_stats {
384-
uint64_t lost; // lost events due to a full ringbuffer
385-
uint64_t sent; // events sent through the ringbuffer
407+
uint64_t lost; // lost events due to a full ringbuffer
408+
uint64_t sent; // events sent through the ringbuffer
409+
uint64_t dns_zero_body; // indicates that the dns body of a sk_buff was unavailable
386410
};
387411

388412
#endif // EBPF_EVENTPROBE_EBPFEVENTPROTO_H

GPL/Events/Network/Probe.bpf.c

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "Helpers.h"
1818
#include "Network.h"
1919
#include "State.h"
20+
#include "Varlen.h"
2021

2122
DECL_FUNC_RET(inet_csk_accept);
2223

@@ -43,6 +44,165 @@ static int inet_csk_accept__exit(struct sock *sk)
4344
return 0;
4445
}
4546

47+
static int udp_skb_handle(struct sk_buff *skb, enum ebpf_net_udp_info evt_type)
48+
{
49+
if (skb == NULL) {
50+
goto out;
51+
}
52+
53+
if (ebpf_events_is_trusted_pid())
54+
goto out;
55+
56+
struct ebpf_dns_event *event = get_event_buffer();
57+
if (event == NULL)
58+
goto out;
59+
60+
// read from skbuf
61+
unsigned char *skb_head = BPF_CORE_READ(skb, head);
62+
// get lengths
63+
u16 net_header_offset = BPF_CORE_READ(skb, network_header);
64+
u16 transport_header_offset = BPF_CORE_READ(skb, transport_header);
65+
size_t network_header_size = 0;
66+
u8 proto = 0;
67+
68+
struct iphdr ip_hdr;
69+
bpf_core_read(&ip_hdr, sizeof(struct iphdr), skb_head + net_header_offset);
70+
if (ip_hdr.version == 4) {
71+
proto = ip_hdr.protocol;
72+
73+
if (bpf_probe_read(event->net.saddr, 4, &ip_hdr.saddr) != 0) {
74+
goto out;
75+
};
76+
77+
if (bpf_probe_read(event->net.daddr, 4, &ip_hdr.daddr) != 0) {
78+
goto out;
79+
}
80+
network_header_size = sizeof(struct iphdr);
81+
event->net.family = EBPF_NETWORK_EVENT_AF_INET;
82+
} else if (ip_hdr.version == 6) {
83+
struct ipv6hdr ip6_hdr;
84+
bpf_core_read(&ip6_hdr, sizeof(struct ipv6hdr), skb_head + net_header_offset);
85+
proto = ip6_hdr.nexthdr;
86+
87+
if (bpf_probe_read(event->net.saddr6, 16, ip6_hdr.saddr.in6_u.u6_addr8) != 0) {
88+
goto out;
89+
}
90+
91+
if (bpf_probe_read(event->net.daddr6, 16, ip6_hdr.daddr.in6_u.u6_addr8) != 0) {
92+
goto out;
93+
}
94+
95+
network_header_size = sizeof(struct ipv6hdr);
96+
event->net.family = EBPF_NETWORK_EVENT_AF_INET6;
97+
} else {
98+
goto out;
99+
}
100+
101+
if (proto != IPPROTO_UDP) {
102+
goto out;
103+
}
104+
105+
struct udphdr udp_hdr;
106+
if (bpf_core_read(&udp_hdr, sizeof(struct udphdr), skb_head + transport_header_offset) != 0) {
107+
goto out;
108+
}
109+
110+
uint16_t dport = bpf_ntohs(udp_hdr.dest);
111+
uint16_t sport = bpf_ntohs(udp_hdr.source);
112+
// filter out non-DNS packets
113+
if (sport != 53 && dport != 53) {
114+
goto out;
115+
}
116+
117+
event->net.dport = dport;
118+
event->net.sport = sport;
119+
event->net.transport = EBPF_NETWORK_EVENT_TRANSPORT_UDP;
120+
121+
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
122+
ebpf_pid_info__fill(&event->pids, task);
123+
bpf_get_current_comm(event->comm, TASK_COMM_LEN);
124+
event->hdr.ts = bpf_ktime_get_ns();
125+
126+
// constrain the read size to make the verifier happy
127+
// see skb_headlen() in skbuff.h
128+
size_t readsize = BPF_CORE_READ(skb, len);
129+
size_t datalen = BPF_CORE_READ(skb, data_len);
130+
size_t headlen = readsize - datalen;
131+
// headlen of zero indicates we have no non-paged data, and thus cannot read
132+
// anything from the root data node
133+
if (headlen == 0) {
134+
u32 zero = 0;
135+
struct ebpf_event_stats *es = bpf_map_lookup_elem(&ringbuf_stats, &zero);
136+
if (es != NULL) {
137+
es->dns_zero_body++;
138+
}
139+
goto out;
140+
}
141+
142+
size_t body_size = headlen;
143+
// for ip_send_skb(), we're at a point in the network stack where we've just prepended the IP
144+
// header, so the normal headlen for the skb_buff includes the headers. Reset them so we *just*
145+
// read the application body.
146+
if (evt_type == EBPF_NETWORK_EVENT_IP_SEND_UDP) {
147+
body_size = headlen - (sizeof(struct udphdr) + network_header_size);
148+
}
149+
150+
event->original_len = headlen;
151+
if (body_size > MAX_DNS_PACKET) {
152+
body_size = MAX_DNS_PACKET;
153+
}
154+
155+
ebpf_vl_fields__init(&event->vl_fields);
156+
struct ebpf_varlen_field *field;
157+
field = ebpf_vl_field__add(&event->vl_fields, EBPF_VL_FIELD_DNS_BODY);
158+
long ret = bpf_probe_read_kernel(field->data, body_size,
159+
skb_head + transport_header_offset + sizeof(struct udphdr));
160+
if (ret != 0) {
161+
bpf_printk("error reading in data buffer: %d", ret);
162+
goto out;
163+
}
164+
ebpf_vl_field__set_size(&event->vl_fields, field, body_size);
165+
166+
event->hdr.type = EBPF_EVENT_NETWORK_DNS_PKT;
167+
event->udp_evt = evt_type;
168+
ebpf_ringbuf_write(&ringbuf, event, EVENT_SIZE(event), 0);
169+
170+
out:
171+
return 0;
172+
}
173+
174+
SEC("fentry/ip_send_skb")
175+
int BPF_PROG(fentry__ip_send_skb, struct net *net, struct sk_buff *skb)
176+
{
177+
return udp_skb_handle(skb, EBPF_NETWORK_EVENT_IP_SEND_UDP);
178+
}
179+
180+
SEC("fentry/skb_consume_udp")
181+
int BPF_PROG(fentry__skb_consume_udp, struct sock *sk, struct sk_buff *skb, int len)
182+
{
183+
// skip peek operations
184+
if (len < 0) {
185+
return 0;
186+
}
187+
return udp_skb_handle(skb, EBPF_NETWORK_EVENT_SKB_CONSUME_UDP);
188+
}
189+
190+
SEC("kprobe/ip_send_skb")
191+
int BPF_KPROBE(kprobe__ip_send_udp, struct net *net, struct sk_buff *skb)
192+
{
193+
return udp_skb_handle(skb, EBPF_NETWORK_EVENT_IP_SEND_UDP);
194+
}
195+
196+
SEC("kprobe/skb_consume_udp")
197+
int BPF_KPROBE(kprobe__skb_consume_udp, struct net *net, struct sk_buff *skb, int len)
198+
{
199+
// skip peek operations
200+
if (len < 0) {
201+
return 0;
202+
}
203+
return udp_skb_handle(skb, EBPF_NETWORK_EVENT_SKB_CONSUME_UDP);
204+
}
205+
46206
SEC("fexit/inet_csk_accept")
47207
int BPF_PROG(fexit__inet_csk_accept)
48208
{

non-GPL/Events/EventsTrace/EventsTrace.c

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ enum cmdline_opts {
6363
NETWORK_CONNECTION_ATTEMPTED,
6464
NETWORK_CONNECTION_ACCEPTED,
6565
NETWORK_CONNECTION_CLOSED,
66+
NETWORK_DNS_PKT,
6667
CMDLINE_MAX
6768
};
6869

@@ -89,6 +90,7 @@ static uint64_t cmdline_to_lib[CMDLINE_MAX] = {
8990
x(NETWORK_CONNECTION_ATTEMPTED)
9091
x(NETWORK_CONNECTION_ACCEPTED)
9192
x(NETWORK_CONNECTION_CLOSED)
93+
x(NETWORK_DNS_PKT)
9294
#undef x
9395
// clang-format on
9496
};
@@ -114,6 +116,7 @@ static const struct argp_option opts[] = {
114116
{"process-load-module", PROCESS_LOAD_MODULE, NULL, false, "Print kernel module load events", 0},
115117
{"net-conn-accept", NETWORK_CONNECTION_ACCEPTED, NULL, false,
116118
"Print network connection accepted events", 0},
119+
{"net-conn-dns-pkt", NETWORK_DNS_PKT, NULL, false, "Print DNS events", 0},
117120
{"net-conn-attempt", NETWORK_CONNECTION_ATTEMPTED, NULL, false,
118121
"Print network connection attempted events", 0},
119122
{"net-conn-closed", NETWORK_CONNECTION_CLOSED, NULL, false,
@@ -173,6 +176,7 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
173176
case NETWORK_CONNECTION_ACCEPTED:
174177
case NETWORK_CONNECTION_ATTEMPTED:
175178
case NETWORK_CONNECTION_CLOSED:
179+
case NETWORK_DNS_PKT:
176180
g_events_env |= cmdline_to_lib[key];
177181
break;
178182
case ARGP_KEY_ARG:
@@ -965,9 +969,8 @@ static void out_ip6_addr(const char *name, const void *addr)
965969
printf("\"%s\":\"%s\"", name, buf);
966970
}
967971

968-
static void out_net_info(const char *name, struct ebpf_net_event *evt)
972+
static void out_net_info(const char *name, struct ebpf_net_info *net, struct ebpf_event_header *hdr)
969973
{
970-
struct ebpf_net_info *net = &evt->net;
971974

972975
printf("\"%s\":", name);
973976
out_object_start();
@@ -977,6 +980,10 @@ static void out_net_info(const char *name, struct ebpf_net_event *evt)
977980
out_string("transport", "TCP");
978981
out_comma();
979982
break;
983+
case EBPF_NETWORK_EVENT_TRANSPORT_UDP:
984+
out_string("transport", "UDP");
985+
out_comma();
986+
break;
980987
}
981988

982989
switch (net->family) {
@@ -1015,7 +1022,7 @@ static void out_net_info(const char *name, struct ebpf_net_event *evt)
10151022
out_comma();
10161023
out_int("network_namespace", net->netns);
10171024

1018-
switch (evt->hdr.type) {
1025+
switch (hdr->type) {
10191026
case EBPF_EVENT_NETWORK_CONNECTION_CLOSED:
10201027
out_comma();
10211028
out_uint("bytes_sent", net->tcp.close.bytes_sent);
@@ -1037,7 +1044,7 @@ static void out_network_event(const char *name, struct ebpf_net_event *evt)
10371044
out_pid_info("pids", &evt->pids);
10381045
out_comma();
10391046

1040-
out_net_info("net", evt);
1047+
out_net_info("net", &evt->net, &evt->hdr);
10411048
out_comma();
10421049

10431050
out_string("comm", (const char *)&evt->comm);
@@ -1051,6 +1058,40 @@ static void out_network_connection_accepted_event(struct ebpf_net_event *evt)
10511058
out_network_event("NETWORK_CONNECTION_ACCEPTED", evt);
10521059
}
10531060

1061+
static void out_network_dns_event(struct ebpf_dns_event *event)
1062+
{
1063+
out_object_start();
1064+
out_event_type("DNS_EVENT");
1065+
out_comma();
1066+
1067+
out_pid_info("pids", &event->pids);
1068+
out_comma();
1069+
1070+
out_net_info("net", &event->net, &event->hdr);
1071+
out_comma();
1072+
1073+
out_string("comm", (const char *)&event->comm);
1074+
out_comma();
1075+
1076+
printf("\"data\":");
1077+
out_array_start();
1078+
struct ebpf_varlen_field *field;
1079+
FOR_EACH_VARLEN_FIELD(event->vl_fields, field)
1080+
{
1081+
for (size_t i = 0; i < field->size; i++) {
1082+
uint8_t part = field->data[i];
1083+
printf("%d", part);
1084+
if (i < field->size - 1) {
1085+
printf(", ");
1086+
}
1087+
}
1088+
}
1089+
out_array_end();
1090+
1091+
out_object_end();
1092+
out_newline();
1093+
}
1094+
10541095
static void out_network_connection_attempted_event(struct ebpf_net_event *evt)
10551096
{
10561097
out_network_event("NETWORK_CONNECTION_ATTEMPTED", evt);
@@ -1130,6 +1171,9 @@ static int event_ctx_callback(struct ebpf_event_header *evt_hdr)
11301171
case EBPF_EVENT_NETWORK_CONNECTION_CLOSED:
11311172
out_network_connection_closed_event((struct ebpf_net_event *)evt_hdr);
11321173
break;
1174+
case EBPF_EVENT_NETWORK_DNS_PKT:
1175+
out_network_dns_event((struct ebpf_dns_event *)evt_hdr);
1176+
break;
11331177
}
11341178

11351179
return 0;

0 commit comments

Comments
 (0)