•19 min read

eBPF & XDP ở quy mô lớn: Giảm thiểu DDoS 10 triệu gói/giây & Lọc cấp độ driver

eBPF & XDP ở quy mô lớn: Giảm thiểu DDoS 10 triệu gói/giây & Lọc cấp độ driver

Các cuộc tấn công DDoS ở tầng mạng, đặc biệt là SYN floods và UDP amplification, vẫn là một mối đe dọa dai dẳng. Mặc dù việc lọc ở không gian kernel truyền thống có hiệu quả, nhưng nó gây ra độ trễ và chi phí CPU do cấp phát sk_buff, duyệt toàn bộ ngăn xếp mạng và chuyển đổi ngữ cảnh. Ở tốc độ đường truyền vượt quá 10 triệu gói tin mỗi giây (Mpps) trên một lõi, những chi phí này trở nên quá lớn. Hướng dẫn này trình bày chi tiết việc triển khai giảm thiểu DDoS hiệu suất cao bằng cách sử dụng eBPF và XDP (eXpress Data Path), tận dụng xử lý gói tin ở cấp độ driver để đạt được khả năng lọc ở tốc độ đường truyền.

Audio Briefing
0:00 / 0:00

Các nguyên tắc cơ bản của XDP để lọc hiệu suất cao

XDP hoạt động ở điểm sớm nhất có thể trong ngăn xếp mạng: trực tiếp trong vòng nhận (RX) của driver card giao diện mạng (NIC). Ngữ cảnh thực thi trước kernel, trước cấp phát sk_buff này rất quan trọng đối với hiệu suất. Một gói tin được xử lý bởi chương trình XDP sẽ không bao giờ đến được ngăn xếp mạng đầy đủ của kernel nếu bị loại bỏ hoặc chuyển hướng. Điều này loại bỏ việc cấp phát bộ nhớ, lỗi bộ nhớ cache và chu kỳ CPU liên quan đến việc tạo sk_buff và xử lý tiếp theo.

Chương trình XDP nhận một cấu trúc xdp_md thô, cung cấp các con trỏ đến điểm bắt đầu và kết thúc của dữ liệu gói tin. Giá trị trả về của chương trình quyết định hành động được thực hiện:

  • XDP_DROP: Gói tin bị loại bỏ ngay lập tức. Đây là hành động chính để giảm thiểu DDoS.
  • XDP_PASS: Gói tin được phép tiếp tục đến ngăn xếp mạng của kernel.
  • XDP_TX: Gói tin được chuyển hướng trở lại cùng cổng NIC. Hữu ích cho việc cân bằng tải hoặc phản xạ.
  • XDP_REDIRECT: Gói tin được chuyển hướng đến một cổng NIC khác hoặc một bản đồ CPU BPF để giao tiếp giữa các CPU.

Ngữ cảnh thực thi chương trình XDP

Một chương trình XDP thực thi trong môi trường eBPF bị hạn chế. Nó không thể thực hiện các lệnh gọi hệ thống tùy ý, truy cập bộ nhớ kernel tùy ý hoặc lặp vô hạn. Điều này đảm bảo sự ổn định và ngăn chặn các chương trình độc hại hoặc có lỗi làm tổn hại đến kernel. Các ràng buộc chính bao gồm:

  • Vòng lặp có giới hạn: Các vòng lặp phải có giới hạn trên đã biết, hữu hạn.
  • Truy cập bộ nhớ: Chỉ có thể truy cập dữ liệu gói tin và bộ nhớ bản đồ BPF.
  • Các hàm trợ giúp: Một tập hợp giới hạn các hàm trợ giúp BPF có sẵn cho các tác vụ như tra cứu bản đồ, tính toán tổng kiểm tra và thao tác gói tin.
Advertisement

Kiến trúc giảm thiểu DDoS

Kiến trúc giảm thiểu của chúng tôi bao gồm:

  1. Chương trình XDP (C): Được triển khai trực tiếp đến NIC. Chương trình này thực hiện phân tích cú pháp và lọc gói tin ban đầu dựa trên địa chỉ IP, giao thức và số cổng. Nó tận dụng các bản đồ BPF để đưa vào danh sách đen động và bộ đếm.
  2. Bản đồ BPF:
    • BPF_MAP_TYPE_LPM_TRIE: Để đưa vào danh sách đen IP khớp tiền tố dài nhất (LPM) hiệu quả. Điều này cho phép chặn toàn bộ mạng con hoặc các IP riêng lẻ.
    • BPF_MAP_TYPE_ARRAY: Đối với bộ đếm gói tin trên mỗi CPU, cung cấp khả năng hiển thị thời gian thực về lưu lượng truy cập bị loại bỏ.
  3. Agent không gian người dùng (Go/Python/Rust):
    • Tải chương trình XDP và gắn nó vào giao diện mạng mục tiêu.
    • Quản lý bản đồ LPM_TRIE, thêm/xóa các IP bị đưa vào danh sách đen dựa trên thông tin tình báo mối đe dọa bên ngoài hoặc phát hiện bất thường cục bộ.
    • Đọc và tổng hợp các bộ đếm từ bản đồ ARRAY, hiển thị chúng qua Prometheus.

