Muna rubuta kariya daga harin DDoS akan XDP. Bangaren nukiliya

Fasahar eXpress Data Path (XDP) tana ba da damar sarrafa zirga-zirgar ababen hawa a kan mu'amalar Linux kafin fakitin su shiga tarin cibiyar sadarwar kwaya. Aikace-aikacen XDP - kariya daga hare-haren DDoS (CloudFlare), matattara masu rikitarwa, tarin ƙididdiga (Netflix). Ana aiwatar da shirye-shiryen XDP ta na'urar kama-da-wane ta eBPF, don haka suna da hani akan duka lambar su da ayyukan kernel da ke akwai dangane da nau'in tacewa.

An yi nufin labarin don cika gazawar abubuwa da yawa akan XDP. Da fari dai, suna ba da lambar da aka shirya wanda nan da nan ke ƙetare sifofin XDP: an shirya shi don tabbatarwa ko kuma yana da sauƙin haifar da matsala. Lokacin da kuke ƙoƙarin rubuta lambar ku daga karce, ba ku da masaniyar abin da za ku yi da kurakurai na yau da kullun. Na biyu, hanyoyin da za a gwada XDP a cikin gida ba tare da VM da hardware ba ba a rufe su ba, duk da cewa suna da nasu ramukan. An yi nufin rubutun ga masu shirye-shirye da suka saba da hanyar sadarwa da Linux waɗanda ke sha'awar XDP da eBPF.

A cikin wannan ɓangaren, za mu fahimci dalla-dalla yadda ake haɗa matatun XDP da yadda za a gwada shi, sannan za mu rubuta sassauƙan sigar sanannen kukis na SYN a matakin sarrafa fakiti. Ba za mu ƙirƙiri "jerin farar fata" ba tukuna
ingantattun abokan ciniki, kiyaye ƙididdiga da sarrafa tacewa - isassun rajistan ayyukan.

Za mu rubuta a C - ba gaye ba ne, amma yana da amfani. Ana samun duk lambar akan GitHub ta hanyar haɗin gwiwa a ƙarshen kuma an raba shi zuwa aikatawa bisa ga matakan da aka bayyana a cikin labarin.

Bayarwa. A tsawon wannan labarin, zan samar da ƙaramin bayani don kawar da hare-haren DDoS, saboda wannan aiki ne na gaske ga XDP da yanki na gwaninta. Koyaya, babban burin shine fahimtar fasaha; wannan ba jagora bane don ƙirƙirar kariyar da aka shirya. Ba a inganta lambar koyawa ba kuma ta tsallake wasu nuances.

Takaitaccen Bayani na XDP

Zan zayyana mahimman mahimman bayanai ne kawai don kada in kwafin takardu da labaran da ke akwai.

Don haka, ana loda lambar tacewa a cikin kwaya. Ana wuce fakiti masu shigowa zuwa tace. A sakamakon haka, tace dole ne ya yanke shawara: shigar da fakitin cikin kwaya (XDP_PASS), fakitin sauke (XDP_DROP) ko kuma mayar da shi (XDP_TX). Tace na iya canza kunshin, wannan gaskiya ne musamman ga XDP_TX. Hakanan zaka iya soke shirin (XDP_ABORTED) kuma sake saita kunshin, amma wannan kwatankwacin ne assert(0) - don gyara kuskure.

EBPF (Extended Berkley Packet Filter) na'ura mai kama da gaskiya an yi shi da gangan don kernel zai iya bincika cewa lambar ba ta madauki kuma baya lalata ƙwaƙwalwar wasu mutane. Tarin hani da dubawa:

  • An haramta madaukai (na baya).
  • Akwai tarin bayanai, amma babu ayyuka (duk ayyukan C dole ne a sanya su cikin layi).
  • An hana samun damar ƙwaƙwalwar ajiya a waje da tari da buffer.
  • Girman lambar yana da iyaka, amma a aikace wannan ba shi da mahimmanci.
  • Kira zuwa ayyukan kwaya na musamman (mataimakan eBPF) ne kawai aka yarda.

