जल्दी से आज़माएँ 超XDP का परिचय

सारांश

यह XDP को जल्दी से आज़माने के लिए एक चर्चा है।
इसलिए, eBPF आदि के विस्तृत विवरण को छोड़ दिया गया है।

XDP एक ऐसा ढांचा है जो Linux कर्नेल के नेटवर्किंग स्टैक के प्रारंभिक चरणों में पैकेट को संसाधित करने के लिए है, और eBPF का उपयोग करके NIC (नेटवर्क इंटरफेस कार्ड) में सीधे प्रोग्राम डालने की अनुमति देता है।

Linux कर्नेल के नेटवर्किंग स्टैक के प्रारंभिक चरणों में पैकेट को प्रबंधित करने के कारण, यह iptables जैसे फ़िल्टरों की तुलना में तेजी से संसाधित कर सकता है।

संक्षेप में, यह समझा जा सकता है कि XDP का प्रोग्राम eBPF का उपयोग करके NIC के इंटरफेस से जोड़ सकता है।

वातावरण निर्माण

यह Ubuntu 22.04 पर आधारित है।
XDP के साथ NIC संचार को नियंत्रित करने के लिए, वर्चुअल मशीनों जैसे VM में प्रयोग करना उचित है।

  • आवश्यक पैकेज स्थापित करें

    $ sudo apt install build-essential clang llvm gcc-multilib libbpf-dev
    

eBPF(XDP) का निर्माण gcc की बजाय clang का उपयोग कर किया जाता है, और eBPF के लिए हेडर फ़ाइलों की आवश्यकता होती है, इसलिए libbpf-dev स्थापित किया गया है।

gcc-multilib बाद में asm/types.h का उपयोग करने के लिए आवश्यक है।

चलिए शुरू करते हैं सभी पैकेट को DROP करना

इंटरफेस की जांच

  • सबसे पहले, इंटरफेस पर संचार आ रहा है या नहीं, इसे देखें

    $ sudo tcpdump -i eht0 -n
    

सामान्यतः, यदि यह स्विच आदि से जुड़ा है, तो ARP जैसे पैकेट आ रहे होंगे।
यदि कोई संचार उत्पन्न नहीं हो रहा है तो कृपया बाहरी से ping आदि करके पैकेट की जांच करें।

XDP प्रोग्राम की तैयारी

  • XDP के साथ सभी संचार को DROP करें

    • xdp.c
    #include <linux/bpf.h>
    #include <bpf/bpf_helpers.h>
    
    SEC("xdp")
    int xdp_drop(struct xdp_md *ctx) {
        return XDP_DROP;
    }
    
    char _license[] SEC("license") = "MIT";
    
  • #include <linux/bpf.h>

    • Linux के BPF (बर्केल बफर फ़िल्टर) से संबंधित कार्यक्षमताएँ प्रदान करने वाले हेडर फ़ाइल
  • #include <bpf/bpf_helpers.h>

    • eBPF प्रोग्राम लिखने के लिए आवश्यक सहायक फ़ंक्शन प्रदान करने वाले हेडर फ़ाइल
  • SEC("xdp")

    • यह मैक्रो अगले परिभाषित फ़ंक्शन के eBPF प्रोग्राम के प्रकार को निर्दिष्ट करता है
    • “xdp” निर्दिष्ट करने पर, अगले परिभाषित फ़ंक्शन XDP प्रोग्राम होगा
  • int xdp_drop(struct xdp_md *ctx)

    • प्रत्येक बार पैकेट प्राप्त होने पर xdp_drop को कॉल किया जाता है।
      इस बार return XDP_DROP; करके सभी पैकेटों को DROP करने का प्रोग्राम है
  • char _license[] SEC("license") = "MIT";

    • eBPF प्रोग्राम के कार्यान्वयन के लिए लाइसेंस निर्दिष्ट करना अनिवार्य है
    • यदि GPL से संबंधित फ़ंक्शन को चलाने का प्रयास किया गया, तो GPL का होना आवश्यक होगा।

निर्माण

  • निर्माण करें

    $ clang -O2 -target bpf -c xdp.c -o xdp.o
    
  • लक्ष्य के रूप में bpf निर्दिष्ट करके निर्माण करने से यह eBPF के लिए एक ऑब्जेक्ट के रूप में निर्माण किया जाएगा।

इंटरफेस पर अटैच करना

  • इंटरफेस पर अटैच करें

    $ sudo ip link set dev eth0 xdp obj xdp.o sec xdp
    

पैकेट की जांच

$ sudo tcpdump -i eth0 -n

ARP, ICMP आदि के पैकेट अब नहीं पहुंचने चाहिए।
XDP tcpdump से पहले संसाधित होता है, इसलिए tcpdump के जरिए पैकेट नहीं देखे जा सकेंगे।

XDP का डिटैच

$ sudo ip link set dev eth0 xdp off

इससे सभी पैकेटों को DROP करने वाला XDP प्रोग्राम डिटैच हो जाएगा,
और संचार फिर से सक्षम होना चाहिए।

थोड़ा और गहरे देखें (ICMP को गिराना)

