Bidh sinn a’ sgrìobhadh dìon an aghaidh ionnsaighean DDoS air XDP. Pàirt niuclasach

Tha teicneòlas eXpress Data Path (XDP) a’ ceadachadh giullachd neo-riaghailteach air trafaic air eadar-aghaidh Linux mus tèid pacaidean a-steach don chruach lìonra kernel. Cur an gnìomh XDP - dìon an aghaidh ionnsaighean DDoS (CloudFlare), sìoltachain iom-fhillte, cruinneachadh staitistig (Netflix). Tha prògraman XDP air an cur an gnìomh leis an inneal brìgheil eBPF, agus mar sin tha cuingealachaidhean aca air an dà chuid an còd aca agus na gnìomhan kernel a tha rim faighinn, a rèir an seòrsa sìoltachain.

Tha an artaigil ag amas air dèanamh suas airson easbhaidhean grunn stuthan air XDP. An toiseach, bidh iad a 'toirt seachad còd deiseil a tha a' dol seachad air feartan XDP sa bhad: deiseil airson dearbhadh no ro shìmplidh airson duilgheadasan adhbhrachadh. Nuair a dh’ fheuchas tu ris a’ chòd agad fhèin a sgrìobhadh bhon fhìor thoiseach, chan eil tuigse sam bith ann dè a nì thu le mearachdan àbhaisteach. San dàrna h-àite, chan eil e a’ còmhdach dhòighean air XDP a dhearbhadh gu h-ionadail às aonais VM agus bathar-cruaidh, a dh’ aindeoin gu bheil na duilgheadasan aca fhèin. Tha an teacsa airson luchd-prògramaidh a tha eòlach air lìonraidhean agus Linux aig a bheil ùidh ann an XDP agus eBPF.

Anns a ’phàirt seo, tuigidh sinn gu mionaideach mar a tha an sìoltachan XDP air a chruinneachadh agus mar a nì thu deuchainn air, an uairsin sgrìobhaidh sinn dreach sìmplidh den inneal briosgaidean SYN ainmeil aig ìre giollachd pacaid. Gus an cruthaich sinn "liosta geal"
luchd-dèiligidh dearbhte, cùm cunntairean agus stiùir an criathrag - logaichean gu leòr.

Sgrìobhaidh sinn ann an C - chan eil seo fasanta, ach practaigeach. Tha a h-uile còd ri fhaighinn air GitHub aig a’ cheangal aig an deireadh agus tha e air a roinn ann an geallaidhean a rèir nan ceumannan a tha air am mìneachadh san artaigil.

Àicheadh. Ann an cùrsa an artaigil, thèid mion-fhuasgladh airson ionnsaighean DDoS ath-bhualadh a leasachadh, oir is e gnìomh fìor a tha seo airson XDP agus an sgìre agam. Ach, is e am prìomh amas an teicneòlas a thuigsinn, chan e stiùireadh a tha seo airson dìon deiseil a chruthachadh. Chan eil an còd oideachaidh air a mheudachadh agus tha e a’ fàgail cuid de nuances.

Sealladh farsaing air XDP

Cha toir mi cunntas ach air na prìomh phuingean gus nach dùblaich mi sgrìobhainnean agus artaigilean a tha ann mar-thà.

Mar sin, tha an còd sìoltachain air a luchdachadh a-steach don kernel. Thèid an sìoltachan seachad air pacaidean a thig a-steach. Mar thoradh air an sin, feumaidh an sìoltachan co-dhùnadh a dhèanamh: am pasgan a chuir chun kernel (XDP_PASS), pasgan tuiteam (XDP_DROP) no cuir air ais e (XDP_TX). Faodaidh an criathrag am pasgan atharrachadh, tha seo gu sònraichte fìor airson XDP_TX. Faodaidh tu cuideachd am prògram a mhilleadh (XDP_ABORTED) agus leig às a’ phacaid, ach tha seo coltach assert(0) - airson debugging.

