Nou ekri pwoteksyon kont atak DDoS sou XDP. Pati nikleyè

Teknoloji eXpress Data Path (XDP) pèmèt pwosesis trafik o aza fèt sou entèfas Linux anvan pake yo antre nan pil rezo nwayo a. Aplikasyon XDP - pwoteksyon kont atak DDoS (CloudFlare), filtè konplèks, koleksyon estatistik (Netflix). Pwogram XDP yo egzekite pa machin vityèl eBPF la, kidonk yo gen restriksyon sou kòd yo ak fonksyon nwayo ki disponib yo depann de kalite filtre a.

Atik la gen entansyon ranpli enpèfeksyon yo nan anpil materyèl sou XDP. Premyèman, yo bay kòd pare ki imedyatman kontoune karakteristik XDP: li prepare pou verifikasyon oswa li twò senp pou lakòz pwoblèm. Lè sa a, ou eseye ekri kòd ou nan grafouyen, ou pa gen okenn lide ki sa yo dwe fè ak erè tipik. Dezyèmman, fason yo teste lokalman XDP san yon VM ak pyès ki nan konpitè yo pa kouvri, malgre lefèt ke yo gen pwòp enkonvenyans yo. Tèks la fèt pou pwogramasyon ki abitye ak rezo ak Linux ki enterese nan XDP ak eBPF.

Nan pati sa a, nou pral konprann an detay ki jan filtè XDP reyini ak ki jan yo teste li, Lè sa a, nou pral ekri yon vèsyon senp nan mekanis bonbon SYN ki byen koni nan nivo pwosesis pake a. Pou kounye a, nou pa pral kreye yon "lis blan"
kliyan verifye, kenbe kontè ak jere filtè a - ase mòso bwa.

Nou pral ekri an C - li pa alamòd, men li pratik. Tout kòd yo disponib sou GitHub atravè lyen ki nan fen a epi yo divize an komèt dapre etap ki dekri nan atik la.

Limit responsabilite nou. Nan kou a nan atik sa a, yo pral devlope yon mini-solisyon pou anpeche atak DDoS, paske sa a se yon travay reyalis pou XDP ak jaden mwen an. Sepandan, objektif prensipal la se konprann teknoloji a; sa a se pa yon gid pou kreye pwoteksyon pare. Kòd leson patikilye a pa optimize epi li omèt kèk nuans.

Brèf Apèsi sou XDP

Mwen pral dekri sèlman pwen kle yo pou pa kopi dokimantasyon ak atik ki egziste deja.

Se konsa, kòd filtre a chaje nan nwayo a. Pake k ap rantre yo pase nan filtè a. Kòm yon rezilta, filtè a dwe pran yon desizyon: pase pake a nan nwayo a (XDP_PASS), lage pake (XDP_DROP) oswa voye li tounen (XDP_TX). Filtè a ka chanje pake a, sa a se laverite espesyalman pou XDP_TX. Ou kapab tou avòte pwogram nan (XDP_ABORTED) ak Reyajiste pake a, men sa a se analogue assert(0) - pou debogaj.

Se machin vityèl eBPF (extended Berkley Packet Filter) fè espre fè senp pou ke nwayo a ka tcheke si kòd la pa antre nan bouk epi li pa domaje memwa lòt moun. Restriksyon ak chèk kimilatif:

  • Bouk (bak) yo entèdi.
  • Gen yon pil pou done, men pa gen fonksyon (tout fonksyon C yo dwe aliye).
  • Aksè memwa andeyò pil la ak tanpon pake yo entèdi.
  • Gwosè kòd la limite, men nan pratik sa a pa trè enpòtan.
  • Se sèlman apèl nan fonksyon nwayo espesyal (eBPF helpers) yo pèmèt.

Konsepsyon ak enstale yon filtè sanble sa a:

  1. Kòd sous (eg kernel.c) konpile nan objè (kernel.o) anba achitekti machin vityèl eBPF. Apati oktòb 2019, Clang sipòte konpilasyon nan eBPF epi li te pwomèt nan GCC 10.1.
  2. Si kòd objè sa a gen apèl nan estrikti nwayo (pa egzanp, tab ak kontè), ID yo ranplase pa zewo, ki vle di ke kòd sa a pa ka egzekite. Anvan ou chaje nan nwayo a, ou bezwen ranplase zewo sa yo ak idantite objè espesifik ki te kreye atravè apèl nwayo (lyen kòd la). Ou ka fè sa ak sèvis piblik ekstèn, oswa ou ka ekri yon pwogram ki pral konekte ak chaje yon filtè espesifik.
  3. Kernel la verifye pwogram ki chaje a. Yo tcheke absans sik ak non-travèse fwontyè pake ak pil. Si verifikatè a pa ka pwouve ke kòd la kòrèk, pwogram nan rejte - ou bezwen pou kapab fè l plezi.
  4. Apre verifikasyon siksè, nwayo a konpile kòd objè achitekti eBPF nan kòd machin achitekti sistèm (jis-an-tan).
  5. Pwogram nan tache ak koòdone a epi li kòmanse trete pake.

