diff --git a/dns-trace b/dns-trace deleted file mode 100755 index 42329c5..0000000 Binary files a/dns-trace and /dev/null differ diff --git a/dns4.pcap b/dns4.pcap new file mode 100644 index 0000000..32adcb5 Binary files /dev/null and b/dns4.pcap differ diff --git a/src/common.h b/src/common.h index 5af0e01..5c78665 100644 --- a/src/common.h +++ b/src/common.h @@ -28,6 +28,12 @@ struct event { char ans[32]; }; +struct query_section{ + char qname[QNAME_SIZE]; + int class; + int type; +}; + struct dns_answer { uint16_t tid; char qname[QNAME_SIZE]; diff --git a/src/dns-trace.ebpf.c b/src/dns-trace.ebpf.c index a3e46b8..c595cfd 100644 --- a/src/dns-trace.ebpf.c +++ b/src/dns-trace.ebpf.c @@ -35,22 +35,59 @@ struct { __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; + unsigned char buf[256]; + unsigned char *c; + int index = 0; + int 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 len; +} /* * This function get the query field and the return the length of it */ -static size_t get_query(struct __sk_buff *skb, struct event *s_event, uint16_t *class, uint16_t *type, size_t tlen){ +//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[QNAME_SIZE] = {0}; + 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, 41); + //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) + // 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, @@ -72,18 +109,35 @@ static size_t get_query(struct __sk_buff *skb, struct event *s_event, uint16_t * } s_event->qname[--index] = '\0'; qname_len++; // For the null character - bpf_printk("%s (%d) %d", s_event->qname, index, qname_len); + // bpf_printk("l: %d", l); // Get class and type len = qname_len; - bpf_skb_load_bytes(skb, tlen + qname_len, type, sizeof(uint16_t)); + bpf_skb_load_bytes(skb, offset + qname_len, &s_query->type, sizeof(uint16_t)); len += 2; - bpf_skb_load_bytes(skb, tlen + qname_len + 2, class, sizeof(uint16_t)); + 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[256] = {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 */ @@ -91,8 +145,7 @@ 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]; - uint16_t class, type; - //struct dns_answer s_dnsanswer; + 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); @@ -105,6 +158,15 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s /* 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; @@ -123,14 +185,15 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s /* Get the query structure */ - size_t tlen = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr); - size_t query_len = get_query(skb, s_event, &class, &type, tlen); + 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(class); - s_event->type = ntohs(type); + 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"); @@ -146,14 +209,26 @@ static int dnsanswer(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, struct dnshdr dns = {0}; uint16_t tid = 0; struct dns_answer s_dnsanswer; - - /* Get DNS header */ - bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr), &dns, sizeof(struct dnshdr)); + struct query_section s_query = {0}; s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0); if (!s_event) return 0; + /* 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); @@ -176,13 +251,19 @@ static int dnsanswer(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, */ /* Get the query response */ - size_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) + sizeof(struct dnshdr); uint16_t class, type; - size_t query_len = get_query(skb, s_event, &class, &type, tlen); + // 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; diff --git a/src/dns-trace.ebpf.o b/src/dns-trace.ebpf.o deleted file mode 100644 index bdd94b2..0000000 Binary files a/src/dns-trace.ebpf.o and /dev/null differ