First commit

This commit is contained in:
geoffrey 2025-01-14 20:12:13 +01:00
commit fa6681095c
10 changed files with 97173 additions and 0 deletions

15
Makefile Normal file

@ -0,0 +1,15 @@
GCC=gcc
CL=clang-11
CFLAGS=-Wall
LIBS=-lbpf
all: dns-trace.ebpf.o dns-trace
dns-trace.ebpf.o: src/dns-trace.ebpf.c
$(CL) -g -O2 -target bpf -c src/dns-trace.ebpf.c -o src/dns-trace.ebpf.o
dns-trace: src/dns-trace.c
$(GCC) $(CFLAGS) src/dns-trace.c -o dns-trace $(LIBS)
clean:
rm -rf src/*.o && rm dns-trace

BIN
dns-trace Executable file

Binary file not shown.

BIN
dns.pcap Normal file

Binary file not shown.

6
exec.sh Executable file

@ -0,0 +1,6 @@
#!/usr/bin/sh
sudo bpftool btf dump file /sys/kernel/btf/vmlinux format c > src/vmlinux.h
make clean
make all && sudo ./dns-trace

27
src/common.h Normal file

@ -0,0 +1,27 @@
#ifndef H_COMMON
#define H_COMMON
struct dnshdr {
uint16_t transactionID;
uint16_t flags;
uint16_t nbQuestions;
uint16_t nbAnswerRRs;
uint16_t nbAuthorityRRs;
uint16_t nbAdditionalRRs;
};
struct dns_query {
char *name;
//char name[112];
uint16_t type;
uint16_t class;
struct dns_query *next;
};
struct event {
int dport;
int sport;
};
#endif

181
src/dns-trace.c Normal file

@ -0,0 +1,181 @@
#include <stdio.h>
#include <stdlib.h>
#include <linux/bpf.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <linux/perf_event.h>
#include <argp.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <net/if.h> /* if_nametoindex */
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <arpa/inet.h>
#include "common.h"
static struct arguments arguments;
static int running = 1;
struct arguments {
char *filename;
int to_output;
};
/*
* Functions for arguments
* https://www.gnu.org/software/libc/manual/html_node/Argp-Example-3.html
*/
static char doc[] = "DNS Trace usage:";
static char args_doc[] = "ARG1 ARG2";
static error_t parse_opts(int key, char *arg, struct argp_state *state){
struct arguments *arguments = state->input;
switch(key){
case 'f':
arguments->filename = arg;
arguments->to_output = 0;
break;
case 'o':
arguments->to_output = 1;
break;
case ARGP_KEY_ARG:
break;
case ARGP_KEY_END:
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
struct arguments parse_args(int argc, char *argv[]){
static const struct argp_option opts[] = {
{"filename", 'f', "FILENAME", 0, "Save result to logs"},
{"to-output", 'o', NULL, 0, "Print to output"},
{NULL, 'h', NULL, OPTION_HIDDEN, "help"},
{},
};
struct arguments arguments;
arguments.filename = NULL;
arguments.to_output = 1;
static struct argp argp = {opts, parse_opts, args_doc, doc};
argp_parse(&argp, argc, argv, 0, 0, &arguments);
return arguments;
}
/* End functions arguments */
static void signalHandler(int signum){
running = 0;
}
static int open_raw_sock(const char *name)
{
struct sockaddr_ll sll;
int sock;
sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL));
if (sock < 0) {
printf("cannot create raw socket\n");
return -1;
}
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = if_nametoindex(name);
sll.sll_protocol = htons(ETH_P_ALL);
if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
//printf("bind to %s: %s\n", name, strerror(errno));
printf("Failed to bind the interface %s\n", name);
close(sock);
return -1;
}
return sock;
}
int handle_event(void *ctx, void *data, size_t data_sz){
struct event *s_event = (struct event*)data;
printf("dport: %d\n", s_event->dport);
printf("sport: %d\n\n", s_event->sport);
return 0;
}
int main(int argc, char *argv[]){
const char *fileObj = "src/dns-trace.ebpf.o";
struct bpf_object *obj;
struct bpf_program *programSkb;
struct ring_buffer *rb;
int err;
int fd_map_data;
arguments = parse_args(argc, argv); // Parsing arguments
signal(SIGINT, signalHandler);
/* Open and load our eBPF object */
obj = bpf_object__open_file(fileObj, NULL);
if (!obj){
printf("Failed to open the file\n");
return -1;
}
err = bpf_object__load(obj);
if (err){
printf("Failed to load object\n");
return -1;
}
/* Retrieving fd of maps */
fd_map_data = bpf_object__find_map_fd_by_name(obj, "m_data");
if (!fd_map_data){
printf("Failed to find the fd map data\n");
bpf_object__close(obj);
return -1;
}
/* Retrieving our programs */
programSkb = bpf_object__find_program_by_name(obj, "detect_dns");
if (!programSkb){
printf("Failed to find program\n");
bpf_object__close(obj);
return -1;
}
bpf_program__attach(programSkb);
int sock = open_raw_sock("wlp0s20f3");
printf("Socket: %d\n", sock);
int prog_fd = bpf_program__fd(programSkb);
printf("Program fd: %d\n", prog_fd);
setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(int));
/* Start the ringbuffer */
rb = ring_buffer__new(fd_map_data, handle_event, NULL, NULL);
if (!rb){
printf("Failed to create the ringbuf\n");
bpf_object__close(obj);
return -1;
}
while(running){
err = ring_buffer__poll(rb, 100 /* timeout, ms */);
if (err == -EINTR){
printf("Failed to get the ringbuf\n");
running = 0;
break;
}
}
ring_buffer__free(rb);
bpf_object__close(obj);
return 0;
}