Depi XDP kouri nan nwayo a, debogaj fèt lè l sèvi avèk mòso bwa tras ak, an reyalite, pake ke pwogram nan filtre oswa jenere. Sepandan, eBPF asire ke kòd la chaje an sekirite pou sistèm nan, kidonk, ou ka fè eksperyans ak XDP dirèkteman sou Linux lokal ou a.

Prepare anviwònman an

Asanble

Clang pa ka dirèkteman pwodwi kòd objè pou achitekti eBPF, kidonk pwosesis la konsiste de de etap:

  1. Konpile kòd C nan bytecode LLVM (clang -emit-llvm).
  2. Konvèti bytecode an kòd objè eBPF (llc -march=bpf -filetype=obj).

Lè w ap ekri yon filtè, yon koup nan dosye ki gen fonksyon oksilyè ak makro pral itil soti nan tès nwayo. Li enpòtan pou yo matche ak vèsyon nwayo a (KVER). Telechaje yo pou helpers/:

export KVER=v5.3.7
export BASE=https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/plain/tools/testing/selftests/bpf
wget -P helpers --content-disposition "${BASE}/bpf_helpers.h?h=${KVER}" "${BASE}/bpf_endian.h?h=${KVER}"
unset KVER BASE

Makefile pou Arch Linux (kernel 5.3.7):

CLANG ?= clang
LLC ?= llc

KDIR ?= /lib/modules/$(shell uname -r)/build
ARCH ?= $(subst x86_64,x86,$(shell uname -m))

CFLAGS = 
    -Ihelpers 
    
    -I$(KDIR)/include 
    -I$(KDIR)/include/uapi 
    -I$(KDIR)/include/generated/uapi 
    -I$(KDIR)/arch/$(ARCH)/include 
    -I$(KDIR)/arch/$(ARCH)/include/generated 
    -I$(KDIR)/arch/$(ARCH)/include/uapi 
    -I$(KDIR)/arch/$(ARCH)/include/generated/uapi 
    -D__KERNEL__ 
    
    -fno-stack-protector -O2 -g

xdp_%.o: xdp_%.c Makefile
    $(CLANG) -c -emit-llvm $(CFLAGS) $< -o - | 
    $(LLC) -march=bpf -filetype=obj -o $@

.PHONY: all clean

all: xdp_filter.o