Tha an inneal brìgheil eBPF (Berkley Packet Filter leudaichte) air a dhèanamh a dh’aona ghnothach sìmplidh gus an urrainn don kernel dèanamh cinnteach nach eil an còd a ’lùbadh agus nach dèan e cron air cuimhne dhaoine eile. Cuingeachaidhean agus sgrùdaidhean mean air mhean:

  • Thathas a’ toirmeasg lùban (leum air ais).
  • Tha stac ann airson dàta, ach chan eil gnìomhan ann (feumaidh a h-uile gnìomh C a bhith air a chòmhdach).
  • Thathas a’ toirmeasg ruigsinneachd gu cuimhne taobh a-muigh na stac agus bufair pacaid.
  • Tha meud a 'chòd cuingealaichte, ach ann an cleachdadh chan eil seo glè chudromach.
  • Chan eil ach gnìomhan kernel sònraichte (luchd-cuideachaidh eBPF) ceadaichte.

Tha leasachadh agus stàladh criathrag a’ coimhead mar seo:

  1. còd stòr (m.e. kernel.c) a’ cur ri chèile airson nì (kernel.o) airson ailtireachd inneal brìgheil eBPF. Bhon Dàmhair 2019, tha cruinneachadh gu eBPF a’ faighinn taic bho Clang agus air a ghealltainn ann an GCC 10.1.
  2. Ma tha fiosan anns a’ chòd nì seo gu structaran kernel (mar eisimpleir, gu bùird is cunntairean), an àite an IDan tha neoni ann, is e sin, chan urrainnear an còd sin a chuir gu bàs. Mus tèid a luchdachadh a-steach don kernel, feumar IDan de nithean sònraichte a chaidh a chruthachadh tro ghlaodhan kernel a chuir an àite nan neamhan sin (ceangal a’ chòd). Faodaidh tu seo a dhèanamh le goireasan taobh a-muigh, no faodaidh tu prògram a sgrìobhadh a nì ceangal agus luchdachadh criathrag sònraichte.
  3. Bidh an kernel a’ dearbhadh am prògram a tha ga luchdachadh. Bidh e a’ dèanamh sgrùdadh air dìth chuairtean agus gun a bhith a’ fàgail crìochan pacaid is stac. Mura h-urrainn don neach-dearbhaidh dearbhadh gu bheil an còd ceart, tha am prògram air a dhiùltadh - feumaidh fear a bhith comasach air a thoileachadh.
  4. Às deidh dearbhadh soirbheachail, bidh an kernel a’ cur ri chèile còd nì ailtireachd eBPF a-steach do chòd inneal ailtireachd siostam (dìreach ann an àm).
  5. Tha am prògram ceangailte ris an eadar-aghaidh agus a 'tòiseachadh a' giollachd phasgan.

Leis gu bheil XDP a’ ruith anns an kernel, bidh dì-bhugachadh air a dhèanamh le logaichean lorg agus, gu dearbh, le pacaidean a bhios am prògram a’ sìoladh no a’ gineadh. Ach, bidh eBPF a’ cumail a’ chòd a chaidh a luchdachadh sìos sàbhailte airson an t-siostaim, gus an urrainn dhut XDP fheuchainn dìreach air an Linux ionadail agad.

Ag ullachadh na h-àrainneachd

Tionndadh

Chan urrainn dha Clang còd nì a chuir a-mach gu dìreach airson ailtireachd eBPF, agus mar sin tha dà cheum sa phròiseas:

  1. Cruinnich còd C gu bytecode LLVM (clang -emit-llvm).
  2. Tionndaidh bytecode gu còd nì eBPF (llc -march=bpf -filetype=obj).

Nuair a bhios tu a’ sgrìobhadh criathrag, thig faidhle no dhà le gnìomhan taice agus macros gu feum bho dheuchainnean kernel. Tha e cudromach gum bi iad a rèir an dreach kernel (KVER). Luchdaich sìos iad gu 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 airson 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 tha an t-slighe gu cinn nan kernel, ARCH - ailtireachd siostam. Faodaidh slighean agus innealan atharrachadh beagan eadar sgaoilidhean.

Eisimpleir eadar-dhealaichte airson 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 cuir a-steach eòlaire le cinn-cinn cuideachail agus grunn chlàran le cinn kernel. samhladh __KERNEL__ a’ ciallachadh gu bheil cinn-cinn UAPI (userspace API) air am mìneachadh airson a’ chòd kernel, leis gu bheil an sìoltachan air a chuir gu bàs anns an kernel.