Zanewa da sanya tacewa yayi kama da haka:

  1. Lambar tushe (misali kernel.c) an haɗa shi cikin abu (kernel.o) don tsarin gine-ginen injina na eBPF. Tun daga Oktoba 2019, Clang yana samun goyan bayan haɗawa zuwa eBPF kuma an yi alkawarinsa a cikin GCC 10.1.
  2. Idan wannan lambar abu ta ƙunshi kira zuwa tsarin kernel (misali, teburi da ƙididdiga), ana maye gurbin ID ɗin su da sifili, wanda ke nufin ba za a iya aiwatar da irin wannan lambar ba. Kafin lodawa cikin kwaya, kuna buƙatar maye gurbin waɗannan sifilai tare da ID na takamaiman abubuwan da aka ƙirƙira ta hanyar kiran kernel (haɗa lambar). Kuna iya yin wannan tare da abubuwan amfani na waje, ko kuma kuna iya rubuta shirin da zai haɗa da loda takamaiman tacewa.
  3. Kwayar tana tabbatar da shirin da aka ɗora. Ana duba rashin hawan keke da gazawar wuce fakiti da iyakoki. Idan mai tabbatarwa ba zai iya tabbatar da cewa lambar daidai ba ne, an ƙi shirin - kuna buƙatar samun damar faranta masa rai.
  4. Bayan ingantaccen tabbaci, kernel ɗin yana tattara lambar abu na eBPF cikin lambar injin don tsarin gine-gine (daidai-lokaci).
  5. Shirin yana haɗawa da ke dubawa kuma ya fara sarrafa fakiti.

Tun da XDP ke gudana a cikin kwaya, ana aiwatar da gyara kuskure ta amfani da rajistan ayyukan ganowa kuma, a zahiri, fakiti waɗanda shirin ke tacewa ko samarwa. Koyaya, eBPF yana tabbatar da cewa lambar da aka zazzage tana da tsaro ga tsarin, saboda haka zaku iya gwaji tare da XDP kai tsaye akan Linux na gida.

Shirya Muhalli

Majalisar

Clang ba zai iya samar da lambar abu kai tsaye don gine-ginen eBPF ba, don haka tsarin ya ƙunshi matakai biyu:

  1. Haɗa lambar C zuwa bytecode LLVM (clang -emit-llvm).
  2. Maida bytecode zuwa lambar abu na eBPF (llc -march=bpf -filetype=obj).

Lokacin rubuta tacewa, fayiloli guda biyu tare da ayyukan taimako da macros zasu yi amfani daga gwajin kwaya. Yana da mahimmanci cewa sun dace da sigar kernel (KVER). Zazzage su zuwa 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 don 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 ya ƙunshi hanyar zuwa kanun kernel, ARCH - tsarin gine-gine. Hanyoyi da kayan aikin na iya bambanta dan kadan tsakanin rarrabawa.

Misali na bambance-bambance na 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 haɗa kundin adireshi tare da masu kai taimako da kundayen adireshi da yawa tare da kanun kernel. Alama __KERNEL__ yana nufin cewa UAPI (userspace API) an ayyana masu kai don lambar kwaya, tunda ana aiwatar da tacewa a cikin kernel.

Ana iya kashe kariyar tari (-fno-stack-protector), saboda mai tabbatar da lambar eBPF har yanzu yana bincikar rashin iyakoki. Yana da daraja kunna ingantawa nan da nan, saboda girman eBPF bytecode yana iyakance.

Bari mu fara da tacewa wanda ya wuce duk fakiti kuma bai yi komai ba:

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

tawagar make yana tarawa xdp_filter.o. A ina zan gwada shi yanzu?

Gwajin tsayawa

Dole ne wurin tsayawa ya haɗa da musaya guda biyu: waɗanda za a sami tacewa kuma daga waɗanne fakiti za a aika. Waɗannan dole ne su zama cikakkun na'urorin Linux tare da nasu IPs don bincika yadda aikace-aikacen yau da kullun ke aiki tare da tacewa.

