Gisulat namon ang proteksyon batok sa mga pag-atake sa DDoS sa XDP. Nukleyar nga bahin

Ang teknolohiya sa eXpress Data Path (XDP) nagtugot sa random nga pagproseso sa trapiko nga ipahigayon sa mga interface sa Linux sa dili pa ang mga pakete mosulod sa kernel network stack. Aplikasyon sa XDP - proteksyon batok sa mga pag-atake sa DDoS (CloudFlare), komplikado nga mga pagsala, koleksyon sa istatistika (Netflix). Ang mga programa sa XDP gipatuman sa eBPF virtual machine, mao nga adunay mga pagdili sa ilang code ug sa magamit nga kernel function depende sa tipo sa filter.

Ang artikulo gituyo aron pun-on ang mga kakulangan sa daghang mga materyales sa XDP. Una, naghatag sila og andam nga code nga diha-diha dayon nag-bypass sa mga bahin sa XDP: giandam kini alang sa pag-verify o yano ra kaayo nga hinungdan sa mga problema. Kung gisulayan nimo nga isulat ang imong code gikan sa wala, wala ka nahibal-an kung unsa ang buhaton sa kasagaran nga mga sayup. Ikaduha, ang mga paagi sa lokal nga pagsulay sa XDP nga walay VM ug hardware wala masakop, bisan pa sa kamatuoran nga sila adunay kaugalingong mga lit-ag. Ang teksto gituyo alang sa mga programmer nga pamilyar sa networking ug Linux nga interesado sa XDP ug eBPF.

Sa niini nga bahin, atong masabtan sa detalye kon sa unsang paagi ang XDP filter gitigum ug sa unsa nga paagi sa pagsulay niini, unya kita mosulat sa usa ka yano nga bersyon sa iladong SYN cookies mekanismo sa packet nga lebel sa pagproseso. Dili pa kami maghimo usa ka "puti nga lista".
gipamatud-an nga mga kliyente, pagtipig sa mga counter ug pagdumala sa filter - igo nga mga troso.

Magsulat kami sa C - dili kini uso, apan praktikal kini. Ang tanan nga code magamit sa GitHub pinaagi sa link sa katapusan ug gibahin sa mga commit sumala sa mga yugto nga gihulagway sa artikulo.

Disclaimer. Sa dagan sa kini nga artikulo, maghimo ako usa ka mini nga solusyon aron mapugngan ang mga pag-atake sa DDoS, tungod kay kini usa ka realistiko nga buluhaton alang sa XDP ug sa akong lugar nga kahanas. Bisan pa, ang panguna nga katuyoan mao ang pagsabut sa teknolohiya; dili kini usa ka giya sa paghimo og andam nga proteksyon. Ang kodigo sa panudlo wala ma-optimize ug wala’y pipila nga mga nuances.

XDP Mubo nga Overview

I-outline ko lang ang yawe nga mga punto aron dili madoble ang dokumentasyon ug mga kasamtangan nga artikulo.

Busa, ang filter code gikarga sa kernel. Ang umaabot nga mga pakete gipasa sa filter. Ingon usa ka sangputanan, ang filter kinahanglan maghimo usa ka desisyon: ipasa ang pakete sa kernel (XDP_PASS), drop packet (XDP_DROP) o ipadala kini balik (XDP_TX). Ang filter mahimong mag-usab sa pakete, kini ilabi na nga tinuod alang sa XDP_TX. Mahimo usab nimo nga i-abort ang programa (XDP_ABORTED) ug i-reset ang package, apan parehas kini assert(0) - para sa pag-debug.

Ang eBPF (extended Berkley Packet Filter) nga virtual nga makina gituyo nga gihimo nga simple aron ang kernel makasusi nga ang code dili mag-loop ug dili makadaut sa memorya sa ubang mga tawo. Kumulatibo nga mga pagdili ug pagsusi:

  • Ang mga loops (paatras) gidili.
  • Adunay usa ka stack alang sa datos, apan walay mga gimbuhaton (ang tanan nga mga gimbuhaton sa C kinahanglan nga inline).
  • Gidili ang pag-access sa memorya sa gawas sa stack ug packet buffer.
  • Limitado ang gidak-on sa code, apan sa praktis kini dili kaayo mahinungdanon.
  • Ang mga tawag lamang sa mga espesyal nga kernel function (mga katabang sa eBPF) ang gitugotan.

