Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8210e56090 | |||
| edda1c6860 | |||
| 0a910af5bb | |||
| 6aa1eed6ee | |||
| b57efa5cad | |||
| 9a67b4f32c | |||
| eeb15e8f7b | |||
| 408ad6aef3 | |||
| 08ece6a46e | |||
| 157577613a | |||
| 9712681972 | |||
| 65258eb429 | |||
| 2cb21c1f55 |
@@ -1,12 +1,13 @@
|
|||||||
GCC=gcc
|
GCC=gcc
|
||||||
CL=clang-11
|
CL=clang-11
|
||||||
CFLAGS=-Wall
|
CFLAGS=-Wall
|
||||||
LIBS=-lbpf
|
LIBS=-L../libbpf/src -l:libbpf.a -lelf -lz
|
||||||
|
#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) -g -O2 -target bpf -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
|
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)
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|
||||||
|
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
+28
-9
@@ -3,6 +3,13 @@
|
|||||||
|
|
||||||
#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;
|
||||||
@@ -12,19 +19,31 @@ 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 saddr;
|
uint32_t client;
|
||||||
int dport;
|
int dport;
|
||||||
int sport;
|
int sport;
|
||||||
|
uint16_t tid;
|
||||||
|
int req_type;
|
||||||
char qname[QNAME_SIZE];
|
char qname[QNAME_SIZE];
|
||||||
int class;
|
uint16_t class;
|
||||||
int type;
|
uint16_t type;
|
||||||
|
uint16_t numAns;
|
||||||
|
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
|
#endif
|
||||||
|
|||||||
+114
-35
@@ -101,93 +101,171 @@ static int open_raw_sock(const char *name)
|
|||||||
return sock;
|
return sock;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mapClass(const int class){
|
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;
|
||||||
|
}
|
||||||
|
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:
|
||||||
printf("IN\n");
|
strncpy(tmp, "IN", 3);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
printf("CS\n");
|
strncpy(tmp, "CS", 3);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
printf("CH\n");
|
strncpy(tmp, "CH", 3);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
printf("HS\n");
|
strncpy(tmp, "HS", 3);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printf("Unknown\n");
|
strncpy(tmp, "Unknown", 8);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return tmp;
|
||||||
}
|
}
|
||||||
static void mapType(const int type){
|
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:
|
||||||
printf("A");
|
strncpy(tmp, "A", 2);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
printf("NS");
|
strncpy(tmp, "NS", 3);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
printf("MD");
|
strncpy(tmp, "MD", 3);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
printf("MF");
|
strncpy(tmp, "MF", 3);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
printf("CNAME");
|
strncpy(tmp, "CNAME", 6);
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
printf("SOA");
|
strncpy(tmp, "SOA", 4);
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
printf("MB");
|
strncpy(tmp, "MB", 3);
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
printf("MG");
|
strncpy(tmp, "MG", 3);
|
||||||
break;
|
break;
|
||||||
case 9:
|
case 9:
|
||||||
printf("MR");
|
strncpy(tmp, "MR", 3);
|
||||||
break;
|
break;
|
||||||
case 10:
|
case 10:
|
||||||
printf("NULL");
|
strncpy(tmp, "NULL", 5);
|
||||||
break;
|
break;
|
||||||
case 11:
|
case 11:
|
||||||
printf("WKS");
|
strncpy(tmp, "WKS", 4);
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
printf("PTR");
|
strncpy(tmp, "PTR", 4);
|
||||||
break;
|
break;
|
||||||
case 13:
|
case 13:
|
||||||
printf("HINFO");
|
strncpy(tmp, "HINFO", 6);
|
||||||
break;
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
printf("MINFO");
|
strncpy(tmp, "MINFO", 6);
|
||||||
break;
|
break;
|
||||||
case 15:
|
case 15:
|
||||||
printf("MX");
|
strncpy(tmp, "MX", 3);
|
||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
printf("TXT");
|
strncpy(tmp, "TXT", 4);
|
||||||
break;
|
break;
|
||||||
|
case 28:
|
||||||
|
strncpy(tmp, "AAAA", 5);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
printf("Unknown\n");
|
strncpy(tmp, "Unknown", 8);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
printf("\n");
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
req_type = mapReqType(s_event->req_type);
|
||||||
|
printf("%-10s", req_type);
|
||||||
|
free(req_type);
|
||||||
|
|
||||||
|
printf("%-30s", s_event->qname);
|
||||||
|
|
||||||
|
class = mapClass(s_event->class);
|
||||||
|
printf("%-5s", class);
|
||||||
|
free(class);
|
||||||
|
|
||||||
|
type = mapType(s_event->type);
|
||||||
|
printf("%-5s", type);
|
||||||
|
free(type);
|
||||||
|
|
||||||
|
}
|
||||||
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;
|
||||||
printf("IP: %s\n", inet_ntoa(*(struct in_addr*)&s_event->saddr));
|
if (s_event->req_type == REQ_QUERY){
|
||||||
printf("dport: %d\n", s_event->dport);
|
print_query(s_event);
|
||||||
printf("sport: %d\n", s_event->sport);
|
}
|
||||||
printf("qname: %s\n", s_event->qname);
|
if (s_event->req_type == REQ_ANSWER){
|
||||||
printf("Class: ");
|
int pos = 0;
|
||||||
mapClass(s_event->class);
|
//for(int i = 0; i < 32; i++)
|
||||||
printf("Type: ");
|
// printf("%d ", s_event->buf[i]);
|
||||||
mapType(s_event->type);
|
//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");
|
printf("\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int main(int argc, char *argv[]){
|
int main(int argc, char *argv[]){
|
||||||
@@ -233,8 +311,9 @@ 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("wlp0s20f3");
|
||||||
int sock = open_raw_sock("enx98e743c667fc");
|
//int sock = open_raw_sock("enx98e743c667fc");
|
||||||
|
int sock = open_raw_sock("lo");
|
||||||
printf("Socket: %d\n", sock);
|
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);
|
printf("Program fd: %d\n", prog_fd);
|
||||||
|
|||||||
+294
-55
@@ -2,6 +2,7 @@
|
|||||||
#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>
|
||||||
@@ -28,19 +29,45 @@ struct {
|
|||||||
__uint(max_entries, 256 * 1024 /* 256kb */);
|
__uint(max_entries, 256 * 1024 /* 256kb */);
|
||||||
} m_data SEC(".maps");
|
} m_data SEC(".maps");
|
||||||
|
|
||||||
/*
|
struct {
|
||||||
* This function get the query field and the return the length of it
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
*/
|
__uint(max_entries, 32768);
|
||||||
static size_t get_query(struct __sk_buff *skb, struct event *s_event, uint16_t *class, uint16_t *type, size_t tlen){
|
__type(key, uint16_t);
|
||||||
size_t len;
|
__type(value, struct dns_answer);
|
||||||
char buf[QNAME_SIZE] = {0};
|
} m_tid SEC(".maps");
|
||||||
int index = 0;
|
|
||||||
int qname_len = 0; // Full length of the qname field
|
|
||||||
char qname[QNAME_SIZE] = {0};
|
|
||||||
char *c;
|
|
||||||
|
|
||||||
|
static size_t get_labels2(struct __sk_buff *skb, size_t offset, struct event *s_event){
|
||||||
bpf_skb_load_bytes(skb, tlen, &buf, 41);
|
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;
|
c = buf;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -64,93 +91,302 @@ static size_t get_query(struct __sk_buff *skb, struct event *s_event, uint16_t *
|
|||||||
}
|
}
|
||||||
s_event->qname[--index] = '\0';
|
s_event->qname[--index] = '\0';
|
||||||
qname_len++; // For the null character
|
qname_len++; // For the null character
|
||||||
bpf_printk("%s (%d) %d", s_event->qname, index, qname_len);
|
bpf_printk("qname: %s", s_event->qname);
|
||||||
|
|
||||||
|
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_labels2(skb, offset, s_event);
|
||||||
|
|
||||||
// Get class and type
|
// Get class and type
|
||||||
len = qname_len;
|
len = qname_len;
|
||||||
bpf_skb_load_bytes(skb, tlen + qname_len, type, sizeof(uint16_t));
|
bpf_skb_load_bytes(skb, offset + qname_len, &type, sizeof(uint16_t));
|
||||||
|
s_event->type = ntohs(type);
|
||||||
len += 2;
|
len += 2;
|
||||||
bpf_skb_load_bytes(skb, tlen + qname_len + 2, class, sizeof(uint16_t));
|
bpf_skb_load_bytes(skb, offset + qname_len + 2, &class, sizeof(uint16_t));
|
||||||
|
s_event->class = ntohs(class);
|
||||||
len += 2;
|
len += 2;
|
||||||
|
|
||||||
return len;
|
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
|
* 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];
|
struct query_section dquery = {0};
|
||||||
uint16_t class, type;
|
|
||||||
// bpf_printk("udp len: %d", ntohs(udp.len));
|
/* 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;
|
||||||
|
|
||||||
|
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->saddr = ip.saddr;
|
s_event->client = ip.saddr;
|
||||||
|
|
||||||
/* Get DNS header */
|
s_event->tid = ntohs(dns.transactionID);
|
||||||
bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr), &dns, sizeof(struct dnshdr));
|
|
||||||
|
|
||||||
if (ntohs(dns.nbQuestions) == 0){
|
/* Get the query section */
|
||||||
bpf_ringbuf_discard(s_event, 0);
|
uint8_t tlen = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr);
|
||||||
return 0;
|
size_t query_len = get_query_section(skb, s_event, tlen);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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){
|
static void dnsanswer_old(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp, int dport, int sport){
|
||||||
return 0;
|
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
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Load dns header
|
||||||
|
if (bpf_skb_load_bytes(skb, offset, &dns, 12) < 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Get the answer */
|
||||||
|
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 = (void *)(long)skb->data;
|
||||||
//void *data_end = (void *)(long)skb->data_end;
|
//void *data_end = (void *)(long)skb->data_end;
|
||||||
//struct ethhdr *eth = data;
|
//struct ethhdr *eth2 = 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};
|
||||||
unsigned long long h_proto, p;
|
__u32 h_proto, p;
|
||||||
unsigned long long dport;
|
__u32 dport;
|
||||||
unsigned long long sport;
|
__u32 sport;
|
||||||
|
|
||||||
//if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end)
|
//if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end)
|
||||||
// return 0;
|
// 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, 12, &p, 2);
|
||||||
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;
|
||||||
@@ -173,6 +409,9 @@ int detect_dns(struct __sk_buff *skb) {
|
|||||||
|
|
||||||
bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr), &udp, sizeof(struct udphdr));
|
bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr), &udp, sizeof(struct udphdr));
|
||||||
|
|
||||||
|
if (udp.len == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
// Check if DNS port
|
// Check if DNS port
|
||||||
dport = ntohs(udp.dest);
|
dport = ntohs(udp.dest);
|
||||||
sport = ntohs(udp.source);
|
sport = ntohs(udp.source);
|
||||||
@@ -180,7 +419,7 @@ 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)
|
||||||
bpf_printk("Response");
|
dnsanswer(skb, ip, udp, dport, sport);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
+95583
-113731
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user