From 08ece6a46e7f4c0e83e25713e7a53341729b780f Mon Sep 17 00:00:00 2001 From: geoffrey Date: Tue, 21 Jan 2025 21:04:27 +0100 Subject: [PATCH] Update project --- dns-trace | Bin 18584 -> 18624 bytes src/common.h | 5 + src/dns-trace.c | 15 +++ src/dns-trace.ebpf.c | 264 +++++++++++++++++++------------------------ src/dns-trace.ebpf.o | Bin 32568 -> 29352 bytes test | Bin 0 -> 16608 bytes test.c | 14 +++ 7 files changed, 149 insertions(+), 149 deletions(-) create mode 100755 test create mode 100644 test.c diff --git a/dns-trace b/dns-trace index b9513f6a31012e87e3a5064db0b1e39ee763a9db..3cc428f4eef65401d5043d330e8d2cbf368fc73f 100755 GIT binary patch delta 2994 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 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 0b7373e6957620619d1931e99a75f6ab7efd20ff..ea3c133390cd922d379065e14a88ae5fb1a64d37 100644 GIT binary patch literal 29352 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 Ym#EBluvI4F8QoBp<8!64XVw${7m=qE(*OVf literal 32568 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 diff --git a/test b/test new file mode 100755 index 0000000000000000000000000000000000000000..b0197663eb0608086edf361a3b259183b7c0d795 GIT binary patch literal 16608 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 + +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; +}