clean:
    rm -f ./*.o

KDIR gen chemen an nan tèt nwayo yo, ARCH - Achitekti sistèm. Chemen ak zouti yo ka varye yon ti kras ant distribisyon.

Egzanp diferans pou Debian 10 (kernel 4.19.67)

# другая команда
CLANG ?= clang
LLC ?= llc-7

# другой каталог
KDIR ?= /usr/src/linux-headers-$(shell uname -r)
ARCH ?= $(subst x86_64,x86,$(shell uname -m))

# два дополнительных каталога -I
CFLAGS = 
    -Ihelpers 
    
    -I/usr/src/linux-headers-4.19.0-6-common/include 
    -I/usr/src/linux-headers-4.19.0-6-common/arch/$(ARCH)/include 
    # далее без изменений

CFLAGS konekte yon anyè ak tèt oksilyè ak plizyè repèrtwar ak tèt nwayo. Senbòl __KERNEL__ vle di ke UAPI (userspace API) tèt yo defini pou kòd nwayo a, depi filtre a egzekite nan nwayo a.

Pwoteksyon Stack ka enfim (-fno-stack-protector), paske verifikatè kòd eBPF toujou tcheke pou vyolasyon pile andeyò limit yo. Li vo vire sou optimize touswit, paske gwosè a nan bytecode eBPF limite.

Ann kòmanse ak yon filtè ki pase tout pake epi ki pa fè anyen:

#include <uapi/linux/bpf.h>

#include <bpf_helpers.h>

SEC("prog")
int xdp_main(struct xdp_md* ctx) {
    return XDP_PASS;
}

char _license[] SEC("license") = "GPL";

Ekip make kolekte xdp_filter.o. Ki kote pou eseye li kounye a?

Ban tès

Kanpe a dwe gen ladan de interfaces: sou ki pral gen yon filtè ak ki soti nan ki pake yo pral voye. Sa yo dwe plen véritable aparèy Linux ak pwòp IP yo nan lòd yo tcheke ki jan aplikasyon regilye yo travay ak filtè nou an.

Aparèy nan kalite veth (Ethernet vityèl) yo apwopriye pou nou: sa yo se yon pè entèfas rezo vityèl "konekte" dirèkteman youn ak lòt. Ou ka kreye yo tankou sa a (nan seksyon sa a tout kòmandman yo ip yo te pote soti nan root):

ip link add xdp-remote type veth peer name xdp-local

Isit la xdp-remote и xdp-local - non aparèy. Sou xdp-local (192.0.2.1/24) yon filtè pral tache, ak xdp-remote (192.0.2.2/24) trafik kap vini yo pral voye. Sepandan, gen yon pwoblèm: interfaces yo sou machin nan menm, ak Linux pa pral voye trafik nan youn nan yo atravè lòt la. Ou ka rezoud sa a ak règ difisil iptables, men yo pral oblije chanje pakè, ki se enkonvenyan pou debogaj. Li pi bon pou itilize espas non rezo yo (apwe netns).

Yon espas non rezo gen yon seri koòdone, tab routage, ak règ NetFilter, izole nan objè ki sanble nan lòt netns. Chak pwosesis kouri nan yon espas non epi sèlman gen aksè a objè yo nan netns sa yo. Pa default, sistèm nan gen yon sèl espas non rezo pou tout objè, kidonk, ou ka travay nan Linux epi ou pa konnen sou netns.

Ann kreye yon nouvo espas non xdp-test epi deplase li la xdp-remote.

ip netns add xdp-test
ip link set dev xdp-remote netns xdp-test

Lè sa a, pwosesis la ap kouri nan xdp-test, pa pral "wè" xdp-local (li pral rete nan netns pa default) epi lè w ap voye yon pake nan 192.0.2.1 li pral transmèt li nan xdp-remote, paske sa a se sèl koòdone sou 192.0.2.0/24 aksesib a pwosesis sa a. Sa a tou travay nan direksyon opoze a.

Lè w ap deplase ant netns, koòdone a desann epi li pèdi adrès li. Pou konfigirasyon koòdone nan netns, ou bezwen kouri ip ... nan espas non kòmand sa a ip netns exec:

ip netns exec xdp-test 
    ip address add 192.0.2.2/24 dev xdp-remote
ip netns exec xdp-test 
    ip link set xdp-remote up

Kòm ou ka wè, sa a pa diferan de anviwònman an xdp-local nan espas non default la:

    ip address add 192.0.2.1/24 dev xdp-local
    ip link set xdp-local up

Si ou kouri tcpdump -tnevi xdp-local, ou ka wè ke pake voye soti nan xdp-test, yo lage nan koòdone sa a:

ip netns exec xdp-test   ping 192.0.2.1

Li se pratik lanse yon kokiy nan xdp-test. Repozitwa a gen yon script ki otomatize travay ak kanpe la; pou egzanp, ou ka configured kanpe la ak lòd la sudo ./stand up epi efase li sudo ./stand down.

Trase

Filtè a asosye ak yon aparèy tankou sa a:

ip -force link set dev xdp-local xdp object xdp_filter.o verbose

Kle -force bezwen konekte yon nouvo pwogram si yon lòt deja lye. "Pa gen nouvèl se yon bon nouvèl" se pa sou lòd sa a, pwodiksyon an se volumineuz nan nenpòt ka. endike verbose opsyonèl, men ak li yon rapò parèt sou travay la nan verifikatè kòd la ak lis asanble a:

Verifier analysis:

0: (b7) r0 = 2
1: (95) exit

Dekonekte pwogram nan soti nan koòdone a:

ip link set dev xdp-local xdp off

Nan yon script sa yo se kòmandman sudo ./stand attach и sudo ./stand detach.

Lè w tache yon filtè, ou ka asire w ke ping kontinye travay, men èske pwogram nan mache? Ann ajoute mòso bwa. Fonksyon bpf_trace_printk() menm jan ak printf(), men sèlman sipòte jiska twa agiman lòt pase modèl la, ak yon lis limite nan spesifye. Makro bpf_printk() senplifye apèl la.

   SEC("prog")
   int xdp_main(struct xdp_md* ctx) {
+      bpf_printk("got packet: %pn", ctx);
       return XDP_PASS;
   }

Pwodiksyon an ale nan chanèl tras nwayo a, ki bezwen aktive:

echo -n 1 | sudo tee /sys/kernel/debug/tracing/options/trace_printk

Gade fil mesaj:

cat /sys/kernel/debug/tracing/trace_pipe

Tou de kòmandman sa yo fè yon apèl sudo ./stand log.

Ping ta dwe kounye a deklanche mesaj tankou sa a:

<...>-110930 [004] ..s1 78803.244967: 0: got packet: 00000000ac510377

Si ou gade ak anpil atansyon nan pwodiksyon verifikatè a, ou pral remake kalkil etranj:

0: (bf) r3 = r1
1: (18) r1 = 0xa7025203a7465
3: (7b) *(u64 *)(r10 -8) = r1
4: (18) r1 = 0x6b63617020746f67
6: (7b) *(u64 *)(r10 -16) = r1
7: (bf) r1 = r10
8: (07) r1 += -16
9: (b7) r2 = 16
10: (85) call bpf_trace_printk#6
<...>

Reyalite a se ke pwogram eBPF pa gen yon seksyon done, kidonk sèl fason yo kode yon fisèl fòma se agiman yo imedya nan kòmandman VM:

$ python -c "import binascii; print(bytes(reversed(binascii.unhexlify('0a7025203a74656b63617020746f67'))))"
b'got packet: %pn'

Pou rezon sa a, pwodiksyon debug anpil gonfle kòd final la.

Voye Pake XDP

Ann chanje filtè a: kite l voye tounen tout pake k ap rantre yo. Sa a se kòrèk nan yon pwen de vi rezo, depi li ta nesesè yo chanje adrès yo nan tèt yo, men kounye a travay la nan prensip enpòtan.

       bpf_printk("got packet: %pn", ctx);
-      return XDP_PASS;
+      return XDP_TX;
   }

Lanse tcpdump sou xdp-remote. Li ta dwe montre ICMP Echo Request ki idantik ki sòti ak fèk ap rantre epi sispann montre ICMP Echo Reply. Men, li pa montre. Li sanble ke pou travay XDP_TX nan pwogram nan sou xdp-local nesesènan koòdone nan pè xdp-remote yon pwogram te tou plase, menm si li te vid, epi li te leve soti vivan.

Ki jan mwen te fè konnen sa a?

Trase chemen yon pake nan nwayo a Mekanis nan evènman perf pèmèt, nan chemen an, itilize menm machin vityèl la, se sa ki, eBPF yo itilize pou fè fas ak eBPF.

Ou dwe fè byen ak sa ki mal, paske pa gen anyen lòt pou fè li soti.

$ sudo perf trace --call-graph dwarf -e 'xdp:*'
   0.000 ping/123455 xdp:xdp_bulk_tx:ifindex=19 action=TX sent=0 drops=1 err=-6
                                     veth_xdp_flush_bq ([veth])
                                     veth_xdp_flush_bq ([veth])
                                     veth_poll ([veth])
                                     <...>

Ki sa ki kòd 6?

$ errno 6
ENXIO 6 No such device or address

Fonksyon veth_xdp_flush_bq() resevwa yon kòd erè nan men veth_xdp_xmit(), kote rechèch pa ENXIO epi jwenn kòmantè a.

Ann retabli filtè minimòm lan (XDP_PASS) nan dosye xdp_dummy.c, ajoute li nan Makefile a, lyen li nan xdp-remote:

ip netns exec remote 
    ip link set dev int xdp object dummy.o

Koulye a, tcpdump montre sa ki espere:

62:57:8e:70:44:64 > 26:0e:25:37:8f:96, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 13762, offset 0, flags [DF], proto ICMP (1), length 84)
    192.0.2.2 > 192.0.2.1: ICMP echo request, id 46966, seq 1, length 64
62:57:8e:70:44:64 > 26:0e:25:37:8f:96, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 13762, offset 0, flags [DF], proto ICMP (1), length 84)
    192.0.2.2 > 192.0.2.1: ICMP echo request, id 46966, seq 1, length 64

Si sèlman ARP yo montre olye de sa, ou bezwen retire filtè yo (sa a fè sa sudo ./stand detach), kite ale ping, Lè sa a, mete filtè epi eseye ankò. Pwoblèm lan se ke filtre a XDP_TX valab pou tou de ARP ak pile
espas non yo xdp-test jere yo "bliye" adrès la MAC 192.0.2.1, li pa yo pral kapab rezoud IP sa a.

Deklarasyon sou pwoblèm nan

Ann ale nan travay la deklare: ekri yon mekanis bonbon SYN sou XDP.

SYN inondasyon rete yon atak DDoS popilè, sans nan ki se jan sa a. Lè yon koneksyon etabli (TCP handshake), sèvè a resevwa yon SYN, asiyen resous pou koneksyon nan lavni, reponn ak yon pake SYNACK epi tann pou yon ACK. Atakè a tou senpleman voye pake SYN soti nan adrès fo nan dè milye yo pou chak segonn soti nan chak lame nan yon botne milti-fòs. Sèvè a oblije asiyen resous imedyatman lè pake a rive, men li lage yo apre yon gwo delè; kòm rezilta, memwa oswa limit yo fin itilize, nouvo koneksyon yo pa aksepte, epi sèvis la pa disponib.

Si ou pa asiyen resous ki baze sou pake SYN la, men sèlman reponn ak yon pake SYNACK, ki jan sèvè a ka konprann ke pake ACK ki te rive pita refere a yon pake SYN ki pa te sove? Apre yo tout, yon atakè kapab tou jenere fo ACKs. Pwen nan bonbon SYN la se kode li nan seqnum paramèt koneksyon kòm yon hash nan adrès, pò ak chanje sèl. Si ACK la te rive rive anvan sèl la te chanje, ou ka kalkile hash la ankò epi konpare li ak acknum. Forge acknum atakè a pa kapab, depi sèl la gen ladan yon sekrè, epi yo pa yo pral kapab sòt nan li akòz yon kanal limite.

Bonbon SYN yo te aplike depi lontan nan nwayo Linux la epi yo ka menm otomatikman aktive si SYN yo rive twò vit ak an mas.

Pwogram edikatif sou TCP lanmen

TCP bay transmisyon done kòm yon kouran bytes, pou egzanp, demann HTTP yo transmèt sou TCP. Se kouran an transmèt an moso nan pakè. Tout pake TCP yo gen drapo ki lojik ak nimewo sekans 32-bit:

  • Konbinezon an nan drapo detèmine wòl nan yon pake patikilye. Drapo a SYN vle di ke sa a se premye pake moun k ap voye a nan koneksyon an. Drapo ACK la vle di moun k la te resevwa tout done koneksyon jiska byte a acknum. Yon pake ka gen plizyè drapo epi yo rele yo pa konbinezon yo, pou egzanp, yon pake SYNACK.

  • Nimewo sekans (seqnum) presize konpanse a nan kouran done pou premye byte ki transmèt nan pake sa a. Pou egzanp, si nan premye pake a ak X byte nan done nimewo sa a te N, nan pwochen pake a ak nouvo done li pral N + X. Nan kòmansman koneksyon an, chak pati chwazi nimewo sa a owaza.

  • Nimewo rekonesans (acknum) se menm konpanse ak seqnum, men li pa detèmine kantite byte ke yo te transmèt, men nimewo a nan premye byte nan men moun k ap resevwa a, ki moun k la pa t 'wè.

Nan kòmansman koneksyon an, pati yo dwe dakò seqnum и acknum. Kliyan an voye yon pake SYN ak li seqnum = X. Sèvè a reponn ak yon pake SYNACK, kote li anrejistre li yo seqnum = Y ak ekspozisyon acknum = X + 1. Kliyan an reponn SYNACK ak yon pake ACK, kote seqnum = X + 1, acknum = Y + 1. Apre sa, transfè done aktyèl la kòmanse.

Si kanmarad la pa rekonèt li resevwa pake a, TCP renvoye li apre yon tan.

Poukisa yo pa toujou itilize bonbon SYN?

Premyèman, si SYNACK oswa ACK la pèdi, ou pral oblije rete tann pou yon re-voye - ralanti etablisman an koneksyon. Dezyèmman, nan pake a SYN - epi sèlman nan li! — gen yon kantite opsyon transmèt ki afekte operasyon an plis nan koneksyon an. Lè yo pa sonje pake SYN k ap fèk ap rantre yo, sèvè a inyore opsyon sa yo; kliyan an p ap voye yo nan pwochen pake yo ankò. TCP ka travay nan ka sa a, men omwen nan premye etap la bon jan kalite a nan koneksyon an ap diminye.

Soti nan pwen de vi pakè, yon pwogram XDP dwe fè bagay sa yo:

  • reponn SYN ak SYNACK ak yon bonbon;
  • reponn ACK ak RST (dekonekte);
  • jete rès pake yo.

Pseudocode nan algorithm la ansanm ak analiz pake:

Если это не Ethernet,
    пропустить пакет.
Если это не IPv4,
    пропустить пакет.
Если адрес в таблице проверенных,               (*)
        уменьшить счетчик оставшихся проверок,
        пропустить пакет.
Если это не TCP,
    сбросить пакет.     (**)
Если это SYN,
    ответить SYN-ACK с cookie.
Если это ACK,
    если в acknum лежит не cookie,
        сбросить пакет.
    Занести в таблицу адрес с N оставшихся проверок.    (*)
    Ответить RST.   (**)
В остальных случаях сбросить пакет.

Youn (*) Pwen yo kote ou bezwen jere eta a nan sistèm lan make - nan premye etap la ou ka fè san yo pa tou senpleman aplike yon TCP lanmen ak jenerasyon an nan yon bonbon SYN kòm yon seqnum.

Sou plas (**), pandan ke nou pa gen yon tab, nou pral sote pake a.

Aplike TCP lanmen

Analize pake a epi verifye kòd la

Nou pral bezwen estrikti header rezo: Ethernet (uapi/linux/if_ether.h), IPv4 (uapi/linux/ip.h) ak TCP (uapi/linux/tcp.h). Mwen pa t janm kapab konekte lèt la akòz erè ki gen rapò ak atomic64_t, Mwen te oblije kopye definisyon ki nesesè yo nan kòd la.

Tout fonksyon yo atribye ba pou lizibilite nan C yo dwe aliye nan pwen apèl la, depi verifikatè eBPF nan nwayo a entèdi so bak, se sa ki, an reyalite, bouk ak apèl fonksyon.

#define INTERNAL static __attribute__((always_inline))

Makro LOG() enfim enprime nan konstriksyon an lage.

Pwogram nan se yon transporteur de fonksyon. Chak resevwa yon pake kote yon header nan nivo apwopriye a make, pou egzanp, process_ether() espere li dwe ranpli ether. Ki baze sou rezilta yo nan analiz jaden, fonksyon an ka pase pake a nan yon nivo ki pi wo. Rezilta fonksyon an se yon aksyon XDP. Pou kounye a, moun k ap okipe SYN ak ACK yo pase tout pake yo.

struct Packet {
    struct xdp_md* ctx;

    struct ethhdr* ether;
    struct iphdr* ip;
    struct tcphdr* tcp;
};

INTERNAL int process_tcp_syn(struct Packet* packet) { return XDP_PASS; }
INTERNAL int process_tcp_ack(struct Packet* packet) { return XDP_PASS; }
INTERNAL int process_tcp(struct Packet* packet) { ... }
INTERNAL int process_ip(struct Packet* packet) { ... }

INTERNAL int
process_ether(struct Packet* packet) {
    struct ethhdr* ether = packet->ether;

    LOG("Ether(proto=0x%x)", bpf_ntohs(ether->h_proto));

    if (ether->h_proto != bpf_ntohs(ETH_P_IP)) {
        return XDP_PASS;
    }

    // B
    struct iphdr* ip = (struct iphdr*)(ether + 1);
    if ((void*)(ip + 1) > (void*)packet->ctx->data_end) {
        return XDP_DROP; /* malformed packet */
    }

    packet->ip = ip;
    return process_ip(packet);
}