LPM Trie để đưa vào danh sách đen IP động

Một bản đồ LPM_TRIE lý tưởng để đưa vào danh sách đen IP do khả năng khớp tiền tố hiệu quả của nó. Điều này cho phép chặn 192.168.1.0/24 bằng một mục nhập duy nhất, thay vì liệt kê tất cả 256 IP.

// bpf_ddos_mitigator.c
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>

// Define a structure for LPM trie keys
// This structure is critical for the LPM trie map.
// The 'prefixlen' field determines how many bits of the 'data' field are used for matching.
// For IPv4, prefixlen can be 32 for a host match, or less for a subnet.
// For IPv6, prefixlen can be 128 for a host match.
struct bpf_lpm_trie_key {
    __u32 prefixlen; // Must be the first field
    __u32 ip_addr;   // IPv4 address in network byte order
};

// Map for blacklisted IPs (LPM Trie)
// Key: bpf_lpm_trie_key (prefixlen + IP)
// Value: __u8 (e.g., 1 to indicate blacklisted)
struct {
    __uint(type, BPF_MAP_TYPE_LPM_TRIE);
    __uint(max_entries, 10240); // Max 10k blacklist entries
    __uint(key_size, sizeof(struct bpf_lpm_trie_key));
    __uint(value_size, sizeof(__u8));
    __uint(map_flags, BPF_F_NO_PREALLOC); // Don't preallocate all memory
} blacklist_ips SEC(".maps");

// Map for packet counters (per-CPU array)
// Key: __u32 (index for counter type, e.g., 0 for dropped, 1 for passed)
// Value: __u64 (counter)
struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __uint(max_entries, 2); // 0: dropped_packets, 1: passed_packets
    __uint(key_size, sizeof(__u32));
    __uint(value_size, sizeof(__u64));
} xdp_stats_map SEC(".maps");

// Helper macro to increment a counter in the xdp_stats_map
static __always_inline void increment_counter(__u32 index) {
    __u64 *counter = bpf_map_lookup_elem(&xdp_stats_map, &index);
    if (counter) {
        __sync_fetch_and_add(counter, 1);
    }
}

SEC("xdp")
int xdp_ddos_mitigator(struct xdp_md *ctx) {
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;

    struct ethhdr *eth = data;
    if (eth + 1 > data_end) {
        return XDP_PASS; // Malformed Ethernet header
    }

    // Only process IPv4 for this example
    if (bpf_ntohs(eth->h_proto) != ETH_P_IP) {
        return XDP_PASS;
    }

    struct iphdr *iph = data + sizeof(*eth);
    if (iph + 1 > data_end) {
        return XDP_PASS; // Malformed IP header
    }

    // Check if source IP is blacklisted
    struct bpf_lpm_trie_key key = {
        .prefixlen = 32, // Exact match for source IP
        .ip_addr = iph->saddr // Source IP in network byte order
    };
    __u8 *blacklisted = bpf_map_lookup_elem(&blacklist_ips, &key);
    if (blacklisted) {
        // IP is blacklisted, drop the packet
        increment_counter(0); // Increment dropped_packets counter
        return XDP_DROP;
    }

    // Basic SYN flood mitigation (TCP SYN packets without ACK)
    if (iph->protocol == IPPROTO_TCP) {
        struct tcphdr *tcph = (void *)iph + (iph->ihl * 4);
        if (tcph + 1 > data_end) {
            return XDP_PASS; // Malformed TCP header
        }

        // Check for SYN flag set and ACK flag not set
        if (tcph->syn && !tcph->ack) {
            // Potentially a SYN flood packet.
            // For production, this would be more sophisticated,
            // e.g., rate limiting, SYN cookie implementation, or state tracking.
            // For now, we'll just drop it if it's a simple SYN.
            // This is a very aggressive rule and might drop legitimate SYNs.
            // A real-world solution would involve more context.
            // For demonstration, we'll drop if source IP is not whitelisted.
            // (No whitelist implemented here, so it's a direct drop for SYN)
            // increment_counter(0); // Increment dropped_packets counter
            // return XDP_DROP;
        }
    }

    // Basic UDP amplification mitigation (e.g., DNS, NTP, SSDP)
    // This is a simplified example. Real mitigation involves
    // checking payload size, specific protocol headers, and known amplification vectors.
    if (iph->protocol == IPPROTO_UDP) {
        struct udphdr *udph = (void *)iph + (iph->ihl * 4);
        if (udph + 1 > data_end) {
            return XDP_PASS; // Malformed UDP header
        }

        // Example: Drop UDP packets to common amplification ports if source is not trusted
        // This is a placeholder. A real system would check for specific
        // query types, response sizes, or use a dynamic trust list.
        __u16 dest_port = bpf_ntohs(udph->dest);
        if (dest_port == 53 || dest_port == 123 || dest_port == 1900) { // DNS, NTP, SSDP
            // For demonstration, we'll drop these if source is not whitelisted.
            // A real system would have more sophisticated logic.
            // increment_counter(0); // Increment dropped_packets counter
            // return XDP_DROP;
        }
    }

    // If not dropped by any rule, pass to the kernel
    increment_counter(1); // Increment passed_packets counter
    return XDP_PASS;
}

