(DDI User Group 2022)
Created: 2022-06-23 Thu 16:02
Carsten Strotmann
DNS(SEC)/DANE/DHCP/IPv6 Trainer und Helfer
RIPE/IETF
tcpdump und Wiresharktcpdump in dieses Beispiel)
tcpdump kann angewiesen werden den BPF Quellcode des tcpdump
Filters auszugeben:
# tcpdump -d port 53 and host 1.1.1.1 Warning: assuming Ethernet (000) ldh [12] (001) jeq #0x86dd jt 19 jf 2 (002) jeq #0x800 jt 3 jf 19 (003) ldb [23] (004) jeq #0x84 jt 7 jf 5 (005) jeq #0x6 jt 7 jf 6 (006) jeq #0x11 jt 7 jf 19 (007) ldh [20] (008) jset #0x1fff jt 19 jf 9 (009) ldxb 4*([14]&0xf) (010) ldh [x + 14] (011) jeq #0x35 jt 14 jf 12 (012) ldh [x + 16] (013) jeq #0x35 jt 14 jf 19 (014) ld [26] (015) jeq #0x1010101 jt 18 jf 16 (016) ld [30] (017) jeq #0x1010101 jt 18 jf 19 (018) ret #262144 (019) ret #0
bpftrace
syscount# syscount-bpfcc -p `pgrep named` -i 10 Tracing syscalls, printing top 10... Ctrl+C to quit. [07:34:19] SYSCALL COUNT futex 547 getpid 121 sendto 113 read 56 write 31 epoll_wait 31 openat 23 close 20 epoll_ctl 20 recvmsg 20
# capable-bpfcc | grep named 07:36:17 0 29378 (named) 24 CAP_SYS_RESOURCE 1 07:36:17 0 29378 (named) 24 CAP_SYS_RESOURCE 1 07:36:17 0 29378 (named) 12 CAP_NET_ADMIN 1 07:36:17 0 29378 (named) 21 CAP_SYS_ADMIN 1 07:36:17 0 29378 named 6 CAP_SETGID 1 07:36:17 0 29378 named 6 CAP_SETGID 1 07:36:17 0 29378 named 7 CAP_SETUID 1 07:36:17 109 29378 named 24 CAP_SYS_RESOURCE 1
bpftrace ist eine kleine Skripting-Sprace ähnlich wie awk oder dtrace
bpftrace Programme binden sich an eBPF-Probes und führen
Funktionen aus, wann immer ein System-Ereignis gemeldet wird
(systemcall, function-call)bpftrace hat Hilfs-Strukturen eingebaut um direkt mit eBPF
Datanstrukturen arbeiten zu könnenbpftrace erlaubt es eBPF Programme im Vergleich zu BCC kompakter
zu schreibenbpftrace Beispielprogrammegethostlatency misst die Latenz der
client-seitigen DNS Namensauflösung durch Systemaufrufe wie
getaddrinfo oder gethostbyname# gethostlatency-bpfcc TIME PID COMM LATms HOST 10:21:58 19183 ping 143.22 example.org 10:22:18 19184 ssh 0.03 host.example.de 10:22:18 19184 ssh 60.59 host.example.de 10:22:35 19185 ping 23.44 isc.org 10:22:49 19186 ping 4459.72 yahoo.co.kr
netqtop - Gibt Statistiken über die Warteschlangen einer
Netzwerkschnittstelle aus. Mit diesem Programm können Informationen
bei der Überlastung einer Netzwerkschnittstelle gesammelt werden.# netqtop-bpfcc -n eth0 -i 10 Mon Nov 15 07:43:29 2021 TX QueueID avg_size [0, 64) [64, 512) [512, 2K) [2K, 16K) [16K, 64K) 0 297.82 2 48 1 4 0 Total 297.82 2 48 1 4 0 RX QueueID avg_size [0, 64) [64, 512) [512, 2K) [2K, 16K) [16K, 64K) 0 70.95 43 34 0 0 0 Total 70.95 43 34 0 0 0 -----------------------------------------------------------------------------
# tcptracer-bpfcc -p $(pgrep named) Tracing TCP established connections. Ctrl-C to end. T PID COMM IP SADDR DADDR SPORT DPORT C 29404 isc-net-0000 4 127.0.0.1 127.0.0.1 41555 953 A 29378 isc-socket-0 4 127.0.0.1 127.0.0.1 953 41555 X 29404 isc-socket-0 4 127.0.0.1 127.0.0.1 41555 953 X 29378 isc-socket-0 4 127.0.0.1 127.0.0.1 953 41555 C 29378 isc-net-0000 4 46.101.109.138 192.33.4.12 43555 53 C 29378 isc-net-0000 4 46.101.109.138 192.33.4.12 33751 53 X 29378 isc-socket-0 4 46.101.109.138 192.33.4.12 43555 53 X 29378 isc-socket-0 4 46.101.109.138 192.33.4.12 33751 53 C 29378 isc-net-0000 4 46.101.109.138 193.0.14.129 38145 53 C 29378 isc-net-0000 4 46.101.109.138 192.33.14.30 40905 53 X 29378 isc-socket-0 4 46.101.109.138 193.0.14.129 38145 53 X 29378 isc-socket-0 4 46.101.109.138 192.33.14.30 40905 53
tcpconnlat gibt die Latenz einer TCP-basierten Verbindung aus,
hier eine ausgehende DNS Anfrage über TCP eines BIND 9 resolvers
(in Beispiel eine Abfrage von microsoft.com txt, wobei die
Antwort zu groß für ein 1232 Byte UDP Paket ist)
isc-net-0000 ist der interne Name des BIND 9 Threads# tcpconnlat-bpfcc PID COMM IP SADDR DADDR DPORT LAT(ms) 29378 isc-net-0000 4 46.101.109.138 193.0.14.129 53 37.50 29378 isc-net-0000 4 46.101.109.138 192.52.178.30 53 14.01 29378 isc-net-0000 4 46.101.109.138 199.9.14.201 53 8.48 29378 isc-net-0000 4 46.101.109.138 192.42.93.30 53 1.90 29378 isc-net-0000 4 46.101.109.138 40.90.4.205 53 14.27 29378 isc-net-0000 4 46.101.109.138 199.254.48.1 53 19.21 29378 isc-net-0000 4 46.101.109.138 192.48.79.30 53 7.66 29378 isc-net-0000 4 46.101.109.138 192.41.162.30 53 7.97 29396 isc-net-0000 4 127.0.0.1 127.0.0.1 53 0.06
udplife ist ein bpftrace um die UDP Roundtrip-Time (hier DNS
round trip time) einer UDP Kommunikation auszugeben (Programm von
Brendan Gregg, siehe Links)# udplife.bt Attaching 8 probes... PID COMM LADDR LPORT RADDR RPORT TX_B RX_B MS 29378 isc-net-00 46.101.109.138 0 199.19.57.1 16503 48 420 268 29378 isc-net-00 46.101.109.138 0 51.75.79.143 81 49 43 13 29378 isc-net-00 46.101.109.138 0 199.6.1.52 16452 48 408 24 29378 isc-net-00 46.101.109.138 0 199.249.120.1 81 44 10 9 29378 isc-net-00 46.101.109.138 0 199.254.31.1 32891 64 30 273 29378 isc-net-00 46.101.109.138 0 65.22.6.1 32891 64 46 266
zone "dnslab.org" {
type forward;
forwarders { 1.1.1.1; 8.8.8.8; };
};
bpftrace Skript erstellen um BIND 9 DNS
Forwarding-Entscheidungen auszugebendns_fwdtable_find in /lib/dns/forward.c. Das schaut
erfolgversprechend aus:
dns_fwdtable_find nimmt als Eingangsparameter einen
Domain-Namen und liefert den Wert 0 wenn der Namen via Forwarding
aufgelöst werden muss, und einen Wert > 0 wenn Forwarding nicht
verwendet wird
bpftrace one-liner gibt uns die Information ob diese
Funktion für diese Aufgabe benutzbar ist:
bpftrace -e 'uretprobe:/lib/x86_64-linux-gnu/libdns-9.16.22-Debian.so:dns_fwdtable_find { print(retval) }'
dns_fwdtable_find
übergeben wird, beim Eintritt in die Funktion speichernretval) auf den Wert Null (0)
prüfen und den Domain Namen ausgeben wenn Forwarding benutzt wirddns_name_t übergeben
dns_name_t. Das 2te Feld ist ein unsigned
char * ndata, dies scheint der Domain-Name zu seindns_name_t befindet sich in der
Datei lib/dns/include/dns/name.h

