Compare commits
No commits in common. "master" and "getquery" have entirely different histories.
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
src/**.swp
|
|
||||||
**.log
|
|
5
Makefile
5
Makefile
@ -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)
|
||||||
|
18
README.md
18
README.md
@ -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
BIN
dns-trace
Binary file not shown.
BIN
dns.pcap
Normal file
BIN
dns.pcap
Normal file
Binary file not shown.
BIN
dns2.pcap
Normal file
BIN
dns2.pcap
Normal file
Binary file not shown.
BIN
dns3.pcap
Normal file
BIN
dns3.pcap
Normal file
Binary file not shown.
4
exec.sh
4
exec.sh
@ -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
|
||||||
|
23
src/common.h
23
src/common.h
@ -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
|
||||||
|
458
src/dns-trace.c
458
src/dns-trace.c
@ -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, ð, sizeof(struct ethhdr));
|
bpf_skb_load_bytes(skb, 0, ð, 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
0
src/skel.ebpf.h
Normal file
149985
src/vmlinux.h
Normal file
149985
src/vmlinux.h
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user