सभी पैकेट को DROP करना ip link set down dev eth0 के समान है
और अनुप्रयोग संदर्भ में कोई मूल्य नहीं है, इसलिए XDP का उपयोग करते हुए केवल ICMP को गिराने का प्रयास करें।

  • xdp.c
    #include <linux/bpf.h>
    #include <bpf/bpf_helpers.h>
    #include <linux/if_ether.h>
    #include <linux/ip.h>
    #include <linux/icmp.h>
    #include <arpa/inet.h>
    
    SEC("xdp")
    int xdp_drop_icmp(struct xdp_md *ctx) {
        void *data_end = (void *)(unsigned long)ctx->data_end;
        void *data = (void *)(unsigned long)ctx->data;
    
        // एथरनेट हेडर का पॉइंटर प्राप्त करें
        struct ethhdr *eth = data;
        if ((void *)eth + sizeof(*eth) > data_end)
            return XDP_PASS;
    
        // एथरनेट फ्रेम में IP पैकेट है या नहीं, यह जांचें
        if (eth->h_proto != htons(ETH_P_IP))
            return XDP_PASS;
    
        // IP हेडर का पॉइंटर प्राप्त करें
        struct iphdr *ip = data + sizeof(*eth);
        if ((void *)ip + sizeof(*ip) > data_end)
            return XDP_PASS;
    
        // क्या प्रोटोकॉल ICMP है यह जांचें
        if (ip->protocol != IPPROTO_ICMP)
            return XDP_PASS;
    
        // ICMP हेडर का पॉइंटर प्राप्त करें
        struct icmphdr *icmp = (void *)ip + sizeof(*ip);
        if ((void *)icmp + sizeof(*icmp) > data_end)
            return XDP_PASS;
    
        // ICMP पैकेट को गिराएं
        return XDP_DROP;
    }
    
    char _license[] SEC("license") = "MIT";
    

ctx से पैकेट के पॉइंटर आदि निकाले जा सकते हैं, इसलिए उनका उपयोग करके प्रक्रिया की जाएगी।

  • data
    • पैकेट डेटा के लिए पॉइंटर
  • data_end
    • डेटा का अंत पॉइंटर
struct ethhdr *eth = data;
if ((void *)eth + sizeof(*eth) > data_end)
    return XDP_PASS;

एथरनेट हेडर को डेटा पॉइंटर में सौंपा गया है।
यदि eth के पॉइंटर और eth हेडर के आकार का योग data_end से बड़ा हो जाता है,
तो पैकेट का आकार एथरनेट हेडर से छोटा है, इसलिए इसे संसाधित नहीं किया जाएगा और कर्नेल को सौंपा जाएगा।

if (eth->h_proto != htons(ETH_P_IP))
    return XDP_PASS;

eth हेडर की जांच की गई है, और h_proto का ETH_P_IP (IPv4 प्रोटोकॉल 0x0800) होना आवश्यक है। htons होस्ट के एंडियन (होस्ट बाइट ऑर्डर) से संचार पैकेट के एंडियन (नेटवर्क बाइट ऑर्डर) में बदलने के लिए एक मैक्रो है। htons (होस्ट से नेटवर्क शॉर्ट)

यदि एथर पैकेट में शामिल प्रोटोकॉल IPv4 नहीं है, तो XDP_PASS को कर्नेल को सौंपा जाएगा।

struct iphdr *ip = data + sizeof(*eth);
if ((void *)ip + sizeof(*ip) > data_end)
    return XDP_PASS;

ip हेडर का पॉइंटर प्राप्त किया गया है। डेटा (प्रारंभिक पॉइंटर) में एथरनेट के हेडर के आकार को जोड़ा गया है ताकि एथरनेट हेडर के अंत को
ip हेडर के पहले पॉइंटर के रूप में प्राप्त किया जा सके।

इस स्थिति में ip हेडर के पॉइंटर से iphdr का आकार data_end से बड़ा नहीं है तो
यह ip हेडर नहीं है, इसलिए XDP_PASS करके कर्नेल को सौंपा जाएगा।

if (ip->protocol != IPPROTO_ICMP)
    return XDP_PASS;

ip पैकेट में शामिल प्रोटोकॉल को IPPROTO_ICMP (1) के रूप में जांचा जाता है।
ICMP के अलावा के प्रोटोकॉल को XDP_PASS करके कर्नेल को सौंपा जाएगा।

struct icmphdr *icmp = (void *)ip + sizeof(*ip);
if ((void *)icmp + sizeof(*icmp) > data_end)
    return XDP_PASS;

icmp हेडर के पॉइंटर को प्राप्त किया जा रहा है।
ip हेडर के पहले पॉइंटर से ip हेडर के आकार को जोड़कर पॉइंटर को प्राप्त किया गया है।
इसके अलावा, यहाँ भी data_end के पार नहीं गया है यह जांच किया जाता है।

यहाँ तक कि XDP_PASS नहीं किए गए पैकेट ICMP हैं, इसलिए XDP_DROP करके
पैकेट को गिराकर लक्ष्य प्राप्त किया जा सकता है।

※ IPPROTO_ICMP के समय में ही गिरा देना भी ठीक रहेगा।

अंत में

C भाषा में लिखना आवश्यक होने के कारण, यह थोड़ी कठिनाई भरा लग सकता है,
लेकिन जब इसे करने की कोशिश की जाती है, तो यह कुछ हद तक सरलता से संभव है।

अगला कदम (2024/09/17 अपडेट)