మేము XDPపై DDoS దాడుల నుండి రక్షణను వ్రాస్తాము. అణు భాగం

eXpress డేటా పాత్ (XDP) టెక్నాలజీ ప్యాకెట్లు కెర్నల్ నెట్‌వర్క్ స్టాక్‌లోకి ప్రవేశించే ముందు Linux ఇంటర్‌ఫేస్‌లలో ట్రాఫిక్‌ను ఏకపక్షంగా ప్రాసెస్ చేయడానికి అనుమతిస్తుంది. XDP యొక్క అప్లికేషన్ - DDoS దాడుల నుండి రక్షణ (CloudFlare), సంక్లిష్ట ఫిల్టర్‌లు, గణాంకాల సేకరణ (నెట్‌ఫ్లిక్స్). XDP ప్రోగ్రామ్‌లు eBPF వర్చువల్ మెషీన్ ద్వారా అమలు చేయబడతాయి మరియు అందువల్ల ఫిల్టర్ రకాన్ని బట్టి వాటి కోడ్ మరియు అందుబాటులో ఉన్న కెర్నల్ ఫంక్షన్‌లు రెండింటిపై పరిమితులు ఉంటాయి.

కథనం XDPలోని అనేక మెటీరియల్‌ల లోపాలను భర్తీ చేయడానికి ఉద్దేశించబడింది. ముందుగా, వారు XDP యొక్క లక్షణాలను తక్షణమే దాటవేసే రెడీమేడ్ కోడ్‌ను అందిస్తారు: ధృవీకరణ కోసం సిద్ధం లేదా సమస్యలను కలిగించడం చాలా సులభం. మీరు తర్వాత మొదటి నుండి మీ స్వంత కోడ్‌ను వ్రాయడానికి ప్రయత్నించినప్పుడు, సాధారణ లోపాలతో ఏమి చేయాలో అర్థం కావడం లేదు. రెండవది, VM మరియు హార్డ్‌వేర్ లేకుండా XDPని స్థానికంగా పరీక్షించే మార్గాలను ఇది కవర్ చేయదు, అయినప్పటికీ వాటికి వారి స్వంత ఆపదలు ఉన్నాయి. XDP మరియు eBPF పట్ల ఆసక్తి ఉన్న నెట్‌వర్క్‌లు మరియు Linux గురించి తెలిసిన ప్రోగ్రామర్‌ల కోసం టెక్స్ట్ ఉద్దేశించబడింది.

ఈ భాగంలో, 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. ఈ ఆబ్జెక్ట్ కోడ్‌లో కెర్నల్ నిర్మాణాలకు కాల్‌లు ఉంటే (ఉదాహరణకు, టేబుల్‌లు మరియు కౌంటర్‌లకు), వాటి IDలకు బదులుగా సున్నాలు ఉన్నాయి, అంటే అలాంటి కోడ్ అమలు చేయబడదు. కెర్నల్‌లోకి లోడ్ చేయడానికి ముందు, ఈ సున్నాలను తప్పనిసరిగా కెర్నల్ కాల్‌ల ద్వారా సృష్టించబడిన నిర్దిష్ట వస్తువుల IDలతో భర్తీ చేయాలి (కోడ్‌ను లింక్ చేయండి). మీరు దీన్ని బాహ్య యుటిలిటీలతో చేయవచ్చు లేదా నిర్దిష్ట ఫిల్టర్‌ను లింక్ చేసి లోడ్ చేసే ప్రోగ్రామ్‌ను మీరు వ్రాయవచ్చు.
  3. లోడ్ అవుతున్న ప్రోగ్రామ్‌ను కెర్నల్ ధృవీకరిస్తుంది. ఇది చక్రాల లేకపోవడం మరియు ప్యాకేజీ మరియు స్టాక్ సరిహద్దుల యొక్క నాన్-ఎగ్జిట్ కోసం తనిఖీ చేస్తుంది. వెరిఫైయర్ కోడ్ సరైనదని నిరూపించలేకపోతే, ప్రోగ్రామ్ తిరస్కరించబడుతుంది - ఒకరు అతనిని సంతోషపెట్టగలగాలి.
  4. విజయవంతమైన ధృవీకరణ తర్వాత, కెర్నల్ eBPF ఆర్కిటెక్చర్ ఆబ్జెక్ట్ కోడ్‌ను సిస్టమ్ ఆర్కిటెక్చర్ మెషిన్ కోడ్‌గా (సమయంలో) కంపైల్ చేస్తుంది.
  5. ప్రోగ్రామ్ ఇంటర్‌ఫేస్‌కు జోడించబడింది మరియు ప్యాకెట్లను ప్రాసెస్ చేయడం ప్రారంభిస్తుంది.