Nau'in na'urar veth (virtual Ethernet) sun dace da mu: waɗannan nau'ikan mu'amalar hanyar sadarwa ce ta “haɗa” kai tsaye da juna. Kuna iya ƙirƙirar su kamar haka (a cikin wannan sashe duk umarni ip ana gudanar da su daga root):

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

Yana da xdp-remote и xdp-local - sunayen na'ura. Kunna xdp-local (192.0.2.1/24) za a haɗa tace, tare da xdp-remote (192.0.2.2/24) za a aika zirga-zirga mai shigowa. Duk da haka, akwai matsala: musaya suna kan na'ura ɗaya, kuma Linux ba zai aika da zirga-zirga zuwa ɗayansu ta ɗayan ba. Kuna iya magance wannan tare da ƙa'idodi masu banƙyama iptables, amma dole ne su canza fakiti, wanda bai dace ba don cirewa. Yana da kyau a yi amfani da wuraren sunaye na cibiyar sadarwa (netns daga baya).

Wurin sunan cibiyar sadarwa yana ƙunshe da saitin musaya, teburi, da ka'idojin NetFilter waɗanda ke ware daga abubuwa iri ɗaya a cikin sauran gidajen yanar sadarwa. Kowane tsari yana gudana a cikin sararin suna kuma yana da damar yin amfani da abubuwan wannan hanyar sadarwa kawai. Ta hanyar tsoho, tsarin yana da sararin sunan cibiyar sadarwa guda ɗaya don duk abubuwa, don haka zaku iya aiki a cikin Linux kuma ba ku sani ba game da netns.

Bari mu ƙirƙiri sabon filin suna xdp-test kuma motsa shi zuwa can xdp-remote.

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

Sa'an nan kuma tsari yana gudana xdp-test, ba zai "gani ba" xdp-local (zai kasance a cikin netns ta tsohuwa) kuma lokacin aika fakiti zuwa 192.0.2.1 zai wuce ta. xdp-remotesaboda ita ce kawai ke dubawa akan 192.0.2.0/24 m ga wannan tsari. Wannan kuma yana aiki a kishiyar shugabanci.

Lokacin matsawa tsakanin netns, mahaɗin yana sauka kuma ya rasa adireshinsa. Don saita dubawa a cikin netns, kuna buƙatar gudu ip ... a cikin wannan umarnin sunaye 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

Kamar yadda kuke gani, wannan bai bambanta da saitin ba xdp-local a cikin tsohowar sunaye:

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

Idan ka gudu tcpdump -tnevi xdp-local, kuna iya ganin fakitin da aka aiko daga xdp-test, ana isar da su zuwa wannan keɓancewa:

ip netns exec xdp-test   ping 192.0.2.1

Ya dace don ƙaddamar da harsashi a ciki xdp-test. Wurin ajiya yana da rubutun da ke sarrafa aiki tare da tsayawar; misali, zaku iya saita tsayawar tare da umarni sudo ./stand up kuma share shi sudo ./stand down.

Bibiya

Ana haɗa tacewa da na'urar kamar haka:

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

Key -force ana buƙatar haɗa sabon shirin idan an riga an haɗa wani. "Babu labari mai kyau" ba game da wannan umarni ba ne, ƙarshe yana da yawa a kowane hali. nuna verbose na zaɓi, amma tare da shi rahoto yana bayyana akan aikin mai tabbatar da lambar tare da lissafin taro:

Verifier analysis:

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

Cire haɗin shirin daga mahaɗin:

ip link set dev xdp-local xdp off

A cikin rubutun waɗannan umarni ne sudo ./stand attach и sudo ./stand detach.

Ta hanyar haɗa tacewa, zaku iya tabbatar da hakan ping ya ci gaba da gudana, amma shirin yana aiki? Bari mu ƙara rajistan ayyukan. Aiki bpf_trace_printk() kama da printf(), amma kawai yana goyan bayan har zuwa gardama guda uku ban da ƙirar, da taƙaitaccen jerin ƙididdiga. Macro bpf_printk() sauƙaƙa kiran.

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

Fitowar tana zuwa tashar gano kernel, wanda ke buƙatar kunnawa:

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

