(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.so
arg1
) 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öschturetprobe:/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