diff --git a/.gitignore b/.gitignore index 79de871..bb8435c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ src/**.swp -src/**.log +**.log diff --git a/dns-trace b/dns-trace index b27ca85..e1e382c 100755 Binary files a/dns-trace and b/dns-trace differ diff --git a/dns-trace_2025-02-08.log b/dns-trace_2025-02-08.log deleted file mode 100644 index 6ff2ade..0000000 --- a/dns-trace_2025-02-08.log +++ /dev/null @@ -1,30 +0,0 @@ -Feb 08 16:03:16 pc-geoffrey dns-trace: Query;tid=68;192.168.1.37:53;class=IN;type=AAAA; -Feb 08 16:03:16 pc-geoffrey dns-trace: Answer;tid=68; -Feb 08 16:03:23 pc-geoffrey dns-trace: Query;tid=2da6;192.168.1.37:53;class=IN;type=AAAA; -Feb 08 16:03:23 pc-geoffrey dns-trace: Answer;tid=2da6; -Feb 08 16:17:34 pc-geoffrey dns-trace: Query;tid=4f3a;192.168.1.37:53;class=IN;type=A; -Feb 08 16:17:34 pc-geoffrey dns-trace: Query;tid=af42;192.168.1.37:53;class=IN;type=AAAA; -Feb 08 16:17:34 pc-geoffrey dns-trace: Answer;tid=4f3a; -Feb 08 16:18:25 pc-geoffrey dns-trace: Query;tid=e29b;192.168.1.37:53;class=IN;type=A; -Feb 08 16:18:25 pc-geoffrey dns-trace: Answer;tid=e29b;192.168.1.37:45247; -Feb 08 16:19:52 pc-geoffrey dns-trace: Query;tid=a9ff;192.168.1.37:53;class=IN;type=AAAA; -Feb 08 16:19:52 pc-geoffrey dns-trace: Answer;tid=a9ff;192.168.1.37:40040; -Feb 08 16:19:52 pc-geoffrey dns-trace: Answer;tid=a9ff;192.168.1.37:40040; -Feb 08 16:19:52 pc-geoffrey dns-trace: Answer;tid=a9ff;192.168.1.37:40040; -Feb 08 16:21:16 pc-geoffrey dns-trace: Query;tid=b7c2;192.168.1.37:53;class=IN;type=AAAA;www.fortinet.com; -Feb 08 16:21:16 pc-geoffrey dns-trace: Answer;tid=b7c2;192.168.1.37:51591; -Feb 08 16:21:16 pc-geoffrey dns-trace: Answer;tid=b7c2;192.168.1.37:51591; -Feb 08 16:21:16 pc-geoffrey dns-trace: Answer;tid=b7c2;192.168.1.37:51591; -Feb 08 16:21:44 pc-geoffrey dns-trace: 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: Answer;tid=9f64;192.168.1.37:52355; -Feb 08 16:21:44 pc-geoffrey dns-trace: 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: Answer;tid=473f;192.168.1.37:59032; -Feb 08 16:21:44 pc-geoffrey dns-trace: Answer;tid=473f;192.168.1.37:59032; -Feb 08 16:21:44 pc-geoffrey dns-trace: Answer;tid=473f;192.168.1.37:59032; -Feb 08 16:21:51 pc-geoffrey dns-trace: 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: Answer;tid=22c8;192.168.1.37:40059; -Feb 08 16:21:51 pc-geoffrey dns-trace: Answer;tid=22c8;192.168.1.37:40059; -Feb 08 16:21:51 pc-geoffrey dns-trace: Answer;tid=22c8;192.168.1.37:40059; -Feb 08 16:21:52 pc-geoffrey dns-trace: 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: Answer;tid=57f3;192.168.1.37:53594; -Feb 08 16:21:52 pc-geoffrey dns-trace: Answer;tid=57f3;192.168.1.37:53594; diff --git a/src/common.h b/src/common.h index 0d727ac..6526fc0 100644 --- a/src/common.h +++ b/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 diff --git a/src/dns-trace.c b/src/dns-trace.c index 03fba55..89c2b5b 100644 --- a/src/dns-trace.c +++ b/src/dns-trace.c @@ -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); diff --git a/src/dns-trace.ebpf.c b/src/dns-trace.ebpf.c index 06fba47..72efe01 100644 --- a/src/dns-trace.ebpf.c +++ b/src/dns-trace.ebpf.c @@ -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); } diff --git a/src/dns-trace.ebpf.o b/src/dns-trace.ebpf.o index 44a84e9..5e9e562 100644 Binary files a/src/dns-trace.ebpf.o and b/src/dns-trace.ebpf.o differ