From 2cb21c1f55868273692b420471091eda154abeac Mon Sep 17 00:00:00 2001 From: gbucchino Date: Wed, 15 Jan 2025 16:26:53 +0100 Subject: [PATCH] Get answer --- src/common.h | 8 +++++++ src/dns-trace.ebpf.c | 55 +++++++++++++++++++++++++++++++++++++++++-- src/dns-trace.ebpf.o | Bin 23008 -> 28800 bytes 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/common.h b/src/common.h index 58b056f..5af0e01 100644 --- a/src/common.h +++ b/src/common.h @@ -25,6 +25,14 @@ struct event { char qname[QNAME_SIZE]; int class; int type; + char ans[32]; +}; + +struct dns_answer { + uint16_t tid; + char qname[QNAME_SIZE]; + char ip[32]; + int ttl; }; #endif diff --git a/src/dns-trace.ebpf.c b/src/dns-trace.ebpf.c index 0d0ef93..a3e46b8 100644 --- a/src/dns-trace.ebpf.c +++ b/src/dns-trace.ebpf.c @@ -28,6 +28,14 @@ struct { __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"); + + /* * This function get the query field and the return the length of it */ @@ -84,6 +92,7 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s struct dnshdr dns = {0}; char saddr[32]; uint16_t class, type; + //struct dns_answer s_dnsanswer; // bpf_printk("udp len: %d", ntohs(udp.len)); s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0); @@ -101,7 +110,6 @@ 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)); @@ -134,6 +142,49 @@ 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){ + struct event *s_event; + 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)); + + s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0); + if (!s_event) + 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 */ + size_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); + bpf_printk("answer qname: %s", s_event->qname); + + /* Get the answer */ + + bpf_ringbuf_submit(s_event, 0); + return 0; } SEC("socket") @@ -180,7 +231,7 @@ int detect_dns(struct __sk_buff *skb) { if (dport == 53) dnsquery(skb, eth, ip, udp, dport, sport); else if(sport == 53) - bpf_printk("Response"); + dnsanswer(skb, eth, ip, udp, dport, sport); return 0; } diff --git a/src/dns-trace.ebpf.o b/src/dns-trace.ebpf.o index 5b9f491214111c263da90a84c012f3f99a31c4a8..bdd94b2ab710c583415747b83669e57fee595a4e 100644 GIT binary patch literal 28800 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