Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
8e9822d232 | |||
65a0f2447d | |||
8abce2236f | |||
be9022d0a6 | |||
![]() |
1449cc961d | ||
0872e2a6a7 | |||
![]() |
6aa4b64a5a | ||
08bc7e98d0 | |||
adbff3257a | |||
aa64b8f160 | |||
1436099101 | |||
c775f97f3c |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
src/**.swp
|
||||
**.log
|
2
Makefile
2
Makefile
@ -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) -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
|
||||
$(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
|
||||
|
||||
dns-trace: src/dns-trace.c
|
||||
$(GCC) $(CFLAGS) src/dns-trace.c -o dns-trace $(LIBS)
|
||||
|
BIN
cname.pcap
BIN
cname.pcap
Binary file not shown.
BIN
dns-trace
BIN
dns-trace
Binary file not shown.
BIN
dns.pcap
BIN
dns.pcap
Binary file not shown.
BIN
dns10.pcap
BIN
dns10.pcap
Binary file not shown.
BIN
dns2.pcap
BIN
dns2.pcap
Binary file not shown.
BIN
dns3.pcap
BIN
dns3.pcap
Binary file not shown.
BIN
dns4.pcap
BIN
dns4.pcap
Binary file not shown.
4
exec.sh
4
exec.sh
@ -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
|
||||
make all && sudo ./dns-trace -i wlp0s20f3 -f dns-trace_`$(echo date '+%F')`.log
|
||||
|
14
src/common.h
14
src/common.h
@ -32,18 +32,4 @@ 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
|
||||
|
377
src/dns-trace.c
377
src/dns-trace.c
@ -9,6 +9,7 @@
|
||||
#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>
|
||||
@ -17,10 +18,15 @@
|
||||
|
||||
#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;
|
||||
};
|
||||
@ -31,7 +37,7 @@ struct arguments {
|
||||
*/
|
||||
|
||||
static char doc[] = "DNS Trace usage:";
|
||||
static char args_doc[] = "ARG1 ARG2";
|
||||
static char args_doc[] = "[ARGS]";
|
||||
|
||||
static error_t parse_opts(int key, char *arg, struct argp_state *state){
|
||||
struct arguments *arguments = state->input;
|
||||
@ -40,6 +46,9 @@ 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;
|
||||
@ -56,12 +65,14 @@ 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};
|
||||
@ -75,9 +86,10 @@ struct arguments parse_args(int argc, char *argv[]){
|
||||
static void signalHandler(int signum){
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static int open_raw_sock(const char *name)
|
||||
{
|
||||
/*
|
||||
* This function create a raw socket and bind it to the ifname
|
||||
*/
|
||||
static int create_rsock(const char *name) {
|
||||
struct sockaddr_ll sll;
|
||||
int sock;
|
||||
|
||||
@ -92,7 +104,6 @@ static int open_raw_sock(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;
|
||||
@ -101,6 +112,9 @@ static int open_raw_sock(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)
|
||||
@ -118,6 +132,9 @@ 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)
|
||||
@ -143,6 +160,9 @@ 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)
|
||||
@ -212,16 +232,43 @@ 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;
|
||||
printf("%s:%-10d", inet_ntoa(*(struct in_addr*)&s_event->client), s_event->dport);
|
||||
printf("%-5x", s_event->tid);
|
||||
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("%-10s", req_type);
|
||||
printf("%s ", req_type);
|
||||
free(req_type);
|
||||
|
||||
printf("%-30s", s_event->qname);
|
||||
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);
|
||||
@ -231,41 +278,265 @@ 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){
|
||||
print_query(s_event);
|
||||
if (arguments.to_output == 1)
|
||||
print_query(s_event);
|
||||
if (arguments.filename != NULL && f != NULL)
|
||||
query_to_log(s_event);
|
||||
}
|
||||
if (s_event->req_type == REQ_ANSWER){
|
||||
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);
|
||||
}
|
||||
if (arguments.to_output == 1)
|
||||
print_answer(s_event);
|
||||
if (arguments.filename != NULL && f != NULL)
|
||||
answer_to_log(s_event);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
int main(int argc, char *argv[]){
|
||||
@ -275,9 +546,31 @@ 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 */
|
||||
@ -311,14 +604,13 @@ 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){
|
||||
@ -336,6 +628,9 @@ int main(int argc, char *argv[]){
|
||||
}
|
||||
}
|
||||
|
||||
if (f != NULL)
|
||||
fclose(f);
|
||||
|
||||
ring_buffer__free(rb);
|
||||
bpf_object__close(obj);
|
||||
|
||||
|
@ -16,60 +16,17 @@
|
||||
#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");
|
||||
|
||||
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){
|
||||
static size_t get_labels(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;
|
||||
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
|
||||
s_event->qname[qname_len] = '.';
|
||||
qname_len++;
|
||||
if (pos == 128 || c == '\0')
|
||||
break;
|
||||
}
|
||||
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,
|
||||
@ -79,20 +36,22 @@ static size_t get_labels(struct __sk_buff *skb, size_t offset, size_t end, struc
|
||||
* 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);
|
||||
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')
|
||||
s_event->qname[qname_len] = c;
|
||||
else
|
||||
s_event->qname[qname_len] = '.';
|
||||
qname_len++;
|
||||
if (pos == 128 || c == '\0')
|
||||
break;
|
||||
}
|
||||
s_event->qname[qname_len - 1] = '\0';
|
||||
qname_len++;
|
||||
// bpf_printk("qname len: %d", qname_len);
|
||||
return qname_len;
|
||||
}
|
||||
|
||||
@ -105,7 +64,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_labels2(skb, offset, s_event);
|
||||
qname_len = get_labels(skb, offset, s_event);
|
||||
|
||||
// Get class and type
|
||||
len = qname_len;
|
||||
@ -118,91 +77,19 @@ 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;
|
||||
@ -223,7 +110,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);
|
||||
size_t query_len = get_query_section(skb, s_event, tlen);
|
||||
get_query_section(skb, s_event, tlen);
|
||||
|
||||
// https://docs.cilium.io/en/stable/reference-guides/bpf/progtypes/
|
||||
s_event->dport = dport;
|
||||
@ -234,60 +121,22 @@ 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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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
|
||||
*/
|
||||
* This function read the dns answer section
|
||||
* 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;
|
||||
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, 12) < 0)
|
||||
if (bpf_skb_load_bytes(skb, offset, &dns, sizeof(struct dnshdr)) < 0)
|
||||
return;
|
||||
|
||||
// Check OpCode
|
||||
@ -312,7 +161,7 @@ static void dnsanswer(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp,
|
||||
|
||||
/* 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);
|
||||
|
||||
@ -323,57 +172,25 @@ static void dnsanswer(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp,
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
s_event->numAns = ans;
|
||||
|
||||
/*
|
||||
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
|
||||
* 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);
|
||||
|
||||
/* 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};
|
||||
@ -381,32 +198,22 @@ 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, ð, 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)
|
||||
@ -423,31 +230,4 @@ 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
131837
src/vmlinux.h
File diff suppressed because it is too large
Load Diff
BIN
test
BIN
test
Binary file not shown.
14
test.c
14
test.c
@ -1,14 +0,0 @@
|
||||
#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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user