Ang pagdesinyo ug pag-instalar sa usa ka filter ingon niini:

  1. Source code (eg kernel.c) gihugpong ngadto sa butang (kernel.o) alang sa eBPF virtual machine architecture. Sukad sa Oktubre 2019, ang paghugpong sa eBPF gisuportahan ni Clang ug gisaad sa GCC 10.1.
  2. Kung kini nga code sa butang adunay mga tawag sa mga istruktura sa kernel (pananglitan, mga lamesa ug mga counter), ang ilang mga ID gipulihan sa mga sero, nga nagpasabut nga ang ingon nga code dili ma-execute. Sa dili pa i-load sa kernel, kinahanglan nimo nga ilisan kini nga mga sero sa mga ID sa piho nga mga butang nga gihimo pinaagi sa mga tawag sa kernel (link ang code). Mahimo nimo kini gamit ang mga eksternal nga kagamitan, o mahimo ka magsulat usa ka programa nga mag-link ug mag-load sa usa ka piho nga filter.
  3. Ang kernel nagpamatuod sa gikarga nga programa. Ang pagkawala sa mga siklo ug kapakyasan nga molapas sa mga utlanan sa packet ug stack gisusi. Kung dili mapamatud-an sa verifier nga husto ang code, gisalikway ang programa - kinahanglan nimo nga mapahimut-an siya.
  4. Human sa malampuson nga pag-verify, gi-compile sa kernel ang eBPF architecture object code ngadto sa machine code para sa system architecture (just-in-time).
  5. Ang programa gilakip sa interface ug nagsugod sa pagproseso sa mga pakete.

Tungod kay ang XDP nagdagan sa kernel, ang pag-debug gihimo gamit ang mga trace log ug, sa tinuud, mga pakete nga gisala o gigama sa programa. Bisan pa, gisiguro sa eBPF nga ang na-download nga code luwas alang sa sistema, aron mahimo ka mag-eksperimento sa XDP direkta sa imong lokal nga Linux.

Pag-andam sa Kalikopan

Assembly

Ang Clang dili direkta nga makahimo og object code alang sa eBPF nga arkitektura, busa ang proseso naglangkob sa duha ka mga lakang:

  1. I-compile ang C code sa LLVM bytecode (clang -emit-llvm).
  2. I-convert ang bytecode ngadto sa eBPF object code (llc -march=bpf -filetype=obj).

Sa pagsulat sa usa ka filter, ang usa ka pares nga mga file nga adunay mga auxiliary function ug macros mahimong mapuslanon gikan sa mga pagsulay sa kernel. Importante nga motakdo sila sa bersyon sa kernel (KVER). I-download kini sa 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 alang sa 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 naglangkob sa dalan sa mga header sa kernel, ARCH - arkitektura sa sistema. Ang mga agianan ug mga himan mahimong magkalainlain og gamay tali sa mga pag-apod-apod.

Pananglitan sa mga kalainan alang sa 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 ikonektar ang usa ka direktoryo nga adunay mga auxiliary header ug daghang mga direktoryo nga adunay mga header sa kernel. Simbolo __KERNEL__ nagpasabot nga ang mga ulohan sa UAPI (userspace API) gihubit alang sa kernel code, tungod kay ang filter gipatuman sa kernel.

Ang proteksyon sa stack mahimong ma-disable (-fno-stack-protector), tungod kay ang eBPF code verifier nagsusi gihapon sa stack out-of-bounds nga mga paglapas. Angayan nga i-on dayon ang mga pag-optimize, tungod kay limitado ang gidak-on sa eBPF bytecode.

Magsugod kita sa usa ka filter nga mopasa sa tanan nga mga pakete ug walay mahimo:

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

team make nangolekta xdp_filter.o. Asa man kini sulayan karon?

pagsulay stand

Ang baruganan kinahanglan nga maglakip sa duha ka mga interface: diin adunay usa ka filter ug gikan diin ang mga pakete ipadala. Kini kinahanglan nga hingpit nga mga aparato sa Linux nga adunay ilang kaugalingon nga mga IP aron masusi kung giunsa ang mga regular nga aplikasyon nagtrabaho sa among filter.

