From 1051b50931697f4036db1bf308fcc6a1723f2fa6 Mon Sep 17 00:00:00 2001 From: gbucchino Date: Wed, 15 Jan 2025 14:12:46 +0100 Subject: [PATCH] Get query from function --- src/common.h | 5 +-- src/dns-trace.ebpf.c | 100 ++++++++++++++++++++++++------------------- src/dns-trace.ebpf.o | Bin 22800 -> 23008 bytes 3 files changed, 59 insertions(+), 46 deletions(-) diff --git a/src/common.h b/src/common.h index f80168d..58b056f 100644 --- a/src/common.h +++ b/src/common.h @@ -12,12 +12,11 @@ struct dnshdr { uint16_t nbAdditionalRRs; }; -struct dns_query { +/*struct dns_query { char *name; uint16_t type; uint16_t class; -// struct dns_query *next; -}; +};*/ struct event { uint32_t saddr; diff --git a/src/dns-trace.ebpf.c b/src/dns-trace.ebpf.c index 44beb2d..0d0ef93 100644 --- a/src/dns-trace.ebpf.c +++ b/src/dns-trace.ebpf.c @@ -29,52 +29,19 @@ struct { } m_data SEC(".maps"); /* - * https://datatracker.ietf.org/doc/html/rfc1035 + * This function get the query field and the return the length of it */ -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); - 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)); - - 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("%s", dquery.name); - //bpf_printk("class: %d", ntohs(dquery.class)); - //bpf_printk("type: %d", ntohs(dquery.type)); - // 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); +static size_t get_query(struct __sk_buff *skb, struct event *s_event, uint16_t *class, uint16_t *type, size_t tlen){ + size_t len; char buf[QNAME_SIZE] = {0}; - - - bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr), &buf, 41); int index = 0; int qname_len = 0; // Full length of the qname field - char *c = buf; char qname[QNAME_SIZE] = {0}; + char *c; + + bpf_skb_load_bytes(skb, tlen, &buf, 41); + c = buf; /* * The qname is composed by a the number of bytes then follow by the label @@ -100,9 +67,56 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s bpf_printk("%s (%d) %d", s_event->qname, index, qname_len); // Get class and type + len = qname_len; + bpf_skb_load_bytes(skb, tlen + qname_len, type, sizeof(uint16_t)); + len += 2; + bpf_skb_load_bytes(skb, tlen + qname_len + 2, class, sizeof(uint16_t)); + len += 2; + + 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]; uint16_t class, type; - bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr) + qname_len, &type, sizeof(uint16_t)); - bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr) + qname_len + 2, &class, sizeof(uint16_t)); + // 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)); + + 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 */ + 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); // https://docs.cilium.io/en/stable/reference-guides/bpf/progtypes/ s_event->dport = dport; @@ -120,7 +134,7 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s } static int dnsanswer(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, struct udphdr udp, int dport, int sport){ - + return 0; } SEC("socket") int detect_dns(struct __sk_buff *skb) { diff --git a/src/dns-trace.ebpf.o b/src/dns-trace.ebpf.o index 22ff216e903f7685c5d541334fa8af6396247502..5b9f491214111c263da90a84c012f3f99a31c4a8 100644 GIT binary patch literal 23008 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 literal 22800 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 CwhIaX