Sibhala ukuvikela ekuhlaselweni kwe-DDoS ku-XDP. Ingxenye yenuzi

Ubuchwepheshe be-eXpress Data Path (XDP) buvumela ukucutshungulwa kwethrafikhi okungahleliwe ukuthi kwenziwe ku-interface ye-Linux ngaphambi kokuba amaphakethe angene kusitaki senethiwekhi ye-kernel. Ukusetshenziswa kwe-XDP - ukuvikela ekuhlaselweni kwe-DDoS (CloudFlare), izihlungi eziyinkimbinkimbi, iqoqo lezibalo (Netflix). Izinhlelo ze-XDP zenziwa umshini obonakalayo we-eBPF, ngakho zinemikhawulo kukho kokubili amakhodi awo kanye nemisebenzi etholakalayo ye-kernel kuye ngohlobo lwesihlungi.

I-athikili ihloselwe ukugcwalisa ukushiyeka kwezinto eziningi zokwakha ku-XDP. Okokuqala, bahlinzeka ngekhodi eyenziwe ngomumo edlula ngokushesha izici ze-XDP: ilungiselelwe ukuqinisekiswa noma ilula kakhulu ukubangela izinkinga. Uma usuzama ukubhala ikhodi yakho kusukela ekuqaleni, awazi ukuthi wenzeni ngamaphutha ajwayelekile. Okwesibili, izindlela zokuhlola i-XDP endaweni ngaphandle kwe-VM kanye nehadiwe azimboziwe, ngaphandle kokuthi zinezingibe zazo. Umbhalo uhloselwe abahleli abajwayelene nenethiwekhi kanye ne-Linux abathanda i-XDP ne-eBPF.

Kule ngxenye, sizoqonda ngokuningiliziwe ukuthi isihlungi se-XDP sihlanganiswe kanjani nokuthi singasihlola kanjani, bese sizobhala inguqulo elula yendlela eyaziwa kakhulu yamakhukhi e-SYN ezingeni lokucubungula iphakethe. Ngeke sidale "uhlu olumhlophe" okwamanje
amaklayenti aqinisekisiwe, gcina izinto zokubala futhi uphathe isihlungi - izingodo ezanele.

Sizobhala ngo-C - akuyona imfashini, kodwa iyasebenza. Yonke ikhodi iyatholakala ku-GitHub ngesixhumanisi ekugcineni futhi ihlukaniswe yaba yizibophezelo ngokwezigaba ezichazwe esihlokweni.

Ukuziphendulela. Ngokuqhubeka kwalesi sihloko, ngizothuthukisa isixazululo esincane sokunqanda ukuhlaselwa kwe-DDoS, ngoba lona umsebenzi ongokoqobo we-XDP kanye nendawo yami yobungcweti. Kodwa-ke, umgomo oyinhloko ukuqonda ubuchwepheshe; lokhu akusona isiqondiso sokudala ukuvikeleka okwenziwe ngomumo. Ikhodi yokufundisa ayithuthukisiwe futhi ishiya ama-nuances athile.

Uhlolojikelele olufushane lwe-XDP

Ngizoveza amaphuzu abalulekile kuphela ukuze ngingaphindi imibhalo nama-athikili akhona.

Ngakho-ke, ikhodi yokuhlunga ilayishwa ku-kernel. Amaphakethe angenayo adluliselwa kusihlungi. Ngenxa yalokho, isihlungi kufanele senze isinqumo: dlulisela iphakethe ku-kernel (XDP_PASS), phonsa iphakethe (XDP_DROP) noma uyithumele emuva (XDP_TX). Isihlungi singashintsha iphakheji, lokhu kuyiqiniso ikakhulukazi ku XDP_TX. Ungakwazi futhi ukuhoxisa uhlelo (XDP_ABORTED) bese usetha kabusha iphakheji, kodwa lokhu kuyafana assert(0) - ukulungisa iphutha.