char _license[] SEC("license") = "GPL";

Để biên dịch chương trình eBPF này, bạn sẽ cần clang và llvm với hỗ trợ backend BPF, cùng với các tiêu đề libbpf.

# Install necessary packages on Debian/Ubuntu
sudo apt update
sudo apt install clang llvm libelf-dev libbpf-dev build-essential

# Compile the BPF program
clang -O2 -target bpf -g -c bpf_ddos_mitigator.c -o bpf_ddos_mitigator.o

Mặt phẳng điều khiển không gian người dùng (Ví dụ Go)

Một agent không gian người dùng chịu trách nhiệm tải chương trình eBPF, quản lý bản đồ và hiển thị các số liệu.

// main.go
package main

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/link"
	"github.com/cilium/ebpf/rlimit"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"net/http"
)

//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall" bpf bpf_ddos_mitigator.c -- -I./headers

// bpf_lpm_trie_key matches the C struct
type bpfLpmTrieKey struct {
	Prefixlen uint32
	IPAddr    uint32 // Network byte order
}

var (
	droppedPackets = prometheus.NewCounter(
		prometheus.CounterOpts{
			Name: "xdp_dropped_packets_total",
			Help: "Total number of packets dropped by XDP DDoS mitigator.",
		},
	)
	passedPackets = prometheus.NewCounter(
		prometheus.CounterOpts{
			Name: "xdp_passed_packets_total",
			Help: "Total number of packets passed by XDP DDoS mitigator.",
		},
	)
)

func init() {
	prometheus.MustRegister(droppedPackets)
	prometheus.MustRegister(passedPackets)
}

