Update project

This commit is contained in:
geoffrey 2025-01-19 18:27:25 +01:00
parent 65258eb429
commit 9712681972
7 changed files with 70760 additions and 123148 deletions

BIN
dns-trace Executable file

Binary file not shown.

@ -233,8 +233,8 @@ int main(int argc, char *argv[]){
}
bpf_program__attach(programSkb);
//int sock = open_raw_sock("wlp0s20f3");
int sock = open_raw_sock("enx98e743c667fc");
int sock = open_raw_sock("wlp0s20f3");
//int sock = open_raw_sock("enx98e743c667fc");
printf("Socket: %d\n", sock);
int prog_fd = bpf_program__fd(programSkb);
printf("Program fd: %d\n", prog_fd);

@ -2,6 +2,7 @@
#define __TARGET_ARCH_x86
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include <linux/if_ether.h>
@ -36,11 +37,11 @@ struct {
} 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;
//size_t len;
char buf[256] = {0};
char *c;
int index = 0;
int qname_len = 0; // Full length of the qname field
size_t qname_len = 0; // Full length of the qname field
bpf_skb_load_bytes(skb, offset, &buf, 41);
c = buf;
@ -58,7 +59,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
return len;
return qname_len;
}
/*
@ -67,11 +68,11 @@ static size_t get_labels(struct __sk_buff *skb, size_t offset, size_t end, struc
//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[256] = {0};
int index = 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;
//char qname[QNAME_SIZE] = {0};
//char *c;
uint8_t flen = skb->len;
/*
We get the size for the buffer
@ -83,11 +84,11 @@ static size_t get_query_section(struct __sk_buff *skb, struct event *s_event, st
return 0;
//bpf_skb_load_bytes(skb, tlen, &buf, l);
bpf_skb_load_bytes(skb, offset, &buf, 41);
c = buf;
//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);
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,
@ -97,7 +98,7 @@ static size_t get_query_section(struct __sk_buff *skb, struct event *s_event, st
* 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') {
/*while (*(c++) != '\0') {
if(*c >= 'a' && *c <= 'z')
s_event->qname[index] = *c;
else if(*c >= 'A' && *c <= 'Z')
@ -108,7 +109,7 @@ static size_t get_query_section(struct __sk_buff *skb, struct event *s_event, st
qname_len++;
}
s_event->qname[--index] = '\0';
qname_len++; // For the null character
qname_len++; // For the null character*/
// bpf_printk("l: %d", l);
// Get class and type
@ -122,7 +123,7 @@ static size_t get_query_section(struct __sk_buff *skb, struct event *s_event, st
}
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
unsigned char buf[25] = {0}; // Need to be unsigned, otherwise, the result is fffff
if(bpf_skb_load_bytes(skb, tlen, &buf, 2) < 0)
return 0;
@ -204,33 +205,97 @@ static int dnsquery(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip, s
return 0;
}
/*
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){
struct event *s_event;
struct dnshdr dns = {0};
struct dnshdr *dns;
uint16_t tid = 0;
struct dns_answer s_dnsanswer;
struct query_section s_query = {0};
//struct dns_answer s_dnsanswer;
//struct query_section s_query = {0};
unsigned char buf[256] = {0};
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 || 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;
//__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 + ntohs(udp.len) > skb->len) {
bpf_printk("outbound");
//return 0;
plen = sizeof(struct dnshdr);
}
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;
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 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)){
if (ntohs(dns->nbQuestions) == 0 && ntohs(dns->nbAnswerRRs)){
bpf_ringbuf_discard(s_event, 0);
return 0;
}
/* Get the Transaction ID */
tid = ntohs(dns.transactionID);
tid = ntohs(dns->transactionID);
bpf_printk("tid: %x", tid);
/*
@ -251,7 +316,7 @@ static int dnsanswer(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip,
*/
/* Get the query response */
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) + sizeof(struct dnshdr);
uint16_t class, type;
// 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);
@ -259,11 +324,11 @@ static int dnsanswer(struct __sk_buff *skb, struct ethhdr eth, struct iphdr ip,
s_event->dport = dport;
s_event->sport = sport;
s_event->class = ntohs(class);
s_event->type = ntohs(type);
s_event->type = ntohs(type);*/
/* Get the answer */
tlen += query_len;
size_t answer_len = get_answer(skb, s_event, tlen);
//tlen += query_len;
//size_t answer_len = get_answer(skb, s_event, tlen);
bpf_ringbuf_submit(s_event, 0);
return 0;
@ -282,6 +347,8 @@ int detect_dns(struct __sk_buff *skb) {
//if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end)
// return 0;
if (skb->len < sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr))
return 0;
//bpf_skb_load_bytes(skb, 12, &p, 2);
bpf_skb_load_bytes(skb, 0, &eth, sizeof(struct ethhdr));
@ -305,6 +372,9 @@ int detect_dns(struct __sk_buff *skb) {
bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr), &udp, sizeof(struct udphdr));
if (udp.len == 0)
return 0;
// Check if DNS port
dport = ntohs(udp.dest);
sport = ntohs(udp.source);