Umshini obonakalayo we-eBPF (Inwetshiwe ye-Berkley Packet Filter) yenziwe yaba lula ngamabomu ukuze i-kernel ikwazi ukuhlola ukuthi ikhodi ayiluphu futhi ayilimazi yini inkumbulo yabanye abantu. Imikhawulo eqongelelwayo namasheke:

  • Amaluphu (emuva) awavunyelwe.
  • Kukhona isitaki sedatha, kodwa awekho amafunction (yonke imisebenzi ye-C kufanele ibe emgqeni).
  • Ukufinyelela kwimemori ngaphandle kwesitaki kanye nebhafa yephakethe akuvunyelwe.
  • Usayizi wekhodi unomkhawulo, kodwa ekusebenzeni lokhu akubalulekile kakhulu.
  • Izingcingo eziya emisebenzini ye-kernel ekhethekile (abasizi be-eBPF) kuphela ezivunyelwe.

Ukuklama nokufaka isihlungi kubukeka kanjena:

  1. Ikhodi yomthombo (isb kernel.c) ihlanganiswa ibe into (kernel.o) ye-eBPF virtual machine architecture. Kusukela ngo-Okthoba 2019, ukuhlanganiswa kwe-eBPF kusekelwa u-Clang futhi kuthenjiswe ku-GCC 10.1.
  2. Uma le khodi yento iqukethe izingcingo eziya ezinhlakeni ze-kernel (isibonelo, amathebula nezibali), ama-ID azo athathelwa indawo ngoziro, okusho ukuthi leyo khodi ayikwazi ukusetshenziswa. Ngaphambi kokulayisha ku-kernel, udinga ukufaka esikhundleni salezi ziro omazisi bezinto ezithile ezidalwe ngezingcingo ze-kernel (xhuma ikhodi). Ungakwenza lokhu ngezinsiza zangaphandle, noma ungabhala uhlelo oluzoxhuma futhi lulayishe isihlungi esithile.
  3. I-kernel iqinisekisa uhlelo olulayishiwe. Ukungabikho kwemijikelezo nokwehluleka ukweqa iphakethe lemingcele yesitaki kuyahlolwa. Uma isiqinisekisi singakwazi ukufakazela ukuthi ikhodi ilungile, uhlelo lunqatshiwe - udinga ukwazi ukumjabulisa.
  4. Ngemuva kokuqinisekisa ngempumelelo, i-kernel ihlanganisa into ye-eBPF ye-architecture ibe ikhodi yomshini yokwakhiwa kwesistimu (ngesikhathi nje).
  5. Uhlelo lunamathela kusixhumi esibonakalayo futhi luqala ukucubungula amaphakethe.

Njengoba i-XDP isebenza ku-kernel, ukulungisa iphutha kwenziwa kusetshenziswa izingodo zokulandela umkhondo futhi, empeleni, amaphakethe ahlungwa uhlelo noma akhiqize. Nokho, i-eBPF iqinisekisa ukuthi ikhodi elandiwe ivikelekile kusistimu, ukuze ukwazi ukuhlola i-XDP ngokuqondile ku-Linux yangakini.

Ukulungisa Imvelo

Umhlangano

I-Clang ayikwazi ukukhiqiza ngokuqondile ikhodi yento yesakhiwo se-eBPF, ngakho-ke inqubo iqukethe izinyathelo ezimbili:

  1. Hlanganisa ikhodi C ku-LLVM bytecode (clang -emit-llvm).
  2. Guqula i-bytecode ibe yikhodi yento ye-eBPF (llc -march=bpf -filetype=obj).

Lapho ubhala isihlungi, amafayela ambalwa anemisebenzi yokusiza namamakhro azoba usizo kusuka ekuhlolweni kwe-kernel. Kubalulekile ukuthi bahambisane nenguqulo ye-kernel (KVER). Zingenise ku 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

I-Makefile ye-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 iqukethe indlela eya kumaheda e-kernel, ARCH - i-architecture yesistimu. Izindlela namathuluzi kungahluka kancane phakathi kokusabalalisa.

