Manoratra fiarovana amin'ny fanafihana DDoS amin'ny XDP izahay. Ampahany nokleary

Ny teknolojia eXpress Data Path (XDP) dia mamela ny fanodinana ny fifamoivoizana kisendrasendra atao amin'ny interface Linux alohan'ny hidiran'ny fonosana ao amin'ny tamba-jotra kernel. Fampiharana XDP - fiarovana amin'ny fanafihana DDoS (CloudFlare), sivana sarotra, fanangonana antontan'isa (Netflix). Ny programa XDP dia tanterahin'ny milina virtoaly eBPF, noho izany dia misy famerana ny kaody sy ny fiasan'ny kernel misy arakaraka ny karazana sivana.

Ny lahatsoratra dia natao hamenoana ny tsy fahampian'ny fitaovana maro ao amin'ny XDP. Voalohany, manome kaody efa vonona izy ireo izay mandingana avy hatrany ny endri-javatra XDP: voaomana amin'ny fanamarinana na tsotra loatra ka miteraka olana. Rehefa manandrana manoratra ny kaodinao avy amin'ny scratch ianao, dia tsy fantatrao izay tokony hatao amin'ny fahadisoana mahazatra. Faharoa, ny fomba fitiliana XDP eo an-toerana tsy misy VM sy fitaovana dia tsy voarakotra, na dia manana ny fandrika manokana aza izy ireo. Ny lahatsoratra dia natao ho an'ny mpandrindra zatra amin'ny tambajotra sy Linux izay liana amin'ny XDP sy eBPF.

Amin'ity ampahany ity dia ho azontsika amin'ny antsipiriany ny fomba fanangonana ny sivana XDP sy ny fomba fitsapana azy, avy eo dia hanoratra dikan-teny tsotra amin'ny mekanika cookies SYN malaza amin'ny ambaratonga fanodinana fonosana. Mbola tsy hamorona β€œlisitra fotsy” izahay
mpanjifa voamarina, mitazona kaontera ary mitantana ny sivana - diary ampy.

Hanoratra amin'ny C izahay - tsy lamaody izany, fa azo ampiharina. Ny kaody rehetra dia hita ao amin'ny GitHub amin'ny alΓ lan'ny rohy amin'ny farany ary mizara ho commits araka ny dingana voalaza ao amin'ny lahatsoratra.

Disclaimer. Mandritra ity lahatsoratra ity dia hamorona vahaolana kely aho mba hialana amin'ny fanafihana DDoS, satria asa tena misy ho an'ny XDP sy ny faritra misy ahy izany. Na izany aza, ny tena tanjona dia ny hahatakatra ny teknolojia; Ny kaody fampianarana dia tsy namboarina ary tsy misy nuances sasany.

XDP topimaso fohy

Ny hevi-dehibe ihany no hosoratako mba tsy handikana antontan-taratasy sy lahatsoratra efa misy.

Noho izany, ampidirina ao anaty kernel ny code sivana. Ampitaina amin'ny sivana ny fonosana miditra. Vokatr'izany dia tsy maintsy mandray fanapahan-kevitra ny sivana: ampidiro ao anaty kernel ny fonosana (XDP_PASS), mitete fonosana (XDP_DROP) na avereno (XDP_TX). Ny sivana dia afaka manova ny fonosana, tena marina izany XDP_TX. Azonao atao koa ny manafoana ny programa (XDP_ABORTED) ary avereno indray ny fonosana, fa mitovy izany assert(0) - ho an'ny debugging.

Ny milina virtoaly eBPF (Extended Berkley Packet Filter) dia natao ho tsotra mba hahafahan'ny kernel manamarina fa tsy mihodina ilay kaody ary tsy manimba ny fitadidian'ny olon-kafa. Fameperana sy fisavana mitambatra:

  • Voarara ny famoriana (mihemotra).
  • Misy stack ho an'ny angon-drakitra, fa tsy misy fiasa (ny fiasa C rehetra dia tsy maintsy ampidirina).
  • Voarara ny fidirana amin'ny fitadidiana ivelan'ny stack sy ny packet buffer.
  • Ny haben'ny kaody dia voafetra, saingy amin'ny fampiharana dia tsy dia misy dikany loatra izany.
  • Antso amin'ny asa kernel manokana (mpanampy eBPF) ihany no azo atao.

