Ke kākau nei mākou i ka pale ʻana i nā hoʻouka kaua DDoS ma XDP. ʻāpana nukelea

Hiki i ka ʻenehana eXpress Data Path (XDP) ke hoʻokō ʻia i ka hana kaʻa kaʻa ma luna o nā kikowaena Linux ma mua o ke komo ʻana o nā ʻeke i ka waihona pūnaewele kernel. Ke noi o XDP - pale i ka DDoS hoʻouka (CloudFlare), kānana paʻakikī, hōʻiliʻili helu (Netflix). Hoʻokō ʻia nā polokalamu XDP e ka mīkini virtual eBPF, no laila ua kaupalena ʻia kā lākou code a me nā hana kernel i loaʻa ma muli o ke ʻano kānana.

Manaʻo ka ʻatikala e hoʻopiha i nā hemahema o nā mea he nui ma XDP. ʻO ka mea mua, hāʻawi lākou i nā code i mākaukau e kāpae koke i nā hiʻohiʻona o XDP: ua mākaukau no ka hōʻoia a maʻalahi paha e hoʻopilikia i nā pilikia. Ke ho'āʻo nei ʻoe e kākau i kāu code mai ka wā ʻōpala, ʻaʻole ʻoe maopopo i ka mea e hana ai me nā hewa maʻamau. ʻO ka lua, ʻaʻole i uhi ʻia nā ala e hoʻāʻo ai i ka XDP kūloko me ka ʻole o kahi VM a me nā hāmeʻa, ʻoiai ʻo ka loaʻa ʻana o kā lākou mau pitfalls. Hoʻolālā ʻia ka kikokikona no nā mea papahana i kamaʻāina me ka pūnaewele a me Linux e makemake nui ana i ka XDP a me ka eBPF.

Ma kēia ʻāpana, e hoʻomaopopo mākou i nā kikoʻī pehea e ʻākoakoa ai ka kānana XDP a pehea e hoʻāʻo ai, a laila e kākau mākou i kahi mana maʻalahi o ka mīkini kuki SYN kaulana i ka pae hoʻoili packet. ʻAʻole mākou e hana i kahi "papa inoa keʻokeʻo".
nā mea kūʻai i hōʻoia ʻia, mālama i nā helu helu a mālama i ka kānana - lawa nā lāʻau.

E kākau mākou ma C - ʻaʻole ia he mea maʻamau, akā hiki ke hana. Loaʻa nā code āpau ma GitHub ma o ka loulou ma ka hopena a ua māhele ʻia i nā hana e like me nā pae i wehewehe ʻia ma ka ʻatikala.

ʻO ka hōʻole. Ma ke kaʻina o kēia ʻatikala, e hoʻomohala wau i kahi mini-solution e pale aku i nā hoʻouka kaua DDoS, no ka mea, he hana maoli kēia no XDP a me koʻu wahi o ka ʻike. Eia nō naʻe, ʻo ka pahuhopu nui ka hoʻomaopopo ʻana i ka ʻenehana; ʻaʻole kēia he alakaʻi i ka hana ʻana i ka pale mākaukau. ʻAʻole maikaʻi ka code aʻoaʻo a haʻalele i kekahi mau nuances.

XDP Pokole Nānā

E wehewehe au i nā mea nui i ʻole e hoʻopālua i nā palapala a me nā ʻatikala.

No laila, hoʻokomo ʻia ke code kānana i loko o ka kernel. Hāʻawi ʻia nā ʻeke komo i ka kānana. ʻO ka hopena, pono e hoʻoholo ka kānana: e hoʻokomo i ka ʻeke i loko o ka kernel (XDP_PASS), hāʻule pūʻolo (XDP_DROP) a i ʻole e hoʻihoʻi (XDP_TX). Hiki i ka kānana ke hoʻololi i ka pūʻolo, pono maoli kēia no XDP_TX. Hiki iā ʻoe ke hoʻopau i ka papahana (XDP_ABORTED) a hoʻonohonoho hou i ka pōʻai, akā like kēia assert(0) - no ka hoʻopau ʻana.