Isibonelo somehluko we-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 xhuma uhla lwemibhalo olunezihloko ezisizayo kanye nemibhalo eminingana enezihloko ze-kernel. Uphawu __KERNEL__ kusho ukuthi izihloko ze-UAPI (userspace API) zichazwa ngekhodi ye-kernel, njengoba isihlungi senziwa ku-kernel.

Ukuvikela isitaki kungakhutshazwa (-fno-stack-protector), ngenxa yokuthi isiqinisekisi sekhodi ye-eBPF sisahlola ukwephulwa kwestaki esingaphandle kwemingcele. Kuyafaneleka ukuvula ukulungiselelwa ngokushesha, ngoba usayizi we-eBPF bytecode unomkhawulo.

Ake siqale ngesihlungi esidlula wonke amaphakethe futhi singenzi lutho:

#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";

Ithimba make iqoqa xdp_filter.o. Ungakuzama kuphi manje?

Isitendi sokuhlola

Isitendi kufanele sifake izixhumanisi ezimbili: lapho kuzoba khona isihlungi nokuthi amaphakethe azothunyelwa kusuka kuphi. Lawa kufanele kube amadivaysi e-Linux agcwele ngokugcwele anama-IP awo ukuze uhlole ukuthi izinhlelo zokusebenza ezivamile zisebenza kanjani nesihlungi sethu.

Amadivayisi ohlobo lwe-veth (virtual Ethernet) asifanele: lezi izixhumi ezibonakalayo zenethiwekhi “ezixhunywe” ngqo komunye nomunye. Ungawadala kanje (kulesi sigaba yonke imiyalo ip zenziwa kusukela root):

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

kuyinto xdp-remote и xdp-local - amagama edivayisi. Vuliwe xdp-local (192.0.2.1/24) isihlungi sizonamathiselwa, nge xdp-remote (192.0.2.2/24) ithrafikhi engenayo izothunyelwa. Kodwa-ke, kunenkinga: izixhumanisi zisemshinini ofanayo, futhi i-Linux ngeke ithumele ithrafikhi kwenye yazo ngomunye. Ungakwazi ukuxazulula lokhu ngemithetho ekhohlisayo iptables, kodwa kuzodingeka bashintshe amaphakheji, okuyinto engalungile ekususeni iphutha. Kungcono ukusebenzisa izikhala zamagama zenethiwekhi (ngemuva kwalokhu inethi).

Isikhala segama senethiwekhi siqukethe isethi yezindawo zokusebenzelana, amathebula omzila, nemithetho ye-NetFilter ehlukanisiwe ezintweni ezifanayo kwamanye amanethi. Inqubo ngayinye isebenza endaweni yamagama futhi inokufinyelela kuphela ezintweni zalawo manethi. Ngokuzenzakalelayo, uhlelo lunesikhala segama senethiwekhi esisodwa sazo zonke izinto, ukuze ukwazi ukusebenza ku-Linux futhi ungazi ngamanethi.

Masidale indawo yamagama entsha xdp-test futhi uyisuse lapho xdp-remote.

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

Bese inqubo iqhubeka xdp-test, ngeke "abone" xdp-local (izohlala kumanethi ngokuzenzakalela) futhi uma ithumela iphakethe ku-192.0.2.1 izolidlula xdp-remotengoba iyona kuphela isikhombimsebenzisi ku-192.0.2.0/24 efinyeleleka kule nqubo. Lokhu kusebenza futhi ngakolunye uhlangothi.

Lapho uhamba phakathi kwamanethi, isixhumi esibonakalayo siyehla futhi silahlekelwe ikheli laso. Ukuze ulungiselele isixhumi esibonakalayo kumanethi, udinga ukugijima ip ... kulesi sikhala segama somyalo 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

Njengoba ubona, lokhu akufani nesilungiselelo xdp-local endaweni yamagama ezenzakalelayo:

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

Uma ugijima tcpdump -tnevi xdp-local, ungabona ukuthi amaphakethe athunyelwe esuka xdp-test, zilethwa kulesi sixhumanisi:

ip netns exec xdp-test   ping 192.0.2.1