Faodar dìon stac a chuir à comas (-fno-stack-protector) leis gu bheil an neach-dearbhaidh còd eBPF a’ sgrùdadh airson crìochan nach eil a-mach à stac co-dhiù. Bu chòir dhut optimizations a chomasachadh sa bhad, leis gu bheil meud còd byte eBPF cuibhrichte.

Feuch an tòisich sinn le criathrag a thèid seachad air a h-uile pacaid agus nach dèan dad:

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

sgioba make a ’cruinneachadh xdp_filter.o. Càite an urrainn dhut deuchainn a dhèanamh air a-nis?

seasamh deuchainn

Bu chòir dà eadar-aghaidh a bhith anns an stand: air am bi criathrag agus às an tèid na pacaidean a chuir. Feumaidh iad sin a bhith nan innealan làn Linux leis na IPan aca fhèin gus dèanamh cinnteach mar a bhios tagraidhean cunbhalach ag obair leis a’ chriathrag againn.

Tha innealan mar veth (Virtual Ethernet) freagarrach dhuinne: tha iad nan paidhir de eadar-aghaidh lìonra brìgheil “ceangailte” gu dìreach ri chèile. Faodaidh tu an cruthachadh mar seo (anns an roinn seo, a h-uile òrdugh ip air a chluich bho root):

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

tha e xdp-remote и xdp-local - ainmean innealan. Air adhart xdp-local (192.0.2.1/24) bidh criathrag ceangailte, le xdp-remote (192.0.2.2/24) thèid trafaic a-steach a chuir. Ach, tha duilgheadas ann: tha an eadar-aghaidh air an aon inneal, agus cha chuir Linux trafaic gu aon dhiubh tron ​​​​fhear eile. Faodaidh tu fuasgladh fhaighinn air le riaghailtean duilich iptables, ach feumaidh iad pasganan atharrachadh, rud a tha mì-ghoireasach nuair a bhios iad a’ dì-bhugachadh. Tha e nas fheàrr ainmean lìonra a chleachdadh (àiteachan ainmean lìonra, tuilleadh lìon).

Tha seata de eadar-aghaidh, clàran slighe, agus riaghailtean NetFilter a tha dealaichte bho nithean co-chosmhail ann an lìonan eile ann an àrainn ainmean an lìonraidh. Bidh gach pròiseas a' ruith ann an cuid de dh'ainmean, agus chan eil ach nithean an lìon seo ri fhaighinn dha. Gu gnàthach, tha aon àite ainm lìonra aig an t-siostam airson a h-uile nì, gus an urrainn dhut obrachadh air Linux agus gun a bhith eòlach air netns.

Cruthaichidh sinn ainm-àite ùr xdp-test agus gluais an sin e xdp-remote.

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

An uairsin bidh am pròiseas a 'ruith a-steach xdp-test, chan fhaic " xdp-local (feumaidh e ann an netns a ghnàth) agus nuair a chuireas tu pacaid gu 192.0.2.1 bheir e troimhe e xdp-remote, oir is e sin an aon eadar-aghaidh aig 192.0.2.0/24 a tha ri fhaighinn don phròiseas seo. Bidh seo cuideachd ag obair air ais.

Nuair a ghluaiseas tu eadar netns, bidh an eadar-aghaidh a’ dol sìos agus a’ call an t-seòlaidh. Gus eadar-aghaidh a stèidheachadh ann an netns, feumaidh tu ruith ip ... san ainm-àite àithne seo 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

Mar a chì thu, chan eil seo eadar-dhealaichte bho shuidheachadh xdp-local anns an ainm-àite bunaiteach:

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

Ma ruitheas tu tcpdump -tnevi xdp-local, chì thu na pacaidean sin air an cur bho xdp-test, air an lìbhrigeadh don eadar-aghaidh seo:

ip netns exec xdp-test   ping 192.0.2.1

Tha e goireasach slige a ruith a-steach xdp-test. Tha sgriobt aig an stòr a nì fèin-ghluasad air obair leis an stand, mar eisimpleir, faodaidh tu an stand a chuir air dòigh leis an àithne sudo ./stand up agus thoir air falbh e sudo ./stand down.