func main() {
	if len(os.Args) < 2 {
		log.Fatalf("Usage: %s <interface>", os.Args[0])
	}
	ifaceName := os.Args[1]

	// Allow the current process to lock memory for eBPF maps.
	if err := rlimit.RemoveMemlock(); err != nil {
		log.Fatalf("Failed to remove memlock rlimit: %v", err)
	}

	// Load pre-compiled programs and maps into the kernel.
	objs := bpfObjects{}
	if err := loadBpfObjects(&objs, nil); err != nil {
		log.Fatalf("Loading eBPF objects: %v", err)
	}
	defer objs.Close()

	iface, err := net.InterfaceByName(ifaceName)
	if err != nil {
		log.Fatalf("Getting interface %s: %v", ifaceName, err)
	}

	// Attach the XDP program to the network interface.
	// Use XDP_FLAGS_DRV_MODE for best performance if driver supports it.
	// Fallback to XDP_FLAGS_SKB_MODE if driver mode fails.
	l, err := link.AttachXDP(link.XDPOptions{
		Program:   objs.XdpDdosMitigator,
		Interface: iface,
		Flags:     uint32(link.XDPDriverMode),
	})
	if err != nil {
		log.Printf("Failed to attach XDP in driver mode, trying SKB mode: %v", err)
		l, err = link.AttachXDP(link.XDPOptions{
			Program:   objs.XdpDdosMitigator,
			Interface: iface,
			Flags:     uint32(link.XDPSkbMode),
		})
		if err != nil {
			log.Fatalf("Failed to attach XDP in SKB mode: %v", err)
		}
	}
	defer l.Close()

	log.Printf("Successfully attached XDP program to interface %q (ID: %d)", ifaceName, objs.XdpDdosMitigator.ID())

	// Example: Add a blacklisted IP (e.g., 192.0.2.1)
	// This would typically come from a dynamic threat feed or detection system.
	blacklistIP := net.ParseIP("192.0.2.1").To4()
	if blacklistIP == nil {
		log.Fatalf("Invalid IP address")
	}
	key := bpfLpmTrieKey{
		Prefixlen: 32, // Exact match
		IPAddr:    uint32(blacklistIP[0])<<24 | uint32(blacklistIP[1])<<16 | uint32(blacklistIP[2])<<8 | uint32(blacklistIP[3]),
	}
	value := uint8(1) // Value doesn't matter, just its presence
	if err := objs.BlacklistIps.Put(key, value); err != nil {
		log.Fatalf("Failed to add IP to blacklist: %v", err)
	}
	log.Printf("Added %s to blacklist.", blacklistIP.String())

	// Start Prometheus metrics server
	go func() {
		http.Handle("/metrics", promhttp.Handler())
		log.Fatal(http.ListenAndServe(":9090", nil))
	}()
	log.Println("Prometheus metrics exposed on :9090/metrics")

	// Periodically read and update Prometheus counters
	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()

	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt, syscall.SIGTERM)

	var prevDropped, prevPassed uint64

	for {
		select {
		case <-ticker.C:
			var currentDropped, currentPassed uint64
			var zero uint32 = 0
			var one uint32 = 1

			if err := objs.XdpStatsMap.Lookup(zero, &currentDropped); err != nil {
				log.Printf("Failed to lookup dropped_packets counter: %v", err)
			}
			if err := objs.XdpStatsMap.Lookup(one, &currentPassed); err != nil {
				log.Printf("Failed to lookup passed_packets counter: %v", err)
			}

			// Update Prometheus counters with delta
			droppedPackets.Add(float64(currentDropped - prevDropped))
			passedPackets.Add(float64(currentPassed - prevPassed))

			prevDropped = currentDropped
			prevPassed = currentPassed

			log.Printf("Dropped: %d, Passed: %d", currentDropped, currentPassed)

		case <-stop:
			log.Println("Detaching XDP program...")
			return
		}
	}
}

Để tạo các ràng buộc Go cho chương trình eBPF, hãy chạy:

go generate ./...

Sau đó, biên dịch và chạy chương trình Go:

go build -o xdp-mitigator main.go
sudo ./xdp-mitigator eth0 # Replace eth0 with your network interface

Đánh giá hiệu suất & Đánh đổi

Tính năngXDP (Chế độ Driver)Không gian Kernel (Netfilter/iptables)
Điểm thực thiVòng RX của NIC, trước sk_buffSau khi cấp phát sk_buff, trong ngăn xếp kernel
Hiệu suất10-100 Mpps/lõi (tốc độ đường truyền trên NIC hiện đại)1-5 Mpps/lõi (phụ thuộc nhiều vào độ phức tạp của quy tắc)
Chi phí CPUTối thiểu, không có sk_buff hoặc chuyển đổi ngữ cảnhĐáng kể, cấp phát sk_buff, duyệt ngăn xếp
Sử dụng bộ nhớTối thiểu, không có sk_buff trên mỗi gói tinCao, sk_buff trên mỗi gói tin
Tính linh hoạtCác hàm trợ giúp BPF hạn chế, ngôn ngữ giống CAPI kernel đầy đủ, bộ quy tắc phức tạp
Tính trạng tháiKhông trạng thái hoặc trạng thái hạn chế qua bản đồ BPFCó trạng thái (ví dụ: theo dõi kết nối)
Triển khaiYêu cầu libbpf, kernel 4.8+ (XDP), 5.x+ (đầy đủ)Tính năng kernel tiêu chuẩn, có sẵn rộng rãi
Trường hợp sử dụngDDoS khối lượng lớn, cân bằng tải, đường dẫn nhanhTường lửa chung, NAT, thực thi chính sách phức tạp