ʻO ka eBPF (extended Berkley Packet Filter) ka mīkini maʻemaʻe maʻalahi i hiki ke nānā i ka kernel ʻaʻole e holo ka code a ʻaʻole e hōʻino i ka hoʻomanaʻo o nā poʻe ʻē aʻe. ʻO nā kaʻina huila a me nā hōʻoia:

  • ʻAʻole pāpā ʻia nā Loops (hope).
  • Aia kahi waihona no ka ʻikepili, akā ʻaʻohe hana (pono e hoʻokomo ʻia nā hana C āpau).
  • Ua pāpā ʻia ke komo ʻana o ka hoʻomanaʻo ma waho o ka waihona a me ka packet buffer.
  • Ua kaupalena ʻia ka nui o ke code, akā ma ka hoʻomaʻamaʻa ʻaʻole ia he mea nui loa.
  • ʻO nā kelepona wale nō i nā hana kernel kūikawā (nā mea kōkua eBPF) ʻae ʻia.

ʻO ka hoʻolālā ʻana a me ke kau ʻana i kahi kānana e like me kēia:

  1. Kumu kumu (eg kernel.c) ua hōʻuluʻulu ʻia i mea (kernel.o) no ka hoʻolālā mīkini virtual eBPF. Ma ʻOkakopa 2019, kākoʻo ʻia ka hui ʻana i ka eBPF e Clang a hoʻohiki ʻia ma GCC 10.1.
  2. Inā loaʻa i kēia code mea nā kelepona i nā hale kernel (e laʻa, nā papa a me nā helu helu), ua pani ʻia kā lākou ID e nā zeros, ʻo ia hoʻi ʻaʻole hiki ke hoʻokō ʻia kēlā code. Ma mua o ka hoʻouka ʻana i loko o ka kernel, pono ʻoe e hoʻololi i kēia mau zeros me nā ID o nā mea kikoʻī i hana ʻia ma o nā kelepona kernel (link the code). Hiki iā ʻoe ke hana i kēia me nā pono waho, a i ʻole hiki iā ʻoe ke kākau i kahi papahana e hoʻopili a hoʻouka i kahi kānana kikoʻī.
  3. Hōʻoia ka kernel i ka polokalamu i hoʻouka ʻia. Hoʻopaʻa ʻia ka nele o nā pōʻai a me ka hiki ʻole ke ʻoi aku ma mua o nā palena o ka packet a me ka stack. Inā ʻaʻole hiki i ka mea hōʻoia ke hōʻoia i ka pololei o ke code, ua hōʻole ʻia ka papahana - pono ʻoe e ʻoluʻolu iā ia.
  4. Ma hope o ka hōʻoia kūleʻa, hoʻohui ka kernel i ka code object architecture eBPF i loko o ka code mīkini no ka hoʻolālā ʻōnaehana (i ka manawa wale nō).
  5. Hoʻopili ka polokalamu i ka interface a hoʻomaka i ka hana ʻana i nā ʻeke.

Ma muli o ka holo ʻana o XDP i ka kernel, hoʻokō ʻia ka debugging me ka hoʻohana ʻana i nā log trace a, ʻoiaʻiʻo, nā ʻeke i kānana a hoʻopuka paha ka papahana. Eia naʻe, hōʻoia ka eBPF i ka paʻa o ka code i hoʻoiho ʻia no ka ʻōnaehana, no laila hiki iā ʻoe ke hoʻokolohua me XDP pololei ma kāu Linux kūloko.

Hoʻomākaukau i ke Kaiapuni

Hōʻuluʻulu

ʻAʻole hiki iā Clang ke hoʻopuka pololei i ka code object no ka hoʻolālā eBPF, no laila ʻelua ʻanuʻu ke kaʻina hana:

  1. Hoʻopili i ka code C i ka LLVM bytecode (clang -emit-llvm).
  2. E hoʻohuli i ka bytecode i ka code object eBPF (llc -march=bpf -filetype=obj).

Ke kākau nei i kahi kānana, ʻelua mau faila me nā hana kōkua a me nā macro e pono ai mai nā hoʻokolohua kernel. He mea nui e hoʻohālikelike lākou i ka mana kernel (KVER). Hoʻoiho iā lākou i 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 no 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 aia ke ala i nā poʻomanaʻo kernel, ARCH — hoʻolālā ʻōnaehana. Hiki ke ʻokoʻa iki nā ala a me nā mea hana ma waena o nā māhele.

Ka laʻana o nā ʻokoʻa no 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 hoʻohui i kahi papa kuhikuhi me nā poʻomanaʻo kōkua a me nā papa kuhikuhi me nā poʻo kernel. hōʻailona __KERNEL__ 'o ia ho'i, ua wehewehe 'ia nā po'omana'o UAPI (userspace API) no ke code kernel, no ka mea, ua ho'okō 'ia ka kānana ma ka kernel.