A' tracadh

Tha an criathrag ceangailte ris an inneal mar seo:

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

Prìomh -force tha feum air prògram ùr a cheangal ma tha fear eile ceangailte mu thràth. Chan eil “Chan e deagh naidheachd a th’ ann an naidheachd ”mu dheidhinn an àithne seo, tha an toradh voluminous co-dhiù. comharrachadh verbose roghainneil, ach leis an sin nochdaidh aithisg mu obair an neach-dearbhaidh còd leis an liosta cruinneachaidh:

Verifier analysis:

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

Sgar am prògram bhon eadar-aghaidh:

ip link set dev xdp-local xdp off

Anns an sgriobtar, is iad seo na h-àitheantan sudo ./stand attach и sudo ./stand detach.

Le bhith a’ ceangal a’ chriathrag, faodaidh tu dèanamh cinnteach sin ping fhathast ag obair, ach a bheil am prògram ag obair? Nach cuir sinn suaicheantasan ris. Gnìomh bpf_trace_printk() coltach ri printf(), ach chan eil e a’ toirt taic ach do suas ri trì argamaidean a bharrachd air a’ phàtran, agus liosta cuibhrichte de shònradairean. Macro bpf_printk() a’ sìmpleachadh a’ ghairm.

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

Bidh an toradh a’ dol chun t-sianal lorg kernel, a dh’ fheumas a bhith air a chomasachadh:

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

Seall sruth na teachdaireachd:

cat /sys/kernel/debug/tracing/trace_pipe

Bidh an dà sgioba sin a’ gairm sudo ./stand log.

Bu chòir do Ping a-nis teachdaireachdan mar seo a thoirt a-mach ann:

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

Ma choimheadas tu gu dlùth air toradh an neach-dearbhaidh, chì thu àireamhachadh neònach:

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

Is e an fhìrinn nach eil earrann dàta aig prògraman eBPF, agus mar sin is e an aon dòigh air an sreang cruth a chòdachadh na h-argamaidean sa bhad mu na h-òrdughan VM:

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

Air an adhbhar seo, tha toradh deasbaid a’ cur gu mòr ris a’ chòd a thig às.

A 'cur pacaidean XDP

Nach atharraich sinn an criathrag: leig leis a h-uile pacaid a thig a-steach a chuir air ais. Tha seo ceàrr bho shealladh lìonra, oir bhiodh e riatanach na seòlaidhean anns na cinn atharrachadh, ach a-nis tha an obair ann am prionnsapal cudromach.

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

Cur air bhog tcpdump air xdp-remote. Bu chòir dha a bhith a’ sealltainn Iarrtas Echo ICMP a tha a’ dol a-mach agus a’ tighinn a-steach agus stad a’ sealltainn ICMP Echo Reply. Ach chan eil e a 'sealltainn. Tionndaidh a-mach gu obair XDP_TX anns a' phrògram airson xdp-local riatanachgus eadar-aghaidh paidhir xdp-remote chaidh prògram a shònrachadh cuideachd, eadhon ged a bha e falamh, agus chaidh a thogail.

Ciamar a bha fios agam?

A’ leantainn slighe pacaid anns an kernel tha an uidheamachd tachartasan perf a’ ceadachadh, co-dhiù, a bhith a’ cleachdadh an aon inneal brìgheil, is e sin, tha eBPF air a chleachdadh airson a bhith air a chuir às a chèile le eBPF.

Feumaidh tu math a dhèanamh de olc, oir chan eil dad eile ri dhèanamh dheth.

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

Dè a th’ ann an còd 6?

$ errno 6
ENXIO 6 No such device or address

gnìomh veth_xdp_flush_bq() A ' faighinn còd na mearachd bho veth_xdp_xmit(), far an lorgar ENXIO agus lorg beachd.

Bheir sinn air ais an sìoltachan as ìsle (XDP_PASS) ann am faidhle xdp_dummy.c, cuir ris an Makefile e, ceangail ri xdp-remote:

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

A-nis tcpdump a’ sealltainn na thathar an dùil:

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