XDP కెర్నల్‌లో నడుస్తుంది కాబట్టి, డీబగ్గింగ్ అనేది ట్రేస్ లాగ్‌లపై ఆధారపడి ఉంటుంది మరియు నిజానికి ప్రోగ్రామ్ ఫిల్టర్ చేసే లేదా ఉత్పత్తి చేసే ప్యాకెట్‌లపై ఆధారపడి ఉంటుంది. అయితే, eBPF డౌన్‌లోడ్ చేసిన కోడ్‌ను సిస్టమ్ కోసం సురక్షితంగా ఉంచుతుంది, కాబట్టి మీరు మీ స్థానిక Linuxలో 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

Arch Linux కోసం Makefile (కెర్నల్ 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 - సిస్టమ్ ఆర్కిటెక్చర్. పంపిణీల మధ్య మార్గాలు మరియు సాధనాలు కొద్దిగా మారవచ్చు.

డెబియన్ 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లతో పూర్తి Linux పరికరాలు అయి ఉండాలి.

వెత్ (వర్చువల్ ఈథర్నెట్) వంటి పరికరాలు మనకు అనుకూలంగా ఉంటాయి: అవి ఒకదానికొకటి నేరుగా “కనెక్ట్ చేయబడిన” వర్చువల్ నెట్‌వర్క్ ఇంటర్‌ఫేస్‌ల జత. మీరు వాటిని ఇలా సృష్టించవచ్చు (ఈ విభాగంలో, అన్ని ఆదేశాలు 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) ఇన్‌కమింగ్ ట్రాఫిక్ పంపబడుతుంది. అయితే, ఒక సమస్య ఉంది: ఇంటర్‌ఫేస్‌లు ఒకే మెషీన్‌లో ఉన్నాయి మరియు Linux వాటిలో ఒకదానికి మరొక దాని ద్వారా ట్రాఫిక్‌ను పంపదు. మీరు గమ్మత్తైన నిబంధనలతో దాన్ని పరిష్కరించవచ్చు iptables, కానీ వారు ప్యాకేజీలను మార్చవలసి ఉంటుంది, ఇది డీబగ్గింగ్ చేసేటప్పుడు అసౌకర్యంగా ఉంటుంది. నెట్‌వర్క్ నేమ్‌స్పేస్‌లను ఉపయోగించడం మంచిది (నెట్‌వర్క్ నేమ్‌స్పేస్‌లు, తదుపరి నెట్న్స్).

నెట్‌వర్క్ నేమ్‌స్పేస్ ఇంటర్‌ఫేస్‌లు, రూటింగ్ టేబుల్‌లు మరియు ఇతర నెట్‌న్స్‌లోని సారూప్య వస్తువుల నుండి వేరుచేయబడిన నెట్‌ఫిల్టర్ నియమాల సమితిని కలిగి ఉంటుంది. ప్రతి ప్రక్రియ కొంత నేమ్‌స్పేస్‌లో నడుస్తుంది మరియు ఈ నెట్న్స్ యొక్క వస్తువులు మాత్రమే దీనికి అందుబాటులో ఉంటాయి. డిఫాల్ట్‌గా, సిస్టమ్ అన్ని ఆబ్జెక్ట్‌లకు ఒకే నెట్‌వర్క్ నేమ్‌స్పేస్‌ను కలిగి ఉంది, కాబట్టి మీరు Linuxలో పని చేయవచ్చు మరియు నెట్‌న్స్ గురించి తెలియదు.

కొత్త నేమ్‌స్పేస్‌ని క్రియేట్ చేద్దాం 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 వద్ద ఉన్న ఏకైక ఇంటర్‌ఫేస్ ఈ ప్రక్రియకు అందుబాటులో ఉంది. ఇది రివర్స్‌లో కూడా పనిచేస్తుంది.

నెట్‌న్స్ మధ్య కదులుతున్నప్పుడు, ఇంటర్‌ఫేస్ డౌన్ అయి చిరునామాను కోల్పోతుంది. నెట్న్స్‌లో ఇంటర్‌ఫేస్‌ను సెటప్ చేయడానికి, మీరు అమలు చేయాలి 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

బదులుగా ARP మాత్రమే చూపబడితే, మీరు ఫిల్టర్‌లను తీసివేయాలి (ఇది చేస్తుంది 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 ప్యాకెట్‌కు చెందినదని సర్వర్ ఎలా అర్థం చేసుకోగలదు? అన్నింటికంటే, దాడి చేసే వ్యక్తి కూడా నకిలీ ACKలను రూపొందించవచ్చు. SYN కుక్కీ యొక్క సారాంశం ఎన్‌కోడ్ చేయడం seqnum కనెక్షన్ పారామితులు చిరునామాలు, పోర్ట్‌లు మరియు మారుతున్న ఉప్పు యొక్క హాష్‌గా. ఉప్పు మారకముందే ACK చేరుకోగలిగితే, మీరు హాష్‌ని మళ్లీ లెక్కించవచ్చు మరియు దానితో పోల్చవచ్చు acknum. నకిలీ acknum దాడి చేసే వ్యక్తి ఉప్పులో రహస్యాన్ని కలిగి ఉంటుంది మరియు పరిమిత ఛానెల్ కారణంగా దానిని క్రమబద్ధీకరించడానికి సమయం ఉండదు.

SYN కుక్కీలు చాలా కాలం పాటు Linux కెర్నల్‌లో అమలు చేయబడ్డాయి మరియు SYN లు చాలా త్వరగా మరియు పెద్దమొత్తంలో వచ్చినట్లయితే కూడా స్వయంచాలకంగా ప్రారంభించబడతాయి.

TCP హ్యాండ్‌షేక్‌పై విద్యా కార్యక్రమం

TCP డేటా బదిలీని బైట్‌ల స్ట్రీమ్‌గా అందిస్తుంది, ఉదాహరణకు, HTTP అభ్యర్థనలు TCP ద్వారా ప్రసారం చేయబడతాయి. స్ట్రీమ్ ప్యాకెట్లలో ముక్కగా ప్రసారం చేయబడుతుంది. అన్ని TCP ప్యాకెట్‌లు లాజికల్ ఫ్లాగ్‌లు మరియు 32-బిట్ సీక్వెన్స్ నంబర్‌లను కలిగి ఉంటాయి:

  • జెండాల కలయిక నిర్దిష్ట ప్యాకేజీ పాత్రను నిర్వచిస్తుంది. SYN ఫ్లాగ్ అంటే కనెక్షన్‌లో పంపినవారి మొదటి ప్యాకెట్ ఇదే. ACK ఫ్లాగ్ అంటే పంపినవారు ఒక బైట్ వరకు మొత్తం కనెక్షన్ డేటాను అందుకున్నారని అర్థం. acknum. ఒక ప్యాకెట్ అనేక ఫ్లాగ్‌లను కలిగి ఉండవచ్చు మరియు వాటి కలయికతో పేరు పెట్టబడింది, ఉదాహరణకు, SYNACK ప్యాకెట్.

  • సీక్వెన్స్ నంబర్ (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 కుక్కీని సీక్నమ్‌గా రూపొందించడం ద్వారా 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ని దాటవేస్తూ, Linux 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 దృక్కోణం నుండి, చెక్కు కూడా అల్పమైనది. గణన అల్గారిథమ్ ప్రాచీనమైనది మరియు అధునాతన దాడి చేసేవారికి బహుశా హాని కలిగించవచ్చు. ఉదాహరణకు, Linux కెర్నల్ క్రిప్టోగ్రాఫిక్ సిప్‌హాష్‌ని ఉపయోగిస్తుంది, అయితే XDP కోసం దాని అమలు స్పష్టంగా ఈ కథనం యొక్క పరిధికి మించినది.

బాహ్య పరస్పర చర్యకు సంబంధించిన కొత్త TODOల కోసం కనిపించింది:

  • XDP ప్రోగ్రామ్ నిల్వ చేయబడదు cookie_seed (ఉప్పు యొక్క రహస్య భాగం) గ్లోబల్ వేరియబుల్‌లో, మీకు కెర్నల్ స్టోర్ అవసరం, దీని విలువ విశ్వసనీయ జనరేటర్ నుండి కాలానుగుణంగా నవీకరించబడుతుంది.

  • ACK ప్యాకెట్‌లోని SYN కుక్కీ సరిపోలితే, మీరు సందేశాన్ని ప్రింట్ చేయాల్సిన అవసరం లేదు, కానీ దాని నుండి ప్యాకెట్‌లను మరింత దాటవేయడానికి ధృవీకరించబడిన క్లయింట్ యొక్క 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

ధృవీకరించబడిన IPల జాబితా లేనంత వరకు, 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

ఒక వ్యాఖ్యను జోడించండి