SEC("prog")
int xdp_main(struct xdp_md* ctx) {
    struct Packet packet;
    packet.ctx = ctx;

    // A
    struct ethhdr* ether = (struct ethhdr*)(void*)ctx->data;
    if ((void*)(ether + 1) > (void*)ctx->data_end) {
        return XDP_PASS;
    }

    packet.ether = ether;
    return process_ether(&packet);
}

Mwen atire atansyon ou sou chèk yo make A ak B. Si ou fè kòmantè A, pwogram nan ap bati, men pral gen yon erè verifikasyon lè w ap chaje:

Verifier analysis:

<...>
11: (7b) *(u64 *)(r10 -48) = r1
12: (71) r3 = *(u8 *)(r7 +13)
invalid access to packet, off=13 size=1, R7(id=0,off=0,r=0)
R7 offset is outside of the packet
processed 11 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

Error fetching program/map!

Kle fisèl invalid access to packet, off=13 size=1, R7(id=0,off=0,r=0): Gen chemen ekzekisyon lè trèzyèm octet depi nan konmansman an nan tanpon an deyò pake a. Li difisil pou konprann nan lis la ki liy nou ap pale, men gen yon nimewo enstriksyon (12) ak yon demonte ki montre liy yo nan kòd sous:

llvm-objdump -S xdp_filter.o | less

