diff --git a/dns-trace b/dns-trace index b9513f6..3cc428f 100755 Binary files a/dns-trace and b/dns-trace differ diff --git a/src/common.h b/src/common.h index 5c78665..a6ad143 100644 --- a/src/common.h +++ b/src/common.h @@ -3,6 +3,9 @@ #define QNAME_SIZE 128 +#define REQ_QUERY 0x00 +#define REQ_ANSWER 0x01 + struct dnshdr { uint16_t transactionID; uint16_t flags; @@ -22,6 +25,8 @@ struct event { uint32_t saddr; int dport; int sport; + uint16_t tid; + int req_type; char qname[QNAME_SIZE]; int class; int type; diff --git a/src/dns-trace.c b/src/dns-trace.c index a84b0a4..77ea100 100644 --- a/src/dns-trace.c +++ b/src/dns-trace.c @@ -101,6 +101,18 @@ static int open_raw_sock(const char *name) return sock; } +static void mapReqType(const int req){ + switch(req){ + case 0x00: + printf("Query\n"); + break; + case 0x01: + printf("Answer\n"); + break; + default: + printf("Unknown"); + }; +} static void mapClass(const int class){ switch(class){ case 1: @@ -182,6 +194,9 @@ int handle_event(void *ctx, void *data, size_t data_sz){ printf("IP: %s\n", inet_ntoa(*(struct in_addr*)&s_event->saddr)); 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); diff --git a/src/dns-trace.ebpf.c b/src/dns-trace.ebpf.c index 539bb70..ca05106 100644 --- a/src/dns-trace.ebpf.c +++ b/src/dns-trace.ebpf.c @@ -36,6 +36,27 @@ struct { __type(value, struct dns_answer); } m_tid SEC(".maps"); +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); + int pos = 1; + while (c != '\0') { + bpf_skb_load_bytes(skb, offset + 12 + 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) + break; + } + s_event->qname[qname_len - 1] = '\0'; + qname_len++; + 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}; @@ -46,6 +67,15 @@ static size_t get_labels(struct __sk_buff *skb, size_t offset, size_t end, struc bpf_skb_load_bytes(skb, offset, &buf, 41); c = buf; + /* + * 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; @@ -58,6 +88,7 @@ static size_t get_labels(struct __sk_buff *skb, size_t offset, size_t end, struc } s_event->qname[--index] = '\0'; qname_len++; // For the null character + bpf_printk("qname: %s", s_event->qname); return qname_len; } @@ -65,58 +96,20 @@ static size_t get_labels(struct __sk_buff *skb, size_t offset, size_t end, struc /* * 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){ +static size_t get_query_section(struct __sk_buff *skb, struct event *s_event, 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; + size_t qname_len = 0; // Full length of the qname field + uint16_t class, type; - //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); + qname_len = get_labels2(skb, offset, s_event); // Get class and type len = qname_len; - bpf_skb_load_bytes(skb, offset + qname_len, &s_query->type, sizeof(uint16_t)); + bpf_skb_load_bytes(skb, offset + 12 + qname_len, &type, sizeof(uint16_t)); + s_event->type = ntohs(type); len += 2; - bpf_skb_load_bytes(skb, offset + qname_len + 2, &s_query->class, sizeof(uint16_t)); + bpf_skb_load_bytes(skb, offset + 12 + qname_len + 2, &class, sizeof(uint16_t)); + s_event->class = ntohs(class); len += 2; return len; @@ -146,7 +139,6 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s 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); @@ -163,7 +155,7 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s 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"); + s_event->req_type = REQ_QUERY; else if(qr == 0x8000) bpf_printk("Response"); bpf_printk("Flags: %x %x", flags, qr); @@ -173,8 +165,7 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s return 0; } - bpf_printk("tid: %x", ntohs(dns.transactionID)); // Use as key map - bpf_printk("nb question: %d", ntohs(dns.nbQuestions)); + 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)); @@ -185,128 +176,105 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s //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); + /* 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); // 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; } +static void dnsanswer_old(struct __sk_buff *skb, struct iphdr ip, struct udphdr udp, int dport, int sport){ + 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 int dnsanswer(struct __sk_buff *skb, struct ethhdr eth, 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 = 0; - //struct dns_answer s_dnsanswer; - //struct query_section s_query = {0}; - unsigned char buf[256] = {0}; // Max dns domain name length + struct dnshdr dns; + uint16_t tid = 0U; uint32_t offset = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr); - __be16 udplen; - // uint32_t offset2 = sizeof(struct ethhdr) + sizeof(struct iphdr); - // bpf_printk("hdr: %d %d %d", sizeof(struct ethhdr), sizeof(struct iphdr), sizeof(struct udphdr)); // -> 14, 20, 8 - if (udp.len == 0) - return 0; - - // Get udp len - udplen = ntohs(udp.len); - - bpf_printk("udp len: %d", udplen); - /* Get DNS header */ - //bpf_skb_load_bytes(skb, offset, &dns, sizeof(struct dnshdr)); - - if (udplen < 0) - return 0; - - if (udplen > 256 || udplen < sizeof(struct dnshdr)) - udplen = sizeof(struct dnshdr); - //return 0; - - - //if (udplen == offset || udplen < offset) - // udplen = sizeof(struct dnshdr); - - //if (udplen > skb->len || udplen > offset) - // udplen = sizeof(struct dnshdr); - //return 0; - //if (udplen > offset) // Works - // return 0; - if (udplen >= offset && udplen <= skb->len){ - - } - bpf_printk("ok"); - if (udplen < offset) - //udplen = sizeof(struct udphdr); - udplen = sizeof(struct dnshdr); - //return 0; - - if (udplen > offset) - udplen = sizeof(struct dnshdr); - - - //__u32 plen = bpf_ntohs(udp.len) - 8; - uint32_t plen = udplen & 0xff; - bpf_printk("ok"); - if (plen < 0 || plen > skb->len) - //udplen = sizeof(struct udphdr); - return 0; - - bpf_printk("payload len: %d", plen); - bpf_printk("%d", skb->len); - bpf_printk("%d %d %d", offset, udplen, udplen - 8); - // bpf_printk("%d", offset + ntohs(udp.len) - 8); // -> we have 99 - - if (offset + udplen > skb->len) { - bpf_printk("outbound"); - //return 0; - plen = sizeof(struct dnshdr); - } - // plen = 57; - if(bpf_skb_load_bytes(skb, offset, &buf, plen) < 0){ - bpf_printk("failed"); - // bpf_ringbuf_discard(s_event, 0); - return 0; - } - - // Cast to dnshdr - dns = (struct dnshdr*)buf; + // Load dns header + if (bpf_skb_load_bytes(skb, offset, &dns, 12) < 0) + return; s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0); if (!s_event) - return 0; + return; // Check OpCode - uint16_t flags = ntohs(dns->flags); + 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 (qr == 0x0) // Query + s_event->req_type = REQ_QUERY; + else if(qr == 0x8000) // Response + s_event->req_type = REQ_ANSWER; - if (ntohs(dns->nbQuestions) == 0 && ntohs(dns->nbAnswerRRs)){ + if (ntohs(dns.nbQuestions) == 0 && ntohs(dns.nbAnswerRRs)){ bpf_ringbuf_discard(s_event, 0); - return 0; + return; } /* Get the Transaction ID */ - tid = ntohs(dns->transactionID); - bpf_printk("tid: %x", tid); - + s_event->tid = ntohs(dns.transactionID); + + /* Get the query section */ + size_t query_len = get_query_section(skb, s_event, offset); + + /* Get answer section */ + for (int i = 0; i < ntohs(dns.nbAnswerRRs); i++){ + + } /* * 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 @@ -339,8 +307,6 @@ static int dnsanswer(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, //tlen += query_len; //size_t answer_len = get_answer(skb, s_event, tlen); bpf_ringbuf_submit(s_event, 0); - - return 0; } /* @@ -355,9 +321,9 @@ int detect_dns(struct __sk_buff *skb) { 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; + __u32 h_proto, p; + __u32 dport; + __u32 sport; //if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end) // return 0; @@ -397,7 +363,7 @@ int detect_dns(struct __sk_buff *skb) { if (dport == 53) dnsquery(skb, eth, ip, udp, dport, sport); else if(sport == 53) - dnsanswer(skb, eth, ip, udp, dport, sport); + dnsanswer(skb, ip, udp, dport, sport); return 0; } diff --git a/src/dns-trace.ebpf.o b/src/dns-trace.ebpf.o index 0b7373e..ea3c133 100644 Binary files a/src/dns-trace.ebpf.o and b/src/dns-trace.ebpf.o differ diff --git a/test b/test new file mode 100755 index 0000000..b019766 Binary files /dev/null and b/test differ diff --git a/test.c b/test.c new file mode 100644 index 0000000..34d81cf --- /dev/null +++ b/test.c @@ -0,0 +1,14 @@ +#include + +int main(void){ + char buf[128] = "Hello world!"; + char t = 'a'; + //char *c = buf; + char *c = &t; + + printf("%c\n", *c); + *c = buf[0]; + printf("%c\n", *c); + + return 0; +}