XDP இல் DDoS தாக்குதல்களுக்கு எதிராக நாங்கள் பாதுகாப்பை எழுதுகிறோம். அணு பகுதி

eXpress Data Path (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 க்கு தொகுத்தல் Clang ஆல் ஆதரிக்கப்படுகிறது மற்றும் GCC 10.1 இல் உறுதியளிக்கப்பட்டுள்ளது.
  2. இந்த ஆப்ஜெக்ட் குறியீட்டில் கர்னல் கட்டமைப்புகளுக்கான அழைப்புகள் இருந்தால் (எடுத்துக்காட்டாக, அட்டவணைகள் மற்றும் கவுண்டர்கள்), அவற்றின் ஐடிகள் பூஜ்ஜியங்களால் மாற்றப்படும், அதாவது அத்தகைய குறியீட்டை இயக்க முடியாது. கர்னலில் ஏற்றுவதற்கு முன், இந்த பூஜ்ஜியங்களை கர்னல் அழைப்புகள் மூலம் உருவாக்கப்பட்ட குறிப்பிட்ட பொருள்களின் ஐடிகளுடன் மாற்ற வேண்டும் (குறியீட்டை இணைக்கவும்). வெளிப்புற பயன்பாடுகள் மூலம் இதைச் செய்யலாம் அல்லது குறிப்பிட்ட வடிப்பானை இணைத்து ஏற்றும் நிரலை எழுதலாம்.
  3. கர்னல் ஏற்றப்பட்ட நிரலை சரிபார்க்கிறது. சுழற்சிகள் இல்லாதது மற்றும் பாக்கெட் மற்றும் ஸ்டேக் எல்லைகளை மீறுவதில் தோல்வி சரிபார்க்கப்படுகிறது. குறியீடு சரியானது என்பதை சரிபார்ப்பவரால் நிரூபிக்க முடியாவிட்டால், நிரல் நிராகரிக்கப்படும் - நீங்கள் அவரைப் பிரியப்படுத்த முடியும்.
  4. வெற்றிகரமான சரிபார்ப்புக்குப் பிறகு, கர்னல் eBPF கட்டிடக்கலை பொருள் குறியீட்டை கணினி கட்டமைப்பிற்கான இயந்திரக் குறியீட்டில் தொகுக்கிறது (சரியான நேரத்தில்).
  5. நிரல் இடைமுகத்துடன் இணைக்கப்பட்டு பாக்கெட்டுகளை செயலாக்கத் தொடங்குகிறது.

XDP கர்னலில் இயங்குவதால், பிழைத்திருத்தம் ட்ரேஸ் பதிவுகள் மற்றும் உண்மையில் நிரல் வடிகட்டி அல்லது உருவாக்கும் பாக்கெட்டுகளைப் பயன்படுத்தி மேற்கொள்ளப்படுகிறது. இருப்பினும், பதிவிறக்கம் செய்யப்பட்ட குறியீடு கணினிக்கு பாதுகாப்பானது என்பதை eBPF உறுதிசெய்கிறது, எனவே நீங்கள் உங்கள் உள்ளூர் லினக்ஸில் நேரடியாக XDP உடன் பரிசோதனை செய்யலாம்.

சூழலைத் தயாரித்தல்

சட்டசபை

eBPF கட்டமைப்பிற்கான பொருள் குறியீட்டை க்ளாங் நேரடியாக உருவாக்க முடியாது, எனவே செயல்முறை இரண்டு படிகளைக் கொண்டுள்ளது:

  1. C குறியீட்டை LLVM பைட்கோடுக்கு தொகுக்கவும் (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 (பயனர்ஸ்பேஸ் 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. இப்போது எங்கு முயற்சி செய்வது?

சோதனை பெஞ்ச்

நிலைப்பாட்டில் இரண்டு இடைமுகங்கள் இருக்க வேண்டும்: அதில் ஒரு வடிகட்டி இருக்கும் மற்றும் அதில் இருந்து பாக்கெட்டுகள் அனுப்பப்படும். வழக்கமான பயன்பாடுகள் எங்கள் வடிப்பானுடன் எவ்வாறு செயல்படுகின்றன என்பதைச் சரிபார்க்க, இவை அவற்றின் சொந்த ஐபிகளைக் கொண்ட முழுமையான லினக்ஸ் சாதனங்களாக இருக்க வேண்டும்.

வெத் (மெய்நிகர் ஈதர்நெட்) வகையின் சாதனங்கள் நமக்கு ஏற்றவை: இவை ஒரு ஜோடி மெய்நிகர் பிணைய இடைமுகங்கள் "இணைக்கப்பட்டுள்ளன". நீங்கள் அவற்றை இப்படி உருவாக்கலாம் (இந்தப் பிரிவில் அனைத்து கட்டளைகளும் 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).

ஒரு பிணைய பெயர்வெளியானது இடைமுகங்கள், ரூட்டிங் அட்டவணைகள் மற்றும் நெட்ஃபில்டர் விதிகளின் தொகுப்பைக் கொண்டுள்ளது, அவை மற்ற நெட்ன்களில் உள்ள ஒத்த பொருட்களிலிருந்து தனிமைப்படுத்தப்படுகின்றன. ஒவ்வொரு செயல்முறையும் ஒரு பெயர்வெளியில் இயங்குகிறது மற்றும் அந்த நெட்ன்களின் பொருட்களை மட்டுமே அணுக முடியும். முன்னிருப்பாக, கணினியில் அனைத்து ஆப்ஜெக்ட்டுகளுக்கும் ஒரே நெட்வொர்க் பெயர்வெளி உள்ளது, எனவே நீங்கள் லினக்ஸில் வேலை செய்யலாம் மற்றும் netns பற்றி தெரியாது.

புதிய பெயர்வெளியை உருவாக்குவோம் xdp-test மற்றும் அதை அங்கு நகர்த்தவும் xdp-remote.

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

பின்னர் செயல்முறை இயங்கும் xdp-test, "பார்க்க மாட்டேன்" xdp-local (அது இயல்புநிலையாக நெட்ன்களில் இருக்கும்) மேலும் ஒரு பாக்கெட்டை 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, அதை Makefile இல் சேர்க்கவும், அதை பிணைக்கவும் 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

அதற்குப் பதிலாக ARPகள் மட்டுமே காட்டப்பட்டால், நீங்கள் வடிப்பான்களை அகற்ற வேண்டும் (இது செய்கிறது sudo ./stand detach), விட்டு விடு ping, வடிகட்டிகளை அமைத்து மீண்டும் முயற்சிக்கவும். பிரச்சனை என்னவென்றால் வடிகட்டி XDP_TX ARP மற்றும் ஸ்டாக் ஆகிய இரண்டிலும் செல்லுபடியாகும்
பெயர்வெளிகள் xdp-test MAC முகவரி 192.0.2.1 ஐ "மறக்க" முடிந்தது, அது இந்த ஐபியை தீர்க்க முடியாது.

பிரச்சனை அறிக்கை

கூறப்பட்ட பணிக்கு செல்லலாம்: XDP இல் SYN குக்கீகள் பொறிமுறையை எழுதவும்.

SYN வெள்ளம் ஒரு பிரபலமான DDoS தாக்குதலாக உள்ளது, இதன் சாராம்சம் பின்வருமாறு. ஒரு இணைப்பு நிறுவப்பட்டால் (TCP ஹேண்ட்ஷேக்), சேவையகம் ஒரு SYN ஐப் பெறுகிறது, எதிர்கால இணைப்புக்கான ஆதாரங்களை ஒதுக்குகிறது, SYNACK பாக்கெட்டுடன் பதிலளிக்கிறது மற்றும் ACKக்காக காத்திருக்கிறது. பல ஆயிரம் வலிமையான போட்நெட்டில் ஒவ்வொரு ஹோஸ்டிலிருந்தும் ஏமாற்றப்பட்ட முகவரிகளிலிருந்து ஒரு நொடிக்கு ஆயிரக்கணக்கான SYN பாக்கெட்டுகளை தாக்குபவர் அனுப்புகிறார். சேவையகம் பாக்கெட் வந்தவுடன் உடனடியாக ஆதாரங்களை ஒதுக்க வேண்டிய கட்டாயத்தில் உள்ளது, ஆனால் ஒரு பெரிய காலக்கெடுவிற்குப் பிறகு அவற்றை வெளியிடுகிறது, இதன் விளைவாக நினைவகம் அல்லது வரம்புகள் தீர்ந்துவிட்டன, புதிய இணைப்புகள் ஏற்றுக்கொள்ளப்படவில்லை, மேலும் சேவை கிடைக்கவில்லை.

நீங்கள் SYN பாக்கெட்டின் அடிப்படையில் ஆதாரங்களை ஒதுக்காமல், SYNACK பாக்கெட்டுடன் மட்டுமே பதிலளித்தால், பின்னர் வந்த ACK பாக்கெட் சேமிக்கப்படாத SYN பாக்கெட்டைக் குறிக்கிறது என்பதை சர்வர் எப்படி புரிந்து கொள்ள முடியும்? எல்லாவற்றிற்கும் மேலாக, ஒரு தாக்குபவர் போலி ACKகளை உருவாக்க முடியும். SYN குக்கீயின் முக்கிய அம்சம் அதை குறியாக்கம் செய்வதாகும் seqnum இணைப்பு அளவுருக்கள் முகவரிகள், துறைமுகங்கள் மற்றும் உப்பு மாறும். உப்பு மாற்றப்படுவதற்கு முன்பு ACK வந்திருந்தால், நீங்கள் ஹாஷை மீண்டும் கணக்கிட்டு அதை ஒப்பிடலாம் acknum. ஃபோர்ஜ் acknum உப்பு இரகசியத்தை உள்ளடக்கியிருப்பதால், தாக்குபவர் அதைச் செய்ய முடியாது.

SYN குக்கீ நீண்ட காலமாக Linux கர்னலில் செயல்படுத்தப்பட்டு வருகிறது, மேலும் SYNகள் மிக விரைவாகவும் மொத்தமாகவும் வந்தாலும் தானாகவே இயக்கப்படும்.

TCP கைகுலுக்கலில் கல்வித் திட்டம்

TCP ஆனது தரவு பரிமாற்றத்தை பைட்டுகளின் ஸ்ட்ரீமாக வழங்குகிறது, எடுத்துக்காட்டாக, HTTP கோரிக்கைகள் TCP வழியாக அனுப்பப்படும். ஸ்ட்ரீம் பாக்கெட்டுகளில் துண்டுகளாக அனுப்பப்படுகிறது. அனைத்து TCP பாக்கெட்டுகளிலும் தருக்கக் கொடிகள் மற்றும் 32-பிட் வரிசை எண்கள் உள்ளன:

  • கொடிகளின் கலவையானது ஒரு குறிப்பிட்ட தொகுப்பின் பங்கை தீர்மானிக்கிறது. SYN கொடி இணைப்பில் அனுப்புநரின் முதல் பாக்கெட் இது என்பதைக் குறிக்கிறது. ACK கொடி என்பது அனுப்புநர் பைட் வரையிலான அனைத்து இணைப்புத் தரவையும் பெற்றுள்ளார் என்று அர்த்தம் acknum. ஒரு பாக்கெட்டில் பல கொடிகள் இருக்கலாம் மற்றும் அவற்றின் கலவையால் அழைக்கப்படுகிறது, எடுத்துக்காட்டாக, ஒரு சினாக் பாக்கெட்.

  • வரிசை எண் (seqnum) இந்த பாக்கெட்டில் அனுப்பப்படும் முதல் பைட்டுக்கான டேட்டா ஸ்ட்ரீமில் ஆஃப்செட்டைக் குறிப்பிடுகிறது. எடுத்துக்காட்டாக, X பைட் தரவு கொண்ட முதல் பாக்கெட்டில் இந்த எண் N ஆக இருந்தால், புதிய தரவு கொண்ட அடுத்த பாக்கெட்டில் அது N+X ஆக இருக்கும். இணைப்பின் தொடக்கத்தில், ஒவ்வொரு பக்கமும் இந்த எண்ணைத் தோராயமாகத் தேர்ந்தெடுக்கும்.

  • ஒப்புகை எண் (ஒப்புகை எண்) - seqnum இன் அதே ஆஃப்செட், ஆனால் இது அனுப்பப்படும் பைட்டின் எண்ணிக்கையை தீர்மானிக்காது, ஆனால் அனுப்புநர் பார்க்காத பெறுநரிடமிருந்து முதல் பைட்டின் எண்ணிக்கை.

இணைப்பின் தொடக்கத்தில், கட்சிகள் ஒப்புக் கொள்ள வேண்டும் 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 நிரல் பின்வருவனவற்றைச் செய்ய வேண்டும்:

  • ஒரு குக்கீ மூலம் SYNACK உடன் SYNக்கு பதிலளிக்கவும்;
  • RST உடன் ACKக்கு பதிலளிக்கவும் (துண்டிக்கவும்);
  • மீதமுள்ள பாக்கெட்டுகளை நிராகரிக்கவும்.

தொகுப்பு பாகுபடுத்தலுடன் அல்காரிதத்தின் சூடோகோட்:

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

ஒன்று (*) கணினியின் நிலையை நீங்கள் நிர்வகிக்க வேண்டிய புள்ளிகள் குறிக்கப்பட்டுள்ளன - முதல் கட்டத்தில், ஒரு SYN குக்கீயை ஒரு seqnum ஆக உருவாக்குவதன் மூலம் TCP ஹேண்ட்ஷேக்கை செயல்படுத்துவதன் மூலம் நீங்கள் அவற்றை இல்லாமல் செய்யலாம்.

அவ்விடத்திலேயே (**), எங்களிடம் டேபிள் இல்லாதபோது, ​​பாக்கெட்டைத் தவிர்ப்போம்.

TCP ஹேண்ட்ஷேக்கை செயல்படுத்துகிறது

தொகுப்பைப் பாகுபடுத்தி குறியீட்டைச் சரிபார்க்கிறது

எங்களுக்கு பிணைய தலைப்பு கட்டமைப்புகள் தேவைப்படும்: ஈதர்நெட் (uapi/linux/if_ether.h), IPv4 (uapi/linux/ip.h) மற்றும் TCP (uapi/linux/tcp.h) தொடர்பான பிழைகள் காரணமாக பின்னதை இணைக்க முடியவில்லை atomic64_t, தேவையான வரையறைகளை நான் குறியீட்டில் நகலெடுக்க வேண்டியிருந்தது.

கர்னலில் உள்ள eBPF சரிபார்ப்பானது பின்னடைவைத் தடைசெய்கிறது, அதாவது, லூப்கள் மற்றும் செயல்பாட்டு அழைப்புகளைத் தடுக்கிறது என்பதால், படிக்கக்கூடியதாக C இல் முன்னிலைப்படுத்தப்பட்ட அனைத்து செயல்பாடுகளும் அழைப்புப் புள்ளியில் இணைக்கப்பட வேண்டும்.

#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-பிட் சொற்களையும் சேர்க்க வேண்டும், மேலும் தலைப்புகளின் அளவு அவற்றில் எழுதப்படும், அதாவது தொகுக்கும் நேரத்தில் தெரியவில்லை. சரிபார்ப்பவர் சாதாரண லூப்பை எல்லை மாறிக்கு தவிர்க்காததால் இது ஒரு பிரச்சனை. ஆனால் தலைப்புகளின் அளவு குறைவாக உள்ளது: ஒவ்வொன்றும் 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-பிட் வார்த்தைகளுக்கு, எளிமையான பதிப்பு செயல்படுத்தப்படுகிறது:

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-பிட் வார்த்தைகளின் 791-பிட் தொகையிலிருந்து செக்ஸம் செய்கிறது.

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 பார்வையில், சரிபார்ப்பு அற்பமானது. கணக்கீட்டு அல்காரிதம் பழமையானது மற்றும் அதிநவீன தாக்குபவர்களால் பாதிக்கப்படக்கூடியது. லினக்ஸ் கர்னல், எடுத்துக்காட்டாக, கிரிப்டோகிராஃபிக் சிப்ஹாஷைப் பயன்படுத்துகிறது, ஆனால் எக்ஸ்டிபிக்கான அதன் செயலாக்கம் இந்த கட்டுரையின் நோக்கத்திற்கு அப்பாற்பட்டது.

வெளிப்புற தகவல்தொடர்பு தொடர்பான புதிய TODOகளுக்காக அறிமுகப்படுத்தப்பட்டது:

  • XDP நிரல் சேமிக்க முடியாது cookie_seed (உப்பின் ரகசிய பகுதி) உலகளாவிய மாறியில், உங்களுக்கு கர்னலில் சேமிப்பு தேவை, அதன் மதிப்பு நம்பகமான ஜெனரேட்டரிலிருந்து அவ்வப்போது புதுப்பிக்கப்படும்.

  • ACK பாக்கெட்டில் SYN குக்கீ பொருந்தினால், நீங்கள் ஒரு செய்தியை அச்சிட வேண்டிய அவசியமில்லை, ஆனால் அதிலிருந்து பாக்கெட்டுகளைத் தொடர்ந்து அனுப்ப, சரிபார்க்கப்பட்ட கிளையண்டின் ஐபியை நினைவில் கொள்ளவும்.

முறையான வாடிக்கையாளர் சரிபார்ப்பு:

$ 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

சரிபார்க்கப்பட்ட ஐபிகளின் பட்டியல் எதுவும் இல்லை என்றாலும், 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

கருத்தைச் சேர்