Kulula ukufaka igobolondo phakathi xdp-test. Inqolobane ineskripthi esisebenza ngokuzenzakalelayo nesitendi; isibonelo, ungamisa ukuma ngomyalo sudo ./stand up bese uyisusa sudo ./stand down.

Ukulandelela

Isihlungi sihlotshaniswa nedivayisi kanje:

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

Ukhiye -force kudingeka ukuxhumanisa uhlelo olusha uma olunye seluvele luxhunyiwe. "Azikho izindaba eziyizindaba ezinhle" akuphathelene nalo myalo, isiphetho sinamandla kunoma yikuphi. khombisa verbose ngokuzithandela, kodwa ngayo kuvela umbiko ngomsebenzi wokuqinisekisa ikhodi enohlu oluhlanganisiwe:

Verifier analysis:

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

Susa ukuxhumanisa uhlelo kusixhumi esibonakalayo:

ip link set dev xdp-local xdp off

Embhalweni lena imiyalo sudo ./stand attach и sudo ./stand detach.

Ngokunamathisela isihlungi, ungaqinisekisa lokho ping iyaqhubeka nokusebenza, kodwa ingabe uhlelo luyasebenza? Ake sengeze izingodo. Umsebenzi bpf_trace_printk() fana no printf(), kodwa isekela kuphela izimpikiswano ezifika kwamathathu ngaphandle kwephethini, kanye nohlu olulinganiselwe lwezicacisi. Imakhro bpf_printk() yenza ucingo lube lula.

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

Okukhiphayo kuya kusiteshi sokulandelela i-kernel, esidinga ukunikwa amandla:

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

Buka uchungechunge lomlayezo:

cat /sys/kernel/debug/tracing/trace_pipe

Yomibili le miyalo ishaya ucingo sudo ./stand log.

I-Ping manje kufanele icuphe imilayezo enjengale:

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

Uma ubhekisisa okukhiphayo komqinisekisi, uzobona izibalo eziyinqaba:

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
<...>

Iqiniso liwukuthi izinhlelo ze-eBPF azinaso isigaba sedatha, ngakho okuwukuphela kwendlela yokufaka ikhodi yochungechunge lwefomethi izimpikiswano ezisheshayo zemiyalo ye-VM:

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

Ngenxa yalesi sizathu, ukukhishwa kwe-debug kuvimbela kakhulu ikhodi ewumphumela.

Ithumela amaphakethe e-XDP

Masiguqule isihlungi: sisivumele sibuyisele wonke amaphakethe angenayo. Lokhu akulungile ngokubuka kwenethiwekhi, ngoba kuzodingeka ukuthi uguqule amakheli kuzihloko, kodwa manje umsebenzi ngomgomo ubalulekile.

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

Yethula tcpdump on xdp-remote. Kufanele ibonise isicelo esifanayo esiphumayo nesingenayo se-ICMP Echo futhi iyeke ukubonisa i-ICMP Echo Reply. Kodwa akubonisi. Kuvele ukuthi umsebenzi XDP_TX ohlelweni ku xdp-local kuyadingekakusixhumi esibonakalayo sokubhanqa xdp-remote kwabelwa nohlelo, noma lwalungenalutho, wakhuliswa.

Ngakwazi kanjani lokhu?

Landelela indlela yephakheji ku-kernel Indlela yemicimbi ye-perf ivumela, ngendlela, ukusebenzisa umshini ofanayo, okungukuthi, i-eBPF isetshenziselwa ukuhlakazeka nge-eBPF.

Kufanele wenze okuhle ngokubi, ngoba akukho okunye ongakwenza ngaphandle kwakho.

$ 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])
                                     <...>

Yini ikhodi 6?

$ errno 6
ENXIO 6 No such device or address

Umsebenzi veth_xdp_flush_bq() ithola ikhodi yephutha evela veth_xdp_xmit(), lapho cinga khona ENXIO futhi uthole ukuphawula.

