diff --git a/dns-trace b/dns-trace index 3cc428f..cf30db0 100755 Binary files a/dns-trace and b/dns-trace differ diff --git a/dns10.pcap b/dns10.pcap new file mode 100644 index 0000000..1214258 Binary files /dev/null and b/dns10.pcap differ diff --git a/src/common.h b/src/common.h index a6ad143..62f6a3f 100644 --- a/src/common.h +++ b/src/common.h @@ -22,7 +22,7 @@ struct dnshdr { };*/ struct event { - uint32_t saddr; + uint32_t client; int dport; int sport; uint16_t tid; @@ -30,7 +30,7 @@ struct event { char qname[QNAME_SIZE]; int class; int type; - char ans[32]; + uint32_t ans; }; struct query_section{ diff --git a/src/dns-trace.c b/src/dns-trace.c index 77ea100..543e060 100644 --- a/src/dns-trace.c +++ b/src/dns-trace.c @@ -191,7 +191,7 @@ static void mapType(const int type){ int handle_event(void *ctx, void *data, size_t data_sz){ struct event *s_event = (struct event*)data; - printf("IP: %s\n", inet_ntoa(*(struct in_addr*)&s_event->saddr)); + printf("IP: %s\n", inet_ntoa(*(struct in_addr*)&s_event->client)); printf("dport: %d\n", s_event->dport); printf("sport: %d\n", s_event->sport); printf("Transaction ID: %x\n", s_event->tid); @@ -202,7 +202,12 @@ int handle_event(void *ctx, void *data, size_t data_sz){ mapClass(s_event->class); printf("Type: "); mapType(s_event->type); + + if (s_event->req_type == REQ_ANSWER) + printf("Data: %s\n", inet_ntoa(*(struct in_addr*)&s_event->ans)); + printf("\n"); + return 0; } int main(int argc, char *argv[]){ diff --git a/src/dns-trace.ebpf.c b/src/dns-trace.ebpf.c index ca05106..3023a03 100644 --- a/src/dns-trace.ebpf.c +++ b/src/dns-trace.ebpf.c @@ -39,22 +39,24 @@ struct { static size_t get_labels2(struct __sk_buff *skb, size_t offset, struct event *s_event){ char c; int qname_len = 0; - bpf_skb_load_bytes(skb, offset + sizeof(struct dnshdr), &c, 1); + 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 + 12 + pos++, &c, 1); + 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) + if (pos == 128 || c == '\0') break; } s_event->qname[qname_len - 1] = '\0'; qname_len++; - bpf_printk("qname len: %d", 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){ @@ -101,14 +103,15 @@ static size_t get_query_section(struct __sk_buff *skb, struct event *s_event, ui 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 len = qname_len; - bpf_skb_load_bytes(skb, offset + 12 + 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; - bpf_skb_load_bytes(skb, offset + 12 + 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; @@ -118,16 +121,60 @@ static size_t get_answer(struct __sk_buff *skb, struct event *s_event, size_t tl size_t len = 0; unsigned char buf[25] = {0}; // Need to be unsigned, otherwise, the result is fffff + // Get the 2 first bytes to identify if it's a message compression or not + bpf_printk("offset: %d", tlen); if(bpf_skb_load_bytes(skb, tlen, &buf, 2) < 0) return 0; + + tlen += 2; // Which is 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 it's the pointer and 0x00c is the position in the DNS header + * 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){ - bpf_printk("Pointer to %x", buf[1]); + /* + * I cannot read the labels, because the eBPF verifier considerate as a infinity loop + */ + // bpf_printk("new offset: %d", sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + buf[1]); + // get_labels2(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + 12, s_event); + + /* + * 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 + uint16_t type, class; + uint32_t ttl; + bpf_printk("offset: %d", tlen); + bpf_skb_load_bytes(skb, tlen, &type, sizeof(uint16_t)); + bpf_printk("type: %d", ntohs(type)); + tlen += 2; + bpf_printk("offset: %d", tlen); + bpf_skb_load_bytes(skb, tlen, &class, sizeof(uint16_t)); + tlen += 4; + bpf_printk("offset: %d", tlen); + bpf_printk("class %d", ntohs(class)); + // Get ttl + bpf_skb_load_bytes(skb, tlen, &ttl, sizeof(uint32_t)); + tlen += 2; + bpf_printk("offset: %d", tlen); + bpf_printk("ttl: %d", ntohs(ttl)); + + // Get data size + uint16_t size; + bpf_skb_load_bytes(skb, tlen, &size, sizeof(uint16_t)); + bpf_printk("size: %d", ntohs(size)); + tlen += 2; + + uint32_t data; + bpf_skb_load_bytes(skb, tlen, &data, sizeof(uint32_t)); + s_event->ans = data; + } + else { + // get_labels2(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr), s_event); } return len; @@ -138,7 +185,6 @@ static size_t get_answer(struct __sk_buff *skb, struct event *s_event, size_t tl 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 dnshdr dns = {0}; - char saddr[32]; // bpf_printk("udp len: %d", ntohs(udp.len)); s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0); @@ -146,7 +192,7 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s return 0; /* Get IP header */ - s_event->saddr = ip.saddr; + s_event->client = ip.saddr; /* Get DNS header */ bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr), &dns, sizeof(struct dnshdr)); @@ -165,19 +211,9 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s return 0; } - s_event->tid = ntohs(dns.transactionID); // Use as key map - - //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); - + s_event->tid = ntohs(dns.transactionID); /* Get the query section */ - // uint8_t tlen = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr); uint8_t tlen = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr); size_t query_len = get_query_section(skb, s_event, tlen); @@ -238,7 +274,7 @@ 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){ +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; @@ -252,6 +288,9 @@ static void dnsanswer(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp if (!s_event) return; + /* Get IP header */ + s_event->client = ip.daddr; + // Check OpCode uint16_t flags = ntohs(dns.flags); uint16_t qr = flags & 0xF000; // Get the QR code: 0 -> query, 1 -> response @@ -260,11 +299,14 @@ static void dnsanswer(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp else if(qr == 0x8000) // Response s_event->req_type = REQ_ANSWER; - if (ntohs(dns.nbQuestions) == 0 && ntohs(dns.nbAnswerRRs)){ + if (ntohs(dns.nbQuestions) == 0){ bpf_ringbuf_discard(s_event, 0); return; } + s_event->dport = dport; + s_event->sport = sport; + /* Get the Transaction ID */ s_event->tid = ntohs(dns.transactionID); @@ -272,7 +314,26 @@ static void dnsanswer(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp size_t query_len = get_query_section(skb, s_event, offset); /* Get answer section */ - for (int i = 0; i < ntohs(dns.nbAnswerRRs); i++){ + uint16_t ans = ntohs(dns.nbAnswerRRs); + if (ans < 0){ + bpf_ringbuf_discard(s_event, 0); + return; + } + + if (ntohs(dns.nbAnswerRRs) > 0){ + /* + * We get a least the 5 last answer + * In the RFC 1035 (https://datatracker.ietf.org/doc/html/rfc1035#section-4.2.1) the max udp payload is 512 bytes + * The program limit te size of the answer + */ + offset += sizeof(struct dnshdr) + query_len; // For the pos in the answer section in the skb + for (uint16_t i = 0; i < ans; i++){ + get_answer(skb, s_event, offset); + if (i == ans || i == 5) + break; + } + } + if (ntohs(dns.nbAuthorityRRs) > 0){ } /* @@ -292,20 +353,7 @@ static void dnsanswer(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp 3 - dans le get query et get answer, on push dans le ring buffer et tout est store dans le struct event */ - /* Get the query response */ - /*uint8_t tlen = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr); - uint16_t class, type; - // size_t query_len = get_query_section(skb, s_event, &class, &type, tlen); - size_t query_len = get_query_section(skb, s_event, &s_query, tlen); - bpf_printk("answer qname: %s", s_event->qname); - s_event->dport = dport; - s_event->sport = sport; - s_event->class = ntohs(class); - s_event->type = ntohs(type);*/ - /* Get the answer */ - //tlen += query_len; - //size_t answer_len = get_answer(skb, s_event, tlen); bpf_ringbuf_submit(s_event, 0); } diff --git a/src/dns-trace.ebpf.c_bck b/src/dns-trace.ebpf.c_bck deleted file mode 100644 index ca9a122..0000000 --- a/src/dns-trace.ebpf.c_bck +++ /dev/null @@ -1,381 +0,0 @@ -#define BPF_NO_GLOBAL_DATA -#define __TARGET_ARCH_x86 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 */); -} m_data SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 32768); // pid_max -> https://linux.die.net/man/5/proc - __type(key, uint16_t); - __type(value, struct dns_answer); -} m_tid SEC(".maps"); - -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; - - while (*(c++) != '\0') { - if(*c >= 'a' && *c <= 'z') - s_event->qname[index] = *c; - else if(*c >= 'A' && *c <= 'Z') - s_event->qname[index] = *c; - else - s_event->qname[index] = '.'; - index++; - qname_len++; - } - s_event->qname[--index] = '\0'; - qname_len++; // For the null character - - 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, uint16_t *class, uint16_t *type, uint8_t tlen){ -static size_t get_query_section(struct __sk_buff *skb, struct event *s_event, struct query_section *s_query, uint8_t offset){ - size_t len; - //char buf[256] = {0}; - //int index = 0; - int qname_len = 0; // Full length of the qname field - //char qname[QNAME_SIZE] = {0}; - //char *c; - uint8_t flen = skb->len; - /* - We get the size for the buffer - We substract the full size of the buffer (skb->len) with the sizes of each headers (eth + ip + udp + dns header) - */ - uint8_t l = flen - offset; - - if (l < 0) - return 0; - - //bpf_skb_load_bytes(skb, tlen, &buf, l); - //bpf_skb_load_bytes(skb, offset, &buf, 41); - //c = buf; - - // get_labels(struct __sk_buff *skb, size_t offset, size_t end, struct event *s_event, struct query_section *s_query) - qname_len = get_labels(skb, offset, 41, s_event, s_query); - /* - * The qname is composed by a the number of bytes then follow by the label - * For instance, for the qname www.bucchino.org, - * the first byte is the number of byte, here, it's 3, then we have www (in hex) - * Then, we have the byte of 8 and follow by the label bucchino (size 8) - * And to finish, we have 3 follow by org and we finish with the \0 character - * For instance, the result is: - * 03 77 77 77 08 62 75 63 63 68 69 6e 6f 03 6f 72 67 00 - */ - /*while (*(c++) != '\0') { - if(*c >= 'a' && *c <= 'z') - s_event->qname[index] = *c; - else if(*c >= 'A' && *c <= 'Z') - s_event->qname[index] = *c; - else - s_event->qname[index] = '.'; - index++; - qname_len++; - } - s_event->qname[--index] = '\0'; - qname_len++; // For the null character*/ - // bpf_printk("l: %d", l); - - // Get class and type - len = qname_len; - bpf_skb_load_bytes(skb, offset + qname_len, &s_query->type, sizeof(uint16_t)); - len += 2; - bpf_skb_load_bytes(skb, offset + qname_len + 2, &s_query->class, sizeof(uint16_t)); - len += 2; - - return len; -} -static size_t get_answer(struct __sk_buff *skb, struct event *s_event, size_t tlen){ - size_t len = 0; - unsigned char buf[25] = {0}; // Need to be unsigned, otherwise, the result is fffff - - if(bpf_skb_load_bytes(skb, tlen, &buf, 2) < 0) - return 0; - /* - * 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 it's the pointer and 0x00c is the position in the DNS header - */ - if (buf[0] == 0xc0){ - bpf_printk("Pointer to %x", buf[1]); - } - - return len; -} -/* - * 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){ - struct event *s_event; - struct dnshdr dns = {0}; - char saddr[32]; - struct query_section s_query = {0}; - // bpf_printk("udp len: %d", ntohs(udp.len)); - - s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0); - if (!s_event) - return 0; - - /* Get IP header */ - s_event->saddr = ip.saddr; - - /* 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 flags = ntohs(dns.flags); - uint16_t qr = flags & 0xF000; // Get the QR code: 0 -> query, 1 -> response - if (qr == 0x0) - bpf_printk("Query"); - else if(qr == 0x8000) - bpf_printk("Response"); - bpf_printk("Flags: %x %x", flags, qr); - - if (ntohs(dns.nbQuestions) == 0){ - bpf_ringbuf_discard(s_event, 0); - 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 */ - uint8_t tlen = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr); - // size_t query_len = get_query_section(skb, s_event, &class, &type, tlen); - size_t query_len = get_query_section(skb, s_event, &s_query, tlen); - - // https://docs.cilium.io/en/stable/reference-guides/bpf/progtypes/ - s_event->dport = dport; - s_event->sport = sport; - s_event->class = ntohs(s_query.class); - s_event->type = ntohs(s_query.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); - - return 0; -} - -/* -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 int dnsanswer(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, struct udphdr udp, int dport, int sport){ - struct event *s_event; - //struct dnshdr dns = {0}; - uint16_t tid = 0; - //struct dns_answer s_dnsanswer; - //struct query_section s_query = {0}; - unsigned char buf[256] = {0}; - uint32_t offset = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr); - uint32_t skblen = skb->len; - uint32_t l; - - if (skblen < 1 || skblen == 0) - return 0; - - bpf_printk("skblen: %d", skb->len); - if (skb->len <= -1) { - bpf_printk("skblen: %d", skb->len); - skblen = 12; // Get the 12 first bytes - } - - bpf_printk("%d %d", skblen, offset); - if (skblen < offset) - return 0; - l = skblen - offset; - - - - bpf_printk("size: %d %d", skb->len, l); - // if (bpf_skb_load_bytes_relative(skb, offset, &buf, l, BPF_HDR_START_MAC) < 0){ - if (bpf_skb_load_bytes(skb, offset, &buf, l) < 0){ - bpf_printk("Failed"); - return 0; - } - s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0); - if (!s_event) - return 0; - /* Get Transaction ID */ - //tid = (uint16_t)buf >> 8; - - //bpf_printk("ttid: %x", ntohs(tid)); - - /* 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 flags = ntohs(dns.flags); - uint16_t qr = flags & 0xF000; // Get the QR code: 0 -> query, 1 -> response - if (qr == 0x0){} // Query - else if(qr == 0x8000){} // Response - - if (ntohs(dns.nbQuestions) == 0 && ntohs(dns.nbAnswerRRs)){ - bpf_ringbuf_discard(s_event, 0); - return 0; - }*/ - - /* Get the Transaction ID */ - //tid = ntohs(dns.transactionID); - //bpf_printk("tid: %x", tid); - - /* - * 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 query response */ - /*uint8_t tlen = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr); - uint16_t class, type; - // size_t query_len = get_query_section(skb, s_event, &class, &type, tlen); - size_t query_len = get_query_section(skb, s_event, &s_query, tlen); - bpf_printk("answer qname: %s", s_event->qname); - s_event->dport = dport; - s_event->sport = sport; - s_event->class = ntohs(class); - s_event->type = ntohs(type);*/ - - /* Get the answer */ - //tlen += query_len; - //size_t answer_len = get_answer(skb, s_event, tlen); - bpf_ringbuf_submit(s_event, 0); - - return 0; -} -SEC("socket") -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 iphdr ip = {0}; - struct udphdr udp = {0}; - unsigned long long h_proto, p; - unsigned long long dport; - unsigned long long sport; - - //if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end) - // return 0; - - //bpf_skb_load_bytes(skb, 12, &p, 2); - bpf_skb_load_bytes(skb, 0, ð, sizeof(struct ethhdr)); - p = eth.h_proto; - if (ntohs(p) != ETH_P_IP) - 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(data, sizeof(struct ethhdr), ip, sizeof(struct ip)); - h_proto = ip.protocol; - // If not UDP packet - if (h_proto != 17) - return 0; - - // bpf_printk("proto: %d", h_proto); - //udp = (struct udphdr*)(data + sizeof(struct ethhdr) + sizeof(struct iphdr)); - - bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr), &udp, sizeof(struct udphdr)); - - // Check if DNS port - dport = ntohs(udp.dest); - sport = ntohs(udp.source); - - if (dport == 53) - dnsquery(skb, eth, ip, udp, dport, sport); - else if(sport == 53) - dnsanswer(skb, eth, ip, udp, dport, sport); - - 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"; diff --git a/src/dns-trace.ebpf.c_bck2 b/src/dns-trace.ebpf.c_bck2 deleted file mode 100644 index 6c32116..0000000 --- a/src/dns-trace.ebpf.c_bck2 +++ /dev/null @@ -1,379 +0,0 @@ -#define BPF_NO_GLOBAL_DATA -#define __TARGET_ARCH_x86 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 */); -} m_data SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 32768); // pid_max -> https://linux.die.net/man/5/proc - __type(key, uint16_t); - __type(value, struct dns_answer); -} m_tid SEC(".maps"); - -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; - - while (*(c++) != '\0') { - if(*c >= 'a' && *c <= 'z') - s_event->qname[index] = *c; - else if(*c >= 'A' && *c <= 'Z') - s_event->qname[index] = *c; - else - s_event->qname[index] = '.'; - index++; - qname_len++; - } - s_event->qname[--index] = '\0'; - qname_len++; // For the null character - - 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, uint16_t *class, uint16_t *type, uint8_t tlen){ -static size_t get_query_section(struct __sk_buff *skb, struct event *s_event, struct query_section *s_query, uint8_t offset){ - size_t len; - //char buf[256] = {0}; - //int index = 0; - int qname_len = 0; // Full length of the qname field - //char qname[QNAME_SIZE] = {0}; - //char *c; - uint8_t flen = skb->len; - /* - We get the size for the buffer - We substract the full size of the buffer (skb->len) with the sizes of each headers (eth + ip + udp + dns header) - */ - uint8_t l = flen - offset; - - if (l < 0) - return 0; - - //bpf_skb_load_bytes(skb, tlen, &buf, l); - //bpf_skb_load_bytes(skb, offset, &buf, 41); - //c = buf; - - // get_labels(struct __sk_buff *skb, size_t offset, size_t end, struct event *s_event, struct query_section *s_query) - qname_len = get_labels(skb, offset, 41, s_event, s_query); - /* - * The qname is composed by a the number of bytes then follow by the label - * For instance, for the qname www.bucchino.org, - * the first byte is the number of byte, here, it's 3, then we have www (in hex) - * Then, we have the byte of 8 and follow by the label bucchino (size 8) - * And to finish, we have 3 follow by org and we finish with the \0 character - * For instance, the result is: - * 03 77 77 77 08 62 75 63 63 68 69 6e 6f 03 6f 72 67 00 - */ - /*while (*(c++) != '\0') { - if(*c >= 'a' && *c <= 'z') - s_event->qname[index] = *c; - else if(*c >= 'A' && *c <= 'Z') - s_event->qname[index] = *c; - else - s_event->qname[index] = '.'; - index++; - qname_len++; - } - s_event->qname[--index] = '\0'; - qname_len++; // For the null character*/ - // bpf_printk("l: %d", l); - - // Get class and type - len = qname_len; - bpf_skb_load_bytes(skb, offset + qname_len, &s_query->type, sizeof(uint16_t)); - len += 2; - bpf_skb_load_bytes(skb, offset + qname_len + 2, &s_query->class, sizeof(uint16_t)); - len += 2; - - return len; -} -static size_t get_answer(struct __sk_buff *skb, struct event *s_event, size_t tlen){ - size_t len = 0; - unsigned char buf[25] = {0}; // Need to be unsigned, otherwise, the result is fffff - - if(bpf_skb_load_bytes(skb, tlen, &buf, 2) < 0) - return 0; - /* - * 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 it's the pointer and 0x00c is the position in the DNS header - */ - if (buf[0] == 0xc0){ - bpf_printk("Pointer to %x", buf[1]); - } - - return len; -} -/* - * 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){ - struct event *s_event; - struct dnshdr dns = {0}; - char saddr[32]; - struct query_section s_query = {0}; - // bpf_printk("udp len: %d", ntohs(udp.len)); - - s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0); - if (!s_event) - return 0; - - /* Get IP header */ - s_event->saddr = ip.saddr; - - /* 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 flags = ntohs(dns.flags); - uint16_t qr = flags & 0xF000; // Get the QR code: 0 -> query, 1 -> response - if (qr == 0x0) - bpf_printk("Query"); - else if(qr == 0x8000) - bpf_printk("Response"); - bpf_printk("Flags: %x %x", flags, qr); - - if (ntohs(dns.nbQuestions) == 0){ - bpf_ringbuf_discard(s_event, 0); - 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 */ - uint8_t tlen = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr); - // size_t query_len = get_query_section(skb, s_event, &class, &type, tlen); - size_t query_len = get_query_section(skb, s_event, &s_query, tlen); - - // https://docs.cilium.io/en/stable/reference-guides/bpf/progtypes/ - s_event->dport = dport; - s_event->sport = sport; - s_event->class = ntohs(s_query.class); - s_event->type = ntohs(s_query.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); - - return 0; -} - -/* -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 int dnsanswer(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, struct udphdr udp, int dport, int sport){ - struct event *s_event; - //struct dnshdr dns = {0}; - uint16_t tid = 0; - //struct dns_answer s_dnsanswer; - //struct query_section s_query = {0}; - unsigned char buf[256] = {0}; - uint32_t offset = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr); - uint32_t skblen = skb->len; - uint32_t l; - - if (skblen < 1 || skblen == 0) - return 0; - - bpf_printk("skblen: %d", skb->len); - if (skb->len <= -1) { - bpf_printk("skblen: %d", skb->len); - skblen = 12; // Get the 12 first bytes - } - - bpf_printk("%d %d", skblen, offset); - if (skblen < offset) - return 0; - l = skblen - offset; - - - - bpf_printk("size: %d %d", skb->len, l); - // if (bpf_skb_load_bytes_relative(skb, offset, &buf, l, BPF_HDR_START_MAC) < 0){ - if (bpf_skb_load_bytes(skb, offset, &buf, l) < 0){ - bpf_printk("Failed"); - return 0; - } - s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0); - if (!s_event) - return 0; - /* Get Transaction ID */ - //tid = (uint16_t)buf >> 8; - - //bpf_printk("ttid: %x", ntohs(tid)); - - /* 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 flags = ntohs(dns.flags); - uint16_t qr = flags & 0xF000; // Get the QR code: 0 -> query, 1 -> response - if (qr == 0x0){} // Query - else if(qr == 0x8000){} // Response - - if (ntohs(dns.nbQuestions) == 0 && ntohs(dns.nbAnswerRRs)){ - bpf_ringbuf_discard(s_event, 0); - return 0; - }*/ - - /* Get the Transaction ID */ - //tid = ntohs(dns.transactionID); - //bpf_printk("tid: %x", tid); - - /* - * 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 query response */ - /*uint8_t tlen = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr); - uint16_t class, type; - // size_t query_len = get_query_section(skb, s_event, &class, &type, tlen); - size_t query_len = get_query_section(skb, s_event, &s_query, tlen); - bpf_printk("answer qname: %s", s_event->qname); - s_event->dport = dport; - s_event->sport = sport; - s_event->class = ntohs(class); - s_event->type = ntohs(type);*/ - - /* Get the answer */ - //tlen += query_len; - //size_t answer_len = get_answer(skb, s_event, tlen); - bpf_ringbuf_submit(s_event, 0); - - return 0; -} -SEC("socket") -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 iphdr *ip; - struct udphdr *udp; - unsigned long long h_proto, p; - unsigned long long dport; - unsigned long long sport; - unsigned char buf[256]; - - if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end) - return 0; - - p = eth->h_proto; - if (ntohs(p) != ETH_P_IP) - return 0; - - //bpf_printk("ip: %d",ntohs(p)); - //bpf_printk("s: %d", skb->data_end); - bpf_skb_load_bytes(skb, 0, &buf, 41); - - ip = (struct iphdr*)(data + sizeof(struct ethhdr)); - - h_proto = ip->protocol; - // If not UDP packet - if (h_proto != 17) - return 0; - - // bpf_printk("proto: %d", h_proto); - //udp = (struct udphdr*)(data + sizeof(struct ethhdr) + sizeof(struct iphdr)); - - /*bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr), &udp, sizeof(struct udphdr)); - - // Check if DNS port - dport = ntohs(udp.dest); - sport = ntohs(udp.source); - - if (dport == 53) - dnsquery(skb, eth, ip, udp, dport, sport); - else if(sport == 53) - dnsanswer(skb, eth, ip, udp, dport, sport);*/ - - 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"; diff --git a/src/dns-trace.ebpf.o b/src/dns-trace.ebpf.o index ea3c133..bbbb9e0 100644 Binary files a/src/dns-trace.ebpf.o and b/src/dns-trace.ebpf.o differ diff --git a/src/skel.ebpf.h b/src/skel.ebpf.h deleted file mode 100644 index e69de29..0000000