موږ په XDP کې د DDoS بریدونو پروړاندې محافظت لیکو. اټومي برخه

د ایکسپرس ډیټا پاټ (XDP) ټیکنالوژي اجازه ورکوي چې په لینکس انٹرفیسونو کې د تصادفي ترافیک پروسس ترسره شي مخکې لدې چې پاکټونه د کرنل شبکې سټیک ته ننوځي. د XDP غوښتنلیک - د DDoS بریدونو پروړاندې محافظت (CloudFlare) ، پیچلي فلټرونه ، د احصایې راټولول (Netflix). د XDP پروګرامونه د eBPF مجازی ماشین لخوا اجرا کیږي، نو دوی د فلټر ډول پورې اړه لري د دوی کوډ او موجود کارنل افعال دواړه محدودیتونه لري.

مقاله د XDP په اړه د ډیری موادو نیمګړتیاوې ډکولو لپاره دي. لومړی، دوی چمتو شوی کوډ چمتو کوي چې سمدلاسه د XDP ځانګړتیاو څخه تیریږي: دا د تایید لپاره چمتو شوی یا د ستونزو رامینځته کولو لپاره خورا ساده دی. کله چې تاسو بیا هڅه وکړئ خپل کوډ له سکریچ څخه ولیکئ، تاسو نه پوهیږئ چې د عادي غلطیو سره څه وکړئ. دوهم، د VM او هارډویر پرته په محلي توګه د XDP ازموینې لارې پوښل شوي ندي، سره له دې چې دوی خپل زیانونه لري. متن د هغو پروګرامرانو لپاره دی چې د شبکې او لینکس سره اشنا دي چې د XDP او eBPF سره علاقه لري.

پدې برخه کې، موږ به په تفصیل سره پوه شو چې د XDP فلټر څنګه راټولیږي او دا څنګه ازموینه کیږي، بیا به موږ د پیکټ پروسس کولو په کچه د پیژندل شوي SYN کوکیز میکانیزم ساده نسخه ولیکو. موږ به تر اوسه یو "سپینه لیست" نه جوړوو
تایید شوي پیرودونکي، کاونټرونه وساتئ او فلټر اداره کړئ - کافي لاګونه.

موږ به په C کې ولیکو - دا فیشن نه دی، مګر دا عملي دی. ټول کوډ په پای کې د لینک له لارې په GitHub کې شتون لري او په مقاله کې بیان شوي مرحلو سره سم په ژمنو ویشل شوي.

ویزه د دې مقالې په جریان کې ، زه به د DDoS بریدونو مخنیوي لپاره یو کوچني حل رامینځته کړم ، ځکه چې دا د XDP او زما د تجربې ساحې لپاره ریښتینی کار دی. په هرصورت، اصلي هدف د ټیکنالوژۍ پوهیدل دي؛ دا د چمتو شوي محافظت رامینځته کولو لارښود ندی. د ښوونې کوډ اصلاح شوی نه دی او ځینې لنډیزونه پریږدي.

د XDP لنډه کتنه

زه به یوازې کلیدي ټکي په ګوته کړم ترڅو د اسنادو او موجوده مقالو نقل نه شي.

نو، د فلټر کوډ په کرنل کې بار شوی. راتلونکی کڅوړې فلټر ته لیږدول کیږي. د پایلې په توګه، فلټر باید پریکړه وکړي: کڅوړه دانی ته انتقال کړئ (XDP_PASS)، غورځول پاکټ (XDP_DROP) یا بیرته یې واستوئ (XDP_TX). فلټر کولی شي بسته بدله کړي، دا په ځانګړې توګه د دې لپاره ریښتیا ده XDP_TX. تاسو کولی شئ برنامه هم لغوه کړئ (XDP_ABORTED) او بسته بیا تنظیم کړئ، مګر دا ورته دی assert(0) - د ډیبګ کولو لپاره.

