Sibhala ukhuseleko kuhlaselo lwe-DDoS kwi-XDP. Inxalenye yenyukliya

Indlela yeDatha ye-eXpress (XDP) iteknoloji ivumela ukusetyenzwa kwetrafikhi ngokungakhethiyo ukuba kwenziwe kujongano lweLinux phambi kokuba iipakethi zingene kwisitaki sothungelwano sekernel. Ukusetyenziswa kwe-XDP - ukukhuselwa ekuhlaselweni kwe-DDoS (CloudFlare), izihlungi ezinzima, ukuqokelela izibalo (iNetflix). Iiprogram ze-XDP zenziwa ngumatshini we-eBPF wenyani, ngoko ke zinezithintelo kuzo zombini ikhowudi yazo kunye nemisebenzi ekhoyo yekernel ngokuxhomekeke kudidi lokucoca.

Eli nqaku lijonge ukugcwalisa ukusilela kwemathiriyeli emininzi kwiXDP. Okokuqala, babonelela ngekhowudi esele ilungile edlula ngokukhawuleza iimpawu ze-XDP: ilungiselelwe ukuqinisekiswa okanye ilula kakhulu ukuba ibangele iingxaki. Xa uzama ukubhala ikhowudi yakho ukusuka ekuqaleni, awuyazi into omawuyenze ngeempazamo eziqhelekileyo. Okwesibini, iindlela zokuvavanya i-XDP yasekhaya ngaphandle kwe-VM kunye ne-hardware ayigqunywanga, ngaphandle kokuba banemigibe yabo. Okubhaliweyo kulungiselelwe abadwelisi benkqubo abaqhelene nothungelwano kunye neLinux abanomdla kwi-XDP kunye ne-eBPF.

Kweli candelo, siya kuqonda ngokweenkcukacha ukuba isihluzi se-XDP sidityaniswe njani kwaye siyivavanya njani, emva koko siya kubhala uguqulelo olulula lwendlela eyaziwa ngokuba yi-SYN cookies kumgangatho wokusetyenzwa kwepakethe. Asiyi kudala "uluhlu olumhlophe" okwangoku
abathengi abaqinisekisiweyo, gcina izinto zokubala kwaye ulawule isihluzo - iilogi ezaneleyo.

Siza kubhala ngo-C - ayikho imfashini, kodwa iyasebenza. Yonke ikhowudi iyafumaneka kwi-GitHub ngekhonkco ekugqibeleni kwaye yahlulwe ibe yimisebenzi ngokwezigaba ezichazwe kwinqaku.

Ukuzikhusela. Ebudeni beli nqaku, ndiza kuphuhlisa isisombululo esincinci ukuthintela uhlaselo lwe-DDoS, kuba lo ngumsebenzi wokwenyani we-XDP kunye nendawo yam yobuchwephesha. Nangona kunjalo, injongo ephambili kukuqonda itekhnoloji; oku akusiyo isikhokelo sokudala ukukhuselwa okulungeleyo. Ikhowudi yokufundisa ayilungiswanga kwaye ishiya ama-nuances athile.

Ushwankathelo olufutshane lweXDP

Ndiza kuchaza kuphela iingongoma eziphambili ukwenzela ukuba ndingaphindi amaxwebhu kunye namanqaku akhoyo.

Ke, ikhowudi yokucoca ilayishwe kwi-kernel. Iipakethi ezingenayo zigqithiselwa kwisihluzi. Ngenxa yoko, isihluzi kufuneka senze isigqibo: gqithisa ipakethe kwi kernel (XDP_PASS), yilahla ipakethi (XDP_DROP) okanye yithumele kwakhona (XDP_TX). Isihluzi sinokutshintsha ipakethe, oku kuyinyani ngokukodwa kwi XDP_TX. Ungayeka kwakhona inkqubo (XDP_ABORTED) kwaye usete ngokutsha umqulu, kodwa oku kuyafana assert(0) -ukulungisa ingxaki.