Masibuyisele isihlungi esincane (XDP_PASS) kufayela xdp_dummy.c, yengeze ku-Makefile, yibophe kuyo xdp-remote:

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

Manje tcpdump ibonisa okulindelekile:

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

Uma kuphela ama-ARP eboniswa esikhundleni salokho, udinga ukususa izihlungi (lokhu kuyakwenza sudo ./stand detach), yekela ping, bese usetha izihlungi bese uyazama futhi. Inkinga ukuthi isihlungi XDP_TX isebenza kokubili ku-ARP futhi uma isitaki
izikhala zamagama xdp-test ikwazile "ukukhohlwa" ikheli le-MAC 192.0.2.1, ngeke ikwazi ukuxazulula le IP.

Ukwakheka kwenkinga

Masiqhubekele emsebenzini oshiwo: bhala indlela yamakhukhi e-SYN ku-XDP.

Isikhukhula se-SYN sihlala siwukuhlasela kwe-DDoS okudumile, ingqikithi yakho ilandelayo. Uma uxhumano lusungulwa (ukuxhawula kwe-TCP), iseva ithola i-SYN, yabela izinsiza zokuxhuma esikhathini esizayo, iphendula ngephakethe le-SYNACK bese ilinda i-ACK. Umhlaseli umane athumele izinkulungwane zamaphakethe e-SYN ngesekhondi ngalinye ukusuka kumakheli angcolile asuka kumsingathi ngamunye ku-botnet yezinkulungwane eziningi. Iseva iphoqeleka ukuthi inikeze izinsiza ngokushesha lapho iphakethe lifika, kodwa lizikhulula ngemva kwesikhathi esikhulu sokuvala, ngenxa yalokho, inkumbulo noma imingcele iphelile, ukuxhumeka okusha akwamukelwe, futhi isevisi ayitholakali.

Uma ungabeki izinsiza ngokusekelwe kuphakethe le-SYN, kodwa uphendula kuphela ngephakethe le-SYNACK, iseva ingaqonda kanjani ukuthi iphakethe le-ACK elifike kamuva libhekisela ephaketheni le-SYN elingazange ligcinwe? Phela, umhlaseli angaphinda akhiqize ama-ACK mbumbulu. Iphuzu lekhukhi le-SYN ukulifaka ikhodi seqnum amapharamitha wokuxhuma njenge-hash yamakheli, amachweba nokushintsha usawoti. Uma i-ACK ikwazile ukufika ngaphambi kokuthi usawoti ushintshwe, ungabala i-hashi futhi bese uyiqhathanisa nayo acknum. Forge acknum umhlaseli akakwazi, njengoba usawoti uhlanganisa imfihlo, futhi ngeke abe nesikhathi sokuhlunga ngenxa yesiteshi esilinganiselwe.

Ikhukhi ye-SYN kade yasetshenziswa ku-Linux kernel futhi ingavulwa ngokuzenzakalela uma ama-SYN efika ngokushesha futhi ngobuningi.

Uhlelo lwezemfundo ngokuxhawulana kwe-TCP

I-TCP inikeza ukudluliswa kwedatha njengokusakaza kwamabhayithi, isibonelo, izicelo ze-HTTP zidluliselwa nge-TCP. Umfudlana usakazwa ngezicucu ngamaphakethe. Wonke amaphakethe e-TCP anamafulegi anengqondo nezinombolo zokulandelana kwe-32-bit:

  • Inhlanganisela yamafulegi inquma indima yephakheji ethile. Ifulegi le-SYN libonisa ukuthi leli yiphakethe lokuqala lomthumeli ekuxhumekeni. Umaka we-ACK usho ukuthi umthumeli uthole yonke idatha yokuxhumana kuze kube yibhayithi acknum. Iphakethe lingaba namafulegi amaningana futhi libizwa ngenhlanganisela yawo, isibonelo, iphakethe le-SYNACK.

  • Inombolo yokulandelana (seqnum) icacisa i-offset ekusakazweni kwedatha yebhayithi yokuqala edluliselwa kuleli phakethe. Isibonelo, uma ephaketheni lokuqala elinamabhayithi angu-X ledatha le nombolo ibingu-N, ephaketheni elilandelayo elinedatha entsha kuzoba ngu-N+X. Ekuqaleni kokuxhumana, uhlangothi ngalunye lukhetha le nombolo ngokungahleliwe.

  • Inombolo yokuvuma (i-acknum) - i-offset efanayo ne-seqnum, kodwa ayinqumi inombolo yebhayithi edluliswayo, kodwa inombolo yebhayithi yokuqala evela kumamukeli, umthumeli angazange ayibone.

