Compare commits

..

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

17 changed files with 132163 additions and 375 deletions

2
.gitignore vendored

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

@ -7,7 +7,7 @@ LIBS=-L../libbpf/src -l:libbpf.a -lelf -lz
all: dns-trace.ebpf.o dns-trace
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 -D __TARGET_ARCH_x86_64 -D __BPF_TRACING__ -L../libbpf/src -l:libbpf.a -c src/dns-trace.ebpf.c -o src/dns-trace.ebpf.o
dns-trace: src/dns-trace.c
$(GCC) $(CFLAGS) src/dns-trace.c -o dns-trace $(LIBS)

BIN
cname.pcap Normal file

Binary file not shown.

BIN
dns-trace

Binary file not shown.

BIN
dns.pcap Normal file

Binary file not shown.

BIN
dns10.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.

BIN
dns4.pcap Normal file

Binary file not shown.

@ -1,6 +1,6 @@
#!/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 all && sudo ./dns-trace -i wlp0s20f3 -f dns-trace_`$(echo date '+%F')`.log
make all && sudo ./dns-trace

@ -32,4 +32,18 @@ struct event {
unsigned char buf[MAX_UDP_PAYLOAD]; // On stocke la data au format size + data
};
struct query_section{
char qname[QNAME_SIZE];
size_t qname_len;
uint16_t class;
uint16_t type;
};
struct dns_answer {
char data[512];
uint16_t class;
uint16_t type;
uint32_t ttl;
};
#endif