I-eBPF (i-Berkley Packet Filter eyandisiweyo) umatshini wenyani wenziwa lula ngabom ukuze i-kernel ikwazi ukujonga ukuba ikhowudi ayilungi kwaye ayonakalisi inkumbulo yabanye abantu. Izithintelo ezongezelelweyo kunye nokuhlolwa:

  • Iiluphu (ngasemva) azivumelekanga.
  • Kukho isitaki sedatha, kodwa akukho misebenzi (yonke imisebenzi ye-C mayifakwe ngaphakathi).
  • Ufikelelo kwimemori ngaphandle kwesitaki kunye nesithinteli sepakethi akuvumelekanga.
  • Ubungakanani bekhowudi bukhawulelwe, kodwa ekusebenzeni oku akubalulekanga kakhulu.
  • Kuphela iifowuni eziya kwi-special kernel function (abancedisi be-eBPF) ezivumelekileyo.

Ukuyila kunye nokuhlohla isihluzo kujongeka ngolu hlobo:

  1. Ikhowudi yemvelaphi (umz kernel.c) ihlanganiswe ibe yinto (kernel.o) ye-eBPF yoyilo lomatshini wenyani. Ukusukela ngo-Okthobha ka-2019, ukudityaniswa kwe-eBPF kuxhaswa nguClang kwaye kuthenjiswe kwi-GCC 10.1.
  2. Ukuba le khowudi yento iqulethe iifowuni kwizakhiwo ze-kernel (umzekelo, iitafile kunye nezinto zokubala), ii-ID zabo zitshintshwa ngo-zero, okuthetha ukuba loo khowudi ayinakuphunyezwa. Ngaphambi kokulayisha kwi-kernel, kufuneka utshintshe ezi zero kunye ne-ID yezinto ezithile ezenziwe ngeefowuni ze-kernel (qhagamshela ikhowudi). Unokwenza oku ngezixhobo zangaphandle, okanye ungabhala inkqubo eya kudibanisa kwaye ilayishe isihluzo esithile.
  3. I-kernel iqinisekisa inkqubo elayishiweyo. Ukungabikho kwemijikelo kunye nokungaphumeleli ukudlula ipakethi kunye nemida ye-stack ihlolwe. Ukuba umqinisekisi akakwazi ukubonisa ukuba ikhowudi ichanekile, inkqubo iyanqatshwa - kufuneka ukwazi ukumkholisa.
  4. Emva kokuqinisekiswa okuyimpumelelo, i-kernel iqokelela ikhowudi yento yokwakha ye-eBPF kwikhowudi yomatshini woyilo lwenkqubo (ngexesha nje).
  5. Inkqubo incamathela kwi-interface kwaye iqala ukucubungula iipakethi.

Ekubeni i-XDP isebenza kwi-kernel, ukulungiswa kweempazamo kuqhutywa kusetyenziswa iilogi zomkhondo kwaye, eneneni, iipakethi ezihluzwayo zeprogram okanye zivelise. Nangona kunjalo, i-eBPF iqinisekisa ukuba ikhowudi ekhutshelweyo ikhuselekile kwisixokelelwano, ngoko unokuzama nge-XDP ngqo kwi-Linux yakho yasekhaya.

Ukulungisa iNdawo

Ndibano

UClang akakwazi ukuvelisa ngokuthe ngqo ikhowudi yento yoyilo lwe-eBPF, ke inkqubo inamanyathelo amabini:

  1. Qokelela ikhowudi yeC kwi-LLVM bytecode (clang -emit-llvm).
  2. Guqula i-bytecode ibe yikhowudi yento ye-eBPF (llc -march=bpf -filetype=obj).

Xa ubhala isihluzo, iifayile ezimbalwa ezinemisebenzi encedisayo kunye neemacros ziya kuba luncedo ukusuka kuvavanyo lwe-kernel. Kubalulekile ukuba zihambelane noguqulelo lwe kernel (KVER). Zikhuphelele 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

Makefile kwiArch 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 iqulathe umendo wentloko yekernel, ARCH - uyilo lwenkqubo. Iindlela kunye nezixhobo zinokwahluka kancinci phakathi konikezelo.