Toy izao ny famolavolana sy fametrahana sivana:

  1. Source code (oh kernel.c) dia natambatra ho zavatra (kernel.o) ho an'ny rafitra milina virtoaly eBPF. Hatramin'ny Oktobra 2019, tohanan'i Clang ny fanangonana amin'ny eBPF ary nampanantenaina ao amin'ny GCC 10.1.
  2. Raha toa ka misy antso amin'ny rafitra kernel (ohatra, tabilao sy kaontera) ity kaody zavatra ity, dia soloina aotra ny kaody misy azy, izay midika fa tsy azo tanterahina ny kaody toy izany. Alohan'ny hampidiranao ao amin'ny kernel dia mila soloinao ireo aotra ireo amin'ny ID ny zavatra manokana noforonina tamin'ny alΓ lan'ny antso kernel (rohy ny code). Azonao atao izany amin'ny fitaovana ivelany, na azonao atao ny manoratra programa izay hampifandray sy hampiditra sivana manokana.
  3. Ny kernel dia manamarina ny programa feno. Ny tsy fisian'ny tsingerina sy ny tsy fahombiazana mihoatra ny fetran'ny fonosana sy ny stack dia voamarina. Raha tsy afaka manaporofo ny fanamarinana fa marina ny kaody, dia nolavina ny programa - mila afaka mampifaly azy ianao.
  4. Aorian'ny fanamarinana mahomby, ny kernel dia manangona ny kaody object architecture eBPF ho lasa code machine ho an'ny architecture system (just-in-time).
  5. Ny programa dia miraikitra amin'ny interface ary manomboka manodina fonosana.

Satria ny XDP dia mandeha ao amin'ny kernel, ny debugging dia atao amin'ny alΓ lan'ny trace logs ary, raha ny marina, ny fonosana izay sivana na ateraky ny programa. Na izany aza, ny eBPF dia miantoka fa ny kaody alaina dia azo antoka ho an'ny rafitra, mba hahafahanao manandrana XDP mivantana amin'ny Linux eo an-toerana.

Fanomanana ny tontolo iainana

fiangonana

Clang dia tsy afaka mamokatra mivantana kaody ho an'ny maritrano eBPF, noho izany dia misy dingana roa ny dingana:

  1. Manangona kaody C amin'ny LLVM bytecode (clang -emit-llvm).
  2. Ampifamadiho ny bytecode ho kaody zavatra eBPF (llc -march=bpf -filetype=obj).

Rehefa manoratra sivana dia ilaina ny rakitra roa miaraka amin'ny fiasa fanampiny sy macro avy amin'ny fitsapana kernel. Zava-dehibe ny mifanandrify amin'ny version kernel (KVER). Download azy ireo amin'ny 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 ho an'ny 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 misy ny lalana mankany amin'ny lohatenin'ny kernel, ARCH - rafitra rafitra. Ny lalana sy ny fitaovana dia mety miovaova kely eo amin'ny fizarana.

Ohatra amin'ny fahasamihafana ho an'ny 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 mampifandray lahatahiry misy lohapejy fanampiny ary lahatahiry maromaro misy lohapejy kernel. marika famantarana __KERNEL__ midika fa ny lohatenin'ny UAPI (userspace API) dia voafaritra ho an'ny kaody kernel, satria ny sivana dia tanterahina ao amin'ny kernel.

Ny fiarovana amin'ny stack dia mety ho kilemaina (-fno-stack-protector), satria ny mpanamarina kaody eBPF dia mbola manara-maso ny fanitsakitsahana tsy misy fetra. Mendrika ny hamadika ny fanatsarana avy hatrany, satria voafetra ny haben'ny bytecode eBPF.

Andeha isika hanomboka amin'ny sivana izay mandalo ny fonosana rehetra ary tsy manao na inona na inona:

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