Ang mga aparato sa veth (virtual Ethernet) nga tipo angay alang kanamo: kini usa ka pares sa mga interface sa virtual nga network nga "konektado" direkta sa usag usa. Mahimo nimong buhaton kini nga sama niini (sa kini nga seksyon ang tanan nga mga mando ip gipatuman gikan sa root):

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

kini mao ang xdp-remote и xdp-local - mga ngalan sa aparato. Sa xdp-local (192.0.2.1/24) usa ka filter ang ilakip, nga adunay xdp-remote (192.0.2.2/24) ang umaabot nga trapiko ipadala. Bisan pa, adunay problema: ang mga interface naa sa parehas nga makina, ug ang Linux dili magpadala sa trapiko sa usa kanila pinaagi sa lain. Mahimo nimo kini masulbad gamit ang malisud nga mga lagda iptables, apan kinahanglan nila nga usbon ang mga pakete, nga dili kombenyente alang sa pag-debug. Mas maayo nga gamiton ang mga namespace sa network (pagkahuman niini netns).

Ang namespace sa network adunay usa ka set sa mga interface, mga routing table, ug mga lagda sa NetFilter nga nahimulag gikan sa parehas nga mga butang sa ubang mga netns. Ang matag proseso nagdagan sa usa ka namespace ug adunay access lamang sa mga butang sa kana nga netns. Sa kasagaran, ang sistema adunay usa ka network namespace alang sa tanan nga mga butang, aron makatrabaho ka sa Linux ug wala mahibal-an ang bahin sa netns.

Magbuhat ta ug bag-ong namespace xdp-test ug ibalhin kini didto xdp-remote.

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

Unya ang proseso nagdagan xdp-test, dili "makakita" xdp-local (kini magpabilin sa netns pinaagi sa default) ug kung magpadala usa ka pakete sa 192.0.2.1 kini moagi niini xdp-remotetungod kay kini ang bugtong interface sa 192.0.2.0/24 nga ma-access niini nga proseso. Naglihok usab kini sa atbang nga direksyon.

Kung naglihok taliwala sa mga netns, ang interface nahinabo ug nawala ang adres niini. Aron ma-configure ang interface sa netns, kinahanglan nimo nga modagan ip ... sa niini nga command namespace 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

Sama sa imong makita, kini walay kalainan sa setting xdp-local sa default nga namespace:

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

Kung modagan ka tcpdump -tnevi xdp-local, imong makita nga ang mga pakete nga gipadala gikan sa xdp-test, gihatag sa kini nga interface:

ip netns exec xdp-test   ping 192.0.2.1

Kombenyente nga maglansad og kabhang xdp-test. Ang repository adunay script nga nag-automate sa pagtrabaho sa stand; pananglitan, mahimo nimong i-configure ang stand gamit ang command sudo ./stand up ug delete kini sudo ./stand down.

Pagsubay

Ang filter nalangkit sa device sama niini:

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

Key -force gikinahanglan sa pagsumpay sa usa ka bag-ong programa kon ang lain na-link na. "Walay balita ang maayong balita" dili bahin sa kini nga mando, ang konklusyon labi ka daghan sa bisan unsang kaso. nagpaila verbose opsyonal, apan uban niini ang usa ka taho makita sa buhat sa code verifier nga adunay listahan sa asembliya:

Verifier analysis:

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

I-unlink ang programa gikan sa interface:

ip link set dev xdp-local xdp off

Sa script kini mga sugo sudo ./stand attach и sudo ./stand detach.

Pinaagi sa paglakip sa usa ka filter, mahimo nimong masiguro kana ping nagpadayon sa pagdagan, apan nagtrabaho ba ang programa? Magdugang ta og mga troso. Kalihokan bpf_trace_printk() susama sa printf(), apan nagsuporta lang hangtod sa tulo ka mga argumento gawas sa sumbanan, ug limitado nga lista sa mga espesipiko. Macro bpf_printk() gipasimple ang tawag.

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

Ang output moadto sa kernel trace channel, nga kinahanglan nga mahimo:

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

Tan-awa ang thread sa mensahe:

cat /sys/kernel/debug/tracing/trace_pipe

Ang duha niini nga mga sugo naghimo sa usa ka tawag sudo ./stand log.

Ang ping kinahanglan nga mag-trigger sa mga mensahe sama niini:

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

Kung imong tan-awon pag-ayo ang output sa verifier, imong mamatikdan ang mga katingad-an nga kalkulasyon:

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