Nan ka sa a li lonje dwèt sou liy lan

LOG("Ether(proto=0x%x)", bpf_ntohs(ether->h_proto));

ki fè li klè ke pwoblèm nan se ether. Li ta toujou konsa.

Repons SYN

Objektif la nan etap sa a se jenere yon pake SYNACK kòrèk ak yon fiks seqnum, ki pral ranplase alavni pa bonbon SYN. Tout chanjman fèt nan process_tcp_syn() ak zòn ki antoure yo.

Verifikasyon pake

Etranj ase, isit la se liy ki pi remakab, oswa pito, kòmantè a nan li:

/* Required to verify checksum calculation */
const void* data_end = (const void*)ctx->data_end;

Lè w ekri premye vèsyon kòd la, yo te itilize nwayo 5.1 la, pou verifye a ki te gen yon diferans ant data_end и (const void*)ctx->data_end. Nan moman ekri atik la, Kernel 5.3.1 pa t 'gen pwoblèm sa a. Petèt du a jwenn aksè nan yon varyab lokal yon fason diferan pase yon jaden. Moral nan istwa a: Avèk gwo sitiyasyon nidifikasyon, senplifye kòd la ka ede.

Apre yo se chèk longè woutin pou tout bèl pouvwa verifikatè a; O MAX_CSUM_BYTES anba a.