Hiki ke hoʻopau ʻia ka pale paʻa (-fno-stack-protector), no ka mea ke nānā mau nei ka mea hōʻoia eBPF code no ka hoʻopaʻa ʻana i waho o ka palena. Pono e hoʻohuli koke i nā optimizations, no ka mea, ua kaupalena ka nui o ka eBPF bytecode.

E hoʻomaka kākou me kahi kānana e hoʻohele i nā ʻeke āpau a ʻaʻohe hana:

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

hui make ʻohiʻohi xdp_filter.o. Ma hea e hoʻāʻo ai i kēia manawa?

Kū hoʻāʻo

Pono e hoʻokomo i ʻelua mau kikowaena: kahi e loaʻa ai kahi kānana a mai kahi e hoʻouna ʻia ai nā ʻeke. Pono kēia mau polokalamu Linux piha me kā lākou IP ponoʻī i mea e nānā ai pehea e hana ai nā noi maʻamau me kā mākou kānana.

Ua kūpono nā ʻano o ka veth (virtual Ethernet) iā mākou: ʻo ia nā ʻelua o nā kikowaena pūnaewele virtual "pili" pololei i kekahi i kekahi. Hiki iā ʻoe ke hana iā lākou e like me kēia (ma kēia ʻāpana nā kauoha āpau ip lawe ʻia mai root):

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

he mea xdp-remote и xdp-local - nā inoa o ka mea hana. Ma ka xdp-local (192.0.2.1/24) e hoʻopili ʻia kahi kānana, me xdp-remote (192.0.2.2/24) e hoʻouna ʻia nā kaʻa komo. Eia nō naʻe, aia kahi pilikia: aia nā interface ma ka mīkini hoʻokahi, a ʻaʻole hoʻouna ʻo Linux i ke kaʻa i kekahi o lākou ma o kekahi. Hiki iā ʻoe ke hoʻoponopono i kēia me nā lula paʻakikī iptables, akā pono lākou e hoʻololi i nā pūʻolo, he mea kūpono ʻole no ka debugging. ʻOi aku ka maikaʻi o ka hoʻohana ʻana i nā inoa inoa pūnaewele (ma hope o netns).

Aia i loko o kahi inoa inoa pūnaewele he pūʻulu o nā pilina, nā papa kuhikuhi, a me nā lula NetFilter i hoʻokaʻawale ʻia mai nā mea like ma nā ʻupena ʻē aʻe. Holo kēlā me kēia kaʻina hana i kahi inoa inoa a loaʻa wale i nā mea o ia netns. Ma ka maʻamau, loaʻa i ka ʻōnaehana kahi inoa inoa pūnaewele no nā mea āpau, no laila hiki iā ʻoe ke hana ma Linux a ʻaʻole ʻike e pili ana i nā netns.

E hana kākou i wahi inoa hou xdp-test a hoʻoneʻe i laila xdp-remote.

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

A laila e holo ana ke kaʻina hana xdp-test, ʻaʻole e "ʻike" xdp-local (e hoʻomau ʻia i nā netns ma ke ʻano maʻamau) a i ka hoʻouna ʻana i kahi ʻeke i 192.0.2.1 e hele ia ma o xdp-remoteno ka mea, ʻo ia wale nō ke kikowaena ma 192.0.2.0/24 hiki ke loaʻa i kēia kaʻina hana. Hana pū kēia ma ka ʻaoʻao ʻē aʻe.

I ka neʻe ʻana ma waena o nā netns, e iho ka interface a nalowale kona helu wahi. No ka hoʻonohonoho ʻana i ka interface ma netns, pono ʻoe e holo ip ... ma keia papa inoa kauoha 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

E like me kāu e ʻike ai, ʻaʻole ʻokoʻa kēia mai ka hoʻonohonoho xdp-local ma ka papa inoa paʻamau:

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

Inā holo ʻoe tcpdump -tnevi xdp-local, hiki iā ʻoe ke ʻike i nā ʻeke i hoʻouna ʻia mai xdp-test, hāʻawi ʻia i kēia interface:

ip netns exec xdp-test   ping 192.0.2.1