Mura h-eil ach ARP air a shealltainn na àite, feumaidh tu na sìoltachain a thoirt air falbh (bidh seo a’ dèanamh sudo ./stand detach), leig às ping, an uairsin stàlaich sìoltachain is feuch ris a-rithist. Is e an duilgheadas a th’ ann gu bheil an criathrag XDP_TX a 'toirt buaidh air ARP cuideachd, agus ma tha an stac
àiteachan ainmean xdp-test chaidh aige air “dìochuimhneachadh” an seòladh MAC 192.0.2.1, cha bhith e comasach dha an IP seo fhuasgladh.

Aithris dhuilgheadas

Gluaisidh sinn air adhart chun ghnìomh ainmichte: gus inneal cookie SYN a sgrìobhadh air XDP.

Gu ruige seo, tha an tuil SYN fhathast na ionnsaigh DDoS mòr-chòrdte, agus tha a bhrìgh mar a leanas. Nuair a thèid ceangal a stèidheachadh (crathadh làimhe TCP), bidh am frithealaiche a’ faighinn SYN, a’ riarachadh ghoireasan airson ceangal san àm ri teachd, a’ freagairt le pacaid SYNACK, agus a’ feitheamh ri ACK. Bidh an neach-ionnsaigh dìreach a’ cur phasganan SYN bho sheòlaidhean meallta anns na mìltean san diog bho gach aoigh ann am botnet ioma-mhìle. Feumaidh an t-seirbheisiche goireasan a riarachadh sa bhad nuair a ruigeas a’ phacaid, ach leigidh e a-mach e às deidh ùine fhada, mar thoradh air an sin, tha cuimhne no crìochan sgìth, cha ghabhar ri ceanglaichean ùra, chan eil an t-seirbheis ri fhaighinn.

Mura h-eil thu a’ riarachadh ghoireasan air a’ phacaid SYN, ach nach freagair thu ach le pacaid SYNACK, ciamar a thuigeas an frithealaiche gur ann leis a’ phacaid SYN a chaidh a’ phacaid ACK a thàinig nas fhaide air adhart nach deach a shàbhaladh? Às deidh na h-uile, faodaidh neach-ionnsaigh cuideachd ACKan meallta a ghineadh. Is e brìgh a’ bhriosgaid SYN a chòdachadh a-steach seqnum paramadairean ceangail mar hash de sheòlaidhean, puirt agus salann ag atharrachadh. Ma chaidh aig an ACK air ruighinn ron atharrachadh salainn, faodaidh tu an hash obrachadh a-mach a-rithist agus coimeas a dhèanamh ris acknum. fuadain acknum chan urrainn don neach-ionnsaigh, leis gu bheil an salann a’ toirt a-steach an dìomhair, agus cha bhith ùine aige a rèiteachadh air sgàth an t-sianail cuibhrichte.

Chaidh briosgaidean SYN a chuir an gnìomh anns an kernel Linux airson ùine mhòr agus faodar eadhon a chomasachadh gu fèin-ghluasadach ma ruigeas SYNs ro luath agus gu ìre mhòr.

Prògram foghlaim air crathadh làimhe TCP

Bidh TCP a’ toirt seachad gluasad dàta mar shruth de bytes, mar eisimpleir, tha iarrtasan HTTP air an tar-chuir thairis air TCP. Tha an t-sruth air a ghluasad pìos air pìos ann am pacaidean. Tha brataichean boolean agus àireamhan sreath 32-bit aig a h-uile pacaid TCP:

  • Tha an cothlamadh de bhrataichean a’ mìneachadh àite pasgan sònraichte. Tha bratach SYN a’ ciallachadh gur e seo a’ chiad phacaid a chuir an neach a chuir air a’ cheangal. Tha bratach ACK a’ ciallachadh gu bheil an neach-cuiridh air a h-uile dàta ceangail fhaighinn suas gu byte. acknum. Faodaidh grunn bhrataichean a bhith air pacaid agus tha e air ainmeachadh às deidh am measgachadh, mar eisimpleir, pacaid SYNACK.

  • Sequence number (sequnum) a’ sònrachadh an chothromachadh san t-sruth dàta airson a’ chiad byte a thèid a chur sa phacaid seo. Mar eisimpleir, mas e N + a bh’ anns a’ chiad phacaid le X bytes de dhàta, anns an ath phacaid le dàta ùr bidh e N + X. Aig toiseach a 'cheangail, bidh gach pàrtaidh a' taghadh an àireamh seo air thuaiream.

  • Àireamh aithneachaidh (acknum) - an aon chothromachadh ri seqnum, ach chan eil e a’ dearbhadh àireamh a ’bhidhe tar-chuir, ach àireamh a’ chiad byte bhon neach a gheibh e, nach fhaca an neach a chuir e.