const u32 ip_len = ip->ihl * 4;
if ((void*)ip + ip_len > data_end) {
    return XDP_DROP; /* malformed packet */
}
if (ip_len > MAX_CSUM_BYTES) {
    return XDP_ABORTED; /* implementation limitation */
}

const u32 tcp_len = tcp->doff * 4;
if ((void*)tcp + tcp_len > (void*)ctx->data_end) {
    return XDP_DROP; /* malformed packet */
}
if (tcp_len > MAX_CSUM_BYTES) {
    return XDP_ABORTED; /* implementation limitation */
}

Dewoulman pake a

Nou ranpli seqnum и acknum, mete ACK (SYN deja fikse):

const u32 cookie = 42;
tcp->ack_seq = bpf_htonl(bpf_ntohl(tcp->seq) + 1);
tcp->seq = bpf_htonl(cookie);
tcp->ack = 1;

Boukante pò TCP, adrès IP ak adrès MAC. Bibliyotèk estanda a pa aksesib nan pwogram XDP, kidonk memcpy() - yon makro ki kache Clang intrinsèques yo.

const u16 temp_port = tcp->source;
tcp->source = tcp->dest;
tcp->dest = temp_port;

const u32 temp_ip = ip->saddr;
ip->saddr = ip->daddr;
ip->daddr = temp_ip;