Ang tinuod mao nga ang mga programa sa eBPF walay seksyon sa datos, mao nga ang bugtong paagi sa pag-encode sa usa ka format nga string mao ang diha-diha nga mga argumento sa mga sugo sa VM:

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

Tungod niini nga hinungdan, ang pag-debug nga output labi nga nagpaburot sa resulta nga code.

Nagpadala mga XDP Packet

Atong usbon ang filter: ipabalik niini ang tanan nga umaabot nga mga pakete. Dili kini husto gikan sa punto sa pagtan-aw sa network, tungod kay kinahanglan nga usbon ang mga adres sa mga ulohan, apan karon ang trabaho sa prinsipyo hinungdanon.

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

Paglusad tcpdump sa xdp-remote. Kinahanglan kini magpakita sa parehas nga paggawas ug umaabot nga ICMP Echo Request ug hunong sa pagpakita sa ICMP Echo Reply. Apan wala kini magpakita. Kini nahimo nga alang sa trabaho XDP_TX sa programa sa xdp-local kinahanglanonsa pares nga interface xdp-remote usa ka programa usab ang gi-assign, bisan kung kini walay sulod, ug siya gipadako.

Giunsa nako pagkahibalo niini?

Pagsubay sa agianan sa usa ka pakete sa kernel Ang mekanismo sa mga panghitabo sa perf nagtugot, sa paagi, gamit ang parehas nga virtual machine, nga mao, ang eBPF gigamit alang sa mga disassemblies nga adunay eBPF.

Kinahanglan nga buhaton nimo ang maayo gikan sa daotan, tungod kay wala nay lain nga makuha gikan niini.

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

Unsa ang code 6?

$ errno 6
ENXIO 6 No such device or address

function veth_xdp_flush_bq() nakadawat og error code gikan sa veth_xdp_xmit(), diin pangitaa pinaagi sa ENXIO ug pangitaa ang komento.

Atong ibalik ang minimum nga filter (XDP_PASS) sa file xdp_dummy.c, idugang kini sa Makefile, ihigot kini sa xdp-remote:

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

Karon tcpdump nagpakita kung unsa ang gilauman:

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

Kung ang mga ARP ra ang gipakita, kinahanglan nimo nga tangtangon ang mga pagsala (kini sudo ./stand detach), buhii ping, dayon i-set ang mga filter ug sulayi pag-usab. Ang problema kay ang filter XDP_TX balido sa duha sa ARP ug kung ang stack
mga namespace xdp-test nakahimo sa "pagkalimot" sa MAC address 192.0.2.1, kini dili makahimo sa pagsulbad niini nga IP.

Pagbuot sa problema

Mopadayon kita sa gipahayag nga buluhaton: pagsulat ug mekanismo sa SYN cookies sa XDP.

Ang SYN flood nagpabilin nga usa ka popular nga pag-atake sa DDoS, ang esensya niini mao ang mosunod. Sa diha nga ang usa ka koneksyon gitukod (TCP handshake), ang server makadawat og SYN, naggahin og mga kapanguhaan alang sa umaabot nga koneksyon, mitubag sa usa ka SYNACK packet ug naghulat alang sa usa ka ACK. Ang tig-atake nagpadala lang og liboan ka SYN packet kada segundo gikan sa mga spoofed nga adres gikan sa matag host sa usa ka multi-thousand-strong botnet. Ang server napugos sa paggahin ug mga kahinguhaan diha-diha dayon sa pag-abot sa pakete, apan gibuhian kini human sa usa ka dako nga timeout; isip resulta, ang panumduman o mga limitasyon nahurot, ang mga bag-ong koneksyon dili gidawat, ug ang serbisyo dili magamit.

Kung dili ka maggahin ug mga kapanguhaan base sa SYN packet, apan motubag lang gamit ang SYNACK packet, unsaon man sa server nga masabtan nga ang ACK packet nga miabot sa ulahi nagtumong sa SYN packet nga wala maluwas? Pagkahuman, ang usa ka tig-atake mahimo usab nga makamugna og peke nga mga ACK. Ang punto sa SYN cookie mao ang pag-encode niini seqnum mga parameter sa koneksyon ingon usa ka hash sa mga adres, pantalan ug pagbag-o sa asin. Kung ang ACK nakahimo sa pag-abot sa wala pa mausab ang asin, mahimo nimong kuwentahon ang hash pag-usab ug itandi kini acknum. Forge acknum ang tig-atake dili mahimo, tungod kay ang asin naglakip sa sekreto, ug wala'y panahon sa paghan-ay niini tungod sa limitado nga agianan.