Đánh đổi:

  • Độ phức tạp: Các chương trình XDP được viết bằng C và yêu cầu hiểu sâu hơn về các giao thức mạng và nội bộ eBPF. Gỡ lỗi có thể khó khăn.
  • Hỗ trợ Driver: Hiệu suất XDP tối ưu (XDP_DRV_MODE) phụ thuộc vào hỗ trợ driver NIC. Nếu không có nó, XDP_SKB_MODE mang lại một số lợi ích nhưng vẫn liên quan đến cấp phát sk_buff.
  • Ngữ cảnh hạn chế: Các chương trình XDP có một cái nhìn hạn chế về hệ thống. Chúng không thể dễ dàng truy cập thông tin tiến trình hoặc trạng thái kernel phức tạp.

Những vấn đề và cách khắc phục trong sản xuất

  1. XDP_DRV_MODE so với XDP_SKB_MODE:

    • Vấn đề: Cố gắng tải một chương trình XDP trong XDP_DRV_MODE trên một driver NIC không hỗ trợ nó sẽ thất bại hoặc âm thầm quay trở lại XDP_SKB_MODE nếu XDP_FLAGS_UPDATE_IF_NOEXIST được sử dụng. Hiệu suất sẽ bị suy giảm đáng kể.
    • Cách khắc phục: Luôn kiểm tra ethtool -i <interface> cho driver và firmware-version. Tham khảo ip link show dev <interface> cho các cờ xdp. Ưu tiên XDP_DRV_MODE và quay trở lại XDP_SKB_MODE một cách duyên dáng nếu cần, nhưng hãy lưu ý đến các tác động về hiệu suất. Đối với sản xuất, hãy đảm bảo NIC của bạn (ví dụ: Intel ixgbe, i40e, mlx5) có hỗ trợ driver phù hợp.
  2. Lỗi trình xác minh eBPF:

    • Vấn đề: Các chương trình eBPF phức tạp, đặc biệt là những chương trình có vòng lặp hoặc truy cập bộ nhớ rộng rãi, có thể bị trình xác minh eBPF của kernel từ chối. Các lỗi phổ biến bao gồm "chương trình quá lớn," "vòng lặp không có giới hạn," hoặc "truy cập bộ nhớ không hợp lệ."
    • Cách khắc phục: Đơn giản hóa logic eBPF của bạn. Chia nhỏ các tác vụ phức tạp thành các hàm nhỏ hơn, có thể xác minh được. Đảm bảo tất cả các vòng lặp có giới hạn rõ ràng. Sử dụng bpf_printk để gỡ lỗi (có thể xem qua sudo cat /sys/kernel/debug/tracing/trace_pipe). Lệnh bpftool prog load với log_level=2 cung cấp đầu ra trình xác minh chi tiết.
  3. Ghim và duy trì bản đồ:

    • Vấn đề: Nếu agent không gian người dùng của bạn gặp sự cố hoặc khởi động lại, chương trình eBPF và các bản đồ của nó có thể bị dỡ bỏ, dẫn đến gián đoạn dịch vụ.
    • Cách khắc phục: Sử dụng ghim bản đồ BPF (bpf_obj_pin) để duy trì bản đồ trong hệ thống tệp BPF (/sys/fs/bpf). Chương trình XDP sau đó có thể được tải và gắn, tham chiếu các bản đồ đã ghim. Điều này cho phép quản lý vòng đời độc lập của chương trình và trạng thái của nó.
  4. Sắp xếp lại/Mất gói tin với XDP_REDIRECT:

    • Vấn đề: Nếu sử dụng XDP_REDIRECT để gửi gói tin đến một giao diện hoặc CPU khác, hãy đảm bảo đầu nhận có thể xử lý lưu lượng truy cập. Chuyển hướng không chính xác có thể dẫn đến mất gói tin hoặc phân phối không theo thứ tự, đặc biệt là trên các hàng đợi hoặc CPU khác nhau mà không có sự đồng bộ hóa thích hợp.
    • Cách khắc phục: Thiết kế cẩn thận logic chuyển hướng của bạn. Đối với giao tiếp giữa các CPU, sử dụng BPF_MAP_TYPE_CPUMAP. Đối với chuyển hướng đến các giao diện khác, đảm bảo giao diện đích được cấu hình chính xác và có đủ dung lượng.
  5. Giới hạn tài nguyên (memlock):

    • Vấn đề: Tải các chương trình hoặc bản đồ eBPF lớn có thể đạt đến giới hạn tài nguyên memlock, khiến bpf_load_program hoặc bpf_create_map thất bại với "Operation not permitted" hoặc "Cannot allocate memory."
    • Cách khắc phục: Tăng giới hạn memlock cho tiến trình không gian người dùng. Đối với các dịch vụ systemd, sử dụng LimitMEMLOCK=infinity. Đối với thực thi thủ công, sử dụng ulimit -l unlimited trước khi chạy chương trình. Trợ giúp rlimit.RemoveMemlock() của Go giải quyết vấn đề này.