Aig toiseach a 'cheangail, feumaidh na pàrtaidhean aontachadh seqnum и acknum. Bidh an neach-dèiligidh a’ cur pacaid SYN leis seqnum = X. Bidh am frithealaiche a’ freagairt le pacaid SYNACK, far am bi e a’ sgrìobhadh a chuid fhèin seqnum = Y agus a' nochdadh acknum = X + 1. Bidh an neach-dèiligidh a’ freagairt SYNACK le pasgan ACK, far a bheil seqnum = X + 1, acknum = Y + 1. Às deidh sin, tòisichidh an gluasad dàta fìor.

Mura h-eil an neach-conaltraidh ag aideachadh gun d’ fhuair e a’ phacaid, bidh TCP ga chuir air ais ro àm a-muigh.

Carson nach eilear a’ cleachdadh briosgaidean SYN an-còmhnaidh?

An toiseach, ma thèid SYNACK no ACK a chall, feumaidh tu feitheamh ri ath-reic - bidh an stèidheachadh ceangail a ’slaodadh sìos. San dàrna h-àite, anns a’ phacaid SYN - agus dìreach ann! - tha grunn roghainnean air an tar-chuir a bheir buaidh air gnìomhachd a’ cheangail a bharrachd. Gun a bhith a’ cuimhneachadh air pacaidean SYN a tha a’ tighinn a-steach, tha am frithealaiche mar sin a’ seachnadh nan roghainnean sin, anns na pacaidean a leanas cha chuir an neach-dèiligidh iad tuilleadh. Faodaidh TCP obrachadh sa chùis seo, ach co-dhiù aig a 'chiad ìre, bidh càileachd a' cheangail a 'dol sìos.

A thaobh pacaidean, bu chòir do phrògram XDP na leanas a dhèanamh:

  • freagair SYN le SYNACK le briosgaid;
  • freagair ACK le RST (dì-cheangal);
  • leig seachad pacaidean eile.

Pseudocode an algairim còmhla ri parsadh pacaid:

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

Aon (*) tha na puingean far am feum thu staid an t-siostaim a riaghladh air an comharrachadh - aig a’ chiad ìre, faodaidh tu a dhèanamh às an aonais le bhith dìreach a ’cur an gnìomh crathadh làimhe TCP le bhith a’ gineadh briosgaid SYN mar seqnum.

Air an làraich (**), fhad 's nach eil bòrd againn, sgiobadh sinn a' phacaid.

Cur an gnìomh crathadh làimhe TCP

Parsadh pacaid agus dearbhadh còd

Feumaidh sinn structaran cinn lìonra: Ethernet (uapi/linux/if_ether.h), IPv4 (uapi/linux/ip.h) agus TCP (uapi/linux/tcp.h). Am fear mu dheireadh nach b’ urrainn dhomh ceangal a dhèanamh ri linn mhearachdan co-cheangailte ris atomic64_t, bha agam ri na mìneachaidhean riatanach a chopaigeadh a-steach don chòd.

Feumaidh a h-uile gnìomh a tha air a chomharrachadh ann an C airson so-leughaidh a bhith air a chòmhdach aig an làrach gairm, leis gu bheil an neach-dearbhaidh eBPF anns an kernel a’ toirmeasg geansaidhean air ais, is e sin, gu dearbh, lùban agus gairmean gnìomh.

#define INTERNAL static __attribute__((always_inline))

Macro LOG() cuir à comas clò-bhualadh ann an togail fuasglaidh.

