This commit is contained in:
geoffrey 2025-02-09 14:52:08 +01:00
parent 65a0f2447d
commit 8e9822d232
7 changed files with 94 additions and 183 deletions

2
.gitignore vendored

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

BIN
dns-trace

Binary file not shown.

@ -1,30 +0,0 @@
Feb 08 16:03:16 pc-geoffrey dns-trace: <info> Query;tid=68;192.168.1.37:53;class=IN;type=AAAA;
Feb 08 16:03:16 pc-geoffrey dns-trace: <info> Answer;tid=68;
Feb 08 16:03:23 pc-geoffrey dns-trace: <info> Query;tid=2da6;192.168.1.37:53;class=IN;type=AAAA;
Feb 08 16:03:23 pc-geoffrey dns-trace: <info> Answer;tid=2da6;
Feb 08 16:17:34 pc-geoffrey dns-trace: <info> Query;tid=4f3a;192.168.1.37:53;class=IN;type=A;
Feb 08 16:17:34 pc-geoffrey dns-trace: <info> Query;tid=af42;192.168.1.37:53;class=IN;type=AAAA;
Feb 08 16:17:34 pc-geoffrey dns-trace: <info> Answer;tid=4f3a;
Feb 08 16:18:25 pc-geoffrey dns-trace: <info> Query;tid=e29b;192.168.1.37:53;class=IN;type=A;
Feb 08 16:18:25 pc-geoffrey dns-trace: <info> Answer;tid=e29b;192.168.1.37:45247;
Feb 08 16:19:52 pc-geoffrey dns-trace: <info> Query;tid=a9ff;192.168.1.37:53;class=IN;type=AAAA;
Feb 08 16:19:52 pc-geoffrey dns-trace: <info> Answer;tid=a9ff;192.168.1.37:40040;
Feb 08 16:19:52 pc-geoffrey dns-trace: <info> Answer;tid=a9ff;192.168.1.37:40040;
Feb 08 16:19:52 pc-geoffrey dns-trace: <info> Answer;tid=a9ff;192.168.1.37:40040;
Feb 08 16:21:16 pc-geoffrey dns-trace: <info> Query;tid=b7c2;192.168.1.37:53;class=IN;type=AAAA;www.fortinet.com;
Feb 08 16:21:16 pc-geoffrey dns-trace: <info> Answer;tid=b7c2;192.168.1.37:51591;
Feb 08 16:21:16 pc-geoffrey dns-trace: <info> Answer;tid=b7c2;192.168.1.37:51591;
Feb 08 16:21:16 pc-geoffrey dns-trace: <info> Answer;tid=b7c2;192.168.1.37:51591;
Feb 08 16:21:44 pc-geoffrey dns-trace: <info> Query;tid=9f64;192.168.1.37:53;class=IN;type=A;domain=safebrowsing.googleapis.com;
Feb 08 16:21:44 pc-geoffrey dns-trace: <info> Answer;tid=9f64;192.168.1.37:52355;
Feb 08 16:21:44 pc-geoffrey dns-trace: <info> Query;tid=473f;192.168.1.37:53;class=IN;type=AAAA;domain=www.fortinet.com;
Feb 08 16:21:44 pc-geoffrey dns-trace: <info> Answer;tid=473f;192.168.1.37:59032;
Feb 08 16:21:44 pc-geoffrey dns-trace: <info> Answer;tid=473f;192.168.1.37:59032;
Feb 08 16:21:44 pc-geoffrey dns-trace: <info> Answer;tid=473f;192.168.1.37:59032;
Feb 08 16:21:51 pc-geoffrey dns-trace: <info> Query;tid=22c8;192.168.1.37:53;class=IN;type=AAAA;domain=www.fortinet.com;
Feb 08 16:21:51 pc-geoffrey dns-trace: <info> Answer;tid=22c8;192.168.1.37:40059;
Feb 08 16:21:51 pc-geoffrey dns-trace: <info> Answer;tid=22c8;192.168.1.37:40059;
Feb 08 16:21:51 pc-geoffrey dns-trace: <info> Answer;tid=22c8;192.168.1.37:40059;
Feb 08 16:21:52 pc-geoffrey dns-trace: <info> Query;tid=57f3;192.168.1.37:53;class=IN;type=A;domain=www.bucchino.org;
Feb 08 16:21:52 pc-geoffrey dns-trace: <info> Answer;tid=57f3;192.168.1.37:53594;
Feb 08 16:21:52 pc-geoffrey dns-trace: <info> Answer;tid=57f3;192.168.1.37:53594;