Ekuqaleni kokuxhumana, izinhlangothi kufanele zivumelane seqnum и acknum. Iklayenti lithumela iphakethe le-SYN kanye nalo seqnum = X. Iseva iphendula ngephakethe le-SYNACK, lapho irekhoda khona seqnum = Y futhi udalula acknum = X + 1. Iklayenti liphendula i-SYNACK ngephakethe le-ACK, lapho seqnum = X + 1, acknum = Y + 1. Ngemva kwalokhu, ukudluliswa kwedatha kwangempela kuyaqala.

Uma untanga engakuvumi ukuthola iphakethe, i-TCP ilithumela kabusha ngemva kokuvala isikhathi.

Kungani amakhukhi e-SYN engasetshenziswa njalo?

Okokuqala, uma i-SYNACK noma i-ACK ilahleka, kuzodingeka ulinde ukuthi ithunyelwe futhi - ukusethwa kokuxhumana kuzohamba kancane. Okwesibili, kuphakheji ye-SYN - futhi kuyo kuphela! — eziningi ongakhetha zithunyelwa ezithinta ukuqhubeka ukusebenza uxhumano. Ngaphandle kokukhumbula amaphakethe e-SYN angenayo, iseva iyaziba lezi zinketho; iklayenti ngeke liwathumele kumaphakethe alandelayo. I-TCP ingasebenza kuleli cala, kodwa okungenani esigabeni sokuqala ikhwalithi yokuxhuma izokwehla.

Mayelana namaphakheji, uhlelo lwe-XDP kufanele lenze lokhu okulandelayo:

  • phendula ku-SYN nge-SYNACK ngekhukhi;
  • phendula ku-ACK nge-RST (nqamula);
  • lahla amaphakethe asele.

I-pseudocode ye-algorithm kanye nokuhlaziya iphakheji:

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

Eyodwa (*) amaphuzu lapho udinga ukuphatha isimo sesistimu amakwe - esigabeni sokuqala ungenza ngaphandle kwawo ngokumane usebenzise ukuxhawula kwe-TCP ngokwenza ikhukhi ye-SYN njenge-seqnum.

Eskhaleni (**), kuyilapho singenalo itafula, sizokweqa iphakethe.

Ukusebenzisa ukuxhawula kwe-TCP

Ukuhlaziya iphakheji nokuqinisekisa ikhodi

Sizodinga izakhiwo zesihloko senethiwekhi: I-Ethernet (uapi/linux/if_ether.h), IPv4 (uapi/linux/ip.h) kanye ne-TCP (uapi/linux/tcp.h). Angikwazanga ukuxhuma okwakamuva ngenxa yamaphutha ahlobene ne atomic64_t, kwadingeka ngikopishe izincazelo ezidingekayo kukhodi.

Yonke imisebenzi egqanyiswe kokuthi C ukuze ifundeke kufanele ifakwe emgqeni lapho kushayelwa khona ucingo, njengoba isiqinisekisi se-eBPF ku-kernel sikwenqabela ukubuyisela emuva, okungukuthi, amaluphu kanye namakholi okusebenza.

#define INTERNAL static __attribute__((always_inline))

Imakhro LOG() ikhubaza ukuphrinta esakhiweni sokukhishwa.