Tha am prògram na loidhne-phìoban de ghnìomhan. Bidh gach fear a’ faighinn pasgan anns a bheil bann-cinn den ìre fhreagarrach air a chomharrachadh, mar eisimpleir, process_ether() a’ feitheamh ri lìonadh ether. Stèidhichte air toraidhean mion-sgrùdadh làraich, faodaidh an gnìomh am pasgan a ghluasad gu ìre nas àirde. Is e toradh na gnìomh gnìomh XDP. Fhad ‘s a bhios an luchd-làimhseachaidh SYN agus ACK a’ leigeil a h-uile pacaid troimhe.

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

Bheir mi aire do na seicichean comharraichte A agus B. Ma bheir thu beachd a-mach A, togaidh am prògram, ach bidh mearachd dearbhaidh ann nuair a bhios tu a’ luchdachadh:

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!

Prìomh shreath invalid access to packet, off=13 size=1, R7(id=0,off=0,r=0): tha slighean cur gu bàs ann nuair a tha an treas byte deug bho thoiseach a’ bhufair taobh a-muigh a’ phacaid. Tha e duilich innse bhon liosta dè an loidhne air a bheil sinn a’ bruidhinn, ach tha àireamh stiùiridh (12) agus disassembler a sheallas loidhnichean a’ chòd stòr:

llvm-objdump -S xdp_filter.o | less

Anns a 'chùis seo, tha e a' comharrachadh an loidhne

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

a tha ga dhèanamh soilleir gu bheil an duilgheadas ether. Bhiodh e an-còmhnaidh mar sin.

Freagair SYN

Is e an amas aig an ìre seo pasgan ceart SYNACK a ghineadh le inneal stèidhichte seqnum, a thèid a chuir na àite le briosgaid SYN san àm ri teachd. Bidh a h-uile atharrachadh a’ gabhail àite ann an process_tcp_syn() agus na sgìrean mun cuairt.

A 'sgrùdadh a' phacaid

Gu neònach, seo an loidhne as iongantaiche, neo an àite sin, beachd air:

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

Nuair a bha thu a’ sgrìobhadh a’ chiad dreach den chòd, chaidh an kernel 5.1 a chleachdadh, airson an dearbhaiche bha eadar-dhealachadh eadar data_end и (const void*)ctx->data_end. Aig àm sgrìobhaidh, cha robh an duilgheadas seo aig kernel 5.3.1. Tha e comasach gun robh an neach-cruinneachaidh a’ faighinn cothrom air caochladair ionadail ann an dòigh eadar-dhealaichte seach raon. Moraltachd na sgeòil: Faodaidh sìmplidh a’ chòd cuideachadh nuair a tha tòrr neadachadh ann.

tuilleadh sgrùdaidhean gnàthach air faid airson glòir an neach-dearbhaidh; O MAX_CSUM_BYTES gu h-ìosal.

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

Sgaoileadh pacaid

Bidh sinn a ’lìonadh a-steach seqnum и acknum, seata ACK (tha SYN air a shuidheachadh mu thràth):

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

Dèan iomlaid air puirt TCP, seòlaidhean IP agus MAC. Chan eil an leabharlann àbhaisteach ri fhaighinn bhon phrògram XDP, mar sin memcpy() - macro a tha a’ falach bun-bheachdan 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);

Ath-àireamhachadh checksum

Feumaidh sgrùdaidhean IPv4 agus TCP a h-uile facal 16-bit a chur ris na cinn-cinn, agus tha meud nan cinn-cinn sgrìobhte annta, is e sin, aig àm an cruinneachaidh chan eil fios againn. Is e duilgheadas a tha seo oir cha leum an dearbhaiche an lùb àbhaisteach gus an caochladair crìche. Ach tha meud nan cinn-cinn cuingealaichte: suas ri 64 bytes gach fear. Faodaidh tu lùb a dhèanamh le àireamh stèidhichte de thursan, a dh'fhaodas crìochnachadh tràth.

Tha mi a’ toirt fa-near gu bheil RFC 1624 mu mar a nì thu ath-àireamhachadh gu ìre air an t-seic mura h-atharraichear ach faclan stèidhichte nam pacaidean. Ach, chan eil am modh uile-choitcheann, agus bhiodh e na bu duilghe am buileachadh a chumail suas.

Gnìomh àireamhachadh 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;
}