د eBPF (توسیع شوي برکلي پاکټ فلټر) مجازی ماشین په قصدي ډول ساده شوی ترڅو دانی کولی شي وګوري چې کوډ لوپ نه کوي او د نورو خلکو حافظې ته زیان نه رسوي. مجموعي محدودیتونه او چکونه:

  • لوپونه (شاته) منع دي.
  • د ډیټا لپاره سټیک شتون لري ، مګر هیڅ فعالیت نلري (ټول C افعال باید په نښه شي).
  • د سټیک او پیکټ بفر څخه بهر حافظې ته لاسرسی منع دی.
  • د کوډ اندازه محدوده ده، مګر په عمل کې دا خورا مهم نه دی.
  • یوازې د ځانګړو کرنل افعالو (eBPF مرستندویانو) ته زنګ وهلو اجازه لري.

د فلټر ډیزاین او نصب کول داسې ښکاري:

  1. د سرچینې کوډ (د مثال په توګه kernel.c) په څیز کې راټولیږي (kernel.o) د eBPF مجازی ماشین جوړښت لپاره. د اکتوبر 2019 پورې، eBPF ته تالیف د کلینګ لخوا ملاتړ کیږي او په GCC 10.1 کې ژمنه شوې.
  2. که چیرې دا اعتراض کوډ د کرنل جوړښتونو ته زنګونه ولري (د مثال په توګه میزونه او کاونټرونه) ، د دوی IDs د صفر سره بدلیږي ، پدې معنی چې دا ډول کوډ نشي اجرا کیدی. مخکې له دې چې کرنل ته پورته شي، تاسو اړتیا لرئ چې دا صفرونه د ځانګړو شیانو IDs سره بدل کړئ چې د کرنل کالونو له لارې رامینځته شوي (کوډ لینک کړئ). تاسو کولی شئ دا د بهرنیو اسانتیاوو سره ترسره کړئ، یا تاسو کولی شئ یو پروګرام ولیکئ چې یو ځانګړی فلټر به لینک او پورته کړي.
  3. کرنل بار شوی برنامه تاییدوي. د سایکل نشتوالی او د پیکټ او سټیک حدونو څخه تیریدو کې ناکامي چیک کیږي. که چیرې تصدیق کوونکی ثابت نشي چې کوډ سم دی، برنامه رد شوې - تاسو اړتیا لرئ د هغه د خوښولو وړ اوسئ.
  4. د بریالي تایید وروسته، کرنل د سیسټم جوړښت (یوازې په وخت کې) لپاره د ماشین کوډ کې د eBPF آرکیټیکچر اعتراض کوډ تالیف کوي.
  5. برنامه انٹرفیس سره نښلوي او د پاکټونو پروسس پیل کوي.

څرنګه چې XDP په کرنل کې چلیږي، ډیبګ کول د ټریس لاګونو په کارولو سره ترسره کیږي او په حقیقت کې هغه پاکټونه چې برنامه فلټر کوي یا تولیدوي. په هرصورت، eBPF ډاډ ورکوي چې ډاونلوډ شوی کوډ د سیسټم لپاره خوندي دی، نو تاسو کولی شئ د XDP سره مستقیم ستاسو په محلي لینکس کې تجربه کړئ.

د چاپیریال چمتو کول

شورا

کلینګ نشي کولی په مستقیم ډول د eBPF جوړښت لپاره د اعتراض کوډ تولید کړي، نو دا پروسه دوه مرحلې لري:

  1. LLVM بایټکوډ ته د C کوډ تالیف کړئ (clang -emit-llvm).
  2. بایټ کوډ د eBPF اعتراض کوډ ته بدل کړئ (llc -march=bpf -filetype=obj).

کله چې د فلټر لیکلو لپاره، یو څو فایلونه د معاون افعال او میکرو سره به ګټور وي د کرنل ازموینې څخه. دا مهمه ده چې دوی د کرنل نسخه سره سمون ولري (KVER). دوی ته ډاونلوډ کړئ 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