Duba layin saƙo:

cat /sys/kernel/debug/tracing/trace_pipe

Duk waɗannan umarni guda biyu suna yin kira sudo ./stand log.

Ping ya kamata yanzu ya fara sa saƙonni kamar haka:

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

Idan ka dubi fitowar mai tantancewa da kyau, za ka lura da ƙididdiga masu ban mamaki:

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

Gaskiyar ita ce, shirye-shiryen eBPF ba su da sashin bayanai, don haka kawai hanyar da za a ɓoye kirtani mai ƙira ita ce gardama nan da nan na umarnin VM:

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

Saboda wannan dalili, fitar da gyara kuskure yana lalata lambar da aka samu sosai.

Aika Fakitin XDP

Bari mu canza tace: bari ya mayar da duk fakiti masu shigowa. Wannan ba daidai ba ne daga ra'ayi na cibiyar sadarwa, tun da zai zama dole don canza adiresoshin a cikin masu kai, amma yanzu aikin a cikin mahimmanci yana da mahimmanci.

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

Kaddamarwa tcpdump a kan xdp-remote. Ya kamata ya nuna irin mai fita da mai shigowa ICMP Echo Request kuma a daina nuna ICMP Echo Reply. Amma bai nuna ba. Sai dai itace don aiki XDP_TX a cikin shirin a kan xdp-local ya zama dolezuwa biyu dubawa xdp-remote an kuma sanya wani shiri ko da babu komai sai aka tashe shi.

Ta yaya na san wannan?

Bincika hanyar kunshin a cikin kwaya Tsarin abubuwan da suka faru na perf yana ba da damar, ta hanya, ta amfani da injin kama-da-wane, wato, ana amfani da eBPF don rarrabuwa tare da eBPF.

Dole ne ku yi alheri daga mugunta, domin ba abin da za ku iya yi.

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

Menene code 6?

$ errno 6
ENXIO 6 No such device or address

aiki veth_xdp_flush_bq() yana karɓar lambar kuskure daga veth_xdp_xmit(), inda ake nema ENXIO kuma sami comment.

Mu dawo da mafi ƙarancin tacewa (XDP_PASS) a cikin fayil xdp_dummy.c, ƙara shi zuwa Makefile, ɗaure shi xdp-remote:

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

Yanzu tcpdump yana nuna abin da ake tsammani:

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

Idan kawai aka nuna ARPs maimakon, kuna buƙatar cire masu tacewa (wannan yana yi sudo ./stand detach), bari mu tafi ping, sannan saita tacewa kuma a sake gwadawa. Matsalar tace XDP_TX inganci duka akan ARP kuma idan tari
wuraren suna xdp-test gudanar da "manta" da MAC address 192.0.2.1, shi ba zai iya warware wannan IP.

Tsara matsalar

Bari mu matsa zuwa aikin da aka bayyana: rubuta tsarin kukis na SYN akan XDP.

Ambaliyar SYN ta kasance sanannen harin DDoS, wanda asalinsa shine kamar haka. Lokacin da aka kafa haɗi (musafaha TCP), uwar garken yana karɓar SYN, ya ware albarkatu don haɗin gaba, amsa tare da fakitin SYNACK kuma yana jiran ACK. Maharin yana aika dubunnan fakitin SYN a sakan daya daga adiresoshin da ba su da tushe daga kowane mai masaukin baki a cikin botnet mai ƙarfi-dubu-dubu. Ana tilasta uwar garken don ware albarkatu nan da nan bayan isowar fakitin, amma ta sake su bayan babban lokacin ƙarewa; sakamakon haka, ƙwaƙwalwar ajiya ko iyakoki sun ƙare, ba a karɓi sabbin hanyoyin sadarwa ba, kuma babu sabis ɗin.