struct ethhdr temp_ether = *ether;
memcpy(ether->h_dest, temp_ether.h_source, ETH_ALEN);
memcpy(ether->h_source, temp_ether.h_dest, ETH_ALEN);

Rekalkile sòm chèk yo

Sòm chèk IPv4 ak TCP mande pou ajoute tout mo 16-bit nan headers yo, epi gwosè headers yo ekri nan yo, se sa ki enkoni nan tan konpile. Sa a se yon pwoblèm paske verifikatè a pa pral bouk nan bouk nòmal la nan limit varyab la. Men, gwosè a nan tèt yo limite: jiska 64 bytes chak. Ou ka fè yon bouk ak yon kantite iterasyon fiks, ki ka fini bonè.

Mwen konstate ke genyen RFC 1624 sou ki jan yo pasyèlman rekalkile sòm chèk la si sèlman mo yo fiks nan pakè yo chanje. Sepandan, metòd la pa inivèsèl, epi aplikasyon an ta pi difisil pou kenbe.

Fonksyon kalkil sòm chèk:

#define MAX_CSUM_WORDS 32
#define MAX_CSUM_BYTES (MAX_CSUM_WORDS * 2)

INTERNAL u32
sum16(const void* data, u32 size, const void* data_end) {
    u32 s = 0;
#pragma unroll
    for (u32 i = 0; i < MAX_CSUM_WORDS; i++) {
        if (2*i >= size) {
            return s; /* normal exit */
        }
        if (data + 2*i + 1 + 1 > data_end) {
            return 0; /* should be unreachable */
        }
        s += ((const u16*)data)[i];
    }
    return s;
}

Malgre ke size verifye pa kòd la rele, kondisyon an dezyèm sòti nesesè pou verifikatè a ka pwouve fini an nan bouk la.

Pou mo 32-bit, yon vèsyon ki pi senp aplike:

INTERNAL u32
sum16_32(u32 v) {
    return (v >> 16) + (v & 0xffff);
}