Ang SYN cookie dugay na nga gipatuman sa Linux kernel ug mahimo pa gani nga awtomatik nga ma-enable kung ang mga SYN moabut nga dali ug daghan.

Edukasyon nga programa sa TCP handshake

Naghatag ang TCP og data transmission isip stream sa bytes, pananglitan, ang HTTP requests kay gipasa sa TCP. Ang sapa gipasa sa mga piraso sa mga pakete. Ang tanan nga mga pakete sa TCP adunay lohikal nga mga bandila ug 32-bit nga mga numero sa han-ay:

  • Ang kombinasyon sa mga bandila nagtino sa papel sa usa ka partikular nga pakete. Ang bandila sa SYN nagpakita nga kini ang unang pakete sa nagpadala sa koneksyon. Ang bandila sa ACK nagpasabot nga ang nagpadala nakadawat sa tanang data sa koneksyon hangtod sa byte acknum. Ang usa ka pakete mahimong adunay daghang mga bandila ug gitawag sa ilang kombinasyon, pananglitan, usa ka pakete sa SYNACK.

  • Sequence number (seqnum) nagtino sa offset sa data stream alang sa unang byte nga gipasa niini nga packet. Pananglitan, kung sa unang pakete nga adunay X bytes sa datos kini nga numero mao ang N, sa sunod nga pakete nga adunay bag-ong datos kini mahimong N+X. Sa sinugdanan sa koneksyon, ang matag kilid mopili niini nga numero nga random.

  • Numero sa pag-ila (acknum) - parehas nga offset sa seqnum, apan wala kini nagtino sa gidaghanon sa byte nga gipasa, apan ang numero sa unang byte gikan sa nakadawat, nga wala makita sa nagpadala.

Sa sinugdanan sa koneksyon, ang mga partido kinahanglan nga magkauyon seqnum и acknum. Nagpadala ang kliyente og SYN packet uban niini seqnum = X. Ang server motubag sa usa ka SYNACK packet, diin kini nagrekord niini seqnum = Y ug nagbutyag acknum = X + 1. Ang kliyente motubag sa SYNACK gamit ang ACK packet, diin seqnum = X + 1, acknum = Y + 1. Pagkahuman niini, magsugod ang aktwal nga pagbalhin sa datos.

Kung ang kauban dili moila sa resibo sa pakete, ang TCP ipadala kini pag-usab human sa usa ka timeout.

Ngano nga ang SYN cookies dili kanunay gigamit?

Una, kung nawala ang SYNACK o ACK, maghulat ka nga ipadala kini pag-usab - ang pag-setup sa koneksyon mohinay. Ikaduha, sa SYN package - ug sa sulod ra! — daghang mga kapilian ang gipasa nga makaapekto sa dugang nga operasyon sa koneksyon. Kung wala’y paghinumdom sa umaabot nga mga pakete sa SYN, ang server sa ingon wala magtagad niini nga mga kapilian; ang kliyente dili magpadala niini sa sunod nga mga pakete. Mahimong molihok ang TCP sa kini nga kaso, apan labing menos sa una nga yugto ang kalidad sa koneksyon mokunhod.

Gikan sa panglantaw sa mga pakete, ang programa sa XDP kinahanglang mobuhat sa mosunod:

  • tubaga ang SYN gamit ang SYNACK gamit ang cookie;
  • pagtubag sa ACK gamit ang RST (disconnect);
  • isalikway ang nahabilin nga mga pakete.

Pseudocode sa algorithm uban sa parsing sa package:

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

Sa usa ka (*) Ang mga punto diin kinahanglan nimo nga pagdumala ang kahimtang sa sistema gimarkahan - sa una nga yugto mahimo nimo nga wala sila pinaagi lamang sa pagpatuman sa usa ka TCP handshake nga adunay henerasyon sa usa ka SYN cookie ingon usa ka seqnum.

Diha diha dayon (**), samtang wala mi lamesa, laktawan namo ang pakete.

Pagpatuman sa TCP handshake