Advertisement

Các câu hỏi thường gặp

  1. Phiên bản kernel nào được yêu cầu cho XDP? XDP được giới thiệu trong kernel Linux 4.8. Để có XDP ổn định và giàu tính năng, nên dùng kernel 4.18+. BPF_MAP_TYPE_LPM_TRIE có sẵn từ 4.11.

  2. Các chương trình XDP có thể kiểm tra tải trọng gói tin ngoài các tiêu đề không? Có, các chương trình XDP có thể kiểm tra toàn bộ tải trọng gói tin, miễn là chúng nằm trong các con trỏ data và data_end. Tuy nhiên, việc phân tích cú pháp các giao thức lớp ứng dụng phức tạp trực tiếp trong eBPF có thể khó khăn do các hạn chế của trình xác minh (ví dụ: vòng lặp có giới hạn, kích thước ngăn xếp). Đối với kiểm tra gói tin sâu, XDP thường hoạt động như một bộ lọc đường dẫn nhanh, chuyển các gói tin thú vị đến một tiến trình không gian người dùng để phân tích thêm.

  3. XDP so sánh với DPDK như thế nào? DPDK (Data Plane Development Kit) là một framework không gian người dùng bỏ qua hoàn toàn kernel bằng cách thăm dò trực tiếp các NIC. Nó cung cấp khả năng kiểm soát và hiệu suất tối ưu nhưng yêu cầu phần cứng chuyên dụng và thường liên quan đến những thay đổi đáng kể ở cấp ứng dụng. Ngược lại, XDP được tích hợp kernel, tận dụng các driver hiện có và mô hình bảo mật của kernel. XDP thường dễ triển khai và tích hợp vào các hệ thống Linux hiện có hơn, cung cấp hiệu suất gần như DPDK cho nhiều trường hợp sử dụng mà không có chi phí bỏ qua kernel hoàn toàn.

  4. Có thể triển khai lọc trạng thái với XDP không? Lọc trạng thái trực tiếp như theo dõi kết nối TCP truyền thống rất khó trong XDP do mô hình thực thi không trạng thái và các ràng buộc của trình xác minh. Tuy nhiên, bạn có thể đạt được trạng thái hạn chế bằng cách sử dụng bản đồ BPF. Ví dụ, một bản đồ BPF_MAP_TYPE_LRU_HASH có thể lưu trữ các bộ kết nối (IP nguồn, IP đích, cổng nguồn, cổng đích) và trạng thái của chúng (ví dụ: SYN_SENT, ESTABLISHED) trong một thời gian ngắn. Điều này yêu cầu quản lý cẩn thận các mục bản đồ (ví dụ: thời gian chờ, thu gom rác) từ không gian người dùng hoặc thông qua các bộ đệm vòng BPF để thông báo sự kiện.

  5. Làm cách nào để kiểm tra chương trình XDP của tôi mà không ảnh hưởng đến lưu lượng truy cập sản xuất? Kiểm tra các chương trình XDP một cách an toàn là rất quan trọng.

    • Máy ảo/Container: Sử dụng môi trường ảo (ví dụ: KVM, Docker với các cặp veth) để mô phỏng lưu lượng mạng và kiểm tra các chương trình XDP mà không ảnh hưởng đến phần cứng vật lý.
    • XDP_SKB_MODE: Mặc dù chậm hơn, XDP_SKB_MODE thường mạnh mẽ hơn trên các driver khác nhau và có thể là một môi trường thử nghiệm ban đầu an toàn hơn.
    • ip link set dev <interface> xdp obj <program.o> section xdp verbose: Lệnh này cho phép tải các chương trình XDP. Sử dụng verbose để nhận thông báo lỗi chi tiết.
    • bpftool: Tiện ích bpftool rất có giá trị để kiểm tra các chương trình, bản đồ đã tải và trạng thái của chúng. bpftool prog show và bpftool map show là rất cần thiết.
    • Bộ tạo lưu lượng: Sử dụng các công cụ như pktgen, hping3 hoặc scapy để tạo các mẫu lưu lượng cụ thể (ví dụ: SYN floods) để kiểm tra logic giảm thiểu của bạn.
Share this article:

Stay Updated

Get the latest posts delivered straight to your inbox.

Free Developer Utilities

Free In-Browser Developer Tools

Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.

Explore Tools
Advertisement