د آرچ لینکس لپاره میک فایل (کرنل 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 د کرنل سرلیکونو ته لاره لري، ARCH - د سیسټم جوړښت. لارې او وسیلې ممکن د توزیع ترمینځ یو څه توپیر ولري.

د Debian 10 لپاره د توپیرونو بیلګه (کرنل 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 یو ډایرکټر د مرستندویه سرلیکونو او څو لارښودونو سره د کرنل سرلیکونو سره وصل کړئ. سمبول __KERNEL__ پدې معنی چې د UAPI (userspace API) سرلیکونه د کرنل کوډ لپاره تعریف شوي، ځکه چې فلټر په کرنل کې اجرا کیږي.

د سټیک محافظت غیر فعال کیدی شي (-fno-stack-protector)، ځکه چې د eBPF کوډ تصدیق کونکی لاهم د حد څخه بهر سرغړونو لپاره چک کوي. دا سمدلاسه د اصلاح کولو ارزښت لري ، ځکه چې د eBPF بایټکوډ اندازه محدود ده.

راځئ چې د فلټر سره پیل وکړو چې ټول پاکټونه تیریږي او هیڅ نه کوي:

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

ټیم make راټولوي xdp_filter.o. اوس چیرته هڅه وکړئ؟

د ازموینې موقف

سټینډ باید دوه انٹرفیسونه ولري: په کوم کې به یو فلټر وي او له کوم څخه به پاکټونه لیږل کیږي. دا باید د خپلو IPs سره بشپړ لینکس وسیلې وي ترڅو وګوري چې منظم غوښتنلیکونه زموږ د فلټر سره څنګه کار کوي.

د ویت (مجازی ایترنیټ) ډول وسیلې زموږ لپاره مناسب دي: دا د مجازی شبکې انٹرفیسونو جوړه ده چې مستقیم یو بل سره "وصل شوي" دي. تاسو کولی شئ دوی د دې په څیر رامینځته کړئ (په دې برخه کې ټول حکمونه ip څخه ترسره کیږي root):

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

دا xdp-remote и xdp-local - د وسایلو نومونه. پر xdp-local (192.0.2.1/24) یو فلټر به ضمیمه شي، سره xdp-remote (192.0.2.2/24) راتلونکی ترافیک به لیږل کیږي. په هرصورت، یوه ستونزه شتون لري: انٹرفیسونه په ورته ماشین کې دي، او لینوکس به د دوی له لارې یو بل ته ټرافيک ونه لیږي. تاسو کولی شئ دا د ستونزمن قواعدو سره حل کړئ iptables، مګر دوی باید کڅوړې بدل کړي ، کوم چې د ډیبګ کولو لپاره ناشونی دی. دا غوره ده چې د شبکې نوم ځایونه وکاروئ (له دې وروسته netns).

د شبکې نوم ځای د انٹرفیسونو سیټ لري، د روټینګ میزونه، او د NetFilter قواعد چې په نورو netns کې د ورته شیانو څخه جلا شوي دي. هره پروسه په نوم ځای کې پرمخ ځي او یوازې د دې netns شیانو ته لاسرسی لري. په ډیفالټ سره، سیسټم د ټولو شیانو لپاره د شبکې یو واحد نوم ځای لري، نو تاسو کولی شئ په لینکس کې کار وکړئ او د netns په اړه نه پوهیږئ.

راځئ چې یو نوی نوم ځای جوړ کړو xdp-test او هلته یې انتقال کړئ xdp-remote.

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

بیا پروسه روانه ده xdp-test، به "نه ګوري" xdp-local (دا به د ډیفالټ په واسطه په netns کې پاتې شي) او کله چې 192.0.2.1 ته پیکټ واستول شي دا به یې له لارې تیر کړي xdp-remoteځکه چې دا په 192.0.2.0/24 کې یوازینی انٹرفیس دی چې دې پروسې ته د لاسرسي وړ دی. دا هم په مخالف لوري کار کوي.

کله چې د netns ترمنځ حرکت کوي، انٹرفیس ښکته ځي او خپل پته له لاسه ورکوي. په netns کې د انٹرفیس تنظیم کولو لپاره، تاسو باید چلولو ته اړتیا ولرئ ip ... په دې کمانډ نوم ځای کې 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

لکه څنګه چې تاسو لیدلی شئ، دا د ترتیب څخه توپیر نلري xdp-local په ډیفالټ نوم ځای کې:

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

که تاسو منډې وهئ tcpdump -tnevi xdp-local، تاسو کولی شئ هغه کڅوړې وګورئ چې له دې څخه لیږل شوي xdp-test، دې انٹرفیس ته سپارل کیږي:

ip netns exec xdp-test   ping 192.0.2.1

دا اسانه ده چې یو شیل دننه کړئ xdp-test. ذخیره یو سکریپټ لري چې د سټینډ سره کار اتومات کوي؛ د مثال په توګه ، تاسو کولی شئ سټینډ د کمانډ سره تنظیم کړئ sudo ./stand up او هغه ړنګ کړئ sudo ./stand down.

تعقیب

فلټر د وسیله سره تړلی دی لکه:

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

کیلي -force د نوي برنامه لینک کولو ته اړتیا لري که چیرې یو بل دمخه لینک شوی وي. "هیڅ خبر ښه خبر نه دی" د دې حکم په اړه ندي، پایله په هر حالت کې پراخه ده. په ګوته کوي verbose اختیاري، مګر د دې سره یو راپور د کوډ تصدیق کونکي په کار کې د مجلس لیست کولو سره څرګندیږي:

Verifier analysis:

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

برنامه له انٹرفیس څخه لرې کړئ:

ip link set dev xdp-local xdp off

په سکریپټ کې دا حکمونه دي sudo ./stand attach и sudo ./stand detach.

د فلټر په ضمیمه کولو سره، تاسو کولی شئ ډاډ ترلاسه کړئ ping چلولو ته دوام ورکوي، مګر ایا برنامه کار کوي؟ راځئ چې لاګونه اضافه کړو. فعالیت bpf_trace_printk() سره ورته printf()، مګر یوازې د نمونې پرته تر دریو پورې نورو دلیلونو ملاتړ کوي ، او د مشخص کونکو محدود لیست. میکرو bpf_printk() زنګ ساده کوي.

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

محصول د کرنل ټریس چینل ته ځي، کوم چې باید فعال شي:

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

د پیغام موضوع وګورئ:

cat /sys/kernel/debug/tracing/trace_pipe

دا دواړه حکمونه تلیفون کوي sudo ./stand log.

پینګ باید اوس د دې په څیر پیغامونه متحرک کړي:

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

که تاسو د تصدیق کونکي محصول ته نږدې وګورئ ، نو تاسو به عجیب محاسبې وګورئ:

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

حقیقت دا دی چې د eBPF برنامې د ډیټا برخه نلري ، نو د فارمیټ سټرینګ کوډ کولو یوازینۍ لار د VM امرونو سمدستي دلیلونه دي:

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

د دې دلیل لپاره، د ډیبګ محصول د پایلې کوډ خورا لوی کوي.

د XDP کڅوړې لیږل

راځئ چې فلټر بدل کړو: اجازه راکړئ چې ټول راتلونکی پاکټونه بیرته واستوو. دا د شبکې له نظره غلط دی، ځکه چې دا به اړین وي چې په سرلیکونو کې پته بدله شي، مګر اوس په اصولو کې کار مهم دی.

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

لانچ tcpdump په xdp-remote. دا باید د ICMP ایکو ځواب ورته ورته بهر او راتلوونکي وښیې او د ICMP اکو ځواب ښودل ودروي. مګر دا نه ښیې. دا معلومه شوه چې د کار لپاره XDP_TX په پروګرام کې xdp-local لازمه دهجوړه انٹرفیس ته xdp-remote یو پروګرام هم ګمارل شوی و، حتی که دا خالي وي، او هغه پورته شو.

زه دا څنګه پوه شوم؟

په کرنل کې د کڅوړې لاره تعقیب کړئ د perf پیښو میکانیزم اجازه ورکوي، په لاره کې، د ورته مجازی ماشین په کارولو سره، دا دی، eBPF د eBPF سره د جلا کولو لپاره کارول کیږي.

تاسو باید له بد څخه ښه جوړ کړئ، ځکه چې نور هیڅ شی شتون نلري چې له بد څخه یې جوړ کړي.

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

کوډ 6 څه شی دی؟

$ errno 6
ENXIO 6 No such device or address

دنده veth_xdp_flush_bq() څخه د خطا کوډ ترلاسه کوي veth_xdp_xmit(), چیرته چې لټون کوي ENXIO او تبصره ومومئ.

راځئ چې لږترلږه فلټر بحال کړو (XDP_PASS) په دوتنه کې xdp_dummy.c، دا په میک فایل کې اضافه کړئ ، دا یې وتړئ xdp-remote:

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

اوس tcpdump ښیې چې څه تمه کیږي:

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

که یوازې د ARPs پرځای ښودل شوي وي، تاسو اړتیا لرئ چې فلټرونه لرې کړئ (دا کار کوي sudo ./stand detach)، پرېږده چي لاړ شي ping، بیا فلټرونه تنظیم کړئ او بیا هڅه وکړئ. ستونزه دا ده چې فلټر XDP_TX دواړه په ARP او که سټیک کې اعتبار لري
نوم ځایونه xdp-test د MAC پته 192.0.2.1 "هیرولو" اداره کول، دا به د دې IP حل کولو توان ونلري.

د ستونزې تشکیل

راځئ چې بیان شوي کار ته لاړ شو: په XDP کې د SYN کوکیز میکانیزم ولیکئ.

د SYN سیلاب د DDoS یو مشهور برید پاتې دی، کوم چې په لاندې ډول دی. کله چې اړیکه جوړه شي (TCP لاسي شیک)، سرور یو SYN ترلاسه کوي، د راتلونکي پیوستون لپاره سرچینې تخصیص کوي، د SYNACK پاکټ سره ځواب ورکوي او ACK ته انتظار کوي. برید کوونکی په ساده ډول په هر ثانیه کې په زرګونو SYN پاکټونه د هر کوربه څخه د سپوفي پتې څخه په څو زره قوي بوټینیټ کې لیږي. سرور مجبور دی چې د کڅوړې په رارسیدو سمدلاسه سرچینې تخصیص کړي ، مګر د لوی وخت پای ته رسیدو وروسته یې خوشې کوي؛ په پایله کې ، حافظه یا محدودیتونه ختم شوي ، نوي اړیکې نه منل کیږي ، او خدمت شتون نلري.

که تاسو د SYN پاکټ پراساس سرچینې نه اخلئ، مګر یوازې د SYNACK پاکټ سره ځواب ووایاست، نو بیا سرور څنګه پوهیدلی شي چې د ACK پاکټ چې وروسته راغلی د SYN پاکټ ته اشاره کوي چې خوندي شوي ندي؟ په هرصورت، یو برید کوونکی کولی شي جعلي ACKs هم تولید کړي. د SYN کوکي نقطه دا ده چې دا کوډ کړئ seqnum د پیوستون پیرامیټونه د پتې، بندرونو او مالګې بدلولو هش په توګه. که چیرې ACK د مالګې د بدلیدو دمخه راشي ، تاسو کولی شئ بیا هش محاسبه کړئ او له سره یې پرتله کړئ acknum. جعل acknum برید کوونکی نشي کولی، ځکه چې مالګه کې راز شامل دی، او د محدود چینل له امله به د هغې له لارې د ترتیب کولو وخت ونه لري.

د SYN کوکي له اوږدې مودې راهیسې په لینکس کرنل کې پلي شوي او حتی په اتوماتيک ډول فعال کیدی شي که چیرې SYN ډیر ژر او په ډله ایز ډول راشي.

د TCP دستګاه په اړه تعلیمي برنامه

TCP د بایټونو د جریان په توګه د معلوماتو لیږد چمتو کوي، د بیلګې په توګه، د HTTP غوښتنې د TCP له لارې لیږدول کیږي. جریان په ټوټو کې په کڅوړو کې لیږدول کیږي. ټول TCP پاکټونه منطقي بیرغونه او د 32-bit ترتیب شمیرې لري:

  • د بیرغونو ترکیب د ځانګړي کڅوړې رول ټاکي. د SYN بیرغ په ګوته کوي چې دا په پیوستون کې د لیږونکي لومړی کڅوړه ده. د ACK بیرغ پدې معنی دی چې لیږونکي تر بایټ پورې د ارتباط ټول معلومات ترلاسه کړي acknum. یو پیکټ کیدای شي څو بیرغونه ولري او د دوی د ترکیب په نوم یادیږي، د بیلګې په توګه، د SYNACK پاکټ.

  • د ترتیب شمیره (seqnum) د لومړي بایټ لپاره چې پدې کڅوړه کې لیږدول کیږي د ډیټا جریان کې آفسیټ مشخص کوي. د مثال په توګه، که د ډیټا د X بایټس سره په لومړۍ کڅوړه کې دا شمیره N وه، په راتلونکي کڅوړه کې د نوي ډیټا سره دا به N + X وي. د پیوستون په پیل کې، هر اړخ دا شمیره په تصادفي ډول غوره کوي.

  • د اعتراف شمیره (اکنوم) - د سیقنوم په څیر ورته آفسیټ، مګر دا د لیږدول شوي بایټ شمیر نه ټاکي، مګر د ترلاسه کونکي څخه د لومړي بایټ شمیره، کوم چې لیږونکي نه لیدل کیږي.

د اړیکو په پیل کې، اړخونه باید موافقه وکړي seqnum и acknum. پیرودونکی د دې سره د SYN پاکټ لیږي seqnum = X. سرور د SYNACK پاکټ سره ځواب ورکوي، چیرته چې دا ثبتوي seqnum = Y او افشا کوي acknum = X + 1. پیرودونکي د ACK پاکټ سره SYNACK ته ځواب ورکوي، چیرته seqnum = X + 1, acknum = Y + 1. له دې وروسته، د اصلي معلوماتو لیږد پیل کیږي.

که ملګری د پاکټ رسید نه مني، TCP یې د وخت پای ته رسیدو وروسته بیا لیږي.

ولې د SYN کوکیز تل نه کارول کیږي؟

لومړی، که SYNACK یا ACK ورک شوی وي، تاسو باید انتظار وکړئ چې دا بیا لیږل کیږي - د پیوستون ترتیب به ورو ورو. دوهم، په SYN بسته کې - او یوازې په دې کې! - یو شمیر اختیارونه لیږدول شوي چې د پیوستون نور عملیات اغیزه کوي. د راتلونکو SYN پاکټونو یادولو پرته، سرور په دې توګه دا اختیارونه له پامه غورځوي؛ پیرودونکي به یې په راتلونکو کڅوړو کې ونه لیږي. TCP کولی شي پدې حالت کې کار وکړي، مګر لږترلږه په لومړي پړاو کې د پیوستون کیفیت به کم شي.

د کڅوړو له نظره، د XDP پروګرام باید لاندې کار وکړي:

  • SYN ته د SYNACK سره د کوکي سره ځواب ورکړئ؛
  • ACK ته د RST سره ځواب ووایی (منقطع)؛
  • پاتې کڅوړې پریږدئ.

د پیکج پارس کولو سره د الګوریتم سیډوکوډ:

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

یو (*) هغه ټکي چیرې چې تاسو اړتیا لرئ د سیسټم حالت اداره کړئ په نښه شوي - په لومړي مرحله کې تاسو کولی شئ پرته له دې چې د SYN کوکي تولید سره د SYN کوکي تولید سره د سیقنوم په توګه په ساده ډول د TCP لاسوند پلي کولو سره ترسره کړئ.

پرځای (**)په داسې حال کې چې موږ میز نه لرو، موږ به کڅوړه پریږدو.

د TCP لاسونه پلي کول

بسته پارس کول او کوډ تایید کول

موږ به د شبکې سرلیک جوړښتونو ته اړتیا ولرو: ایترنیټ (uapi/linux/if_ether.h, IPv4 (uapi/linux/ip.h) او TCP (uapi/linux/tcp.h). زه د اړونده غلطیو له امله وروستی وصل نشم کولی atomic64_t، زه باید اړین تعریفونه په کوډ کې کاپي کړم.

ټول فنکشنونه چې د لوستلو وړتیا لپاره په C کې روښانه شوي باید د زنګ په نقطه کې دننه وي، ځکه چې په کرنل کې د eBPF تصدیق کوونکی د بیکټریک کولو څخه منع کوي، دا په حقیقت کې لوپس او فنکشن کالونه دي.

#define INTERNAL static __attribute__((always_inline))

میکرو LOG() په ریلیز جوړ کې چاپ کول غیر فعالوي.

برنامه د دندو لیږدونکی دی. هر یو یو پاکټ ترلاسه کوي په کوم کې چې د ورته کچې سرلیک روښانه شوی، د بیلګې په توګه، process_ether() تمه لري چې ډک شي ether. د ساحې تحلیل پایلو پراساس ، فنکشن کولی شي پاکټ لوړې کچې ته ورسوي. د فعالیت پایله د XDP عمل دی. د اوس لپاره، SYN او ACK سمبالونکي ټول پاکټونه تیریږي.

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

زه ستاسو پام د A او B نښه شوي چکونو ته را اړوم. که تاسو د A په اړه تبصره وکړئ، برنامه به رامینځته شي، مګر د بارولو په وخت کې به د تایید تېروتنه وي:

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!

کلیدي تار invalid access to packet, off=13 size=1, R7(id=0,off=0,r=0): د اجرا کولو لارې شتون لري کله چې د بفر له پیل څخه دیارلس بایټ د کڅوړې څخه بهر وي. دا ستونزمنه ده چې د لیست کولو څخه پوه شو چې موږ د کومې کرښې په اړه خبرې کوو، مګر د لارښوونې شمیره (12) او یو جلا کوونکی شتون لري چې د سرچینې کوډ کرښې ښیې:

llvm-objdump -S xdp_filter.o | less

په دې حالت کې دا کرښه ته اشاره کوي

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

کوم چې دا روښانه کوي چې ستونزه ده ether. دا به تل همداسې وي.

SYN ته ځواب ورکړئ

پدې مرحله کې هدف د ثابت سره سم SYNACK پاکټ رامینځته کول دي seqnum، کوم چې به په راتلونکي کې د SYN کوکي لخوا بدل شي. ټول بدلونونه په کې واقع کیږي process_tcp_syn() او شاوخوا سیمې.

د بسته بندي تصدیق

په عجیبه توګه، دلته خورا د پام وړ کرښه ده، یا بلکه، دې ته تفسیر:

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

کله چې د کوډ لومړۍ نسخه لیکلو ، 5.1 کرنل کارول کیده ، د تصدیق کونکي لپاره چې د دوی ترمینځ توپیر شتون درلود data_end и (const void*)ctx->data_end. د لیکلو په وخت کې، کرنل 5.3.1 دا ستونزه نه درلوده. دا ممکنه ده چې کمپیلر د ساحې په پرتله په مختلف ډول محلي متغیر ته لاسرسی ولري. د کیسې اخلاق: د کوډ ساده کول کولی شي مرسته وکړي کله چې ډیری ځړول شتون ولري.

بل د تصدیق کونکي د ویاړ لپاره معمول اوږدوالی چیکونه دي؛ او MAX_CSUM_BYTES لاندې.

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

د کڅوړې خلاصول

موږ ډکوو seqnum и acknum، ACK ترتیب کړئ (SYN لا دمخه ټاکل شوی دی):

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

د TCP بندرونه، IP پته او MAC پتې بدل کړئ. معیاري کتابتون د XDP برنامې څخه د لاسرسي وړ ندي ، نو memcpy() - یو میکرو چې د کلینګ داخلي توکي پټوي.

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

د چکسمونو بیا حساب کول

IPv4 او TCP چکسمونه په سرلیکونو کې د ټولو 16-bit کلمو اضافه کولو ته اړتیا لري، او د سرلیکونو اندازه په دوی کې لیکل کیږي، دا د تالیف په وخت کې نامعلوم دی. دا یوه ستونزه ده ځکه چې تصدیق کونکی به نورمال لوپ د حد متغیر ته نه پریږدي. مګر د سرلیکونو اندازه محدوده ده: تر 64 بایټ پورې هر یو. تاسو کولی شئ د یو ثابت شمیر تکرارونو سره لوپ جوړ کړئ، کوم چې ژر پای ته رسیدلی شي.

زه یادونه کوم چې شتون لري RFC 1624 که چیرې یوازې د کڅوړو ټاکل شوي ټکي بدل شوي وي د چیکسم د جزوي بیا محاسبې څرنګوالي په اړه. په هرصورت، دا طریقه نړیواله نه ده، او پلي کول به یې ساتل خورا ستونزمن وي.

د چکسم محاسبه فعالیت:

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

که څه هم size د زنګ وهلو کوډ لخوا تایید شوی، د وتلو دوهم شرط اړین دی ترڅو تصدیق کونکی د لوپ بشپړیدل ثابت کړي.

د 32-bit کلمو لپاره، یو ساده نسخه پلي کیږي:

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

په حقیقت کې د چکسمونو بیا حساب کول او د پاکټ بیرته لیږل:

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;

دنده carry() د RFC 32 په وینا، د 16-bit کلمو د 791-bit مجموعې څخه چکسم جوړوي.

د TCP لاس لیک تصدیق

فلټر په سمه توګه سره اړیکه رامینځته کوي netcat, وروستی ACK ورک شوی، کوم چې لینکس د RST پاکټ سره ځواب ورکړ، ځکه چې د شبکې سټیک SYN نه و ترلاسه کړی - دا په SYNACK بدل شوی او بیرته لیږل شوی - او د OS له نظره، یو پاکټ راغلی چې د خلاصولو سره تړاو نلري. اړیکې

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

دا مهمه ده چې د بشپړ غوښتنلیکونو سره وګورئ او مشاهده کړئ tcpdump په xdp-remote ځکه، د مثال په توګه، hping3 غلط چکونو ته ځواب نه ورکوي.

د XDP له نظره، تایید پخپله کوچنی دی. د محاسبې الګوریتم ابتدايي دی او احتمال لري د پیچلي برید کونکي لپاره زیانمنونکي وي. د مثال په توګه د لینکس کرنل د کریپټوګرافیک سیپ هش کاروي، مګر د XDP لپاره یې پلي کول په واضح ډول د دې مقالې له دائرې څخه بهر دي.

د بهرنیو اړیکو اړوند نوي TODOs لپاره معرفي شوي:

  • د XDP برنامه نشي ذخیره کولی cookie_seed (د مالګې پټه برخه) په نړیوال متغیر کې، تاسو په دانه کې ذخیره کولو ته اړتیا لرئ، چې ارزښت به یې په دوره توګه د باور وړ جنراتور څخه تازه شي.

  • که د SYN کوکي د ACK په پاکټ کې سره سمون خوري، تاسو اړتیا نلرئ یو پیغام چاپ کړئ، مګر د تایید شوي پیرودونکي IP په یاد ولرئ ترڅو له هغې څخه د کڅوړو لیږدولو ته دوام ورکړئ.

د مشروع پیرودونکي تایید:

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

لاګونه ښیې چې چک تیر شوی (flags=0x2 - دا SYN دی، flags=0x10 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

پداسې حال کې چې د تایید شوي IPs لیست شتون نلري، پخپله د SYN سیلاب څخه هیڅ ډول ساتنه شتون نلري، مګر دلته د لاندې کمانډ لخوا پیل شوي د ACK سیلاب غبرګون دی:

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

د ننوتلو نومونه:

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

پایلې

ځینې ​​​​وختونه په عمومي توګه eBPF او په ځانګړې توګه XDP د پرمختیایي پلیټ فارم په پرتله د پرمختللي مدیر وسیلې په توګه وړاندې کیږي. په حقیقت کې، XDP د کرنل لخوا د پاکټونو پروسس کولو کې د مداخلې لپاره یوه وسیله ده، او نه د کرنل سټیک لپاره بدیل، لکه DPDK او نور د کرنل بای پاس اختیارونه. له بلې خوا ، XDP تاسو ته اجازه درکوي خورا پیچلي منطق پلي کړئ ، کوم چې سربیره پردې ، د ترافیک پروسس کولو کې مداخلې پرته تازه کول اسانه دي. تصدیق کونکی لوی ستونزې نه رامینځته کوي؛ په شخصي توګه ، زه به دا د کارن اسپیس کوډ برخو لپاره رد نه کړم.

په دویمه برخه کې، که موضوع په زړه پورې وي، موږ به د تایید شوي مراجعینو او منحلولو جدول بشپړ کړو، کاونټرونه پلي کړو او د فلټر اداره کولو لپاره د یوزر اسپیس افادیت ولیکئ.

سرچینې:

سرچینه: www.habr.com

Add a comment