Pag-parse sa package ug pag-verify sa code

Kinahanglan namon ang mga istruktura sa header sa network: Ethernet (uapi/linux/if_ether.h), IPv4 (uapi/linux/ip.h) ug TCP (uapi/linux/tcp.h). Wala nako makonektar ang ulahi tungod sa mga sayup nga may kalabutan sa atomic64_t, Kinahanglan nakong kopyahon ang gikinahanglan nga mga kahulugan ngadto sa code.

Ang tanan nga mga gimbuhaton nga gipasiugda sa C alang sa pagkabasa kinahanglan nga inline sa punto sa pagtawag, tungod kay ang eBPF verifier sa kernel nagdili sa backtracking, nga mao, sa tinuud, mga loop ug function nga tawag.

#define INTERNAL static __attribute__((always_inline))

Macro LOG() nagpugong sa pag-imprinta sa pagpagawas sa pagtukod.

Ang programa usa ka conveyor sa mga gimbuhaton. Ang matag usa makadawat sa usa ka pakete diin ang katugbang nga lebel nga ulohan gipasiugda, pananglitan, process_ether() nagpaabot nga kini mapuno ether. Pinasukad sa mga resulta sa pag-analisa sa uma, ang function mahimo’g ipasa ang pakete sa mas taas nga lebel. Ang resulta sa function mao ang XDP nga aksyon. Sa pagkakaron, ang SYN ug ACK handler mopasa sa tanang packet.

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

Gidani nako ang imong atensyon sa mga tseke nga gimarkahan nga A ug B. Kung mokomentaryo ka sa A, ang programa magtukod, apan adunay sayup sa pag-verify kung nagkarga:

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!

Key hilo invalid access to packet, off=13 size=1, R7(id=0,off=0,r=0): Adunay mga agianan sa pagpatuman kung ang ikanapulog tulo nga byte gikan sa sinugdanan sa buffer naa sa gawas sa pakete. Lisud sabton gikan sa listahan kung unsang linya ang atong gihisgutan, apan adunay instruksyon nga numero (12) ug usa ka disassembler nga nagpakita sa mga linya sa source code:

llvm-objdump -S xdp_filter.o | less

Sa kini nga kaso kini nagpunting sa linya

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

nga nagpatin-aw nga ang problema mao ether. Kini kanunay nga ingon niini.

Tubag sa SYN

Ang tumong niini nga yugto mao ang pagmugna og saktong SYNACK packet nga adunay fixed seqnum, nga pulihan sa umaabot sa SYN cookie. Ang tanan nga mga pagbag-o mahitabo sa process_tcp_syn() ug palibot nga mga dapit.

Pagpamatuod sa package

Katingad-an, ania ang labing katingad-an nga linya, o labi pa, ang komentaryo niini:

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

Sa pagsulat sa unang bersyon sa code, ang 5.1 kernel gigamit, alang sa verifier nga adunay kalainan tali sa data_end и (const void*)ctx->data_end. Sa panahon sa pagsulat, ang kernel 5.3.1 wala niini nga problema. Posible nga ang compiler nag-access sa usa ka lokal nga variable nga lahi sa usa ka field. Moral sa istorya: Ang pagpayano sa code makatabang kung adunay daghang salag.

Sunod mao ang naandan nga mga pagsusi sa gitas-on alang sa himaya sa verifier; O MAX_CSUM_BYTES sa ubos.

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

Pagbuklad sa pakete

Among pun-an seqnum и acknum, itakda ang ACK (Nakatakda na ang SYN):

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

Ibaylo ang mga TCP port, IP address ug MAC address. Ang standard nga librarya dili ma-access gikan sa XDP nga programa, busa memcpy() - usa ka macro nga nagtago sa 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);

Pag-usab sa mga checksum

Ang IPv4 ug TCP checksums nagkinahanglan sa pagdugang sa tanang 16-bit nga mga pulong sa mga ulohan, ug ang gidak-on sa mga ulohan gisulat ngadto kanila, nga mao, wala mahibaloi sa panahon sa pag-compile. Kini usa ka problema tungod kay ang verifier dili molaktaw sa normal nga loop sa boundary variable. Apan limitado ang gidak-on sa mga ulohan: hangtod sa 64 bytes matag usa. Makahimo ka og loop nga adunay usa ka piho nga gidaghanon sa mga pag-uli, nga mahimong matapos sa sayo.

