Compare commits

..

No commits in common. "master" and "getquery" have entirely different histories.

14 changed files with 150154 additions and 588 deletions

2
.gitignore vendored

@ -1,2 +0,0 @@
src/**.swp
**.log

@ -1,13 +1,12 @@
GCC=gcc GCC=gcc
CL=clang-11 CL=clang-11
CFLAGS=-Wall CFLAGS=-Wall
LIBS=-L../libbpf/src -l:libbpf.a -lelf -lz LIBS=-lbpf
#LIBS=-lbpf
all: dns-trace.ebpf.o dns-trace all: dns-trace.ebpf.o dns-trace
dns-trace.ebpf.o: src/dns-trace.ebpf.c dns-trace.ebpf.o: src/dns-trace.ebpf.c
$(CL) -Wall -g -O2 -target bpf -D __TARGET_ARCH_x86_64 -D __BPF_TRACING__ -c src/dns-trace.ebpf.c -o src/dns-trace.ebpf.o $(CL) -g -O2 -target bpf -c src/dns-trace.ebpf.c -o src/dns-trace.ebpf.o
dns-trace: src/dns-trace.c dns-trace: src/dns-trace.c
$(GCC) $(CFLAGS) src/dns-trace.c -o dns-trace $(LIBS) $(GCC) $(CFLAGS) src/dns-trace.c -o dns-trace $(LIBS)

@ -1,18 +0,0 @@
# Introduction
## Requirements
First, you need to install these packages:
```
apt-get install bpftool clang libbpf-dev gcc-multilib
```
Clone the project and compile it:
```
$ git clone https://github.com/libbpf/libbpf/
cd libbpf/src && make
```
After that, you can execute the program:

BIN
dns-trace

Binary file not shown.

BIN
dns.pcap Normal file

Binary file not shown.

BIN
dns2.pcap Normal file

Binary file not shown.

BIN
dns3.pcap Normal file

Binary file not shown.

@ -1,6 +1,6 @@
#!/usr/bin/sh #!/usr/bin/sh
#sudo bpftool btf dump file /sys/kernel/btf/vmlinux format c > src/vmlinux.h sudo bpftool btf dump file /sys/kernel/btf/vmlinux format c > src/vmlinux.h
make clean make clean
make all && sudo ./dns-trace -i wlp0s20f3 -f dns-trace_`$(echo date '+%F')`.log make all && sudo ./dns-trace

@ -3,13 +3,6 @@
#define QNAME_SIZE 128 #define QNAME_SIZE 128
#define REQ_QUERY 0x00
#define REQ_ANSWER 0x01
/* See section 2.3.4 RFC 1035 */
#define MAX_UDP_PAYLOAD 512
#define MAx_NAME_LEN 255
struct dnshdr { struct dnshdr {
uint16_t transactionID; uint16_t transactionID;
uint16_t flags; uint16_t flags;
@ -19,17 +12,19 @@ struct dnshdr {
uint16_t nbAdditionalRRs; uint16_t nbAdditionalRRs;
}; };
/*struct dns_query {
char *name;
uint16_t type;
uint16_t class;
};*/
struct event { struct event {
uint32_t client; uint32_t saddr;
int dport; int dport;
int sport; int sport;
uint16_t tid;
int req_type;
char qname[QNAME_SIZE]; char qname[QNAME_SIZE];
uint16_t class; int class;
uint16_t type; int type;
uint16_t numAns;
unsigned char buf[MAX_UDP_PAYLOAD]; // On stocke la data au format size + data
}; };
#endif #endif

@ -9,7 +9,6 @@
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <time.h>
#include <fcntl.h> #include <fcntl.h>
#include <net/if.h> /* if_nametoindex */ #include <net/if.h> /* if_nametoindex */
#include <linux/if_ether.h> #include <linux/if_ether.h>
@ -18,15 +17,10 @@
#include "common.h" #include "common.h"
/* Global variables */
static struct arguments arguments; static struct arguments arguments;
static int running = 1; static int running = 1;
static FILE *f;
static char hostname[127];
struct arguments { struct arguments {
char *interface;
char *filename; char *filename;
int to_output; int to_output;
}; };
@ -37,7 +31,7 @@ struct arguments {
*/ */
static char doc[] = "DNS Trace usage:"; static char doc[] = "DNS Trace usage:";
static char args_doc[] = "[ARGS]"; static char args_doc[] = "ARG1 ARG2";
static error_t parse_opts(int key, char *arg, struct argp_state *state){ static error_t parse_opts(int key, char *arg, struct argp_state *state){
struct arguments *arguments = state->input; struct arguments *arguments = state->input;
@ -46,9 +40,6 @@ static error_t parse_opts(int key, char *arg, struct argp_state *state){
arguments->filename = arg; arguments->filename = arg;
arguments->to_output = 0; arguments->to_output = 0;
break; break;
case 'i':
arguments->interface = arg;
break;
case 'o': case 'o':
arguments->to_output = 1; arguments->to_output = 1;
break; break;
@ -65,14 +56,12 @@ static error_t parse_opts(int key, char *arg, struct argp_state *state){
struct arguments parse_args(int argc, char *argv[]){ struct arguments parse_args(int argc, char *argv[]){
static const struct argp_option opts[] = { static const struct argp_option opts[] = {
{"interface", 'i', "INTERFACE", 0, "Ifname for listening"},
{"filename", 'f', "FILENAME", 0, "Save result to logs"}, {"filename", 'f', "FILENAME", 0, "Save result to logs"},
{"to-output", 'o', NULL, 0, "Print to output"}, {"to-output", 'o', NULL, 0, "Print to output"},
{NULL, 'h', NULL, OPTION_HIDDEN, "help"}, {NULL, 'h', NULL, OPTION_HIDDEN, "help"},
{}, {},
}; };
struct arguments arguments; struct arguments arguments;
arguments.interface = NULL;
arguments.filename = NULL; arguments.filename = NULL;
arguments.to_output = 1; arguments.to_output = 1;
static struct argp argp = {opts, parse_opts, args_doc, doc}; static struct argp argp = {opts, parse_opts, args_doc, doc};
@ -86,10 +75,9 @@ struct arguments parse_args(int argc, char *argv[]){
static void signalHandler(int signum){ static void signalHandler(int signum){
running = 0; running = 0;
} }
/*
* This function create a raw socket and bind it to the ifname static int open_raw_sock(const char *name)
*/ {
static int create_rsock(const char *name) {
struct sockaddr_ll sll; struct sockaddr_ll sll;
int sock; int sock;
@ -104,6 +92,7 @@ static int create_rsock(const char *name) {
sll.sll_ifindex = if_nametoindex(name); sll.sll_ifindex = if_nametoindex(name);
sll.sll_protocol = htons(ETH_P_ALL); sll.sll_protocol = htons(ETH_P_ALL);
if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) { if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
//printf("bind to %s: %s\n", name, strerror(errno));
printf("Failed to bind the interface %s\n", name); printf("Failed to bind the interface %s\n", name);
close(sock); close(sock);
return -1; return -1;
@ -112,431 +101,93 @@ static int create_rsock(const char *name) {
return sock; return sock;
} }
/* static void mapClass(const int class){
* This function map the type of DNS request
*/
static char *mapReqType(const int req){
char *tmp = malloc(8);
if (tmp == NULL)
return NULL;
switch(req){
case 0x00:
strncpy(tmp, "Query", 6);
break;
case 0x01:
strncpy(tmp, "Answer", 7);
break;
default:
strncpy(tmp, "Unknown", 8);
};
return tmp;
}
/*
* This function map the DNS class RR
*/
static char *mapClass(const int class){
char *tmp = malloc(8);
if (tmp == NULL)
return NULL;
memset(tmp, 0, 8);
switch(class){ switch(class){
case 1: case 1:
strncpy(tmp, "IN", 3); printf("IN\n");
break; break;
case 2: case 2:
strncpy(tmp, "CS", 3); printf("CS\n");
break; break;
case 3: case 3:
strncpy(tmp, "CH", 3); printf("CH\n");
break; break;
case 4: case 4:
strncpy(tmp, "HS", 3); printf("HS\n");
break; break;
default: default:
strncpy(tmp, "Unknown", 8); printf("Unknown\n");
break; break;
} }
return tmp;
} }
/* static void mapType(const int type){
* This function map the DNS type RR
*/
static char *mapType(const int type){
char *tmp = malloc(8);
if (tmp == NULL)
return NULL;
/*
* type DNS defined in RFC:
* https://datatracker.ietf.org/doc/html/rfc1035#section-3.2.2
* https://datatracker.ietf.org/doc/html/rfc3596
*/
switch(type){ switch(type){
case 1: case 1:
strncpy(tmp, "A", 2); printf("A");
break; break;
case 2: case 2:
strncpy(tmp, "NS", 3); printf("NS");
break; break;
case 3: case 3:
strncpy(tmp, "MD", 3); printf("MD");
break; break;
case 4: case 4:
strncpy(tmp, "MF", 3); printf("MF");
break; break;
case 5: case 5:
strncpy(tmp, "CNAME", 6); printf("CNAME");
break; break;
case 6: case 6:
strncpy(tmp, "SOA", 4); printf("SOA");
break; break;
case 7: case 7:
strncpy(tmp, "MB", 3); printf("MB");
break; break;
case 8: case 8:
strncpy(tmp, "MG", 3); printf("MG");
break; break;
case 9: case 9:
strncpy(tmp, "MR", 3); printf("MR");
break; break;
case 10: case 10:
strncpy(tmp, "NULL", 5); printf("NULL");
break; break;
case 11: case 11:
strncpy(tmp, "WKS", 4); printf("WKS");
break; break;
case 12: case 12:
strncpy(tmp, "PTR", 4); printf("PTR");
break; break;
case 13: case 13:
strncpy(tmp, "HINFO", 6); printf("HINFO");
break; break;
case 14: case 14:
strncpy(tmp, "MINFO", 6); printf("MINFO");
break; break;
case 15: case 15:
strncpy(tmp, "MX", 3); printf("MX");
break; break;
case 16: case 16:
strncpy(tmp, "TXT", 4); printf("TXT");
break; break;
case 28:
strncpy(tmp, "AAAA", 5);
break;
default: default:
strncpy(tmp, "Unknown", 8); printf("Unknown\n");
break; break;
} }
return tmp;
}
/*
* This function get the localtime into the rsyslog format
*/
static int syslog_time(time_t ts, char t[32], size_t l){
const char format[] = "%b %d %T";
struct tm *lt = localtime(&ts);
if(strftime(t, l, format, lt) == 0)
return -1;
return 0;
}
/*
* This function get the hostname of the system
* If not find, the hostname is ubuntu
*/
static void get_hostname(){
/* Get the hostname */
if (gethostname(hostname, 127) == -1){
printf("Failed to get the hostname\n");
strncpy(hostname, "ubuntu", 7);
}
}
/*
* This function print to the stdout the query section
*/
static void print_query(struct event *s_event){
char *req_type, *class, *type;
char t[32];
time_t ts = time(NULL);
syslog_time(ts, t, sizeof(t));
printf("%-20s", t);
req_type = mapReqType(s_event->req_type);
printf("%s ", req_type);
free(req_type);
printf("%5s:%d\t", inet_ntoa(*(struct in_addr*)&s_event->client), s_event->dport);
printf("%x\t", s_event->tid);
class = mapClass(s_event->class);
printf("%-5s", class);
free(class);
type = mapType(s_event->type);
printf("%-5s", type);
free(type);
printf("%s", s_event->qname);
printf("\n"); printf("\n");
} }
/*
* This function save to rsyslog file the common information
*/
static void header_to_log(struct event *s_event){
char t[32];
char tid[12];
char src[40];
char *req_type;
time_t ts = time(NULL);
if (syslog_time(ts, t, sizeof(t)) == 0)
fwrite(t, strlen(t), 1, f);
fwrite(" ", 1, 1, f);
fwrite(hostname, strlen(hostname), 1, f);
fwrite(" dns-trace: ", 12, 1, f);
fwrite("<info> ", 7, 1, f);
req_type = mapReqType(s_event->req_type);
fwrite(req_type, strlen(req_type), 1, f);
free(req_type);
fwrite(";", 1, 1, f);
snprintf(tid, 12, "tid=%x;", s_event->tid);
fwrite(tid, strlen(tid), 1, f);
snprintf(src, 40, "%s:%d;", inet_ntoa(*(struct in_addr*)&s_event->client), s_event->dport);
fwrite(src, strlen(src), 1, f);
}
/*
* This function save to log file the query section in rsylog format
* <time> <hostname> <procname>: <info> <data>
*/
static void query_to_log(struct event *s_event){
char *class, *type;
char s_class[16], s_type[16];
header_to_log(s_event);
class = mapClass(s_event->class);
snprintf(s_class, 16, "class=%s;", class);
fwrite(s_class, strlen(s_class), 1, f);
free(class);
type = mapType(s_event->type);
snprintf(s_type, 16, "type=%s;", type);
fwrite(s_type, strlen(s_type), 1, f);
free(type);
fwrite("domain=", 7, 1, f);
fwrite(s_event->qname, strlen(s_event->qname), 1, f);
fwrite(";\n", 2, 1, f);
}
/*
* This function get labels from DNS answer
*/
static void get_labels(unsigned char *buf, char *qname){
int pos = 0;
while (*buf++ != '\0') {
if((*buf >= 'a' && *buf <= 'z') || (*buf >= 'A' && *buf <= 'Z'))
*(qname + pos) = *buf;
else if (*buf >= '0' && *buf <= '9')
*(qname + pos) = *buf;
else
*(qname + pos) = '.';
pos++;
}
qname[pos - 1] = '\0';
}
/*
* This function read the event buf which contains the DNS answer section
*/
static void get_answer(struct event *s_event, uint16_t *class, uint16_t *type, uint16_t *size, uint32_t *ttl, int *pos){
int p = *pos;
*type = s_event->buf[p++];
*type |= s_event->buf[p++] << 8;
*class = s_event->buf[p++];
*class |= s_event->buf[p++] << 8;
*ttl = s_event->buf[p++];
*ttl |= s_event->buf[p++] << 8;
*ttl |= s_event->buf[p++] << 16;
*ttl |= s_event->buf[p++] << 24;
*size = s_event->buf[p++];
*size |= s_event->buf[p++] << 8;
*pos = p;
}
/*
* This function save to rsyslog format the answer section
*/
static void answer_to_log(struct event *s_event){
int pos = 0;
for (int i = 0; i < s_event->numAns; i++){
char *s_class, *s_type;
uint16_t i_type, i_class, i_size;
uint32_t i_ttl;
char b_class[16], b_type[16], b_ttl[16];
/*
* According to the RFC 1035 (https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.4)
* In the section 4.1.4, message compression, the first two bits are set at 11 (0xc),
* that's means, it's a pointer.
* For instance, the two bytes 0xc00c, 0xc (11) it's the pointer and 0x00c is the position in the DNS header
*/
uint16_t msg = s_event->buf[pos++];
msg |= s_event->buf[pos++] << 8;
header_to_log(s_event);
get_answer(s_event, &i_class, &i_type, &i_size, &i_ttl, &pos);
i_type = ntohs(i_type);
i_class = ntohs(i_class);
i_ttl = ntohl(i_ttl);
i_size = ntohs(i_size);
s_class = mapClass(i_class);
snprintf(b_class, 16, "class=%s;", s_class);
fwrite(b_class, strlen(b_class), 1, f);
free(s_class);
s_type = mapType(i_type);
snprintf(b_type, 16, "type=%s;", s_type);
fwrite(b_type, strlen(b_type), 1, f);
free(s_type);
snprintf(b_ttl, 16, "ttl=%d;", i_ttl);
fwrite(b_ttl, strlen(b_ttl), 1, f);
// Decode data
if (i_type == 1) { // -> A
uint32_t ip = s_event->buf[pos] + (s_event->buf[pos+1] << 8) + (s_event->buf[pos+2] << 16) + (s_event->buf[pos+3] << 24);
char s_ip[36];
snprintf(s_ip, 36, "ip=%s;", inet_ntoa(*(struct in_addr*)&ip));
fwrite(s_ip, strlen(s_ip), 1, f);
}
if (i_type == 5) { // -> CNAME
char cname[i_size];
char buf[i_size + 7]; // 7 -> cname=;
get_labels(s_event->buf + pos, cname);
snprintf(buf, i_size + 7, "cname=%s;", cname);
fwrite(buf, strlen(buf), 1, f);
}
if (i_type == 28){ // -> AAAA
char buf[128];
snprintf(buf, 128, "ipv6=%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x;",
s_event->buf[pos], s_event->buf[pos + 1],
s_event->buf[pos + 2], s_event->buf[pos + 3],
s_event->buf[pos + 4], s_event->buf[pos + 5],
s_event->buf[pos + 6], s_event->buf[pos + 7],
s_event->buf[pos + 8], s_event->buf[pos + 9],
s_event->buf[pos + 10], s_event->buf[pos + 11],
s_event->buf[pos + 12], s_event->buf[pos + 13],
s_event->buf[pos + 14], s_event->buf[pos + 15]);
fwrite(buf, strlen(buf), 1, f);
}
pos += i_size;
fwrite("\n", 1, 1, f);
}
}
/*
* This function print to the stdout the answer section
*/
static void print_answer(struct event *s_event){
char *req_type;
int pos = 0;
char t[32];
time_t ts = time(NULL);
for (int i = 0; i < s_event->numAns; i++){
uint16_t type, class, size;
char *s_type, *s_class;
uint32_t ttl;
uint16_t msg = s_event->buf[pos++];
msg |= s_event->buf[pos++] << 8;
/* Print answer hdr */
syslog_time(ts, t, sizeof(t));
printf("%-20s", t);
req_type = mapReqType(s_event->req_type);
printf("%s ", req_type);
free(req_type);
printf("%5s:%5d\t", inet_ntoa(*(struct in_addr*)&s_event->client), s_event->dport);
printf("%x\t", s_event->tid);
get_answer(s_event, &class, &type, &size, &ttl, &pos);
type = ntohs(type);
class = ntohs(class);
ttl = ntohl(ttl);
size = ntohs(size);
/* Print answer data */
s_class = mapClass(class);
printf("%-5s", s_class);
free(s_class);
s_type = mapType(type);
printf("%s\t", s_type);
free(s_type);
if (type == 1) { // -> A
uint32_t ip = s_event->buf[pos] + (s_event->buf[pos+1] << 8) + (s_event->buf[pos+2] << 16) + (s_event->buf[pos+3] << 24);
printf("%s %5d", inet_ntoa(*(struct in_addr*)&ip), ttl);
}
if (type == 5) { // -> CNAME
char cname[size];
get_labels(s_event->buf + pos, cname);
printf("%s %d ", cname, ttl);
}
if (type == 28){ // -> AAAA
for (int i = 0; i < size; i++){
if (i % 2 == 0)
printf("%x", s_event->buf[pos]);
else{
if (i < (size - 1))
printf("%x:", s_event->buf[pos]);
else
printf("%x", s_event->buf[pos]);
}
pos++;
}
printf(" %d ", ttl);
}
pos += size;
printf("\n");
}
}
/*
* This function is called when a new event is pushed in the ring buffer from the eBPf program
*/
int handle_event(void *ctx, void *data, size_t data_sz){ int handle_event(void *ctx, void *data, size_t data_sz){
struct event *s_event = (struct event*)data; struct event *s_event = (struct event*)data;
if (s_event->req_type == REQ_QUERY){ printf("IP: %s\n", inet_ntoa(*(struct in_addr*)&s_event->saddr));
if (arguments.to_output == 1) printf("dport: %d\n", s_event->dport);
print_query(s_event); printf("sport: %d\n", s_event->sport);
if (arguments.filename != NULL && f != NULL) printf("qname: %s\n", s_event->qname);
query_to_log(s_event); printf("Class: ");
} mapClass(s_event->class);
if (s_event->req_type == REQ_ANSWER){ printf("Type: ");
if (arguments.to_output == 1) mapType(s_event->type);
print_answer(s_event); printf("\n");
if (arguments.filename != NULL && f != NULL)
answer_to_log(s_event);
}
return 0; return 0;
} }
int main(int argc, char *argv[]){ int main(int argc, char *argv[]){
@ -546,31 +197,9 @@ int main(int argc, char *argv[]){
struct ring_buffer *rb; struct ring_buffer *rb;
int err; int err;
int fd_map_data; int fd_map_data;
int sock;
arguments = parse_args(argc, argv); // Parsing arguments arguments = parse_args(argc, argv); // Parsing arguments
if (arguments.interface == NULL){
printf("You must specified the interface name\n");
exit(-1);
}
printf("Listen to %s\n", arguments.interface);
sock = create_rsock(arguments.interface);
if (sock == -1){
printf("Failed to listen to the interface %s\n", arguments.interface);
exit(-1);
}
if (arguments.filename != NULL){
f = fopen(arguments.filename, "a");
if (f == NULL){
printf("Failed to create the file %s\n", arguments.filename);
return -1;
}
printf("Save to %s\n", arguments.filename);
}
signal(SIGINT, signalHandler); signal(SIGINT, signalHandler);
/* Open and load our eBPF object */ /* Open and load our eBPF object */
@ -604,13 +233,13 @@ int main(int argc, char *argv[]){
} }
bpf_program__attach(programSkb); bpf_program__attach(programSkb);
//int sock = open_raw_sock("wlp0s20f3");
int sock = open_raw_sock("enx98e743c667fc");
printf("Socket: %d\n", sock);
int prog_fd = bpf_program__fd(programSkb); int prog_fd = bpf_program__fd(programSkb);
printf("Program fd: %d\n", prog_fd);
setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(int)); setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(int));
/* Get the hostname of the system */
get_hostname();
/* Start the ringbuffer */ /* Start the ringbuffer */
rb = ring_buffer__new(fd_map_data, handle_event, NULL, NULL); rb = ring_buffer__new(fd_map_data, handle_event, NULL, NULL);
if (!rb){ if (!rb){
@ -628,9 +257,6 @@ int main(int argc, char *argv[]){
} }
} }
if (f != NULL)
fclose(f);
ring_buffer__free(rb); ring_buffer__free(rb);
bpf_object__close(obj); bpf_object__close(obj);

@ -2,7 +2,6 @@
#define __TARGET_ARCH_x86 #define __TARGET_ARCH_x86
#include <linux/bpf.h> #include <linux/bpf.h>
#include <bpf/bpf_helpers.h> #include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#include <bpf/bpf_tracing.h> #include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h> #include <bpf/bpf_core_read.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
@ -16,17 +15,34 @@
#include "common.h" #include "common.h"
/*
* Helper:
* Issue: invalid indirect read from stack R2 off
* Fix: check if all variables is initialised
* Issue: R1 invalid mem access 'inv'
* Fix: the value can be NULL
*/
struct { struct {
__uint(type, BPF_MAP_TYPE_RINGBUF); __uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024 /* 256kb */); __uint(max_entries, 256 * 1024 /* 256kb */);
} m_data SEC(".maps"); } m_data SEC(".maps");
static size_t get_labels(struct __sk_buff *skb, size_t offset, struct event *s_event){ /*
char c; * This function get the query field and the return the length of it
int qname_len = 0; */
static size_t get_query(struct __sk_buff *skb, struct event *s_event, uint16_t *class, uint16_t *type, size_t tlen){
size_t len;
char buf[QNAME_SIZE] = {0};
int index = 0;
int qname_len = 0; // Full length of the qname field
char qname[QNAME_SIZE] = {0};
char *c;
bpf_skb_load_bytes(skb, tlen, &buf, 41);
c = buf;
bpf_skb_load_bytes(skb, offset, &c, 1); // Get the first byte, which is the length
int pos = 1;
/* /*
* The qname is composed by a the number of bytes then follow by the label * The qname is composed by a the number of bytes then follow by the label
* For instance, for the qname www.bucchino.org, * For instance, for the qname www.bucchino.org,
@ -36,188 +52,126 @@ static size_t get_labels(struct __sk_buff *skb, size_t offset, struct event *s_e
* For instance, the result is: * For instance, the result is:
* 03 77 77 77 08 62 75 63 63 68 69 6e 6f 03 6f 72 67 00 * 03 77 77 77 08 62 75 63 63 68 69 6e 6f 03 6f 72 67 00
*/ */
while (c != '\0') { while (*(c++) != '\0') {
bpf_skb_load_bytes(skb, offset + pos++, &c, 1); if(*c >= 'a' && *c <= 'z')
s_event->qname[index] = *c;
if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) else if(*c >= 'A' && *c <= 'Z')
s_event->qname[qname_len] = c; s_event->qname[index] = *c;
else if(c >= '0' && c <= '9')
s_event->qname[qname_len] = c;
else else
s_event->qname[qname_len] = '.'; s_event->qname[index] = '.';
index++;
qname_len++; qname_len++;
if (pos == 128 || c == '\0')
break;
} }
s_event->qname[qname_len - 1] = '\0'; s_event->qname[--index] = '\0';
qname_len++; qname_len++; // For the null character
// bpf_printk("qname len: %d", qname_len); bpf_printk("%s (%d) %d", s_event->qname, index, qname_len);
return qname_len;
}
/*
* This function get the query field and the return the length of it
*/
static size_t get_query_section(struct __sk_buff *skb, struct event *s_event, uint8_t offset){
size_t len;
size_t qname_len = 0; // Full length of the qname field
uint16_t class, type;
offset += sizeof(struct dnshdr);
qname_len = get_labels(skb, offset, s_event);
// Get class and type // Get class and type
len = qname_len; len = qname_len;
bpf_skb_load_bytes(skb, offset + qname_len, &type, sizeof(uint16_t)); bpf_skb_load_bytes(skb, tlen + qname_len, type, sizeof(uint16_t));
s_event->type = ntohs(type);
len += 2; len += 2;
bpf_skb_load_bytes(skb, offset + qname_len + 2, &class, sizeof(uint16_t)); bpf_skb_load_bytes(skb, tlen + qname_len + 2, class, sizeof(uint16_t));
s_event->class = ntohs(class);
len += 2; len += 2;
return len; return len;
} }
/* /*
* https://datatracker.ietf.org/doc/html/rfc1035 * https://datatracker.ietf.org/doc/html/rfc1035
*/ */
static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, struct udphdr udp, int dport, int sport){ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, struct udphdr udp, int dport, int sport){
struct event *s_event; struct event *s_event;
struct dnshdr dns = {0}; struct dnshdr dns = {0};
char saddr[32];
/* Get DNS header */ uint16_t class, type;
bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr), &dns, sizeof(struct dnshdr)); // bpf_printk("udp len: %d", ntohs(udp.len));
// Check OpCode
uint16_t qr = ntohs(dns.flags) & 0xF000; // Get the QR code: 0 -> query, 1 -> response
/* If it's not a query, we do not continue */
if(qr != 0x0)
return 0;
if (ntohs(dns.nbQuestions) == 0)
return 0;
s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0); s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0);
if (!s_event) if (!s_event)
return 0; return 0;
s_event->req_type = REQ_QUERY;
/* Get IP header */ /* Get IP header */
s_event->client = ip.saddr; s_event->saddr = ip.saddr;
s_event->tid = ntohs(dns.transactionID); /* Get DNS header */
bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr), &dns, sizeof(struct dnshdr));
/* Get the query section */ if (ntohs(dns.nbQuestions) == 0){
uint8_t tlen = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr); bpf_ringbuf_discard(s_event, 0);
get_query_section(skb, s_event, tlen); return 0;
}
bpf_printk("tid: %x", ntohs(dns.transactionID)); // Use as key map
bpf_printk("nb question: %d", ntohs(dns.nbQuestions));
//struct dns_query dquery;
//bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr), &dquery, sizeof(struct dns_query));
// bpf_printk("size: %d %d %d", tlen, skb->len, (skb->len - tlen));
//dlen = (skb->len - tlen);
//bpf_printk("DNS packet len: %d", dlen);
//qlen = dlen - sizeof(struct dnshdr);
//bpf_printk("size: %d %d", sizeof(struct dnshdr), qlen);
/* Get the query structure */
size_t tlen = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr);
size_t query_len = get_query(skb, s_event, &class, &type, tlen);
// https://docs.cilium.io/en/stable/reference-guides/bpf/progtypes/ // https://docs.cilium.io/en/stable/reference-guides/bpf/progtypes/
s_event->dport = dport; s_event->dport = dport;
s_event->sport = sport; s_event->sport = sport;
s_event->class = ntohs(class);
s_event->type = ntohs(type);
//if(bpf_probe_read_user_str(&s_event->qname, sizeof(s_event->qname), qname) < 0)
// bpf_printk("Failed to copy qname");
// Add to map
bpf_ringbuf_submit(s_event, 0); bpf_ringbuf_submit(s_event, 0);
return 0; return 0;
} }
/* static int dnsanswer(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, struct udphdr udp, int dport, int sport){
* This function read the dns answer section return 0;
* All the answer is store in the buf variable and handle in the user space
*/
static void dnsanswer(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp, int dport, int sport){
struct event *s_event;
struct dnshdr dns;
uint32_t offset = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr);
size_t tlen = ntohs(udp.len);
unsigned int index = 0U;
if (tlen < 0 || tlen >= 256)
return;
// Load dns header
if (bpf_skb_load_bytes(skb, offset, &dns, sizeof(struct dnshdr)) < 0)
return;
// Check OpCode
uint16_t qr = ntohs(dns.flags) & 0xF000; // Get the QR code: 0 -> query, 1 -> response
if(qr != 0x8000) // Not a response, we do not continue
return;
if (ntohs(dns.nbQuestions) == 0)
return;
s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0);
if (!s_event)
return;
s_event->req_type = REQ_ANSWER;
/* Get IP header */
s_event->client = ip.daddr;
s_event->dport = dport;
s_event->sport = sport;
/* Get the Transaction ID */
s_event->tid = ntohs(dns.transactionID);
/* Get the query section */
size_t query_len = get_query_section(skb, s_event, offset);
/* Get answer section */
uint16_t ans = ntohs(dns.nbAnswerRRs);
if (ans <= 0){
bpf_ringbuf_discard(s_event, 0);
return;
}
s_event->numAns = ans;
/*
* Load query and answers
* It's a little dirty to do that, to load byte by byte,
* otherwise, I have an issue with the eBPF verifier
*/
offset += sizeof(struct dnshdr) + query_len;
for (index = 0; index < tlen || index == MAX_UDP_PAYLOAD; index++)
bpf_skb_load_bytes(skb, offset + index, s_event->buf + index, 1);
bpf_ringbuf_submit(s_event, 0);
} }
/*
* skb -> http://oldvger.kernel.org/~davem/skb_data.html
*/
SEC("socket") SEC("socket")
int detect_dns(struct __sk_buff *skb) { int detect_dns(struct __sk_buff *skb) {
//void *data = (void *)(long)skb->data;
//void *data_end = (void *)(long)skb->data_end;
//struct ethhdr *eth = data;
struct ethhdr eth = {0}; struct ethhdr eth = {0};
struct iphdr ip = {0}; struct iphdr ip = {0};
struct udphdr udp = {0}; struct udphdr udp = {0};
__u32 h_proto, p; unsigned long long h_proto, p;
__u32 dport; unsigned long long dport;
__u32 sport; unsigned long long sport;
//if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end)
// return 0;
if (skb->len < sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr)) //bpf_skb_load_bytes(skb, 12, &p, 2);
return 0;
bpf_skb_load_bytes(skb, 0, &eth, sizeof(struct ethhdr)); bpf_skb_load_bytes(skb, 0, &eth, sizeof(struct ethhdr));
p = eth.h_proto; p = eth.h_proto;
if (ntohs(p) != ETH_P_IP) if (ntohs(p) != ETH_P_IP)
return 0; return 0;
// bpf_printk("ip: %d",ntohs(p));
//ip = (struct iphdr*)(data + sizeof(struct ethhdr));
bpf_skb_load_bytes(skb, sizeof(struct ethhdr), &ip, sizeof(struct iphdr)); bpf_skb_load_bytes(skb, sizeof(struct ethhdr), &ip, sizeof(struct iphdr));
//bpf_skb_load_bytes(data, sizeof(struct ethhdr), ip, sizeof(struct ip));
h_proto = ip.protocol; h_proto = ip.protocol;
// If not UDP packet // If not UDP packet
if (h_proto != 17) if (h_proto != 17)
return 0; return 0;
bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr), &udp, sizeof(struct udphdr)); // bpf_printk("proto: %d", h_proto);
//udp = (struct udphdr*)(data + sizeof(struct ethhdr) + sizeof(struct iphdr));
if (udp.len == 0) bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr), &udp, sizeof(struct udphdr));
return 0;
// Check if DNS port // Check if DNS port
dport = ntohs(udp.dest); dport = ntohs(udp.dest);
@ -226,8 +180,35 @@ int detect_dns(struct __sk_buff *skb) {
if (dport == 53) if (dport == 53)
dnsquery(skb, eth, ip, udp, dport, sport); dnsquery(skb, eth, ip, udp, dport, sport);
else if(sport == 53) else if(sport == 53)
dnsanswer(skb, ip, udp, dport, sport); bpf_printk("Response");
return 0; return 0;
} }
/*SEC("xdp")
int detect_dns(struct xdp_md *ctx){
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
struct iphdr *ip;
struct udphdr *udp;
__u16 h_proto;
__u16 dport;
if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end)
return XDP_DROP;
ip = (struct iphdr*)(data + sizeof(struct ethhdr));
udp = (struct udphdr*)(data + sizeof(struct ethhdr) + sizeof(struct iphdr));
h_proto = ip->protocol;
// If not UDP packet
if (h_proto != 17)
return XDP_PASS;
// Check if DNS port
dport = udp->dest;
bpf_printk("Dport: %d", (dport));
return XDP_PASS;
}*/
char LICENSE[] SEC("license") = "GPL"; char LICENSE[] SEC("license") = "GPL";

Binary file not shown.

0
src/skel.ebpf.h Normal file

149985
src/vmlinux.h Normal file

File diff suppressed because it is too large Load Diff