Umzekelo womahluko 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 Qhagamshela uvimba weefayili oneentloko ezincedisayo kunye nezalathisi ezininzi ezineentloko zekernel. Uphawu __KERNEL__ ithetha ukuba i-UAPI (isithuba se-API) iiheader zichazwe kwikhowudi ye-kernel, kuba isihluzo senziwe kwi-kernel.

Ukukhuselwa kwesitaki kunokucinywa (-fno-stack-protector), kuba uqinisekiso lwekhowudi ye-eBPF lusajonga ukwaphulwa kwe-stack out-of-bounds. Kuyafaneleka ukuba uvule ukulungiswa kwangoko, kuba ubungakanani be-bytecode ye-eBPF bulinganiselwe.

Masiqale ngesihluzo esidlula zonke iipakethi kwaye singenzi nto:

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

Iqela make uqokelela xdp_filter.o. Ungayizama phi ngoku?

ibhentshi yovavanyo

Indawo yokuma kufuneka ibandakanye i-interfaces ezimbini: apho kuya kubakho isihluzi kwaye apho iipakethi ziya kuthunyelwa. Ezi kufuneka zibe zizixhobo zeLinux ezipheleleyo ezinee-IP zazo ukuze kujongwe ukuba usetyenziso oluqhelekileyo lusebenza njani nesihluzi sethu.

Izixhobo zohlobo lwe-veth (virtual Ethernet) zifanelekile kuthi: ezi zimbini zonxibelelwano lwenethiwekhi "ezixhunyiwe" ngokuthe ngqo omnye komnye. Ungazenza ngolu hlobo (kweli candelo yonke imiyalelo ip ziqhutywa ukusuka root):

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

kuyinto xdp-remote и xdp-local - amagama esixhobo. Vula xdp-local (192.0.2.1/24) isihluzi siya kuqhotyoshelwa, nge xdp-remote (192.0.2.2/24) itrafikhi engenayo iya kuthunyelwa. Nangona kunjalo, kukho ingxaki: ujongano kukumatshini omnye, kwaye iLinux ayizukuthumela itrafikhi kwenye yazo ngaphaya komnye. Ungayisombulula oku ngemithetho ekhohlisayo iptables, kodwa kuya kufuneka batshintshe iipakethe, ezingalunganga xa ulungisa iimpazamo. Kungcono ukusebenzisa izithuba zamagama womnatha (emva koku netns).

Isithuba segama lomsebenzi womnatha sineseti yojongano, iitafile zendlela, kunye neNetFilter imithetho ebekwe yodwa kwizinto ezifanayo kwezinye iinethi. Inkqubo nganye iqhuba kwisithuba samagama kwaye inofikelelo kuphela kwizinto zezo net. Ngokungagqibekanga, inkqubo inesithuba segama sothungelwano esinye kuzo zonke izinto, ukuze ukwazi ukusebenza kwiLinux kwaye ungazi malunga nenetha.

Masenze indawo entsha yamagama xdp-test kwaye uyisuse apho xdp-remote.

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

Emva koko inkqubo iyasebenza xdp-test, andiyi "kubona" xdp-local (iya kuhlala kwinetha ngokungagqibekanga) kwaye xa uthumela ipakethi ku-192.0.2.1 iya kudlula kuyo. xdp-remotekuba kuphela ujongano kwi-192.0.2.0/24 efikelelekayo kule nkqubo. Oku kusebenza kwelinye icala.

Xa uhamba phakathi kweenetha, i-interface iyehla kwaye ilahlekelwe yidilesi yayo. Ukuqwalasela ujongano kwi-netns, kufuneka usebenze ip ... kwesi sithuba samagama somyalelo 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

Njengoko ubona, oku akwahlukanga kwisimo xdp-local kwisithuba samagama esimiselweyo:

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

Ukuba uyabaleka tcpdump -tnevi xdp-local, ungabona ukuba iipakethi zithunyelwe ukusuka xdp-test, zisiwe kolu jongano:

ip netns exec xdp-test   ping 192.0.2.1

Kukulungele ukusungula iqokobhe ngaphakathi xdp-test. I-repository ineskripthi esenza ngokuzenzekelayo ukusebenza kunye nesigxina; sudo ./stand up kwaye uyicime sudo ./stand down.

Ukulandela umkhondo

Isihluzi sidityaniswa nesixhobo esinje:

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

Ngundoqo -force Kufuneka ukudibanisa inkqubo entsha ukuba enye sele inxulunyanisiwe. "Akukho ndaba zimnandi" azikho malunga nalo myalelo, isiphelo sinamandla kuyo nayiphi na imeko. bonisa verbose ngokuzikhethela, kodwa kunye nayo kuvela ingxelo kumsebenzi wekhowudi yokuqinisekisa ngoluhlu lwendibano:

Verifier analysis:

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

Susa unxulumaniso lwenkqubo kujongano:

ip link set dev xdp-local xdp off

Kwiscript le yimiyalelo sudo ./stand attach и sudo ./stand detach.

Ngokuncamathisela isihluzo, ungaqiniseka ukuba ping iyaqhubeka ukusebenza, kodwa ngaba inkqubo iyasebenza? Makhe songeze iinkuni. Umsebenzi bpf_trace_printk() iyelelene kwi printf(), kodwa ixhasa kuphela ukuya kuthi ga kwiimpikiswano ezintathu ngaphandle kwepatheni, kunye noluhlu oluqingqiweyo lwabaxeli. Ubukhulu becala bpf_printk() yenza lula umnxeba.

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

Imveliso iya kwisitishi somkhondo we kernel, ekufuneka yenziwe:

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

Jonga umsonto womyalezo:

cat /sys/kernel/debug/tracing/trace_pipe

Yomibini le miyalelo yenza umnxeba sudo ./stand log.

I-Ping ngoku kufuneka iqalise imiyalezo efana nale:

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

Ukuba ujongisisa isiphumo somqinisekisi, uya kuqaphela izibalo ezingaqhelekanga:

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

Inyani yeyokuba iinkqubo ze-eBPF azinalo icandelo ledatha, ke ekuphela kwendlela yokufaka ikhowudi yoluhlu lwefomathi ziingxoxo ezikhawulezileyo zemiyalelo ye-VM:

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

Ngesi sizathu, imveliso yedebug ibhuqa kakhulu ikhowudi enesiphumo.

Ukuthumela iiPakethi zeXDP

Masitshintshe isihluzo: sivumele sibuyisele zonke iipakethi ezingenayo. Oku akuchanekanga kumbono womnatha, kuba kuya kufuneka ukuba utshintshe iidilesi kwii-headers, kodwa ngoku umsebenzi ngomgaqo ubalulekile.

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

Qalisa tcpdump phezu xdp-remote. Kufuneka ibonise isicelo esifanayo esiphumayo kunye nengenayo ye-ICMP Echo kwaye uyeke ukubonisa i-ICMP Echo Reply. Kodwa ayibonakali. Kuyavela ukuba umsebenzi XDP_TX kwinkqubo kwi xdp-local iyimfunekokujongano lwesibini xdp-remote kwabelwa inkqubo, nokuba yayingenanto, yaye wakhuliswa.

Ndiyazi njani le nto?

Landela umendo wepakethe kwi-kernel I-perf events mechanism ivumela, ngendlela, ukusebenzisa umatshini ofanayo, oko kukuthi, i-eBPF isetyenziselwa ukuchithwa kunye ne-eBPF.

Kufuneka wenze okulungileyo kokubi, kuba akukho nto yimbi unokuyenza.

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

Yintoni ukhowudi 6?

$ errno 6
ENXIO 6 No such device or address

Umsebenzi veth_xdp_flush_bq() ifumana ikhowudi yemposiso kwi veth_xdp_xmit(), apho phendla nge ENXIO kwaye ufumane inkcazo.

Masibuyisele esona sihluzo sisezantsi (XDP_PASS) kwifayile xdp_dummy.c, yongeze kwi-Makefile, yibophe kuyo xdp-remote:

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