He mea maʻalahi ke hoʻokomo i kahi pūpū i loko xdp-test. Loaʻa i ka waihona kahi palapala e hoʻokaʻawale i ka hana me ke kū; no ka laʻana, hiki iā ʻoe ke hoʻonohonoho i ke kū me ke kauoha. sudo ./stand up a holoi aku sudo ./stand down.

Ka ʻimi ʻana

Hoʻopili ʻia ka kānana me ka mea hana penei:

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

Kaomi -force pono e hoʻopili i kahi polokalamu hou inā pili kekahi. "ʻAʻohe nūhou he nūhou maikaʻi" ʻaʻole ia e pili ana i kēia kauoha, he voluminous ka hopena i kekahi hihia. kuhikuhi verbose koho, akā me ia e hōʻike ʻia ma ka hana a ka mea hōʻoia code me kahi papa inoa hui:

Verifier analysis:

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

Wehe i ka loulou i ka polokalamu mai ka interface:

ip link set dev xdp-local xdp off

Ma ka palapala he mau kauoha keia sudo ./stand attach и sudo ./stand detach.

Ma ka hoʻopili ʻana i kahi kānana, hiki iā ʻoe ke hōʻoia i kēlā ping Ke hoʻomau nei ka holo ʻana, akā hana ka papahana? E hoʻohui kākou i nā lāʻau. Hana bpf_trace_printk() like me printf(), akā kākoʻo wale i ʻekolu manaʻo hoʻopaʻapaʻa ʻē aʻe ma mua o ke kumu, a me kahi papa inoa palena o nā mea kikoʻī. Macro bpf_printk() hoʻomāmā i ke kelepona.

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

Hele ka hopena i ke kahawai kernel trace, pono e ʻae ʻia:

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

Nānā i ka pae memo:

cat /sys/kernel/debug/tracing/trace_pipe

Ke kāhea nei kēia mau kauoha ʻelua sudo ./stand log.

Pono ka ping e hoʻomaka i nā memo e like me kēia:

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

Inā ʻoe e nānā pono i ka hopena o ka mea hōʻoia, e ʻike ʻoe i nā helu ʻokoʻa:

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

ʻO ka ʻoiaʻiʻo, ʻaʻohe ʻāpana ʻikepili i nā polokalamu eBPF, no laila ʻo ke ala wale nō e hoʻopili ai i kahi string format ʻo ia nā hoʻopaʻapaʻa koke o nā kauoha VM:

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

No kēia kumu, hoʻopau nui ka debug output i ka code hopena.

Hoʻouna ʻana i nā ʻeke XDP

E hoʻololi kākou i ke kānana: e hoʻihoʻi mai i nā ʻeke komo mai. ʻAʻole pololei kēia mai kahi ʻike pūnaewele, no ka mea he mea pono ke hoʻololi i nā ʻōlelo i nā poʻo, akā i kēia manawa he mea nui ka hana ma ke kumu.

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

Hoʻolana tcpdump maluna o xdp-remote. Pono ia e hōʻike i ka ICMP Echo Request like a me ka komo ʻana a hoʻōki i ka hōʻike ʻana i ka ICMP Echo Reply. ʻAʻole hōʻike. ʻIke ʻia no ka hana XDP_TX ma ka papahana ma xdp-local pono aii ka pilina pili xdp-remote ua hāʻawi pū ʻia kahi papahana, ʻoiai he mea ʻole, a ua hānai ʻia ʻo ia.

Pehea wau i ʻike ai i kēia?

E ʻimi i ke ala o kahi pūʻolo ma ka kernel Hiki i ka mīkini hanana perf, ma ke ala, me ka hoʻohana ʻana i ka mīkini virtual like, ʻo ia hoʻi, hoʻohana ʻia ka eBPF no nā disassemblies me eBPF.

Pono ʻoe e hana i ka maikaʻi mai loko mai o ka hewa, no ka mea, ʻaʻohe mea ʻē aʻe e loaʻa ai.

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

He aha ke code 6?

$ errno 6
ENXIO 6 No such device or address

kuleana pili i veth_xdp_flush_bq() loaʻa i kahi helu kuhi mai veth_xdp_xmit(), kahi e huli ai ENXIO a loaa ka manao.

E hoʻihoʻi hou i ka kānana liʻiliʻi (XDP_PASS) ma ka waihona xdp_dummy.c, hoʻohui i ka Makefile, hoʻopaʻa iā ia xdp-remote:

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

Ano tcpdump hōʻike i ka mea i manaʻo ʻia:

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