bpftrace beutzt eine der Programmiersprache C ähnliche Syntax,
daher können wir die Definition der Datenstruktur aus dem BIND 9
Quellcode direkt in das bpftrace Skript importieren
isc_buffer_t wird für unser
Skript nicht benötigt und da diese Felder keine eingebauten
Datentypen beschreiben kommentieren wir diese aus:
#!/usr/bin/bpftrace
struct dns_name {
unsigned int magic;
unsigned char *ndata;
unsigned int length;
unsigned int labels;
unsigned int attributes;
unsigned char *offsets;
// isc_buffer_t *buffer;
// ISC_LINK(dns_name_t) link;
// ISC_LIST(dns_rdataset_t) list;
};
[...]
BEGIN Pseudo-Probe wird beim Start des Skriptes aktiv und
gibt eine Nachricht auf das Terminal aus um dem Benutzer darüber zu
informieren das das Skript erfolgreich gestartet wurde
[...]
BEGIN
{
print("Waiting for forward decision...\n");
}
[...]
uprobe (User-Space Entry-Probe)dns_fwdtable_find in
der dynamischen Bibliotheks-Datei
/lib/x86_64-linux-gnu/libdns-9.16.22-Debian.soarg1) wird in ein struct
dns_name gecastet und das Feld ndata referenziert@dns_name[tid]
(indiziert mit der Thread ID (tid) des laufenden BIND 9 Threads
im Prozess) gespeichert
[...]
uprobe:/lib/x86_64-linux-gnu/libdns-9.16.22-Debian.so:dns_fwdtable_find
{
@dns_name[tid] = ((struct dns_name *)arg1)->ndata
}
[...]
uretprobe - User-space
Funktion Return Probe)
0 (Domain Name muss über
Forwarding aufgelöst werden) wird der Wert der Variable
@dns_name[tid] in eine Zeichenkette gewandelt und ausgegeben@dns_name[tid] wird nicht weiter benötigt und
gelöscht
uretprobe:/lib/x86_64-linux-gnu/libdns-9.16.22-Debian.so:dns_fwdtable_find
{
if (retval == 0) {
printf("Forwarded domain name: %s\n", str(@dns_name[tid]));
}
delete(@dns_name[tid]);
}
#!/usr/bin/bpftrace
struct dns_name {
unsigned int magic;
unsigned char *ndata;
unsigned int length;
unsigned int labels;
unsigned int attributes;
unsigned char *offsets;
// isc_buffer_t *buffer;
// ISC_LINK(dns_name_t) link;
// ISC_LIST(dns_rdataset_t) list;
};
BEGIN
{
print("Waiting for forward decision...\n");
}
uprobe:/lib/x86_64-linux-gnu/libdns-9.16.22-Debian.so:dns_fwdtable_find
{
@dns_name[tid] = ((struct dns_name *)arg1)->ndata
}
uretprobe:/lib/x86_64-linux-gnu/libdns-9.16.22-Debian.so:dns_fwdtable_find
{
if (retval == 0) {
printf("Forwarded domain name: %s\n", str(@dns_name[tid]));
}
delete(@dns_name[tid]);
}
bpftrace Skript aktiv
dnslab.org per Forwarding weitergeleitet, jedoch nicht die
Anfragen an ietf.org
interface = "eth0";
updatetime = 15;
filters = (
{
enabled = true,
action = 0,
udp_enabled = true,
udp_dport = 53
}
);
Von David Calavera, Lorenzo Fontana (November 2019)
Von Brendan Gregg (Dezember 2020)
Von Brendan Gregg (Dezember 2019)
eBPF selbst ausprobieren: Diese Workshop-Session zeigt wie eine einfache Firewall gegen gegen UDP DDoS Angriffe auf einen DNS Server aussehen kann: https://doh.defaultroutes.de/ddi2022/ebpf-workshop.html
Kontakt:
carsten@dnsworkshop.de