Ngoku tcpdump ibonisa okulindelekileyo:

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

Ukuba kuphela ii-ARP ezibonisiwe endaweni yoko, kufuneka ususe izihluzi (oku kwenza sudo ./stand detach), Yekela ping, emva koko usete izihluzi kwaye uzame kwakhona. Ingxaki kukuba isihluzo XDP_TX isebenza zombini kwi-ARP kwaye ukuba isitakhi
izithuba zamagama xdp-test ikwazile "ukulibala" idilesi ye-MAC 192.0.2.1, ayiyi kukwazi ukusombulula le IP.

Џџ ѕЃ ° °

Masiqhubele phambili kumsebenzi ochaziweyo: bhala inkqubo yeekuki zeSYN kwiXDP.

Umkhukula we-SYN uhlala uhlaselo oludumileyo lweDDoS, umongo walo ulandelayo. Xa uxhulumaniso lusekwe (i-TCP handshake), umncedisi ufumana i-SYN, unikezela izibonelelo zoxhumo oluzayo, uphendule ngepakethi ye-SYNACK kwaye ulinde i-ACK. Umhlaseli uthumela nje amawaka eepakethi zeSYN ngesekhondi ukusuka kwiidilesi ezigqwethiweyo ukusuka kumamkeli ngamnye kwi-botnet engamawaka-amandla. Umncedisi unyanzelekile ukuba abele izixhobo ngokukhawuleza ekufikeni kwepakethi, kodwa azikhulule emva kwexesha elikhulu, ngenxa yoko, imemori okanye imida iphelile, uxhulumaniso olutsha aluvunyelwanga, kwaye inkonzo ayifumaneki.

Ukuba awubeki izibonelelo ngokusekelwe kwipakethi ye-SYN, kodwa uphendule kuphela ngepakethi ye-SYNACK, umncedisi unokuqonda njani ukuba ipakethe ye-ACK efike kamva ibhekisela kwipakethi ye-SYN engazange igcinwe? Emva kwayo yonke loo nto, umhlaseli unokuvelisa ii-ACK zobuxoki. Inqaku lekuki yeSYN kukuyifaka ikhowudi kuyo seqnum iiparameters zokuxhuma njenge-hash yeedilesi, amachweba kunye nokutshintsha ityuwa. Ukuba i-ACK ikwazile ukufika ngaphambi kokuba ityuwa iguqulwe, unokubala i-hash kwakhona kwaye uyithelekise nayo acknum. Forge acknum umhlaseli akakwazi, ekubeni ityuwa ibandakanya imfihlelo, kwaye ayiyi kuba nexesha lokuyihlela ngenxa yejelo elilinganiselweyo.

Icookie yeSYN kudala iphunyezwa kwiLinux kernel kwaye inokwenziwa ngokuzenzekelayo ukuba iiSYNs zifika ngokukhawuleza kwaye zininzi.

Inkqubo yezemfundo kwi-TCP ukuxhawula izandla

I-TCP inikeza ukuhanjiswa kwedatha njengomlambo wee-bytes, umzekelo, izicelo ze-HTTP zithunyelwa kwi-TCP. Umlambo usasazwa ngamaqhekeza kwiipakethi. Zonke iipakethi ze-TCP zineeflegi ezinengqiqo kunye neenombolo ze-32-bit zokulandelelana:

  • Ukudibanisa iiflegi kugqiba indima yephakheji ethile. Iflegi ye-SYN ibonisa ukuba le yipakethi yokuqala yomthumeli kuqhagamshelwano. Iflegi ye-ACK ithetha ukuba umthumeli ufumene yonke idatha yoqhagamshelwano ukuya kwi-byte acknum. Ipakethi ingaba neeflegi ezininzi kwaye ibizwa ngokudibanisa kwazo, umzekelo, ipakethe yeSYNACK.

  • Inombolo yolandelelwano (i-seqnum) ikhankanya i-offset kumjelo wedatha kwi-byte yokuqala ethunyelwa kule pakethi. Umzekelo, ukuba kwipakethe yokuqala ene-X bytes yedatha eli nani lalingu-N, kwipakethe elandelayo enedatha entsha iya kuba ngu-N+X. Ekuqaleni koqhakamshelwano, icala ngalinye likhetha eli nani ngokungakhethiyo.

  • Inombolo yokuvuma (i-acknum) - i-offset efanayo ne-seqnum, kodwa ayinqumi inani le-byte ehanjiswayo, kodwa inani le-byte yokuqala evela kummkeli, apho umthumeli engazange abone.