Uhlelo lungumdluliseli wemisebenzi. Ngamunye uthola iphakethe lapho unhlokweni wezinga elihambisanayo ugqanyiswa khona, isibonelo, process_ether() lilindele ukuthi ligcwaliswe ether. Ngokusekelwe emiphumeleni yokuhlaziywa kwenkundla, umsebenzi ungadlulisela iphakethe ezingeni eliphezulu. Umphumela womsebenzi isenzo se-XDP. Okwamanje, izibambi ze-SYN ne-ACK zidlula wonke amaphakethe.

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);
}

Ngidonsela ukunaka kwakho kumasheke amakwe u-A no-B. Uma uphawula ngo-A, uhlelo luzokwakha, kodwa kuzoba nephutha lokuqinisekisa uma kulayishwa:

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!

Iyunithi yezinhlamvu engukhiye invalid access to packet, off=13 size=1, R7(id=0,off=0,r=0): Kukhona izindlela zokusebenzisa lapho ibhayithi yeshumi nantathu kusukela ekuqaleni kwebhafa ingaphandle kwephakethe. Kunzima ukuqonda kusuka ohlwini ukuthi yimuphi umugqa esikhuluma ngawo, kodwa kunenombolo yomyalelo (12) kanye ne-disassembler ebonisa imigqa yekhodi yomthombo:

llvm-objdump -S xdp_filter.o | less

Kulokhu ikhomba umugqa

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

okwenza kucace ukuthi inkinga ikhona ether. Kuyoba njalo.

Phendula ku-SYN

Umgomo kulesi sigaba ukukhiqiza iphakethe le-SYNACK elilungile elinesigxili seqnum, ezothathelwa indawo ikhukhi le-SYN ngokuzayo. Zonke izinguquko zenzeka ku process_tcp_syn() kanye nezindawo ezizungezile.

Ukuqinisekiswa kwephakheji

Ngokudabukisayo, nawu umugqa ophawuleka kakhulu, noma kunalokho, ukuphawula kuwo:

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

Lapho kubhalwa inguqulo yokuqala yekhodi, kwasetshenziswa i-5.1 kernel, yesiqinisekisi okwakunomehluko phakathi kwayo. data_end и (const void*)ctx->data_end. Ngesikhathi sokuloba, i-kernel 5.3.1 yayingenayo le nkinga. Kungenzeka ukuthi umhlanganisi ubefinyelela okuguquguqukayo kwendawo ngokuhlukile kunenkambu. Isimilo sendaba: uma isidleke sikukhulu, ukwenza ikhodi ibe lula kungasiza.

Okulandelayo ukuhlola ubude besikhathi ngenkazimulo yesiqinisekisi; O MAX_CSUM_BYTES ngezansi.

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 */
}

Ivula iphakheji

Siyagcwalisa seqnum и acknum, setha okuthi ACK (SYN isivele isethiwe):

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

Shintshanisa izimbobo ze-TCP, ikheli le-IP namakheli e-MAC. Umtapo wolwazi ojwayelekile awutholakali ohlelweni lwe-XDP, ngakho memcpy() - i-macro efihla i-Clang intrinsics.

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);

Ukubalwa kabusha kwamasheke

I-IPv4 ne-TCP checksums idinga ukwengezwa kwawo wonke amagama angu-16-bit kumaheda, futhi usayizi wezihloko ubhalwa kuwo, okungukuthi, awaziwa ngesikhathi sokuhlanganiswa. Lokhu kuyinkinga ngoba isiqinisekisi ngeke seqe iluphu evamile iye kokuhluka komngcele. Kodwa ubukhulu bezihloko bunqunyelwe: kufika ku-64 bytes ngayinye. Ungenza iluphu ngenombolo egxilile yokuphindaphinda, engaphela kusenesikhathi.

Ngiyaqaphela ukuthi kukhona RFC 1624 mayelana nendlela yokubala kabusha ngokwengxenye isheke uma kuphela amagama angaguquki amaphakheji ashintshiwe. Kodwa-ke, le ndlela ayiyona yonke indawo, futhi ukuqaliswa kuzoba nzima kakhulu ukukugcina.

Umsebenzi wokubala we-Checksum:

#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;
}