@ -9,7 +9,6 @@
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <net/if.h> /* if_nametoindex */
#include <linux/if_ether.h>
@ -18,15 +17,10 @@
#include "common.h"
/* Global variables */
static struct arguments arguments;
static int running = 1;
static FILE *f;
static char hostname[127];
struct arguments {
char *interface;
char *filename;
int to_output;
};
@ -37,7 +31,7 @@ struct arguments {
*/
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){
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->to_output = 0;
break;
case 'i':
arguments->interface = arg;
break;
case 'o':
arguments->to_output = 1;
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[]){
static const struct argp_option opts[] = {
{"interface", 'i', "INTERFACE", 0, "Ifname for listening"},
{"filename", 'f', "FILENAME", 0, "Save result to logs"},
{"to-output", 'o', NULL, 0, "Print to output"},
{NULL, 'h', NULL, OPTION_HIDDEN, "help"},
{},
};
struct arguments arguments;
arguments.interface = NULL;
arguments.filename = NULL;
arguments.to_output = 1;
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){
running = 0;
}
/*
* This function create a raw socket and bind it to the ifname
*/
static int create_rsock(const char *name) {
static int open_raw_sock(const char *name)
{
struct sockaddr_ll sll;
int sock;
@ -104,6 +92,7 @@ static int create_rsock(const char *name) {
sll.sll_ifindex = if_nametoindex(name);
sll.sll_protocol = htons(ETH_P_ALL);
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);
close(sock);
return -1;
@ -112,9 +101,6 @@ static int create_rsock(const char *name) {
return sock;
}
/*
* This function map the type of DNS request
*/
static char *mapReqType(const int req){
char *tmp = malloc(8);
if (tmp == NULL)
@ -132,9 +118,6 @@ static char *mapReqType(const int req){
};
return tmp;
}
/*
* This function map the DNS class RR
*/
static char *mapClass(const int class){
char *tmp = malloc(8);
if (tmp == NULL)
@ -160,9 +143,6 @@ static char *mapClass(const int class){
}
return tmp;
}
/*
* This function map the DNS type RR
*/
static char *mapType(const int type){
char *tmp = malloc(8);
if (tmp == NULL)
@ -232,43 +212,16 @@ static char *mapType(const int type){
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);
printf("%s:%-10d", inet_ntoa(*(struct in_addr*)&s_event->client), s_event->dport);
printf("%-5x", s_event->tid);
req_type = mapReqType(s_event->req_type);
printf("%s ", req_type);
printf("%-10s", 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);
printf("%-30s", s_event->qname);
class = mapClass(s_event->class);
printf("%-5s", class);
@ -278,265 +231,41 @@ static void print_query(struct event *s_event){
printf("%-5s", type);
free(type);
printf("%s", s_event->qname);
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){
struct event *s_event = (struct event*)data;
if (s_event->req_type == REQ_QUERY){
if (arguments.to_output == 1)
print_query(s_event);
if (arguments.filename != NULL && f != NULL)
query_to_log(s_event);
print_query(s_event);
}
if (s_event->req_type == REQ_ANSWER){
if (arguments.to_output == 1)
print_answer(s_event);
if (arguments.filename != NULL && f != NULL)
answer_to_log(s_event);
int pos = 0;
//for(int i = 0; i < 32; i++)
// printf("%d ", s_event->buf[i]);
//printf("\n");
for (int i = 0; i < s_event->numAns; i++){
print_query(s_event);
uint16_t type2 = (s_event->buf[pos++]) + (s_event->buf[pos++] << 8);
uint16_t class2 = (s_event->buf[pos++]) + (s_event->buf[pos++] << 8);
uint32_t ttl2 = (s_event->buf[pos++]) + (s_event->buf[pos++] << 8) + (s_event->buf[pos++] << 16) + (s_event->buf[pos++] << 24);
uint16_t size2 = (s_event->buf[pos++]) + (s_event->buf[pos++] << 8);
type2 = ntohs(type2);
class2 = ntohs(class2);
ttl2 = ntohl(ttl2);
size2 = ntohs(size2);
if (type2 == 1) {// -> A
uint32_t ip = s_event->buf[pos++] + (s_event->buf[pos++] << 8) + (s_event->buf[pos++] << 16) + (s_event->buf[pos++] << 24);
printf("%s (%d)%5d", inet_ntoa(*(struct in_addr*)&ip), type2, ttl2);
}
if (type2 == 28){ // -> AAAA
}
printf("\n");
printf("%d\n", pos);
}
}
printf("\n");
return 0;
}
int main(int argc, char *argv[]){
@ -546,31 +275,9 @@ int main(int argc, char *argv[]){
struct ring_buffer *rb;
int err;
int fd_map_data;
int sock;
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);
/* Open and load our eBPF object */
@ -604,13 +311,14 @@ int main(int argc, char *argv[]){
}
bpf_program__attach(programSkb);
// int sock = open_raw_sock("wlp0s20f3");
//int sock = open_raw_sock("enx98e743c667fc");
int sock = open_raw_sock("lo");
printf("Socket: %d\n", sock);
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));
/* Get the hostname of the system */
get_hostname();
/* Start the ringbuffer */
rb = ring_buffer__new(fd_map_data, handle_event, NULL, NULL);
if (!rb){
@ -628,9 +336,6 @@ int main(int argc, char *argv[]){
}
}
if (f != NULL)
fclose(f);
ring_buffer__free(rb);
bpf_object__close(obj);

@ -16,32 +16,36 @@
#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 {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024 /* 256kb */);
} m_data SEC(".maps");
static size_t get_labels(struct __sk_buff *skb, size_t offset, struct event *s_event){
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 32768);
__type(key, uint16_t);
__type(value, struct dns_answer);
} m_tid SEC(".maps");
static size_t get_labels2(struct __sk_buff *skb, size_t offset, struct event *s_event){
char c;
int qname_len = 0;
//bpf_printk("labels off: %d", offset);
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
* For instance, for the qname www.bucchino.org,
* the first byte is the number of byte, here, it's 3, then we have www (in hex)
* Then, we have the byte of 8 and follow by the label bucchino (size 8)
* And to finish, we have 3 follow by org and we finish with the \0 character
* For instance, the result is:
* 03 77 77 77 08 62 75 63 63 68 69 6e 6f 03 6f 72 67 00
*/
while (c != '\0') {
bpf_skb_load_bytes(skb, offset + pos++, &c, 1);
if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
s_event->qname[qname_len] = c;
else if(c >= '0' && c <= '9')
if(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
s_event->qname[qname_len] = c;
else
s_event->qname[qname_len] = '.';
@ -51,9 +55,46 @@ static size_t get_labels(struct __sk_buff *skb, size_t offset, struct event *s_e
}
s_event->qname[qname_len - 1] = '\0';
qname_len++;
// dquery->qname_len = qname_len;
// bpf_printk("qname: %s", s_event->qname);
// bpf_printk("qname len: %d", qname_len);
return qname_len;
}
static size_t get_labels(struct __sk_buff *skb, size_t offset, size_t end, struct event *s_event, struct query_section *s_query){
//size_t len;
char buf[256] = {0};
char *c;
int index = 0;
size_t qname_len = 0; // Full length of the qname field
bpf_skb_load_bytes(skb, offset, &buf, 41);
c = buf;
/*
* The qname is composed by a the number of bytes then follow by the label
* For instance, for the qname www.bucchino.org,
* the first byte is the number of byte, here, it's 3, then we have www (in hex)
* Then, we have the byte of 8 and follow by the label bucchino (size 8)
* And to finish, we have 3 follow by org and we finish with the \0 character
* For instance, the result is:
* 03 77 77 77 08 62 75 63 63 68 69 6e 6f 03 6f 72 67 00
*/
while (*(c++) != '\0') {
if(*c >= 'a' && *c <= 'z')
s_event->qname[index] = *c;
else if(*c >= 'A' && *c <= 'Z')
s_event->qname[index] = *c;
else
s_event->qname[index] = '.';
index++;
qname_len++;
}
s_event->qname[--index] = '\0';
qname_len++; // For the null character
bpf_printk("qname: %s", s_event->qname);
return qname_len;
}
/*
* This function get the query field and the return the length of it
@ -64,7 +105,7 @@ static size_t get_query_section(struct __sk_buff *skb, struct event *s_event, ui
uint16_t class, type;
offset += sizeof(struct dnshdr);
qname_len = get_labels(skb, offset, s_event);
qname_len = get_labels2(skb, offset, s_event);
// Get class and type
len = qname_len;
@ -77,19 +118,91 @@ static size_t get_query_section(struct __sk_buff *skb, struct event *s_event, ui
return len;
}
static unsigned int get_answer(struct __sk_buff *skb, struct event *s_event, size_t tlen, unsigned int index){
unsigned char buf[2] = {0}; // Need to be unsigned, otherwise, the result is fffff
unsigned int offset = index;
// Get the 2 first bytes to identify if it's a message compression or not
if(bpf_skb_load_bytes(skb, tlen, &buf, 2) < 0)
return 0;
tlen += 2; // For the message compression
/*
* 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
*/
if (buf[0] == 0xc0){
/*
* I cannot read the labels, because the eBPF verifier considerate as a infinity loop
*/
/*
* According to the RFC 1035, the structure of answer is like that:
* https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.3
*/
// Get the class and type
if ((void*)(offset) >= MAX_UDP_PAYLOAD - sizeof(uint16_t))
return 0;
bpf_skb_load_bytes(skb, tlen, s_event->buf + offset, sizeof(uint16_t));
uint16_t type = s_event->buf[0] + (s_event->buf[1] << 8);
tlen += 2;
if ((void*)(offset += 2) >= MAX_UDP_PAYLOAD - sizeof(uint16_t))
return 0;
//offset += 2;
// For class
if(bpf_skb_load_bytes(skb, tlen, s_event->buf + offset, sizeof(uint16_t)) < 0)
return 0;
tlen += 2;
if ((void*)(offset += 2) >= MAX_UDP_PAYLOAD - sizeof(uint16_t))
return 0;
// Get ttl
if(bpf_skb_load_bytes(skb, tlen, s_event->buf + offset, sizeof(uint32_t)) < 0)
return 0;
if ((void*)(offset += 4) >= MAX_UDP_PAYLOAD - sizeof(uint32_t))
return 0;
tlen += 4;
// Get data size
uint16_t size;
bpf_skb_load_bytes(skb, tlen, &size, sizeof(uint16_t));
bpf_skb_load_bytes(skb, tlen, s_event->buf + offset, sizeof(uint16_t));
if ((void*)(offset += 2) >= MAX_UDP_PAYLOAD - sizeof(uint16_t))
return 0;
tlen += 2;
uint32_t data;
if (s_event->type == 1) { // -> A
bpf_skb_load_bytes(skb, tlen, s_event->buf + offset, sizeof(uint32_t));
}
if ((void*)(offset += ntohs(size)) >= MAX_UDP_PAYLOAD - sizeof(uint16_t))
return 0;
tlen += ntohs(size);
}
else {
// get_labels2(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr), s_event);
}
bpf_printk("End offset: %d", offset);
return offset;
}
/*
* 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){
struct event *s_event;
struct dnshdr dns = {0};
struct query_section dquery = {0};
/* Get DNS header */
bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr), &dns, sizeof(struct dnshdr));
// 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;
@ -110,7 +223,7 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s
/* Get the query section */
uint8_t tlen = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr);
get_query_section(skb, s_event, tlen);
size_t query_len = get_query_section(skb, s_event, tlen);
// https://docs.cilium.io/en/stable/reference-guides/bpf/progtypes/
s_event->dport = dport;
@ -121,22 +234,60 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s
return 0;
}
static void dnsanswer_old(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp, int dport, int sport){
char buf[256] = {0}; // Max dns domain name length
//__u16 udplen = 0U;
// Check with ip.len
//const __u32 tot_len = ntohs(ip.tot_len);
// __u32 payload_len = (ntohs(ip.tot_len) - sizeof(struct iphdr) - sizeof(struct udphdr));
__u32 payload_len = (ntohs(ip.tot_len) - sizeof(struct iphdr) - sizeof(struct udphdr)) & 0xff;
if (payload_len > skb->len)
return;
bpf_printk("payload len %d", payload_len);
if (payload_len <= -1) {
bpf_printk("payload len %d", payload_len);
}
// Get udp len
//udplen = ntohs(udp.len);
//bpf_printk("udp len: %d", udplen);
//if (udplen <= 0)
// return 0;
/*if (offset + udplen > skb->len) {
bpf_printk("outbound");
plen = sizeof(struct dnshdr);
}*/
/*long err = bpf_skb_load_bytes(skb, offset, &buf, payload_len);
if(err < 0){
bpf_printk("failed");
// bpf_ringbuf_discard(s_event, 0);
return;
}*/
// Cast to dnshdr
// dns = (struct dnshdr*)buf;
}
/*
* This function read the dns answer section
* All the answer is store in the buf variable and handle in the user space
*/
TODO: je recupere tout le skb->data, grace au skb->len
grace a ca, j'aurai tout le payload udp et toute les donnees dns
je pourrai facilement parcourir les data
*/
static void dnsanswer(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp, int dport, int sport){
struct event *s_event;
struct dnshdr dns;
uint16_t tid = 0U;
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)
if (bpf_skb_load_bytes(skb, offset, &dns, 12) < 0)
return;
// Check OpCode
@ -172,25 +323,57 @@ static void dnsanswer(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp,
return;
}
s_event->numAns = ans;
if (ans > 0){
/*
* We get a least the 5 last answer
* In the RFC 1035 (https://datatracker.ietf.org/doc/html/rfc1035#section-2.3.4) the max udp payload is 512 bytes
* The program limit size of the answer
*/
offset += sizeof(struct dnshdr) + query_len; // For the pos in the answer section in the skb
unsigned int offset_ans = 0;
for (uint16_t i = 0; i < ans; i++){
offset_ans += get_answer(skb, s_event, offset, offset_ans);
offset += offset_ans + 2; // +2 for the message compression
//offset_ans += offset_ans;
// For eBPF verifier, to be sure we leave the loop
if (i == ans || i == 5 || offset_ans >= 512)
break;
}
s_event->numAns = ans;
}
if (ntohs(dns.nbAuthorityRRs) > 0){
}
/*
* In the user space, if the haven't have the answer, we can have an error
* The solution is to push to the ring buffer and the answer is store in
* the struct event
* Or, we push to the ring buffer and the query only with the map
* but, if we haven't have the answer, we need print the query
*/
/*
* 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
Pour recuperer les infos:
1 - dans le getquery, on push dans le ringbuffer et dans le userspace, on recupere aussi la reponse
mais si la reponse, nous l'avons pas encore, ca fail et dans le get answer on push dans une map
2 - on push dans le ring buffer quand on a la reponse avec la requette car c'est dans le field query
cependant, si on a pas la reponse, on n'aura jamais la query
3 - dans le get query et get answer, on push dans le ring buffer et tout est store dans le struct event
*/
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);
/* Get the answer */
bpf_ringbuf_submit(s_event, 0);
}
/*
* skb -> http://oldvger.kernel.org/~davem/skb_data.html
*/
SEC("socket")
int detect_dns(struct __sk_buff *skb) {
//void *data = (void *)(long)skb->data;
//void *data_end = (void *)(long)skb->data_end;
//struct ethhdr *eth2 = data;
struct ethhdr eth = {0};
struct iphdr ip = {0};
struct udphdr udp = {0};
@ -198,22 +381,32 @@ int detect_dns(struct __sk_buff *skb) {
__u32 dport;
__u32 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))
return 0;
//bpf_skb_load_bytes(skb, 12, &p, 2);
bpf_skb_load_bytes(skb, 0, &eth, sizeof(struct ethhdr));
p = eth.h_proto;
if (ntohs(p) != ETH_P_IP)
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(data, sizeof(struct ethhdr), ip, sizeof(struct ip));
h_proto = ip.protocol;
// If not UDP packet
if (h_proto != 17)
return 0;
// bpf_printk("proto: %d", h_proto);
//udp = (struct udphdr*)(data + sizeof(struct ethhdr) + sizeof(struct iphdr));
bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr), &udp, sizeof(struct udphdr));
if (udp.len == 0)
@ -230,4 +423,31 @@ int detect_dns(struct __sk_buff *skb) {
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";

Binary file not shown.

131837
src/vmlinux.h Normal file

File diff suppressed because it is too large Load Diff

BIN
test Executable file

Binary file not shown.

14
test.c Normal file

@ -0,0 +1,14 @@
#include <stdio.h>
int main(void){
char buf[128] = "Hello world!";
char t = 'a';
//char *c = buf;
char *c = &t;
printf("%c\n", *c);
*c = buf[0];
printf("%c\n", *c);
return 0;
}