ekipa make manangona xdp_filter.o. Aiza no hanandrana azy izao?

Fijoroana fitsapana

Ny fijoroana dia tsy maintsy misy fifandraisana roa: izay hisy sivana ary avy aiza no handefasana fonosana. Ireo dia tsy maintsy fitaovana Linux feno miaraka amin'ny IP-ny manokana mba hanamarinana ny fomba fiasan'ny fampiharana mahazatra amin'ny sivana.

Ireo fitaovana amin'ny karazana veth (Ethernet virtoaly) dia mety ho antsika: ireo dia fifandraisana virtoaly roa "mifandray" mivantana amin'ny tsirairay. Azonao atao ny mamorona azy ireo toy izao (amin'ity fizarana ity ny baiko rehetra ip dia tanterahina avy root):

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

izany xdp-remote ΠΈ xdp-local - anaran'ny fitaovana. On xdp-local (192.0.2.1/24) hasiana sivana, miaraka amin'ny xdp-remote (192.0.2.2/24) ny fifamoivoizana miditra dia halefa. Na izany aza, misy ny olana: ny interface dia eo amin'ny milina iray ihany, ary ny Linux dia tsy handefa fifamoivoizana amin'ny iray amin'izy ireo amin'ny iray hafa. Azonao atao ny mamaha izany amin'ny fitsipika sarotra iptables, fa tsy maintsy manova fonosana izy ireo, izay tsy mety amin'ny debugging. Tsara kokoa ny mampiasa ny anaran'ny tambajotra (netns).

Ny espace anaran-tambajotra dia misy andiana interface tsara, tabilao zotra ary fitsipika NetFilter izay mitokana amin'ny zavatra mitovy amin'ny netns hafa. Ny dingana tsirairay dia mandeha amin'ny toerana misy anarana ary tsy misy afa-tsy ireo zavatra ao amin'io netns io. Amin'ny alΓ lan'ny default, ny rafitra dia manana sehatra anaran-tambajotra tokana ho an'ny zavatra rehetra, ka afaka miasa amin'ny Linux ianao ary tsy mahafantatra momba ny netns.

Andao hamorona sehatra anarana vaovao xdp-test ary afindrao any xdp-remote.

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

Avy eo dia mandeha ny dingana xdp-test, tsy "hahita" xdp-local (hijanona ao amin'ny netns izany raha default) ary rehefa mandefa fonosana amin'ny 192.0.2.1 dia handalo izany xdp-remotesatria io no hany interface tsara amin'ny 192.0.2.0/24 azo idirana amin'ity dingana ity. Izany koa dia miasa amin'ny lalana mifanohitra.

Rehefa mifindra eo anelanelan'ny netns dia midina ny interface ary very ny adiresiny. Mba hanitsiana ny interface amin'ny netns dia mila mihazakazaka ianao ip ... ao amin'ity namespace baiko ity 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

Araka ny hitanao dia tsy misy hafa amin'ny toe-javatra izany xdp-local ao amin'ny toeran'ny anarana default:

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

Raha mihazakazaka ianao tcpdump -tnevi xdp-local, hitanao fa ny fonosana nalefa avy amin'ny xdp-test, dia alefa amin'ity interface ity:

ip netns exec xdp-test   ping 192.0.2.1

Mety tsara ny mampiditra akorandriaka xdp-test. Ny tahiry dia manana script izay mandeha ho azy amin'ny fijoroana ohatra, azonao atao ny manamboatra ny fijoroana miaraka amin'ny baiko sudo ./stand up ary vonoy izany sudo ./stand down.

hay fantarina

Ny sivana dia mifandray amin'ny fitaovana toy izao:

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

manan-danja -force mila mampifandray programa vaovao raha toa ka efa mifandray ny iray hafa. "Tsy misy vaovao dia vaovao tsara" dia tsy momba an'io baiko io, ny fehin-kevitra dia be dia be amin'ny tranga rehetra. manondro verbose azo atao, fa miaraka amin'izany dia misy tatitra momba ny asan'ny mpanamarina kaody miaraka amin'ny lisitry ny fivoriambe:

Verifier analysis:

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

Esory ny programa amin'ny interface:

ip link set dev xdp-local xdp off

Ao amin'ny script ireo dia baiko sudo ./stand attach ΠΈ sudo ./stand detach.

Amin'ny fametahana sivana dia azonao antoka izany ping mitohy mandeha, fa miasa ve ny programa? Andeha isika hanampy lozisialy. asa bpf_trace_printk() mitovy amin'ny printf(), fa tsy manohana afa-tsy tohan-kevitra telo ankoatry ny lamina, ary lisitra voafetra amin'ny famaritana. Makro bpf_printk() manamora ny antso.

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

Ny vokatra dia mankany amin'ny fantsona kernel trace, izay mila alefa:

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

Jereo ny lohatenin'ny hafatra:

cat /sys/kernel/debug/tracing/trace_pipe

Samy manao antso ireo baiko roa ireo sudo ./stand log.

Ny Ping dia tokony hiteraka hafatra toy izao:

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

Raha mijery akaiky ny vokarin'ny mpanamarina ianao dia ho hitanao fa misy kajikajy hafahafa:

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

Ny zava-misy dia ny programa eBPF dia tsy manana fizarana angon-drakitra, ka ny hany fomba hametahana tady format dia ny tohan-kevitra avy hatrany amin'ny baiko VM:

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

Noho izany antony izany, ny famoahana debug dia manenika ny kaody aterany.

Mandefa fonosana XDP

Andao hanova ny sivana: avelao izy hamerina ny fonosana rehetra miditra. Tsy mety izany amin'ny fomba fijerin'ny tambajotra, satria ilaina ny manova ny adiresy ao amin'ny lohapejy, fa ankehitriny ny asa amin'ny foto-kevitra dia zava-dehibe.

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

fandefasana tcpdump amin'ny xdp-remote. Tokony hampiseho ICMP Echo Request mitovy ny mivoaka sy miditra ary ajanony ny fanehoana ICMP Echo Reply. Saingy tsy miseho izany. Hita fa noho ny asa XDP_TX ao amin'ny programa amin'ny xdp-local ilainamankany amin'ny interface tsara xdp-remote nisy fandaharana koa nomena, na dia foana aza, ary izy no notezaina.

Ahoana no nahafantarako izany?

Araho ny lalan'ny fonosana iray ao anaty kernel Ny mekanika hetsika perf dia mamela, raha ny marina, mampiasa milina virtoaly mitovy, izany hoe, ny eBPF dia ampiasaina amin'ny famongorana miaraka amin'ny eBPF.

Tsy maintsy manao ny tsara amin'ny ratsy ianao, satria tsy misy zavatra hafa hivoahan'izany.

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

Inona ny code 6?

$ errno 6
ENXIO 6 No such device or address

asa veth_xdp_flush_bq() mandray kaody fahadisoana avy amin'ny veth_xdp_xmit(), aiza no tadiavina ENXIO ary tadiavo ny hevitra.

Andao haverina ny sivana kely indrindra (XDP_PASS) ao anaty rakitra xdp_dummy.c, ampio ao amin'ny Makefile, afehezo amin'ny xdp-remote:

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

ankehitriny tcpdump mampiseho izay andrasana:

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

Raha ny ARP ihany no aseho, dia mila esorinao ny sivana (izany no sudo ./stand detach), Afoizo ping, dia mametraha sivana ary andramo indray. Ny olana dia ny sivana XDP_TX manankery na amin'ny ARP na raha ny stack
anaran-tsehatra xdp-test nahavita "manadino" ny adiresy MAC 192.0.2.1, tsy ho afaka hamaha ity IP ity.

Fanambarana olana

Andao hiroso amin'ilay asa voalaza: manorata mekanika cookies SYN amin'ny XDP.

Ny tondra-drano SYN dia mijanona ho fanafihana DDoS malaza, ary toy izao manaraka izao ny votoatiny. Rehefa vita ny fifandraisana (TCP handshake), dia mahazo SYN ny mpizara, manome loharano ho an'ny fifandraisana ho avy, mamaly amin'ny fonosana SYNACK ary miandry ACK. Mandefa fonosana SYN an'arivony isan-tsegondra fotsiny ny mpanafika avy amin'ny adiresy sandoka avy amin'ny mpampiantrano tsirairay ao anaty botnet an'arivony mahery. Ny mpizara dia voatery manome loharano avy hatrany rehefa tonga ny fonosana, fa mamoaka azy ireo aorian'ny fe-potoana lehibe, noho izany dia lany ny fahatsiarovana na ny fetra, tsy ekena ny fifandraisana vaovao, ary tsy misy ny serivisy;

Raha tsy manome loharano mifototra amin'ny fonosana SYN ianao, fa mamaly amin'ny fonosana SYNACK fotsiny, ahoana no ahafahan'ny mpizara hahatakatra fa ny fonosana ACK izay tonga taty aoriana dia manondro fonosana SYN izay tsy voavonjy? Rehefa dinihina tokoa, ny mpanafika dia afaka mamorona ACK sandoka ihany koa. Ny tanjona amin'ny cookie SYN dia ny fametahana azy seqnum fifandraisana masontsivana ho toy ny hash ny adiresy, seranan-tsambo sy ny fanovana sira. Raha nahavita tonga ny ACK talohan'ny niova ny sira, dia azonao atao ny manao kajy indray ny hash ary mampitaha azy acknum. Forge acknum ny mpanafika dia tsy afaka, satria ny sira dia ahitana ny tsiambaratelo, ary tsy hanam-potoana handaminana izany noho ny fantsona voafetra.

Efa hatry ny ela no nampiharina tao amin'ny kernel Linux ny cookie SYN ary azo alefa ho azy mihitsy aza raha tonga haingana sy be dia be ny SYN.

Fandaharana fanabeazana momba ny fifampikasohana TCP

Ny TCP dia manome fifindran'ny angona ho toy ny onjam-bytes, ohatra, ny fangatahana HTTP dia alefa amin'ny TCP. Ampitaina amin'ny ampahany amin'ny fonosana ny renirano. Ny fonosana TCP rehetra dia manana saina lojika sy laharana 32-bit:

  • Ny fitambaran'ny saina no mamaritra ny andraikitry ny fonosana iray manokana. Ny saina SYN dia manondro fa ity no fonosan'ny mpandefa voalohany amin'ny fifandraisana. Ny saina ACK dia midika fa ny mpandefa dia nahazo ny angon-drakitra fifandraisana rehetra hatramin'ny byte acknum. Ny fonosana dia afaka manana saina maromaro ary antsoina amin'ny fitambarany, ohatra, fonosana SYNACK.

  • Ny laharan'ny filaharana (seqnum) dia mamaritra ny offset ao amin'ny stream data ho an'ny byte voalohany ampitaina amin'ity fonosana ity. Ohatra, raha ao amin'ny fonosana voalohany misy angona X bytes io isa io dia N, amin'ny fonosana manaraka misy angona vaovao dia N+X izany. Eo am-piandohan'ny fifandraisana dia mifidy an'io isa io ny andaniny tsirairay.

  • Laharana fankasitrahana (acknum) - mitovy ny offset amin'ny seqnum, saingy tsy mamaritra ny isan'ny byte alefa, fa ny isan'ny byte voalohany avy amin'ny mpandray, izay tsy hitan'ny mpandefa.

Amin'ny fiandohan'ny fifandraisana dia tsy maintsy manaiky ny andaniny seqnum ΠΈ acknum. Ny mpanjifa dia mandefa fonosana SYN miaraka amin'ny seqnum = X. Mamaly amin'ny fonosana SYNACK ny mpizara, izay mirakitra ny azy seqnum = Y ary mampiharihary acknum = X + 1. Ny mpanjifa dia mamaly ny SYNACK amin'ny fonosana ACK, izay seqnum = X + 1, acknum = Y + 1. Aorian'izany dia manomboka ny famindrana angon-drakitra tena izy.

Raha tsy manaiky ny fandraisany ny fonosana ny mpiara-mianatra, dia alefan'ny TCP izany rehefa tapitra ny fotoana.

Nahoana no tsy ampiasaina foana ny cookies SYN?

Voalohany, raha very ny SYNACK na ACK dia tsy maintsy miandry ny fandefasana azy indray ianao - hihena ny fametrahana ny fifandraisana. Faharoa, ao amin'ny fonosana SYN - ary ao anatiny ihany! - safidy maromaro no ampitaina izay misy fiantraikany amin'ny fampandehanana bebe kokoa ny fifandraisana. Raha tsy tsaroana ny fonosana SYN ho avy, dia tsy miraharaha ireo safidy ireo ny mpizara; Ny TCP dia afaka miasa amin'ity tranga ity, fa farafaharatsiny amin'ny dingana voalohany dia hihena ny kalitaon'ny fifandraisana.

Avy amin'ny fomba fijery fonosana, ny programa XDP dia tsy maintsy manao izao manaraka izao:

  • mamaly SYN miaraka amin'ny SYNACK miaraka amin'ny cookie;
  • mamaly ny ACK amin'ny RST (disconnect);
  • ario ny entana sisa.

Pseudocode amin'ny algorithm miaraka amin'ny parsing fonosana:

Если это Π½Π΅ Ethernet,
    ΠΏΡ€ΠΎΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚.
Если это Π½Π΅ IPv4,
    ΠΏΡ€ΠΎΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚.
Если адрСс Π² Ρ‚Π°Π±Π»ΠΈΡ†Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½Π½Ρ‹Ρ…,               (*)
        ΡƒΠΌΠ΅Π½ΡŒΡˆΠΈΡ‚ΡŒ счСтчик ΠΎΡΡ‚Π°Π²ΡˆΠΈΡ…ΡΡ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΎΠΊ,
        ΠΏΡ€ΠΎΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚.
Если это Π½Π΅ TCP,
    ΡΠ±Ρ€ΠΎΡΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚.     (**)
Если это SYN,
    ΠΎΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ SYN-ACK с cookie.
Если это ACK,
    Ссли Π² acknum Π»Π΅ΠΆΠΈΡ‚ Π½Π΅ cookie,
        ΡΠ±Ρ€ΠΎΡΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚.
    ЗанСсти Π² Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ адрСс с N ΠΎΡΡ‚Π°Π²ΡˆΠΈΡ…ΡΡ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΎΠΊ.    (*)
    ΠžΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ RST.   (**)
Π’ ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Ρ… случаях ΡΠ±Ρ€ΠΎΡΠΈΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚.

One (*) Ny teboka izay ilainao hitantana ny toetry ny rafitra dia voamarika - amin'ny dingana voalohany azonao atao tsy misy azy ireo amin'ny fampiharana tsotra TCP tΓ nana miaraka amin'ny famokarana cookie SYN ho seqnum.

Eo an-toerana (**), na dia tsy manana latabatra aza isika dia handalo ny fonosana.

Fampiharana TCP handshake

Famaritana ny fonosana ary manamarina ny code

Mila rafitra lohatenin'ny tambajotra izahay: Ethernet (uapi/linux/if_ether.h), IPv4 (uapi/linux/ip.h) sy TCP (uapi/linux/tcp.h). Tsy afaka nampifandray ity farany aho noho ny fahadisoana mifandraika amin'ny atomic64_t, Tsy maintsy nandika ny famaritana ilaina tao amin'ny kaody aho.

Ny fiasa rehetra asongadina ao amin'ny C ho mora vakina dia tsy maintsy ampidirina amin'ny toerana iantsoana, satria ny verifier eBPF ao amin'ny kernel dia mandrara ny fiverenana, izany hoe, raha ny marina, ny antso an-tariby sy ny asa.

#define INTERNAL static __attribute__((always_inline))

Macro LOG() manakana ny fanontana amin'ny fananganana famoahana.

Ny programa dia conveyor ny asa. Ny tsirairay dia mahazo fonosana iray ahitana ny lohatenin'ny ambaratonga mifanaraka amin'izany, ohatra, process_ether() manantena ny ho feno ether. Miorina amin'ny valin'ny famakafakana an-tsaha, ny fiasa dia afaka mampita ny fonosana mankany amin'ny ambaratonga ambony. Ny vokatry ny asa dia ny hetsika XDP. Amin'izao fotoana izao, ny mpandrindra SYN sy ACK dia mandalo ny fonosana rehetra.

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

Misarika ny sainao amin'ny seky misy marika A sy B aho. Raha maneho hevitra A ianao dia hiorina ny programa, saingy hisy hadisoana fanamarinana rehefa mampiditra:

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!

Tady fanalahidy invalid access to packet, off=13 size=1, R7(id=0,off=0,r=0): Misy lalan'ny fanatanterahana rehefa ivelan'ny fonosana ny byte fahatelo ambin'ny folo avy amin'ny fiandohan'ny buffer. Sarotra ny mahazo avy amin'ny lisitra izay andalana resahinay, fa misy ny laharana torolΓ lana (12) sy ny disassembler mampiseho ny andalana loharano:

llvm-objdump -S xdp_filter.o | less

Amin'ity tranga ity dia manondro ny tsipika

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

izay milaza mazava fa ny olana ether. Ho toy izao foana izany.

Valio ny SYN

Ny tanjona amin'ity dingana ity dia ny hamorona fonosana SYNACK marina miaraka amin'ny raikitra seqnum, izay hosoloina cookie SYN amin'ny ho avy. Miseho ao amin'ny process_tcp_syn() sy ny manodidina.

Fanamarinana fonosana

Hafahafa ihany, ity ny andalana miavaka indrindra, na ny marimarina kokoa, ny fanehoan-kevitra momba azy:

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

Rehefa nanoratra ny dikan-teny voalohany amin'ny kaody dia nampiasaina ny kernel 5.1, ho an'ny fanamarinana izay misy fahasamihafana eo amin'ny data_end ΠΈ (const void*)ctx->data_end. Tamin'ny fotoana nanoratana dia tsy nanana io olana io ny kernel 5.3.1. Mety ho tsy mitovy amin'ny saha ny fidirana amin'ny variable eo an-toerana ny compiler. Ny fitsipi-pitondran-tena: rehefa lehibe ny akany, dia afaka manampy ny fanatsorana ny kaody.

Manaraka izany dia ny fisavana ny halavany mahazatra ho an'ny voninahitry ny mpanamarina; O MAX_CSUM_BYTES eto ambany.

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

Famoahana ny fonosana

Mameno seqnum ΠΈ acknum, mametraka ACK (efa napetraka ny SYN):

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

Ampifamadiho ny seranan-tsambo TCP, adiresy IP ary adiresy MAC. Ny tranomboky mahazatra dia tsy azo idirana amin'ny programa XDP, noho izany memcpy() - macro izay manafina ny intrinsics 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);

Recalculation ny checksums

Ny fanamarinana IPv4 sy TCP dia mitaky ny fampidirana ny teny 16-bit rehetra ao amin'ny lohapejy, ary ny haben'ny lohapejy dia voasoratra ao anatiny, izany hoe, tsy fantatra amin'ny fotoana fanangonana. Olana ity satria tsy hitsipaka ny tadivavarana mahazatra mankany amin'ny fari-piadidiana ny mpanamarina. Saingy voafetra ny haben'ny lohapejy: hatramin'ny 64 bytes ny tsirairay. Azonao atao ny manao tadivavarana miaraka amin'ny famerimberenana maromaro, izay mety hifarana aloha.

Marihiko fa misy RFC 1624 momba ny fomba famerenana amin'ny ampahany ny checksum raha ny teny raikitra amin'ny fonosana ihany no ovaina. Na izany aza, ny fomba dia tsy manerana izao rehetra izao, ary ny fampiharana dia ho sarotra kokoa ny mihazona.

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

NA size voamarina amin'ny alΓ lan'ny kaody fiantsoana, ilaina ny fepetra fivoahana faharoa mba ahafahan'ny mpanamarina manaporofo ny fahavitan'ny loop.

Ho an'ny teny 32-bit, misy dikan-teny tsotra kokoa:

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

Raha ny marina dia mamerina ny checksum ary mamerina ny fonosana:

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;

asa carry() manao checksum avy amin'ny fitambarana 32-bit amin'ny teny 16-bit, araka ny RFC 791.

Fanamarinana fifampikasihan-tanana TCP

Mametraka fifandraisana tsara amin'ny sivana netcat, tsy hita ny ACK farany, izay namaly ny Linux tamin'ny fonosana RST, satria tsy nahazo SYN ny tamba-jotra tambajotra - niova ho SYNACK ary naverina - ary avy amin'ny fomba fijery OS dia tonga ny fonosana iray izay tsy mifandray amin'ny fanokafana. fifandraisana.

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

Zava-dehibe ny manamarina amin'ny fampiharana feno ary mandinika tcpdump amin'ny xdp-remote satria ohatra, hping3 tsy mamaly checksum diso.

Amin'ny fomba fijery XDP, ny fanamarinana dia tsy misy dikany. Ny algorithm amin'ny kajy dia voalohany ary azo inoana fa mora voan'ny mpanafika be pitsiny. Ny kernel Linux, ohatra, dia mampiasa ny kriptografika SipHash, fa ny fampiharana azy ho an'ny XDP dia mazava ho azy fa mihoatra ny faritr'ity lahatsoratra ity.

Nampidirina ho an'ny TODO vaovao mifandraika amin'ny fifandraisana ivelany:

  • Ny programa XDP dia tsy afaka mitahiry cookie_seed (ny ampahany miafina amin'ny sira) amin'ny fari-piainana manerantany, mila fitehirizana ao amin'ny kernel ianao, izay havaozina tsindraindray avy amin'ny mpamokatra azo itokisana.

  • Raha mifanaraka amin'ny fonosana ACK ny cookie SYN dia tsy mila manonta hafatra ianao, fa tadidio ny IP an'ny mpanjifa voamarina mba hanohizana ny fandefasana fonosana avy aminy.

Fanamarinana mpanjifa ara-dalΓ na:

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

Asehon'ny logs fa nandalo ny seky (flags=0x2 - SYN, flags=0x10 dia 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

Na dia tsy misy lisitry ny IP voamarina aza, dia tsy hisy fiarovana amin'ny tondra-drano SYN mihitsy, fa ity ny fanehoan-kevitra momba ny tondra-drano ACK natomboka tamin'ity baiko manaraka ity:

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

Lahatsoratra momba ny log:

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

famaranana

Indraindray ny eBPF amin'ny ankapobeny sy ny XDP manokana dia aseho bebe kokoa amin'ny maha-fitaovan'ny mpitantana mandroso fa tsy amin'ny sehatra fampandrosoana. Raha ny marina, ny XDP dia fitaovana hanakanana ny fanodinana ny fonosana amin'ny alΓ lan'ny kernel, fa tsy safidy ho an'ny stack kernel, toy ny DPDK sy ny safidy bypass kernel hafa. Amin'ny lafiny iray, ny XDP dia mamela anao hampihatra lojika saro-takarina, izay, ankoatra izany, dia mora havaozina tsy misy fanelingelenana amin'ny fanodinana ny fifamoivoizana. Ny mpanamarina dia tsy miteraka olana lehibe ho an'ny tena manokana, tsy mandΓ  izany aho amin'ny ampahany amin'ny codespace mpampiasa.

Ao amin'ny tapany faharoa, raha mahaliana ny lohahevitra, dia hamita ny latabatry ny mpanjifa voamarina sy ny fahatapahan-jiro izahay, hampihatra ny kaontera ary hanoratra fampiasa amin'ny sehatry ny mpampiasa hitantana ny sivana.

andinin-tsoratra masina:

Source: www.habr.com

Add a comment