381
src/dns-trace.ebpf.c_bck Normal file

@ -0,0 +1,381 @@
#define BPF_NO_GLOBAL_DATA
#define __TARGET_ARCH_x86
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include <linux/if_ether.h>
#include <linux/udp.h>
#include <linux/net.h>
#include <linux/socket.h>
#include <sys/socket.h>
#include <linux/net.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include "common.h"
/*
* Helper:
* Issue: invalid indirect read from stack R2 off
* Fix: check if all variables is initialised
* Issue: R1 invalid mem access 'inv'
* Fix: the value can be NULL
*/
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__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");
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};
char *c;
int index = 0;
size_t 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 qname_len;
}
/*
* 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){
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;
//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);
// Get class and type
len = qname_len;
bpf_skb_load_bytes(skb, offset + qname_len, &s_query->type, sizeof(uint16_t));
len += 2;
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[25] = {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
*/
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];
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);
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));
// 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;
}
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 */
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(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;
}
/*
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){
struct event *s_event;
//struct dnshdr dns = {0};
uint16_t tid = 0;
//struct dns_answer s_dnsanswer;
//struct query_section s_query = {0};
unsigned char buf[256] = {0};
uint32_t offset = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr);
uint32_t skblen = skb->len;
uint32_t l;
if (skblen < 1 || skblen == 0)
return 0;
bpf_printk("skblen: %d", skb->len);
if (skb->len <= -1) {
bpf_printk("skblen: %d", skb->len);
skblen = 12; // Get the 12 first bytes
}
bpf_printk("%d %d", skblen, offset);
if (skblen < offset)
return 0;
l = skblen - offset;
bpf_printk("size: %d %d", skb->len, l);
// if (bpf_skb_load_bytes_relative(skb, offset, &buf, l, BPF_HDR_START_MAC) < 0){
if (bpf_skb_load_bytes(skb, offset, &buf, l) < 0){
bpf_printk("Failed");
return 0;
}
s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0);
if (!s_event)
return 0;
/* Get Transaction ID */
//tid = (uint16_t)buf >> 8;
//bpf_printk("ttid: %x", ntohs(tid));
/* 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);
/*
* 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 */
/*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_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;
}
SEC("socket")
int detect_dns(struct __sk_buff *skb) {
//void *data = (void *)(long)skb->data;
//void *data_end = (void *)(long)skb->data_end;
//struct ethhdr *eth = data;
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;
//if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end)
// return 0;
//bpf_skb_load_bytes(skb, 12, &p, 2);
bpf_skb_load_bytes(skb, 0, &eth, sizeof(struct ethhdr));
p = eth.h_proto;
if (ntohs(p) != ETH_P_IP)
return 0;
// bpf_printk("ip: %d",ntohs(p));
//ip = (struct iphdr*)(data + sizeof(struct ethhdr));
bpf_skb_load_bytes(skb, sizeof(struct ethhdr), &ip, sizeof(struct iphdr));
//bpf_skb_load_bytes(data, sizeof(struct ethhdr), ip, sizeof(struct ip));
h_proto = ip.protocol;
// If not UDP packet
if (h_proto != 17)
return 0;
// bpf_printk("proto: %d", h_proto);
//udp = (struct udphdr*)(data + sizeof(struct ethhdr) + sizeof(struct iphdr));
bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr), &udp, sizeof(struct udphdr));
// Check if DNS port
dport = ntohs(udp.dest);
sport = ntohs(udp.source);
if (dport == 53)
dnsquery(skb, eth, ip, udp, dport, sport);
else if(sport == 53)
dnsanswer(skb, eth, ip, udp, dport, sport);
return 0;
}
/*SEC("xdp")
int detect_dns(struct xdp_md *ctx){
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
struct iphdr *ip;
struct udphdr *udp;
__u16 h_proto;
__u16 dport;
if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end)
return XDP_DROP;
ip = (struct iphdr*)(data + sizeof(struct ethhdr));
udp = (struct udphdr*)(data + sizeof(struct ethhdr) + sizeof(struct iphdr));
h_proto = ip->protocol;
// If not UDP packet
if (h_proto != 17)
return XDP_PASS;
// Check if DNS port
dport = udp->dest;
bpf_printk("Dport: %d", (dport));
return XDP_PASS;
}*/
char LICENSE[] SEC("license") = "GPL";