Ged a size air a dhearbhadh leis a’ chòd gairm, tha feum air an dàrna suidheachadh fàgail gus an urrainn don neach-dearbhaidh deireadh na lùb a dhearbhadh.

Airson faclan 32-bit, thèid dreach nas sìmplidh a chuir an gnìomh:

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

Dìreach ag ath-àireamhachadh nan checksums agus a’ cur a’ phacaid air ais:

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;

gnìomh carry() a’ dèanamh seic a-mach à suim 32-bit de dh’fhaclan 16-bit, a rèir RFC 791.

Sgrùdadh crathadh làimhe TCP

Bidh an criathrag gu ceart a’ stèidheachadh ceangal le netcat, a’ leum air an ACK mu dheireadh, ris an do fhreagair Linux le pacaid RST, leis nach d’ fhuair an stac lìonra SYN - chaidh a thionndadh gu SYNACK agus a chuir air ais - agus bho shealladh an OS, thàinig pacaid nach robh co-cheangailte ri ceanglaichean fosgailte.

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

Tha e cudromach sgrùdadh a dhèanamh le tagraidhean làn-chuimseach agus coimhead tcpdump air xdp-remote oir, mar eisimpleir, hping3 chan eil e a’ freagairt seicichean ceàrr.

Bho shealladh XDP, tha an t-seic fhèin beag. Tha an algairim àireamhachaidh prìomhaideach agus is dòcha gu bheil e so-leònte ri neach-ionnsaigh sòlaimte. Bidh an kernel Linux, mar eisimpleir, a ’cleachdadh an SipHash criptografach, ach tha e soilleir gu bheil a bhuileachadh airson XDP taobh a-muigh raon an artaigil seo.

A’ nochdadh airson TODOn ùra co-cheangailte ri eadar-obrachadh bhon taobh a-muigh:

  • Chan urrainn don phrògram XDP stòradh cookie_seed (am pàirt dìomhair den t-salann) ann an caochladair cruinne, feumaidh tu stòr kernel a thèid an luach ùrachadh bho àm gu àm bho ghineadair earbsach.

  • Ma tha am briosgaid SYN a’ maidseadh sa phacaid ACK, chan fheum thu teachdaireachd a chlò-bhualadh, ach cuimhnich air IP an neach-dèiligidh dearbhte gus leantainn air adhart a’ dol seachad air pacaidean bhuaithe.

Dearbhadh le neach-dèiligidh dligheach:

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

Tha na logaichean a’ sealltainn gun deach an seic seachad (flags=0x2 tha SYN, flags=0x10 tha 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

Cho fad 's nach eil liosta de IPan dearbhte ann, cha bhi dìon sam bith ann an-aghaidh tuil SYN fhèin, ach seo an fhreagairt don tuil ACK a chuir an òrdugh seo air bhog:

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

Logaichean a-steach:

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

co-dhùnadh

Aig amannan tha eBPF san fharsaingeachd agus XDP gu sònraichte air an taisbeanadh mar barrachd de inneal rianadair adhartach na àrd-ùrlar leasachaidh. Gu dearbh, tha XDP na inneal airson a bhith a’ cur bacadh air giullachd pacaid kernel, agus chan e roghainn eile an àite a’ chruach kernel, leithid DPDK agus roghainnean seach-rathad kernel eile. Air an làimh eile, tha XDP a 'toirt cothrom dhut loidsig caran iom-fhillte a chur an gnìomh, a tha, a bharrachd air sin, furasta ùrachadh gun stad ann an giullachd trafaig. Chan eil an dearbhaiche a 'cruthachadh dhuilgheadasan mòra, gu pearsanta cha bhithinn a' diùltadh a leithid airson pàirtean den chòd luchd-cleachdaidh.

Anns an dàrna pàirt, ma tha an cuspair inntinneach, cuiridh sinn crìoch air a’ chlàr de luchd-dèiligidh dearbhte agus brisidh sinn ceanglaichean, cuiridh sinn an gnìomh cunntairean agus sgrìobhaidh sinn goireas àite-cleachdaidh gus an sìoltachan a riaghladh.

Tùsan:

Source: www.habr.com

Cuir beachd ann