Inā hōʻike ʻia nā ARP wale nō, pono ʻoe e wehe i nā kānana (hana kēia sudo ./stand detach), hoʻokuʻu ping, a laila hoʻonoho i nā kānana a hoʻāʻo hou. ʻO ka pilikia ka kānana XDP_TX kūpono ma ARP a inā ʻo ka waihona
nā papa inoa xdp-test hiki iā ia ke "poina" i ka helu MAC 192.0.2.1, ʻaʻole hiki ke hoʻoholo i kēia IP.

Ka hoʻokumu ʻana i ka pilikia

E neʻe kāua i ka hana i ʻōlelo ʻia: e kākau i kahi mīkini kuki SYN ma XDP.

Noho ʻo SYN flood i kahi hoʻouka kaua DDoS kaulana, ʻo ke ʻano o ia mea penei. Ke hoʻokumu ʻia kahi pilina (TCP handshake), loaʻa i ke kikowaena kahi SYN, hoʻokaʻawale i nā kumuwaiwai no ka pilina e hiki mai ana, pane me kahi ʻeke SYNACK a kali i kahi ACK. Hoʻouna wale aku ka mea hoʻouka kaua i mau tausani o nā ʻeke SYN i kēlā me kēia kekona mai nā helu kuhi ʻia mai kēlā me kēia pūʻali i loko o kahi botnet he nui tausani. Ua koi ʻia ke kikowaena e hoʻokaʻawale i nā kumuwaiwai i ka hiki ʻana mai o ka ʻeke, akā hoʻokuʻu iā lākou ma hope o kahi manawa nui; ʻo ka hopena, pau ka hoʻomanaʻo a i ʻole nā ​​palena, ʻaʻole ʻae ʻia nā pilina hou, a ʻaʻole i loaʻa ka lawelawe.

Inā ʻaʻole ʻoe e hoʻokaʻawale i nā kumuwaiwai e pili ana i ka ʻeke SYN, akā e pane wale me kahi ʻeke SYNACK, pehea lā e hiki ai i ke kikowaena ke hoʻomaopopo ʻo ka ʻeke ACK i hiki mai ma hope e pili ana i kahi ʻeke SYN ʻaʻole i mālama ʻia? Ma hope o nā mea a pau, hiki i ka mea hoʻouka ke hana i nā ACK hoʻopunipuni. ʻO ke kiko o ka kuki SYN ʻo ia ke hoʻopili iā ia i loko seqnum nā palena pili e like me ka hash o nā helu wahi, nā awa a me ka paʻakai hoʻololi. Inā hiki mai ka ACK ma mua o ka hoʻololi ʻana i ka paʻakai, hiki iā ʻoe ke helu hou i ka hash a hoʻohālikelike me acknum. Forge acknum ʻAʻole hiki i ka mea hoʻouka kaua, no ka mea, aia ka paʻakai i ka mea huna, a ʻaʻohe manawa e hoʻokaʻawale iā ia ma muli o ke kahawai palena.

Ua lōʻihi ka hoʻokō ʻia ʻana o ka kuki SYN ma ka kernel Linux a hiki ke hoʻohana ʻia inā hiki koke nā SYN a me ka nui.

Papahana hoʻonaʻauao ma TCP lima lima

Hāʻawi ʻo TCP i ka hoʻoili ʻikepili ma ke ʻano he kahawai o nā bytes, no ka laʻana, hoʻouna ʻia nā noi HTTP ma luna o TCP. Hoʻouna ʻia ke kahawai i nā ʻāpana i nā ʻeke. Loaʻa i nā ʻeke TCP āpau nā hae logical a me nā helu kaʻina 32-bit:

  • ʻO ka hui pū ʻana o nā hae e hoʻoholo i ke kuleana o kahi pūʻolo. Hōʻike ka hae SYN ʻo ia ka ʻeke mua a ka mea hoʻouna ma ka pilina. 'O ka hae ACK 'o ia ho'i, ua loa'a i ka mea ho'ouna ka 'ikepili pili a hiki i ka byte acknum. Hiki i kekahi ʻeke ke loaʻa nā hae he nui a ua kapa ʻia e ko lākou hui ʻana, no ka laʻana, kahi ʻeke SYNACK.

  • Hōʻike ka helu helu (seqnum) i ka offset ma ke kahawai ʻikepili no ka byte mua i hoʻouna ʻia i kēia ʻeke. No ka laʻana, inā i loko o ka ʻeke mua me nā X bytes o kēia helu he N, ma ka ʻeke aʻe me ka ʻikepili hou ʻo ia ka N+X. I ka hoʻomaka ʻana o ka hoʻohui ʻana, koho ʻole kēlā me kēia ʻaoʻao i kēia helu.

  • Helu hōʻoia (acknum) - ʻo ka offset like me ka seqnum, akā ʻaʻole ia e hoʻoholo i ka helu o ka byte i hoʻouna ʻia, akā ʻo ka helu o ka byte mua mai ka mea loaʻa, ʻaʻole i ʻike ʻia e ka mea hoʻouna.