379
src/dns-trace.ebpf.c_bck2 Normal file

@ -0,0 +1,379 @@
#define BPF_NO_GLOBAL_DATA
#define __TARGET_ARCH_x86
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include <linux/if_ether.h>
#include <linux/udp.h>
#include <linux/net.h>
#include <linux/socket.h>
#include <sys/socket.h>
#include <linux/net.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include "common.h"
/*
* Helper:
* Issue: invalid indirect read from stack R2 off
* Fix: check if all variables is initialised
* Issue: R1 invalid mem access 'inv'
* Fix: the value can be NULL
*/
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__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");
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};
char *c;
int index = 0;
size_t 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 qname_len;
}
/*
* 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){
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;
//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);
// Get class and type
len = qname_len;
bpf_skb_load_bytes(skb, offset + qname_len, &s_query->type, sizeof(uint16_t));
len += 2;
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[25] = {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
*/
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];
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);
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));
// 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;
}
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 */
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(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;
}
/*
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){
struct event *s_event;
//struct dnshdr dns = {0};
uint16_t tid = 0;
//struct dns_answer s_dnsanswer;
//struct query_section s_query = {0};
unsigned char buf[256] = {0};
uint32_t offset = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr);
uint32_t skblen = skb->len;
uint32_t l;
if (skblen < 1 || skblen == 0)
return 0;
bpf_printk("skblen: %d", skb->len);
if (skb->len <= -1) {
bpf_printk("skblen: %d", skb->len);
skblen = 12; // Get the 12 first bytes
}
bpf_printk("%d %d", skblen, offset);
if (skblen < offset)
return 0;
l = skblen - offset;
bpf_printk("size: %d %d", skb->len, l);
// if (bpf_skb_load_bytes_relative(skb, offset, &buf, l, BPF_HDR_START_MAC) < 0){
if (bpf_skb_load_bytes(skb, offset, &buf, l) < 0){
bpf_printk("Failed");
return 0;
}
s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0);
if (!s_event)
return 0;
/* Get Transaction ID */
//tid = (uint16_t)buf >> 8;
//bpf_printk("ttid: %x", ntohs(tid));
/* 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);
/*
* 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 */
/*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_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;
}
SEC("socket")
int detect_dns(struct __sk_buff *skb) {
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
struct ethhdr *eth = data;
struct iphdr *ip;
struct udphdr *udp;
unsigned long long h_proto, p;
unsigned long long dport;
unsigned long long sport;
unsigned char buf[256];
if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end)
return 0;
p = eth->h_proto;
if (ntohs(p) != ETH_P_IP)
return 0;
//bpf_printk("ip: %d",ntohs(p));
//bpf_printk("s: %d", skb->data_end);
bpf_skb_load_bytes(skb, 0, &buf, 41);
ip = (struct iphdr*)(data + sizeof(struct ethhdr));
h_proto = ip->protocol;
// If not UDP packet
if (h_proto != 17)
return 0;
// bpf_printk("proto: %d", h_proto);
//udp = (struct udphdr*)(data + sizeof(struct ethhdr) + sizeof(struct iphdr));
/*bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr), &udp, sizeof(struct udphdr));
// Check if DNS port
dport = ntohs(udp.dest);
sport = ntohs(udp.source);
if (dport == 53)
dnsquery(skb, eth, ip, udp, dport, sport);
else if(sport == 53)
dnsanswer(skb, eth, ip, udp, dport, sport);*/
return 0;
}
/*SEC("xdp")
int detect_dns(struct xdp_md *ctx){
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
struct iphdr *ip;
struct udphdr *udp;
__u16 h_proto;
__u16 dport;
if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) > data_end)
return XDP_DROP;
ip = (struct iphdr*)(data + sizeof(struct ethhdr));
udp = (struct udphdr*)(data + sizeof(struct ethhdr) + sizeof(struct iphdr));
h_proto = ip->protocol;
// If not UDP packet
if (h_proto != 17)
return XDP_PASS;
// Check if DNS port
dport = udp->dest;
bpf_printk("Dport: %d", (dport));
return XDP_PASS;
}*/
char LICENSE[] SEC("license") = "GPL";

BIN
src/dns-trace.ebpf.o Normal file

Binary file not shown.

193018
src/vmlinux.h

File diff suppressed because it is too large Load Diff