Ekuqaleni koqhagamshelwano, amaqela kufuneka avumelane seqnum и acknum. Umxhasi uthumela ipakethi yeSYN kunye nayo seqnum = X. Umncedisi uphendula ngepakethi yeSYNACK, apho irekhoda khona seqnum = Y kwaye ubhenca acknum = X + 1. Umxhasi uphendula kwi-SYNACK ngepakethi ye-ACK, apho seqnum = X + 1, acknum = Y + 1. Emva koku, ugqithiso lwedatha lwangempela luqala.

Ukuba intanga ayivumi ukufumana ipakethi, i-TCP iyithumela kwakhona emva kwexesha.

Kutheni iikuki zeSYN zingasoloko zisetyenziswa?

Okokuqala, ukuba i-SYNACK okanye i-ACK ilahlekile, kuya kufuneka ulinde ukuba ithunyelwe kwakhona - ucwangciso loqhagamshelwano luya kucotha. Okwesibini, kwiphakheji ye-SYN - kwaye kuyo kuphela! — inani leenketho ziyahanjiswa ezichaphazela ukuqhubela phambili kokusebenza koqhagamshelwano. Ngaphandle kokukhumbula iipakethi ze-SYN ezingenayo, umncedisi akazihoyi ezi zikhetho; umxhasi akayi kuzithumela kwiipakethi ezilandelayo. I-TCP inokusebenza kule meko, kodwa ubuncinci kwinqanaba lokuqala umgangatho wokudibanisa uya kuncipha.

Ngokwembono yeephakheji, inkqubo yeXDP kufuneka yenze oku kulandelayo:

  • phendula kwi-SYN nge-SYNACK ngecookie;
  • phendula kwi-ACK nge-RST (khulula);
  • lahla iipakethi eziseleyo.

I-pseudocode ye-algorithm kunye nokwahlulahlula kwephakheji:

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

Nye (*) amanqaku apho kufuneka ulawule imeko yenkqubo iphawulwe - kwinqanaba lokuqala ungenza ngaphandle kwabo ngokulula ukuphumeza ukuxhawula TCP kunye nesizukulwana SYN cookie njenge seqnum.

Ngqo kwichaphaza (**), ngoxa singenayo itafile, siya kuyitsiba ipakethi.

Ukuphumeza ukuxhawula ngesandla kwe-TCP

Ukwahlulahlula iphakheji kunye nokuqinisekisa ikhowudi

Siza kufuna izakhiwo zentloko yenethiwekhi: Ethernet (uapi/linux/if_ether.h), IPv4 (uapi/linux/ip.h) kunye ne-TCP (uapi/linux/tcp.h). Andikwazanga ukudibanisa le yokugqibela ngenxa yeempazamo ezinxulumene ne atomic64_t, Kwafuneka ndikhuphele iinkcazo eziyimfuneko kwikhowudi.

Yonke imisebenzi egxininiswe ku-C ukuze ifundeke kufuneka ifakwe kwi-inline kwindawo yokufowuna, kuba i-eBPF verifier in kernel iyakunqanda ukubuyisela umva, oko kukuthi, i-loops kunye neefowuni zokusebenza.

#define INTERNAL static __attribute__((always_inline))

Ubukhulu becala LOG() ikhubaza ushicilelo kwisakhiwo sokukhulula.