Idan baku ware albarkatu bisa fakitin SYN ba, amma kawai amsa da fakitin SYNACK, ta yaya uwar garken zata iya fahimtar cewa fakitin ACK da ya zo daga baya yana nufin fakitin SYN da ba a ajiye ba? Bayan haka, maharin kuma yana iya ƙirƙirar ACKs na karya. Manufar kuki ɗin SYN shine a ɓoye shi a ciki seqnum sigogin haɗin kai azaman hash na adireshi, tashar jiragen ruwa da canza gishiri. Idan ACK yayi nasarar isowa kafin a canza gishiri, zaku iya sake lissafin zanta kuma ku kwatanta shi da acknum. Ƙirƙira acknum maharin ba zai iya ba, tunda gishiri ya haɗa da sirrin, kuma ba zai sami lokaci don warware shi ba saboda ƙayyadaddun tashoshi.

An dade ana aiwatar da kuki na SYN a cikin kwaya ta Linux kuma ana iya kunna ta ta atomatik idan SYNs sun zo da sauri da yawa.

Shirin ilimi akan musafaha TCP

TCP yana ba da watsa bayanai azaman rafi na bytes, misali, buƙatun HTTP ana watsa su akan TCP. Ana watsa rafi a guntu a cikin fakiti. Duk fakitin TCP suna da tutoci masu ma'ana da lambobi masu 32-bit:

  • Haɗin tutoci yana ƙayyade rawar wani kunshin. Tutar SYN tana nuna cewa wannan shine fakitin farko na mai aikawa akan haɗin. Tutar ACK tana nufin cewa mai aikawa ya karɓi duk bayanan haɗin kai har zuwa byte acknum. Fakiti na iya samun tutoci da yawa kuma ana kiran su ta hanyar haɗin gwiwa, misali, fakitin SYNACK.

  • Lambar jeri (seqnum) tana ƙayyadad da kashewa a cikin rafin bayanai don byte na farko da aka watsa a cikin wannan fakiti. Misali, idan a cikin fakitin farko mai x bytes na bayanai wannan lambar N, a fakiti na gaba mai sabbin bayanai zai zama N+X. A farkon haɗin, kowane gefe yana zaɓar wannan lambar ba da gangan ba.

  • Lambar girmamawa (acknum) - daidaitaccen daidai yake da seqnum, amma bai ƙayyade adadin byte ɗin da ake watsawa ba, amma lambar farkon byte daga mai karɓa, wanda mai aikawa bai gani ba.

A farkon haɗin gwiwar, dole ne ƙungiyoyi su amince seqnum и acknum. Abokin ciniki yana aika fakitin SYN tare da shi seqnum = X. Sabar tana amsawa da fakitin SYNACK, inda yake yin rikodin sa seqnum = Y da fallasa acknum = X + 1. Abokin ciniki yana amsa SYNACK tare da fakitin ACK, inda seqnum = X + 1, acknum = Y + 1. Bayan wannan, ainihin canja wurin bayanai yana farawa.

Idan takwarorinsu bai amince da karɓar fakitin ba, TCP yana sake aika shi bayan an ƙare.

Me yasa ba koyaushe ake amfani da kukis na SYN ba?

Da farko, idan SYNACK ko ACK ya ɓace, dole ne ku jira don sake aika shi - saitin haɗin zai ragu. Abu na biyu, a cikin kunshin SYN - kuma kawai a ciki! - da dama zažužžukan da ake aikatãwa da cewa rinjayar da ƙarin aiki na dangane. Ba tare da tunawa da fakitin SYN masu shigowa ba, uwar garken ta yi watsi da waɗannan zaɓuɓɓukan; abokin ciniki ba zai aika su cikin fakiti na gaba ba. TCP na iya aiki a wannan yanayin, amma aƙalla a matakin farko ingancin haɗin zai ragu.

Ta fuskar fakiti, shirin XDP dole ne ya yi abubuwan da ke biyowa:

  • amsa SYN tare da SYNACK tare da kuki;
  • amsa ga ACK tare da RST (cire haɗin gwiwa);
  • jefar da sauran fakitin.

Pseudocode na algorithm tare da fakitin fakiti:

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

Daya (*) wuraren da kuke buƙatar sarrafa yanayin tsarin suna alama - a matakin farko za ku iya yin ba tare da su ba ta hanyar aiwatar da musafaha na TCP kawai tare da tsara kuki na SYN azaman seqnum.

