First commit
This commit is contained in:
commit
fa6681095c
15
Makefile
Normal file
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
BIN
dns-trace
Executable file
Binary file not shown.
BIN
dns.pcap
Normal file
BIN
dns.pcap
Normal file
Binary file not shown.
6
exec.sh
Executable file
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
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
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
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, ð, 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
BIN
src/dns-trace.ebpf.o
Normal file
Binary file not shown.
0
src/skel.ebpf.h
Normal file
0
src/skel.ebpf.h
Normal file
96767
src/vmlinux.h
Normal file
96767
src/vmlinux.h
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user