177
src/dns-trace.ebpf.c Normal file

@ -0,0 +1,177 @@
#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");
/*
* 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, int tlen){
struct event s_event = {0};
struct dnshdr dns = {0};
size_t dlen = 0;
size_t qlen = 0; // Question len
s_event.dport = dport;
s_event.sport = sport;
bpf_printk("port: %d %d", s_event.dport, s_event.sport);
//if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr) > data_end)
// return 0;
//dns = (struct dnshdr *)(data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr));
//tlen += sizeof(struct dnshdr);
bpf_printk("udp len: %d", ntohs(udp.len));
bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr), &dns, sizeof(struct dnshdr));
if (ntohs(dns.nbQuestions) == 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);
char buf[128] = {0};
bpf_skb_load_bytes(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct dnshdr), &buf, 41);
int index = 0;
char c = 0;
char t[15];
while (index != 15){
c = buf[index];
if (c == '\0')
break;
t[index] = c;
if (c == 0x3)
t[index] = '.';
index++;
}
bpf_printk("%s", t);
// https://docs.cilium.io/en/stable/reference-guides/bpf/progtypes/
if (bpf_ringbuf_output(&m_data, &s_event, sizeof(struct event), 0) == 0)
return 0;
//s_event = bpf_ringbuf_reserve(&m_data, sizeof(*s_event), 0);
//if (!s_event)
// return 0;
//bpf_ringbuf_discard(s_event, 0);
//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;
size_t tlen = 0;
//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;
tlen += sizeof(struct ethhdr);
if (ntohs(p) != ETH_P_IP)
return 0;
tlen += sizeof(struct iphdr);
// 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);
tlen += sizeof(struct udphdr);
//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)
// return 0;
if (dport == 53)
dnsquery(skb, eth, ip, udp, dport, sport, tlen);
else if(sport == 53)
bpf_printk("Response");
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.

0
src/skel.ebpf.h Normal file

96767
src/vmlinux.h Normal file

File diff suppressed because it is too large Load Diff