Inkqubo ngumdlulisi wemisebenzi. Ngamnye ufumana ipakethe apho umgangatho ohambelanayo wesihloko ugxininisiwe, umzekelo, process_ether() ilindele ukuba izaliswe ether. Ngokusekelwe kwiziphumo zokuhlalutya intsimi, umsebenzi unokudlula ipakethi ukuya kwinqanaba eliphezulu. Isiphumo somsebenzi sisenzo seXDP. Okwangoku, abaphathi be-SYN kunye ne-ACK badlula zonke iipakethi.

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

Nditsala ingqalelo yakho kwiitshekhi eziphawulwe A kunye B. Ukuba uphawula ngaphandle A, inkqubo iya kwakha, kodwa kuya kubakho impazamo yokuqinisekisa xa ulayisha:

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!

Umtya ongundoqo invalid access to packet, off=13 size=1, R7(id=0,off=0,r=0): Kukho iindlela zokwenziwa xa i-byte yeshumi elinesithathu ukusuka ekuqaleni kwesithinteli ingaphandle kwepakethi. Kunzima ukuqonda kuluhlu ukuba ngowuphi umgca esithetha ngawo, kodwa kukho inombolo yomyalelo (12) kunye ne-disassembler ebonisa imigca yekhowudi yomthombo:

llvm-objdump -S xdp_filter.o | less

Kule meko ikhomba kumgca

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

nto leyo eyenza kucace ukuba ingxaki ether. Bekuya kuhlala kunje.

Phendula ku-SYN

Injongo kweli nqanaba kukuvelisa ipakethi echanekileyo yeSYNACK enesigxina seqnum, eya kutshintshwa kwixa elizayo yi SYN cookie. Lonke utshintsho lwenzeka ngaphakathi process_tcp_syn() kunye neendawo ezingqongileyo.

Ukuqinisekiswa kwephakheji

Okumangalisayo kukuba, nanku owona mgca uphawulekayo, okanye kunoko, inkcazo kuwo:

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

Xa ubhala inguqulelo yokuqala yekhowudi, i-5.1 kernel isetyenzisiwe, ukwenzela ungqinisiso apho kwakukho umahluko phakathi data_end и (const void*)ctx->data_end. Ngexesha lokubhalwa kwayo, ikernel 5.3.1 ibingenayo le ngxaki. Kunokwenzeka ukuba umqokeleli wayefikelela kuguquko lwasekhaya ngokwahlukileyo kunentsimi. Ukuziphatha kwebali: Ukwenza lula ikhowudi kunokunceda xa kukho indlwane eninzi.

Okulandelayo ziitshekhi zobude besiqhelo zozuko lwesiqinisekisi; O MAX_CSUM_BYTES ngezantsi.

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

Ukutyhila ipakethe

Gcwalisa seqnum и acknum, seta ACK (SYN sele isetiwe):

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

Tshintsha izibuko ze-TCP, idilesi ye-IP kunye needilesi ze-MAC. Ithala leencwadi elisemgangathweni alifumaneki kwinkqubo yeXDP, ngoko memcpy() - i-macro efihla i-intrinsics ye-Clang.

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 kwakhona kweetshekhi

IPv4 kunye ne-TCP checksums zifuna ukongezwa kwawo onke amagama e-16-bit kwi-header, kwaye ubungakanani beeheda zibhalwe kuzo, oko kukuthi, ayaziwa ngexesha lokuqokelela. Le yingxaki kuba umqinisekisi akayi kutsiba i-loop eqhelekileyo ukuya kuguquko lomda. Kodwa ubungakanani beeheda bukhawulelwe: ukuya kuthi ga kwi-64 bytes nganye. Unokwenza i-loop ngenani elimiselweyo lokuphindaphinda, elinokuphela kwangaphambili.

Ndiyaqaphela ukuba kukho RFC 1624 malunga nendlela yokubala kwakhona ngokuyinxenye itshekhisum ukuba kuphela amagama amiselweyo eepakethe atshintshiweyo. Nangona kunjalo, indlela ayikho jikelele, kwaye ukuphunyezwa kuya kuba nzima ukuyigcina.

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