I ka hoʻomakaʻana o ka pilina, pono nāʻaoʻao eʻae seqnum и acknum. Hoʻouna ka mea kūʻai i kahi ʻeke SYN me kāna seqnum = X. Pane ke kikowaena me kahi ʻeke SYNACK, kahi e hoʻopaʻa ai i kāna seqnum = Y a hōʻike acknum = X + 1. Pane ka mea kūʻai aku iā SYNACK me kahi ʻeke ACK, kahi seqnum = X + 1, acknum = Y + 1. Ma hope o kēia, hoʻomaka ka hoʻoili ʻikepili maoli.

Inā ʻaʻole ʻae ka hoa i ka loaʻa ʻana o ka ʻeke, hoʻouna hou ʻo TCP iā ia ma hope o ka manawa pau.

No ke aha ʻaʻole hoʻohana mau ʻia nā kuki SYN?

ʻO ka mea mua, inā nalowale ʻo SYNACK a i ʻole ACK, pono ʻoe e kali a hoʻouna hou ʻia - e lohi ka hoʻonohonoho pili. ʻO ka lua, i loko o ka pūʻolo SYN - a i loko wale nō! - ua hoʻouna ʻia kekahi mau koho e pili ana i ka hana hou o ka pilina. Me ka hoʻomanaʻo ʻole i nā ʻeke SYN e hiki mai ana, ʻaʻole mālama ka server i kēia mau koho; ʻaʻole e hoʻouna ka mea kūʻai aku iā lākou i nā ʻeke aʻe. Hiki i ka TCP ke hana i kēia hihia, akā ma ka liʻiliʻi ma ka pae mua e emi ka maikaʻi o ka pilina.

Mai kahi hiʻohiʻona pūʻolo, pono e hana kahi papahana XDP i kēia aʻe:

  • e pane iā SYN me SYNACK me kahi kuki;
  • pane iā ACK me RST (hoʻokaʻawale);
  • e hoʻolei i nā ʻeke i koe.

Pseudocode o ka algorithm me ka hoʻopili pūʻolo:

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

ʻekahi (*) ʻO nā wahi kahi e pono ai ʻoe e hoʻokele i ka mokuʻāina o ka ʻōnaehana e hōʻailona ʻia - ma ka pae mua hiki iā ʻoe ke hana me ka ʻole o lākou ma ka hoʻokō wale ʻana i kahi lima lima TCP me ka hana ʻana o kahi kuki SYN ma ke ʻano he seqnum.

Ma kahi (**), ʻoiai ʻaʻohe papaʻaina, e hoʻokuʻu mākou i ka ʻeke.

Ke hoʻokō nei i ka lulu lima TCP

Hoʻopili i ka pūʻolo a hōʻoia i ke code

Pono mākou i nā hale poʻomanaʻo pūnaewele: Ethernet (uapi/linux/if_ether.h), IPv4 (uapi/linux/ip.h) a me TCP (uapi/linux/tcp.h). ʻAʻole hiki iaʻu ke hoʻohui i ka hope ma muli o nā hewa e pili ana atomic64_t, Pono wau e kope i nā wehewehe pono i loko o ke code.

ʻO nā hana a pau i hōʻike ʻia ma C no ka heluhelu ʻana, pono e hoʻopaʻa ʻia ma kahi o ke kelepona ʻana, ʻoiai ʻo ka eBPF verifier i loko o ka kernel pāpā i ka backtracking, ʻo ia hoʻi, nā puka lou a me nā kelepona hana.

#define INTERNAL static __attribute__((always_inline))

Makoleko LOG() hoʻopau i ka paʻi ʻana i ke kūkulu hoʻokuʻu.

