diff --git a/dns-trace b/dns-trace index 52e97db..46ae51c 100755 Binary files a/dns-trace and b/dns-trace differ diff --git a/src/common.h b/src/common.h index 147b458..34ebbf3 100644 --- a/src/common.h +++ b/src/common.h @@ -6,6 +6,10 @@ #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 { uint16_t transactionID; uint16_t flags; @@ -15,12 +19,6 @@ struct dnshdr { uint16_t nbAdditionalRRs; }; -/*struct dns_query { - char *name; - uint16_t type; - uint16_t class; -};*/ - struct event { uint32_t client; int dport; @@ -30,9 +28,8 @@ struct event { char qname[QNAME_SIZE]; uint16_t class; uint16_t type; - uint32_t ans[5]; uint16_t numAns; - uint32_t ttl; + unsigned char buf[MAX_UDP_PAYLOAD]; // On stocke la data au format size + data }; struct query_section{ @@ -43,9 +40,10 @@ struct query_section{ }; struct dns_answer { - char qname[QNAME_SIZE]; - char ip[32]; - int ttl; + 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 786f1e9..4566159 100644 --- a/src/dns-trace.c +++ b/src/dns-trace.c @@ -148,6 +148,11 @@ static char *mapType(const int type){ 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){ case 1: strncpy(tmp, "A", 2); @@ -197,6 +202,9 @@ static char *mapType(const int type){ case 16: strncpy(tmp, "TXT", 4); break; + case 28: + strncpy(tmp, "AAAA", 5); + break; default: strncpy(tmp, "Unknown", 8); break; @@ -204,8 +212,7 @@ static char *mapType(const int type){ return tmp; } -int handle_event(void *ctx, void *data, size_t data_sz){ - struct event *s_event = (struct event*)data; +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); @@ -224,28 +231,43 @@ int handle_event(void *ctx, void *data, size_t data_sz){ printf("%-5s", type); free(type); +} +int handle_event(void *ctx, void *data, size_t data_sz){ + struct event *s_event = (struct event*)data; + if (s_event->req_type == REQ_QUERY){ + print_query(s_event); + } if (s_event->req_type == REQ_ANSWER){ - for (int i = 0; i < s_event->numAns; i++) + int pos = 0; + for(int i = 0; i < 32; i++) + printf("%d ", s_event->buf[i]); + 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 = ntohs(ttl2); + 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("%d %d %d", s_event->ttl, ntohs(ttl2), ntohs(size2)); + printf("%s (%d)%5d", inet_ntoa(*(struct in_addr*)&ip), type2, ttl2); + } + if (type2 == 28){ // -> AAAA + + } + printf("\n"); + pos += 2; + printf("%d\n", pos); + } + /*for (int i = 0; i < s_event->numAns; i++) printf("%s ", inet_ntoa(*(struct in_addr*)&s_event->ans[i])); - printf("%5d", s_event->ttl); + printf("%5d", s_event->ttl);*/ } - - /*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); - printf("Request type: "); - mapReqType(s_event->req_type); - printf("qname: %s\n", s_event->qname); - printf("Class: "); - 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; diff --git a/src/dns-trace.ebpf.c b/src/dns-trace.ebpf.c index 1dc72cd..91000e9 100644 --- a/src/dns-trace.ebpf.c +++ b/src/dns-trace.ebpf.c @@ -31,7 +31,7 @@ struct { struct { __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 32768); // pid_max -> https://linux.die.net/man/5/proc + __uint(max_entries, 32768); __type(key, uint16_t); __type(value, struct dns_answer); } m_tid SEC(".maps"); @@ -118,16 +118,15 @@ static size_t get_query_section(struct __sk_buff *skb, struct event *s_event, ui return len; } -static size_t get_answer(struct __sk_buff *skb, struct event *s_event, size_t tlen, int index){ - size_t len = 0; - unsigned char buf[25] = {0}; // Need to be unsigned, otherwise, the result is fffff +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 - 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 + bpf_printk("tlen: %d", tlen); + tlen += 2; /* * 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), @@ -138,8 +137,6 @@ static size_t get_answer(struct __sk_buff *skb, struct event *s_event, size_t tl /* * 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: @@ -147,45 +144,54 @@ static size_t get_answer(struct __sk_buff *skb, struct event *s_event, size_t tl */ // 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)); + if ((void*)(offset) >= MAX_UDP_PAYLOAD - sizeof(uint16_t)) + return 0; + bpf_printk("offset: %d", offset); + 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; - // bpf_printk("offset: %d", tlen); - bpf_skb_load_bytes(skb, tlen, &class, sizeof(uint16_t)); + offset += 2; + + if ((void*)(offset += 2) >= MAX_UDP_PAYLOAD - sizeof(uint16_t)) + return 0; + if(bpf_skb_load_bytes(skb, tlen, s_event->buf + offset, sizeof(uint16_t)) < 0) + return 0; tlen += 4; - // bpf_printk("offset: %d", tlen); + // Get ttl - bpf_skb_load_bytes(skb, tlen, &ttl, sizeof(uint32_t)); + 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 += 2; - // bpf_printk("offset: %d", tlen); - s_event->ttl = ntohs(ttl); // Get data size uint16_t size; bpf_skb_load_bytes(skb, tlen, &size, sizeof(uint16_t)); - // bpf_printk("size: %d", ntohs(size)); + 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; - // if class is A, we push to an ipv4 map - - // if class is AAAA, we push to an ipv6 map - - // if class is SOa, we push to an soa map - - // etc... - uint32_t data; - bpf_skb_load_bytes(skb, tlen, &data, sizeof(uint32_t)); - s_event->ans[index] = data; + if (s_event->type == 1) { // -> A + bpf_skb_load_bytes(skb, tlen, s_event->buf + offset, sizeof(uint32_t)); + } + //offset += ntohs(size); + tlen += ntohs(size); + //tlen += 2; + //offset += 2; // For the 2 first bytes (message compression) + //if ((void*)(offset += ntohs(size)) >= MAX_UDP_PAYLOAD - ntohs(size)) + // return 0; + //if ((void*)(offset += 2) >= MAX_UDP_PAYLOAD - sizeof(uint16_t)) + // return 0; } else { // get_labels2(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr), s_event); } - - return len; + bpf_printk("tlen: %d", tlen); + return offset; } /* * https://datatracker.ietf.org/doc/html/rfc1035 @@ -320,21 +326,22 @@ static void dnsanswer(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp, return; } - if (ntohs(dns.nbAnswerRRs) > 0){ + 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 te size of the answer + * The program limit size of the answer */ offset += sizeof(struct dnshdr) + query_len; // For the pos in the answer section in the skb - uint16_t i; - for (i = 0; i < ans; i++){ - get_answer(skb, s_event, offset, i); + 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; // For eBPF verifier, to be sure we leave the loop - if (i == ans || i == 5) + if (i == ans || i == 5 || offset_ans >= 512) break; } - s_event->numAns = i; + s_event->numAns = ans; } if (ntohs(dns.nbAuthorityRRs) > 0){ diff --git a/src/dns-trace.ebpf.o b/src/dns-trace.ebpf.o index c242db0..e6bf9db 100644 Binary files a/src/dns-trace.ebpf.o and b/src/dns-trace.ebpf.o differ diff --git a/test b/test index b019766..3554f9c 100755 Binary files a/test and b/test differ