Nakuba size kuqinisekiswe ikhodi yokushaya, isimo sokuphuma sesibili siyadingeka ukuze isiqinisekisi sikwazi ukufakazela ukuqedwa kweluphu.

Ngamagama angama-32-bit, inguqulo elula isetshenziswa:

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

Empeleni ukubala kabusha amasheke bese ubuyisela iphakethe:

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;

Umsebenzi carry() yenza isheke kusuka ku-32-bit isamba samagama angu-16-bit, ngokusho kwe-RFC 791.

Ukuqinisekiswa kokuxhawula kwe-TCP

Isihlungi simisa kahle ukuxhumana netcat, ilahlekelwe i-ACK yokugcina, lapho i-Linux iphendule khona ngephakethe le-RST, njengoba isitaki senethiwekhi asizange sithole i-SYN - siguqulelwe ku-SYNACK futhi sibuyiselwe emuva - futhi kusukela ekubukeni kwe-OS, iphakethe lafika elingahlobene nokuvula. ukuxhumana.

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

Kubalulekile ukuhlola ngezinhlelo zokusebenza ezigcwele futhi uqaphele tcpdump on xdp-remote ngoba, isibonelo, hping3 ayiphenduli kumashekeshi angalungile.

Ngokombono we-XDP, ukuqinisekiswa ngokwako kuyinto encane. I-algorithm yokubala ingeyakudala futhi kungenzeka ibe sengozini kumhlaseli oyinkimbinkimbi. I-Linux kernel, isibonelo, isebenzisa i-cryptographic SipHash, kodwa ukuqaliswa kwayo kwe-XDP kungaphezu kobubanzi balesi sihloko.

Kwethulwe ama-TODO amasha ahlobene nokuxhumana kwangaphandle:

  • Uhlelo lwe-XDP alukwazi ukugcina cookie_seed (ingxenye eyimfihlo kasawoti) ku-variable global, udinga isitoreji ku-kernel, inani layo elizobuyekezwa ngezikhathi ezithile kusuka ku-generator ethembekile.

  • Uma ikhukhi le-SYN lifana nephakethe le-ACK, awudingi ukuphrinta umlayezo, kodwa khumbula i-IP yeklayenti eliqinisekisiwe ukuze uqhubeke nokudlulisa amaphakethe kuyo.

Ukuqinisekiswa kweklayenti okusemthethweni:

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

Amalogi akhombisa ukuthi isheke liphasile (flags=0x2 - lokhu kuyi-SYN, flags=0x10 i-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

Nakuba lungekho uhlu lwama-IP aqinisekisiwe, ngeke kube khona ukuvikeleka kukhukhula we-SYN ngokwawo, kodwa nansi ukusabela kukhukhula we-ACK owethulwe ngomyalo olandelayo:

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

Okufakiwe kwelogi:

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

isiphetho

Kwesinye isikhathi i-eBPF iyonke kanye ne-XDP ikakhulukazi yethulwa kakhulu njengethuluzi lomlawuli elithuthukile kunenkundla yokuthuthukisa. Ngempela, i-XDP iyithuluzi lokuphazamisa ukucutshungulwa kwamaphakethe yi-kernel, futhi hhayi enye i-kernel stack, njenge-DPDK nezinye izinketho zokudlula i-kernel. Ngakolunye uhlangothi, i-XDP ikuvumela ukuthi usebenzise i-logic eyinkimbinkimbi, okuyinto, ngaphezu kwalokho, kulula ukuyibuyekeza ngaphandle kokuphazamiseka ekucubungulweni kwethrafikhi. Isiqinisekisi asidali izinkinga ezinkulu ngokwami, ngeke nginqabe lokhu ngezingxenye zekhodi yendawo yomsebenzisi.

Engxenyeni yesibili, uma isihloko sithakazelisa, sizoqedela ithebula lamaklayenti aqinisekisiwe nokunqanyulwa, sisebenzise izinto zokubala futhi sibhale insiza yendawo yomsebenzisi ukuze silawule isihlungi.

Izinkomba:

Source: www.habr.com

Engeza amazwana