A wurin (**), yayin da ba mu da tebur, za mu tsallake fakitin.

Ana aiwatar da musafaha na TCP

Fassara fakitin da tabbatar da lambar

Za mu buƙaci tsarin kan hanyar sadarwa: Ethernet (uapi/linux/if_ether.hIPv4 (uapi/linux/ip.hda TCP (uapi/linux/tcp.h). Na kasa haɗa na ƙarshe saboda kurakuran da suka shafi atomic64_t, Dole ne in kwafi ma'anar da suka dace a cikin lambar.

Duk ayyukan da aka yi wa alama a cikin C don karantawa dole ne a sanya su cikin layi a wurin kira, tunda mai tabbatar da eBPF a cikin kernel ya hana ja da baya, wato, madaukai da kiran aiki.

#define INTERNAL static __attribute__((always_inline))

Macro LOG() yana hana bugu a ginin saki.

Shirin mai isar da ayyuka ne. Kowannensu yana karɓar fakiti wanda a cikinsa aka haskaka taken matakin daidai, misali, process_ether() yana sa ran a cika shi ether. Dangane da sakamakon binciken filin, aikin zai iya wuce fakitin zuwa matsayi mafi girma. Sakamakon aikin shine aikin XDP. A yanzu, masu SYN da ACK sun wuce duk fakiti.

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

Ina jawo hankalin ku ga cak ɗin da aka yiwa alama A da B. Idan kun yi sharhi A, shirin zai gina, amma za a sami kuskuren tabbatarwa yayin lodawa:

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!

Mabuɗin kirtani invalid access to packet, off=13 size=1, R7(id=0,off=0,r=0): Akwai hanyoyin aiwatarwa lokacin da byte na goma sha uku daga farkon buffer yana wajen fakitin. Yana da wuya a fahimta daga jeri ko wane layi muke magana akai, amma akwai lambar umarni (12) da mai rarrabawa da ke nuna layin lambar tushe:

llvm-objdump -S xdp_filter.o | less

A wannan yanayin yana nuna layin

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

wanda ya bayyana cewa matsalar ita ce ether. Zai kasance haka koyaushe.

Amsa ga SYN

Manufar a wannan mataki shine samar da fakitin SYNACK daidai tare da kafaffen seqnum, wanda za a maye gurbinsa a nan gaba ta hanyar kuki na SYN. Duk canje-canje suna faruwa a cikin process_tcp_syn() da kewaye.

Tabbatar da fakitin

Abin ban mamaki, ga layi mafi ban mamaki, ko kuma, sharhin da aka yi masa:

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

Lokacin rubuta sigar farko ta lambar, an yi amfani da kernel 5.1, don tabbatarwa wanda akwai bambanci tsakanin data_end и (const void*)ctx->data_end. A lokacin rubutawa, kernel 5.3.1 bai sami wannan matsala ba. Yana yiwuwa mai tarawa yana samun dama ga canjin gida daban da filin. Dabi'ar labarin: Sauƙaƙe lambar na iya taimakawa lokacin da ake yawan gida.

Na gaba akwai duba tsawon lokaci na yau da kullun don ɗaukakar mai tabbatarwa; O MAX_CSUM_BYTES a kasa.

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

Ana buɗe kunshin

Mun cika seqnum и acknum, saita ACK (An riga an saita SYN):

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

Canza tashar jiragen ruwa na TCP, adireshin IP da adiresoshin MAC. Ba a iya samun daidaitaccen ɗakin karatu daga shirin XDP, don haka memcpy() - macro wanda ke ɓoye abubuwan 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);

Sake ƙididdige ƙididdiga

IPV4 da TCP checksum suna buƙatar ƙarin duk kalmomin 16-bit a cikin masu kai, kuma an rubuta girman rubutun a cikin su, wato, ba a sani ba a lokacin tattarawa. Wannan matsala ce saboda mai tabbatarwa ba zai tsallake madaidaicin madaidaicin madaidaicin iyaka ba. Amma girman kai yana iyakance: har zuwa 64 bytes kowanne. Kuna iya yin madauki tare da ƙayyadadden adadin maimaitawa, wanda zai iya ƙare da wuri.

Na lura cewa akwai RFC 1624 game da yadda za a sake ƙididdige juzu'i na checksum idan kawai an canza ƙayyadaddun kalmomin fakitin. Duk da haka, hanyar ba ta duniya ba ce, kuma aiwatarwa zai fi wuya a kiyaye.

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

Ko da yake size tabbatar da lambar kiran, yanayin fita na biyu ya zama dole domin mai tabbatarwa zai iya tabbatar da kammala madauki.

Don kalmomi 32-bit, ana aiwatar da mafi sauƙi:

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

A haƙiƙa ana sake ƙididdige ƙididdige ƙididdiga da aika fakitin baya:

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;

aiki carry() yana yin lissafin kuɗi daga jimlar 32-bit na kalmomi 16-bit, bisa ga RFC 791.

Tabbatar da musafaha TCP

Tace daidai yana kafa haɗi tare da netcat, ya ɓace ACK na ƙarshe, wanda Linux ya amsa tare da fakitin RST, tun da tarin cibiyar sadarwa bai karɓi SYN ba - an canza shi zuwa SYNACK kuma an mayar da shi - kuma daga ma'anar OS, fakiti ya zo wanda ba shi da alaƙa da buɗewa. haɗi.

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

Yana da mahimmanci a bincika tare da cikakkun aikace-aikacen aikace-aikacen da lura tcpdump a kan xdp-remote domin misali, hping3 baya mayar da martani ga lissafin da ba daidai ba.

Daga ra'ayi na XDP, tabbatarwa kanta ba komai bane. Algorithm na ƙididdigewa na farko ne kuma mai yuwuwa yana da rauni ga ƙwararrun mahari. Kernel na Linux, alal misali, yana amfani da SipHash na sirri, amma aiwatar da shi don XDP a sarari ya wuce iyakar wannan labarin.

An gabatar don sababbin TODOs masu alaƙa da sadarwar waje:

  • Shirin XDP ba zai iya adanawa ba cookie_seed (ɓangaren sirri na gishiri) a cikin canjin duniya, kuna buƙatar ajiya a cikin kwaya, wanda za a sabunta darajarsa lokaci-lokaci daga janareta mai dogara.

  • Idan kuki SYN yayi daidai a cikin fakitin ACK, ba kwa buƙatar buga saƙo, amma ku tuna IP na abokin ciniki da aka tabbatar don ci gaba da wucewa fakiti daga ciki.

Tabbacin abokin ciniki na halal:

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

Littattafan sun nuna cewa cak ɗin ya wuce (flags=0x2 - wannan shine SYN, flags=0x10 da 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

Duk da yake babu jerin ingantattun IPs, ba za a sami kariya daga ambaliyar SYN da kanta ba, amma ga martani ga ambaliya ta ACK da aka ƙaddamar ta hanyar umarni mai zuwa:

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

Shiga ciki:

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

ƙarshe

Wani lokaci eBPF gabaɗaya da XDP musamman ana gabatar da su azaman kayan aikin mai ci gaba fiye da azaman dandamali na haɓakawa. Lallai, XDP kayan aiki ne don tsoma baki tare da sarrafa fakiti ta kernel, kuma ba madadin tarin kwaya ba, kamar DPDK da sauran zaɓuɓɓukan kewayar kwaya. A gefe guda, XDP yana ba ku damar aiwatar da dabaru masu rikitarwa, wanda, ƙari kuma, yana da sauƙin sabuntawa ba tare da katsewa ba a cikin sarrafa zirga-zirga. Mai tabbatarwa baya haifar da manyan matsaloli; da kaina, ba zan ƙi wannan don sassan lambar sararin mai amfani ba.

A kashi na biyu, idan batun yana da ban sha'awa, za mu kammala tebur na abokan ciniki da aka tabbatar da kuma cire haɗin, aiwatar da ƙididdiga da rubuta mai amfani da sararin samaniya don sarrafa tacewa.

Tunani:

source: www.habr.com

Add a comment