Nangona size iqinisekiswe yikhowudi yokufowuna, imeko yesibini yokuphuma iyimfuneko ukuze umqinisekisi aqinisekise ukugqitywa kweluphu.

Kumagama angama-32-bit, inguqulelo elula iyaphunyezwa:

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

Ngokwenyani ukubala kwakhona iitshekhi kunye nokuthumela ipakethi emva:

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 itshekhi ukusuka kwi-32-bit sum yamagama-bit-16, ngokwe-RFC 791.

Ukuqinisekiswa kokuxhawula ngesandla kwe-TCP

Isihluzi siseka ngokuchanekileyo umdibaniso nge netcat, ilahlekile i-ACK yokugqibela, apho i-Linux iphendule ngepakethi ye-RST, ekubeni i-stack yenethiwekhi ayizange ifumane i-SYN - iguqulelwe kwi-SYNACK kwaye ithunyelwe emva - kwaye ukusuka kwindawo yokujonga i-OS, ipakethi yafika eyayingahambelani nokuvula. imidibaniso.

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

Kubalulekile ukujonga kunye nezicelo ezipheleleyo kwaye uqaphele tcpdump phezu xdp-remote ngokuba, umzekelo, hping3 ayiphenduli kwii-checksum ezingachanekanga.

Ukusuka kwimbono ye-XDP, uqinisekiso ngokwalo luncinci. I-algorithm yokubala yeyakudala kwaye inokuba sesichengeni kumhlaseli ophucukileyo. I-Linux kernel, umzekelo, isebenzisa i-cryptographic SipHash, kodwa ukuphunyezwa kwayo kwe-XDP ngokucacileyo ngaphaya kobubanzi beli nqaku.

Yaziswa kwii-TODO ezintsha ezinxulumene nonxibelelwano lwangaphandle:

  • Inkqubo ye-XDP ayikwazi ukugcina cookie_seed (inxalenye eyimfihlo yetyuwa) kwi-variable variable global, udinga ukugcinwa kwi-kernel, ixabiso eliya kuhlaziywa ngezikhathi ezithile ukusuka kwi-generator ethembekileyo.

  • Ukuba i-cookie ye-SYN ihambelana nepakethi ye-ACK, akudingeki ukuba uprinte umyalezo, kodwa khumbula i-IP yomxhasi oqinisekisiweyo ukuze uqhubeke nokudlulisa iipakethi kuyo.

Ukuqinisekiswa komxhasi okusemthethweni:

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

Iilog zibonisa ukuba itshekhi igqithile (flags=0x2 - le yiSYN, flags=0x10 ngu 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

Ngelixa akukho luhlu lwee-IPs eziqinisekisiweyo, akuyi kubakho khuseleko kwi-SYN yezikhukhula ngokwayo, kodwa nantsi indlela yokusabela kumkhukula we-ACK oqaliswe ngumyalelo ulandelayo:

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

Log amangeniso:

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

isiphelo

Ngamanye amaxesha i-eBPF ngokubanzi kunye ne-XDP ngokukodwa zibonakaliswa njengesixhobo somlawuli ophucukileyo kuneqonga lophuhliso. Ngokwenene, i-XDP sisixhobo sokuphazamisa ukusetyenzwa kweepakethi yi-kernel, kwaye ayisiyiyo enye indawo kwi-kernel stack, njenge-DPDK kunye nezinye iinketho zokudlula i-kernel. Kwelinye icala, i-XDP ikuvumela ukuba usebenzise ingqiqo entsonkothileyo, ekulula ukuyihlaziya ngaphandle kokuphazamiseka ekusetyenzweni kwetrafikhi. Umqinisekisi akenzi iingxaki ezinkulu ngokobuqu, andizukwala oku kwiindawo zekhowudi yesithuba.

Kwicandelo lesibini, ukuba isihloko sinomdla, siya kugqiba itheyibhile yabathengi abaqinisekisiweyo kunye nokuqhawula, ukuphumeza izinto zokubala kwaye ubhale i-userspace utility ukulawula isihluzo.

Izalathiso:

umthombo: www.habr.com

Yongeza izimvo