@ -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

@ -353,13 +353,99 @@ static void get_labels(unsigned char *buf, char *qname){
}
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);
}
}
@ -390,19 +476,7 @@ static void print_answer(struct event *s_event){
printf("%5s:%5d\t", inet_ntoa(*(struct in_addr*)&s_event->client), s_event->dport);
printf("%x\t", s_event->tid);
type = s_event->buf[pos++];
type |= s_event->buf[pos++] << 8;
class = s_event->buf[pos++];
class |= s_event->buf[pos++] << 8;
ttl = s_event->buf[pos++];
ttl |= s_event->buf[pos++] << 8;
ttl |= s_event->buf[pos++] << 16;
ttl |= s_event->buf[pos++] << 24;
size = s_event->buf[pos++];
size |= s_event->buf[pos++] << 8;
get_answer(s_event, &class, &type, &size, &ttl, &pos);
type = ntohs(type);
class = ntohs(class);

@ -16,14 +16,6 @@
#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 */);
@ -59,8 +51,6 @@ static size_t get_labels(struct __sk_buff *skb, size_t offset, struct event *s_e
}
s_event->qname[qname_len - 1] = '\0';
qname_len++;
// dquery->qname_len = qname_len;
// bpf_printk("qname: %s", s_event->qname);
// bpf_printk("qname len: %d", qname_len);
return qname_len;
}
@ -87,77 +77,6 @@ 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 ((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 ((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 ((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 ((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 ((offset += 2) >= MAX_UDP_PAYLOAD - sizeof(uint16_t))
return 0;
tlen += 2;
if (s_event->type == 1) { // -> A
bpf_skb_load_bytes(skb, tlen, s_event->buf + offset, sizeof(uint32_t));
}
if ((offset += ntohs(size)) >= MAX_UDP_PAYLOAD - sizeof(uint16_t))
return 0;
tlen += ntohs(size);
}
else {
// get_labels(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
*/
@ -202,46 +121,10 @@ 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){
//__u16 udplen = 0U;
// Check with ip.len
//const __u32 tot_len = ntohs(ip.tot_len);
// __u32 payload_len = (ntohs(ip.tot_len) - sizeof(struct iphdr) - sizeof(struct udphdr));
__u32 payload_len = (ntohs(ip.tot_len) - sizeof(struct iphdr) - sizeof(struct udphdr)) & 0xff;
if (payload_len > skb->len)
return;
bpf_printk("payload len %d", payload_len);
if (payload_len <= -1) {
bpf_printk("payload len %d", payload_len);
}
// Get udp len
//udplen = ntohs(udp.len);
//bpf_printk("udp len: %d", udplen);
//if (udplen <= 0)
// return 0;
/*if (offset + udplen > skb->len) {
bpf_printk("outbound");
plen = sizeof(struct dnshdr);
}*/
/*long err = bpf_skb_load_bytes(skb, offset, &buf, payload_len);
if(err < 0){
bpf_printk("failed");
// bpf_ringbuf_discard(s_event, 0);
return;
}*/
// Cast to dnshdr
// dns = (struct dnshdr*)buf;
}
/*
* This function read the dns answer section
* All the answer is store in the buf variable and handle in the user space
*/
static void dnsanswer(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp, int dport, int sport){
struct event *s_event;
struct dnshdr dns;
@ -297,11 +180,9 @@ static void dnsanswer(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp,
* otherwise, I have an issue with the eBPF verifier
*/
offset += sizeof(struct dnshdr) + query_len;
//while (index < tlen){
for (index = 0; index < tlen || index == MAX_UDP_PAYLOAD; index++) {
for (index = 0; index < tlen || index == MAX_UDP_PAYLOAD; index++)
bpf_skb_load_bytes(skb, offset + index, s_event->buf + index, 1);
// index++;
}
bpf_ringbuf_submit(s_event, 0);
}

Binary file not shown.