Namatikdan nako nga adunay RFC 1624 mahitungod sa kon sa unsang paagi sa partially recalculate sa checksum kon lamang ang fixed nga mga pulong sa mga packages giusab. Bisan pa, ang pamaagi dili unibersal, ug ang pagpatuman mas lisud nga mapadayon.

Function sa pagkalkula sa 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;
}

Bisan pa size gipamatud-an pinaagi sa calling code, ang ikaduhang exit nga kondisyon gikinahanglan aron ang verifier makapamatuod sa pagkompleto sa loop.

Alang sa 32-bit nga mga pulong, usa ka mas simple nga bersyon ang gipatuman:

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

Sa tinuud nga pagkalkula pag-usab sa mga checksum ug pagpadala sa pakete balik:

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;

function carry() naghimo ug checksum gikan sa 32-bit sum sa 16-bit nga mga pulong, sumala sa RFC 791.

TCP handshake verification

Ang filter sa husto nga pagtukod sa usa ka koneksyon uban sa netcat, nawala ang katapusang ACK, diin ang Linux mitubag sa usa ka RST packet, tungod kay ang network stack wala makadawat sa SYN - kini nakabig ngadto sa SYNACK ug gipadala balik - ug gikan sa OS nga punto sa panglantaw, usa ka pakete miabut nga walay kalabutan sa pag-abli mga koneksyon.

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

Mahinungdanon nga susihon ang mga bug-os nga aplikasyon ug obserbahan tcpdump sa xdp-remote kay pananglitan, hping3 dili motubag sa sayop nga mga checksum.

Gikan sa usa ka punto sa XDP, ang pag-verify mismo gamay ra. Ang kalkulasyon nga algorithm kay karaan ug lagmit huyang sa usa ka sopistikado nga tig-atake. Ang Linux kernel, pananglitan, naggamit sa cryptographic SipHash, apan ang pagpatuman niini alang sa XDP klaro nga lapas sa kasangkaran niini nga artikulo.

Gipaila alang sa bag-ong mga TODO nga may kalabutan sa eksternal nga komunikasyon:

  • Ang XDP nga programa dili makatipig cookie_seed (ang tinago nga bahin sa asin) sa usa ka global variable, kinahanglan nimo ang pagtipig sa kernel, ang kantidad nga matag karon ug unya ma-update gikan sa usa ka kasaligan nga generator.

  • Kung ang SYN cookie motakdo sa ACK packet, dili nimo kinahanglan nga mag-imprinta og mensahe, apan hinumdomi ang IP sa gi-verify nga kliyente aron makapadayon sa pagpasa sa mga pakete gikan niini.

Lehitimong pag-verify sa kliyente:

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

Ang mga troso nagpakita nga ang tseke milabay (flags=0x2 - kini si SYN, flags=0x10 mao ang 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

Samtang wala'y listahan sa napamatud-an nga mga IP, walay proteksyon gikan sa SYN nga baha mismo, apan ania ang reaksyon sa usa ka ACK nga baha nga gilunsad sa mosunod nga sugo:

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

Mga entry sa log:

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

konklusyon

Usahay ang eBPF sa kinatibuk-an ug ang XDP sa partikular gipresentar nga labaw pa nga usa ka advanced nga himan sa administrador kaysa usa ka plataporma sa pagpalambo. Sa tinuud, ang XDP usa ka himan alang sa pagpanghilabot sa pagproseso sa mga pakete pinaagi sa kernel, ug dili usa ka alternatibo sa kernel stack, sama sa DPDK ug uban pang mga kapilian sa pag-bypass sa kernel. Sa laing bahin, gitugotan ka sa XDP nga ipatuman ang labi ka komplikado nga lohika, nga, dugang pa, dali nga ma-update nga wala’y paghunong sa pagproseso sa trapiko. Ang verifier dili makamugna og dagkong mga problema; sa personal, dili ko kini balibaran alang sa mga bahin sa userspace code.

Sa ikaduha nga bahin, kung ang hilisgutan makapaikag, atong kompletohon ang lamesa sa napamatud-an nga mga kliyente ug mga disconnection, ipatuman ang mga counter ug pagsulat sa usa ka userspace utility aron pagdumala sa filter.

Mga reperensiya:

Source: www.habr.com

Idugang sa usa ka comment