commit 400356e2cebc67e71fa4dbefd0db9c831b60f968 Author: geoffrey Date: Tue Feb 11 09:15:01 2025 +0100 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb8435c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +src/**.swp +**.log diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ce94622 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +GCC=gcc +CL=clang-11 +CFLAGS=-Wall +LIBS=-L../libbpf/src -l:libbpf.a -lelf -lz +#LIBS=-lbpf + +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 + +dns-trace: src/dns-trace.c + $(GCC) $(CFLAGS) src/dns-trace.c -o dns-trace $(LIBS) + +clean: + rm -rf src/*.o && rm dns-trace diff --git a/README.md b/README.md new file mode 100644 index 0000000..b04a198 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# 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: + + diff --git a/dns-trace b/dns-trace new file mode 100755 index 0000000..e1e382c Binary files /dev/null and b/dns-trace differ diff --git a/exec.sh b/exec.sh new file mode 100755 index 0000000..b70fafb --- /dev/null +++ b/exec.sh @@ -0,0 +1,6 @@ +#!/usr/bin/sh + + +#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 diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..6526fc0 --- /dev/null +++ b/src/common.h @@ -0,0 +1,35 @@ +#ifndef H_COMMON +#define H_COMMON + +#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 { + uint16_t transactionID; + uint16_t flags; + uint16_t nbQuestions; + uint16_t nbAnswerRRs; + uint16_t nbAuthorityRRs; + uint16_t nbAdditionalRRs; +}; + +struct event { + uint32_t client; + int dport; + int sport; + uint16_t tid; + int req_type; + char qname[QNAME_SIZE]; + uint16_t class; + uint16_t type; + uint16_t numAns; + unsigned char buf[MAX_UDP_PAYLOAD]; // On stocke la data au format size + data +}; + +#endif diff --git a/src/dns-trace.c b/src/dns-trace.c new file mode 100644 index 0000000..89c2b5b --- /dev/null +++ b/src/dns-trace.c @@ -0,0 +1,638 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* if_nametoindex */ +#include +#include +#include + +#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; +}; + +/* + * Functions for arguments + * https://www.gnu.org/software/libc/manual/html_node/Argp-Example-3.html + */ + +static char doc[] = "DNS Trace usage:"; +static char args_doc[] = "[ARGS]"; + +static error_t parse_opts(int key, char *arg, struct argp_state *state){ + struct arguments *arguments = state->input; + switch(key){ + case 'f': + arguments->filename = arg; + arguments->to_output = 0; + break; + case 'i': + arguments->interface = arg; + break; + case 'o': + arguments->to_output = 1; + break; + case ARGP_KEY_ARG: + break; + case ARGP_KEY_END: + break; + default: + return ARGP_ERR_UNKNOWN; + + } + return 0; +} + +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}; + + argp_parse(&argp, argc, argv, 0, 0, &arguments); + return arguments; +} + +/* End functions arguments */ + +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) { + struct sockaddr_ll sll; + int sock; + + sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL)); + if (sock < 0) { + printf("cannot create raw socket\n"); + return -1; + } + + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_ifindex = if_nametoindex(name); + sll.sll_protocol = htons(ETH_P_ALL); + if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) { + printf("Failed to bind the interface %s\n", name); + close(sock); + return -1; + } + + return sock; +} + +/* + * 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){ + case 1: + strncpy(tmp, "IN", 3); + break; + case 2: + strncpy(tmp, "CS", 3); + break; + case 3: + strncpy(tmp, "CH", 3); + break; + case 4: + strncpy(tmp, "HS", 3); + break; + default: + strncpy(tmp, "Unknown", 8); + break; + } + return tmp; +} +/* + * 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){ + case 1: + strncpy(tmp, "A", 2); + break; + case 2: + strncpy(tmp, "NS", 3); + break; + case 3: + strncpy(tmp, "MD", 3); + break; + case 4: + strncpy(tmp, "MF", 3); + break; + case 5: + strncpy(tmp, "CNAME", 6); + break; + case 6: + strncpy(tmp, "SOA", 4); + break; + case 7: + strncpy(tmp, "MB", 3); + break; + case 8: + strncpy(tmp, "MG", 3); + break; + case 9: + strncpy(tmp, "MR", 3); + break; + case 10: + strncpy(tmp, "NULL", 5); + break; + case 11: + strncpy(tmp, "WKS", 4); + break; + case 12: + strncpy(tmp, "PTR", 4); + break; + case 13: + strncpy(tmp, "HINFO", 6); + break; + case 14: + strncpy(tmp, "MINFO", 6); + break; + case 15: + strncpy(tmp, "MX", 3); + break; + case 16: + strncpy(tmp, "TXT", 4); + break; + case 28: + strncpy(tmp, "AAAA", 5); + break; + default: + strncpy(tmp, "Unknown", 8); + 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"); +} +/* + * 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(" ", 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 + *