ʻO ka papahana kahi mea lawe i nā hana. Loaʻa i kēlā me kēia me kahi ʻeke kahi i hōʻike ʻia ai ke poʻomanaʻo pae kūpono, no ka laʻana, process_ether() manaʻo e hoʻopiha ʻia ether. Ma muli o nā hopena o ka nānā ʻana o ke kahua, hiki i ka hana ke hele i ka ʻeke i kahi kiʻekiʻe. ʻO ka hopena o ka hana ka hana XDP. I kēia manawa, hāʻawi nā mea lawelawe SYN a me ACK i nā ʻeke āpau.

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

Ke huki nei au i kou manaʻo i nā māka i kaha ʻia ʻo A a me B. Inā ʻoe e ʻōlelo iā A, e kūkulu ka papahana, akā aia ka hewa hōʻoia i ka wā e hoʻouka ai:

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!

kaula kī invalid access to packet, off=13 size=1, R7(id=0,off=0,r=0): Aia nā ala hoʻokō inā aia ka ʻumikūmākolu byte mai ka hoʻomaka ʻana o ka buffer ma waho o ka ʻeke. He paʻakikī ke hoʻomaopopo mai ka papa inoa i ka laina a mākou e kamaʻilio nei, akā aia kahi helu aʻo (12) a me kahi disassembler e hōʻike ana i nā laina o ke kumu kumu:

llvm-objdump -S xdp_filter.o | less

Ma keia hihia, kuhikuhi i ka laina

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

ka mea e maopopo ai ka pilikia ether. Penei mau.

Pane iā SYN

ʻO ka pahuhopu i kēia pae, ʻo ia ka hoʻopuka ʻana i kahi ʻeke SYNACK kūpono me kahi paʻa seqnum, ka mea e pani ʻia i ka wā e hiki mai ana e ke kuki SYN. Loaʻa nā loli a pau ma process_tcp_syn() a me na wahi e pili ana.

Hōʻoia pūʻolo

ʻO ka mea kupanaha, eia ka laina kupaianaha loa, a i ʻole, ʻo ka ʻōlelo ʻana iā ia:

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

I ke kākau ʻana i ka mana mua o ke code, ua hoʻohana ʻia ka kernel 5.1, no ka mea hōʻoia i loaʻa kahi ʻokoʻa ma waena o data_end и (const void*)ctx->data_end. I ka manawa kākau, ʻaʻohe pilikia o ka kernel 5.3.1. He ʻokoʻa paha ke komo ʻana o ka mea hōʻuluʻulu i kahi hoʻololi kūloko ma mua o kahi kahua. Moral o ka moʻolelo: ʻO ka hoʻomaʻamaʻa ʻana i ke code hiki ke kōkua inā nui ka pūnana.

ʻO ka mea aʻe ka nānā lōʻihi maʻamau no ka nani o ka mea hōʻoia; O MAX_CSUM_BYTES ma lalo.

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

Ke wehe nei i ka pūʻolo

Hoʻopiha mākou seqnum и acknum, hoʻonoho iā ACK (Ua hoʻonohonoho mua ʻia ʻo SYN):

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

E hoʻololi i nā awa TCP, IP address a me MAC address. ʻAʻole hiki ke loaʻa ka waihona maʻamau mai ka papahana XDP, no laila memcpy() - he macro e hūnā ana i ka 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);

Ka helu hou ʻana i nā helu helu

Pono nā loiloi IPv4 a me TCP e hoʻohui i nā huaʻōlelo 16-bit a pau i nā poʻomanaʻo, a ua kākau ʻia ka nui o nā poʻo i loko o lākou, ʻo ia hoʻi, ʻike ʻole ʻia i ka manawa hōʻuluʻulu. He pilikia kēia no ka mea ʻaʻole e lele ka mea hōʻoia i ka loop loop maʻamau i ka hoʻololi palena. Akā ua kaupalena ʻia ka nui o nā poʻomanaʻo: a hiki i 64 bytes kēlā me kēia. Hiki iā ʻoe ke hana i kahi loop me kahi helu paʻa o nā iteration, hiki ke hoʻopau koke.

ʻIke au aia aia RFC 1624 e pili ana i ka helu ʻana hapa o ka checksum inā hoʻololi wale ʻia nā huaʻōlelo paʻa o nā pūʻolo. Eia naʻe, ʻaʻole maʻamau ke ʻano, a ʻoi aku ka paʻakikī o ka hoʻokō ʻana.

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