Aktyèlman rekalkile sòm chèk yo epi voye pake a tounen:

ip->check = 0;
ip->check = carry(sum16(ip, ip_len, data_end));

u32 tcp_csum = 0;
tcp_csum += sum16_32(ip->saddr);
tcp_csum += sum16_32(ip->daddr);
tcp_csum += 0x0600;
tcp_csum += tcp_len << 8;
tcp->check = 0;
tcp_csum += sum16(tcp, tcp_len, data_end);
tcp->check = carry(tcp_csum);

return XDP_TX;

Fonksyon carry() fè yon sòm chèk soti nan yon sòm 32-bit nan mo 16-bit, dapre RFC 791.

Verifikasyon TCP lanmen

Filtè a kòrèkteman etabli yon koneksyon ak netcat, sote ACK final la, a ki Linux reponn ak yon pake RST, depi pile rezo a pa t 'resevwa SYN a - li te konvèti nan SYNACK ak voye tounen - ak nan pwen de vi OS la, yon pake te rive ki pa te gen rapò ak koneksyon louvri.

$ sudo ip netns exec xdp-test   nc -nv 192.0.2.1 6666
192.0.2.1 6666: Connection reset by peer

Li enpòtan pou tcheke ak aplikasyon konplè epi kontwole tcpdump sou xdp-remote paske, pa egzanp, hping3 pa reponn a chèk ki pa kòrèk.

Soti nan yon pwen de vi XDP, verifikasyon nan tèt li se trivial. Algorithm nan kalkil se primitif ak gen anpil chans vilnerab a yon atakè sofistike. Kernel Linux la, pou egzanp, sèvi ak kriptografik SipHash, men aplikasyon li pou XDP klèman pi lwen pase sijè ki abòde lan atik sa a.

Te parèt pou nouvo TODO ki gen rapò ak entèraksyon ekstèn:

  • Pwogram XDP a pa kapab sere cookie_seed (pati sekrè sèl la) nan yon varyab mondyal, ou bezwen depo nan nwayo a, valè a nan ki pral detanzantan mete ajou soti nan yon dèlko serye.

  • Si bonbon SYN la matche ak nan pake ACK la, ou pa bezwen enprime yon mesaj, men sonje IP kliyan verifye a pou w ka kontinye pase pakè ladan l.

Verifikasyon kliyan lejitim:

$ sudoip netns exec xdp-test   nc -nv 192.0.2.1 6666
192.0.2.1 6666: Connection reset by peer

Jounal yo anrejistre fini chèk la (flags=0x2 - sa a se SYN, flags=0x10 se ACK):

Ether(proto=0x800)
  IP(src=0x20e6e11a dst=0x20e6e11e proto=6)
    TCP(sport=50836 dport=6666 flags=0x2)
Ether(proto=0x800)
  IP(src=0xfe2cb11a dst=0xfe2cb11e proto=6)
    TCP(sport=50836 dport=6666 flags=0x10)
      cookie matches for client 20200c0

Pandan ke pa gen okenn lis IP verifye, pa pral gen okenn pwoteksyon kont inondasyon SYN nan tèt li, men isit la se reyaksyon an nan yon inondasyon ACK te lanse pa lòd sa a:

sudo ip netns exec xdp-test   hping3 --flood -A -s 1111 -p 2222 192.0.2.1

Log antre:

Ether(proto=0x800)
  IP(src=0x15bd11a dst=0x15bd11e proto=6)
    TCP(sport=3236 dport=2222 flags=0x10)
      cookie mismatch

Konklizyon

Pafwa eBPF an jeneral ak XDP an patikilye yo prezante plis kòm yon zouti administratè avanse pase kòm yon platfòm devlopman. Vreman vre, XDP se yon zouti pou entèfere ak pwosesis la nan pake pa nwayo a, epi li pa yon altènativ a chemine nwayo a, tankou DPDK ak lòt opsyon kontoune nwayo. Nan lòt men an, XDP pèmèt ou aplike lojik byen konplèks, ki, Anplis, se fasil mete ajou san entèripsyon nan pwosesis trafik. Verifikatè a pa kreye gwo pwoblèm; pèsonèlman, mwen pa ta refize sa a pou pati nan kòd espas itilizatè.

Nan dezyèm pati a, si sijè a enteresan, nou pral ranpli tablo kliyan verifye ak dekoneksyon, aplike kontè epi ekri yon sèvis piblik espas itilizatè pou jere filtè a.

Lyen:

Sous: www.habr.com

Add nouvo kòmantè