ʻOiai size hōʻoia ʻia e ke code kelepona, pono ke kūlana puka lua i hiki i ka mea hōʻoia ke hōʻoia i ka pau ʻana o ka loop.

No nā huaʻōlelo 32-bit, ua hoʻokō ʻia kahi mana maʻalahi:

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

Ke helu hou nei i nā checksums a hoʻihoʻi i ka ʻeke:

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;

kuleana pili i carry() hana i kahi helu helu mai kahi huina 32-bit o nā huaʻōlelo 16-bit, e like me ka RFC 791.

ʻO ka hōʻoia lima lima TCP

Hoʻokumu pono ka kānana i kahi pilina me netcat, nele i ka ACK hope loa, i pane aku ai ʻo Linux me kahi ʻeke RST, no ka mea ʻaʻole i loaʻa i ka waihona pūnaewele ʻo SYN - ua hoʻohuli ʻia i SYNACK a hoʻihoʻi ʻia - a mai ka ʻaoʻao o ka OS, ua hiki mai kahi ʻeke i pili ʻole i ka wehe. pili.

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

He mea nui e nānā me nā noi piha a nānā tcpdump maluna o xdp-remote no ka mea, laʻana, hping3 ʻAʻole pane i nā helu helu hewa.

Mai kahi manaʻo XDP, he mea liʻiliʻi ka hōʻoia ponoʻī. He mea maʻamau ka algorithm helu helu a hiki ke pilikia i ka mea hoʻouka kaua. ʻO ka Linux kernel, no ka laʻana, hoʻohana i ka cryptographic SipHash, akā ʻo kāna hoʻokō ʻana no XDP ua maopopo loa ma waho o ke kiko o kēia ʻatikala.

Hoʻokomo ʻia no nā TODO hou e pili ana i ke kamaʻilio ʻana i waho:

  • ʻAʻole hiki i ka polokalamu XDP ke mālama cookie_seed (ʻo ka ʻāpana huna o ka paʻakai) i loko o kahi hoʻololi honua, pono ʻoe e mālama i ka kernel, ka waiwai o ia mea e hoʻonui ʻia i kēlā me kēia manawa mai kahi mea hana pono.

  • Inā pili ka kuki SYN i ka ʻeke ACK, ʻaʻole pono ʻoe e paʻi i kahi leka, akā e hoʻomanaʻo i ka IP o ka mea kūʻai aku i hōʻoia ʻia i mea e hoʻomau ai i ka hele ʻana i nā ʻeke mai ia mea.

Ka hōʻoia ʻana o ka mea kūʻai aku:

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

Hōʻike nā moʻolelo ua hala ka māka (flags=0x2 - ʻo SYN kēia, flags=0x10 ʻo 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

ʻOiai ʻaʻohe papa inoa o nā IP i hōʻoia ʻia, ʻaʻohe palekana mai ka waikahe SYN ponoʻī, akā eia ka pane i kahi kahe ACK i hoʻokumu ʻia e kēia kauoha:

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

Nā mea kākau moʻolelo:

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

hopena

I kekahi manawa, hōʻike ʻia ka eBPF ma ke ʻano nui a me ka XDP ma ke ʻano he mea hana hoʻokele holomua ma mua o kahi kahua hoʻomohala. ʻOiaʻiʻo, he mea hana ʻo XDP no ka hoʻopili ʻana i ka hana ʻana o nā paʻi e ka kernel, ʻaʻole kahi koho ʻē aʻe i ka pahu kernel, e like me DPDK a me nā koho ʻē aʻe kernel bypass. Ma ka ʻaoʻao ʻē aʻe, ʻae ʻo XDP iā ʻoe e hoʻokō i nā loiloi paʻakikī, ʻoi aku ka maʻalahi o ka hoʻonui ʻana me ka ʻole o ka hoʻopau ʻana i ka hana kaʻa. ʻAʻole hana ka mea hōʻoia i nā pilikia nui; ʻo wau iho, ʻaʻole wau e hōʻole i kēia no nā ʻāpana o ka code userspace.

Ma ka ʻaoʻao ʻelua, inā hoihoi ke kumuhana, e hoʻopau mākou i ka papa o nā mea kūʻai aku i hōʻoia ʻia a me nā wehe ʻana, e hoʻokō i nā helu helu a kākau i kahi mea hoʻohana hoʻohana e mālama i ka kānana.

Nā Manaʻo:

Source: www.habr.com

Pākuʻi i ka manaʻo hoʻopuka