15、XDP 编程:从加载、测试到应用

XDP 编程:从加载、测试到应用

1. 验证 XDP 程序加载效果

在加载 XDP 程序后,需要验证其是否按预期工作。可以通过在外部机器上再次执行 nmap 命令来观察端口 8000 是否不再可达:

# nmap -sS 192.168.33.11
Starting Nmap 7.70 ( https://nmap.org ) at 2019-04-07 01:07 CEST
Nmap scan report for 192.168.33.11
Host is up (0.00039s latency).
Not shown: 998 closed ports
PORT    STATE SERVICE
22/tcp  open  ssh

另外,还可以尝试通过浏览器访问该程序或进行任何 HTTP 请求。当以 192.168.33.11 为目标时,任何类型的测试都应该失败。

如果需要将机器恢复到原始状态,可以分离程序并关闭设备的 XDP:

# ip link set dev enp0s8 xdp off

2. 使用 BCC 加载 XDP 程序

2.1 内核空间程序 program.c

首先创建一个内核空间程序 program.c ,除了导入 BPF 和协议相关的结构体和函数定义所需的头文件外,还使用 BPF_TABLE 宏声明一个 BPF_MAP_TYPE_PERCPU_ARRAY 类型的映射,用于存储每个 IP 协议索引的数据包计数器:

#define KBUILD_MODNAME "program"
#include <linux/bpf.h>
#include <linux/in.h>
#include <linux/ip.h>
BPF_TABLE("percpu_array", uint32_t, long, packetcnt, 256);

int myprogram(struct xdp_md *ctx) {
  int ipsize = 0;
  void *data = (void *)(long)ctx->data;
  void *data_end = (void *)(long)ctx->data_end;
  struct ethhdr *eth = data;
  struct iphdr *ip;
  long *cnt;
  __u32 idx;
  ipsize = sizeof(*eth);
  ip = data + ipsize;
  ipsize += sizeof(struct iphdr);

  if (data + ipsize > data_end) {
    return XDP_DROP;
  }
  idx = ip->protocol;
  cnt = packetcnt.lookup(&idx);
  if (cnt) {
    *cnt += 1;
  }
  if (ip->protocol == IPPROTO_TCP) {
    return XDP_DROP;
  }
  return XDP_PASS;
}

2.2 用户空间加载器 loader.py

加载器 loader.py 由实际的加载逻辑和打印数据包计数的循环两部分组成:

#!/usr/bin/python
from bcc import BPF
import time
import sys

device = "enp0s8"
b = BPF(src_file="program.c")
fn = b.load_func("myprogram", BPF.XDP)
b.attach_xdp(device, fn, 0)
packetcnt = b.get_table("packetcnt")

prev = [0] * 256
print("Printing packet counts per IP protocol-number, hit CTRL+C to stop")
while 1:
    try:
        for k in packetcnt.keys():
            val = packetcnt.sum(k).value
            i = k.value
            if val:
                delta = val - prev[i]
                prev[i] = val
                print("{}: {} pkt/s".format(i, delta))
        time.sleep(1)
    except KeyboardInterrupt:
        print("Removing filter from device")
        break
b.remove_xdp(device, 0)

使用 root 权限执行加载器来测试程序:

# python program.py

输出结果会每秒显示一行数据包计数器:

Printing packet counts per IP protocol-number, hit CTRL+C to stop
6: 10 pkt/s
17: 3 pkt/s
^CRemoving filter from device

3. 测试 XDP 程序

3.1 测试环境的挑战

在测试 XDP 程序时,最大的困难在于需要重现一个所有组件都能提供正确数据包的环境。虽然虚拟化技术使创建工作环境变得容易,但复杂的设置可能会限制测试环境的可重复性和可编程性。此外,在虚拟化环境中分析高频 XDP 程序的性能时,虚拟化成本会使测试变得无效。

3.2 BPF_PROG_TEST_RUN 命令

内核开发者实现了 BPF_PROG_TEST_RUN 命令来测试 XDP 程序。该命令可以执行 XDP 程序,并传入输入数据包和输出数据包。程序执行后,输出数据包变量会被填充,同时返回 XDP 代码。

3.3 使用 Python 单元测试框架进行 XDP 测试

以下是使用 Python 单元测试框架进行 XDP 测试的代码:

from bcc import BPF, libbcc
from scapy.all import Ether, IP, raw, TCP, UDP
import ctypes
import unittest

class XDPExampleTestCase(unittest.TestCase):
    SKB_OUT_SIZE = 1514  # mtu 1500 + 14 ethernet size
    bpf_function = None

    def _xdp_test_run(self, given_packet, expected_packet, expected_return):
        size = len(given_packet)
        given_packet = ctypes.create_string_buffer(raw(given_packet), size)
        packet_output = ctypes.create_string_buffer(self.SKB_OUT_SIZE)
        packet_output_size = ctypes.c_uint32()
        test_retval = ctypes.c_uint32()
        duration = ctypes.c_uint32()
        repeat = 1
        ret = libbcc.lib.bpf_prog_test_run(self.bpf_function.fd,
                                           repeat,
                                           ctypes.byref(given_packet),
                                           size,
                                           ctypes.byref(packet_output),
                                           ctypes.byref(packet_output_size),
                                           ctypes.byref(test_retval),
                                           ctypes.byref(duration))
        self.assertEqual(ret, 0)
        self.assertEqual(test_retval.value, expected_return)
        if expected_packet:
            self.assertEqual(
                packet_output[:packet_output_size.value], raw(expected_packet))

    def setUp(self):
        bpf_prog = BPF(src_file=b"program.c")
        self.bpf_function = bpf_prog.load_func(b"myprogram", BPF.XDP)

    def test_drop_tcp(self):
        given_packet = Ether() / IP() / TCP()
        self._xdp_test_run(given_packet, None, BPF.XDP_DROP)

    def test_pass_udp(self):
        given_packet = Ether() / IP() / UDP()
        expected_packet = Ether() / IP() / UDP()
        self._xdp_test_run(given_packet, expected_packet, BPF.XDP_PASS)

    def test_transform_dst(self):
        given_packet = Ether() / IP() / TCP(dport=9090)
        expected_packet = Ether(dst='08:00:27:dd:38:2a') / \
            IP() / TCP(dport=9090)
        self._xdp_test_run(given_packet, expected_packet, BPF.XDP_TX)

if __name__ == '__main__':
    unittest.main()

3.4 测试用例说明

  • test_drop_tcp :测试是否会丢弃所有 TCP 数据包。
  • test_pass_udp :测试是否允许所有 UDP 数据包通过,并且数据包不会被修改。
  • test_transform_dst :测试当 TCP 数据包的目标端口为 9090 时,是否会更改其目标 MAC 地址并返回 XDP_TX

3.5 运行测试

使用以下命令运行测试:

sudo python test_xdp.py

输出结果会报告测试是否通过:

...
--------------------------------
Ran 3 tests in 4.676s
OK

如果修改 program.c 中的代码,例如将最后一个 XDP_PASS 改为 XDP_DROP ,再次运行测试,测试将会失败:

.F.
======================================================================
FAIL: test_pass_udp (__main__.XDPExampleTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_xdp.py", line 48, in test_pass_udp 
    self._xdp_test_run(given_packet, expected_packet, BPF.XDP_PASS)
  File "test_xdp.py", line 31, in _xdp_test_run
    self.assertEqual(test_retval.value, expected_return)
AssertionError: 1 != 2
----------------------------------------------------------------------
Ran 3 tests in 4.667s
FAILED (failures=1)

4. XDP 的应用场景

4.1 网络监控

传统的网络监控系统要么通过编写内核模块,要么从用户空间访问 proc 文件来实现。但这些方法存在维护和调试困难、计算成本高等问题。而 XDP 可以使用 XDP 程序将需要提取的数据发送到映射中,然后由加载器将指标存储到存储后端,并应用算法或绘制结果图。

4.2 DDoS 缓解

在 NIC 级别就能看到数据包,确保在系统尚未花费足够的计算资源来判断数据包是否有用之前,就拦截任何可能的数据包。在典型场景中, bpf 映射可以指示 XDP 程序丢弃来自特定源的数据包,从而使攻击者难以浪费系统的计算资源。

4.3 负载均衡

XDP 程序可以用于负载均衡,但 XDP 只能在数据包到达的同一 NIC 上重新传输数据包。

以下是 XDP 应用场景的总结表格:
| 应用场景 | 优势 |
| ---- | ---- |
| 网络监控 | 避免内核模块的维护和调试困难,减少计算成本 |
| DDoS 缓解 | 在早期拦截数据包,节省计算资源 |
| 负载均衡 | 可在 NIC 级别进行数据包处理 |

通过以上内容,我们了解了 XDP 程序的加载、测试和应用。希望这些信息能帮助你更好地使用 XDP 进行网络编程。

5. 网络监控的具体实现流程

5.1 编写 XDP 程序

首先,我们需要编写一个 XDP 程序来收集网络数据包的相关信息。以下是一个简单的示例:

#define KBUILD_MODNAME "monitor"
#include <linux/bpf.h>
#include <linux/in.h>
#include <linux/ip.h>
BPF_TABLE("percpu_array", uint32_t, long, packet_cnt, 256);

int monitor(struct xdp_md *ctx) {
    int ipsize = 0;
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;
    struct ethhdr *eth = data;
    struct iphdr *ip;
    long *cnt;
    __u32 idx;

    ipsize = sizeof(*eth);
    ip = data + ipsize;
    ipsize += sizeof(struct iphdr);

    if (data + ipsize > data_end) {
        return XDP_PASS;
    }

    idx = ip->protocol;
    cnt = packet_cnt.lookup(&idx);
    if (cnt) {
        *cnt += 1;
    }

    return XDP_PASS;
}

这个程序会统计不同 IP 协议的数据包数量,并将结果存储在 packet_cnt 映射中。

5.2 编写用户空间加载器

接下来,我们需要编写一个用户空间加载器来加载 XDP 程序,并定期读取 packet_cnt 映射中的数据。以下是一个示例:

#!/usr/bin/python
from bcc import BPF
import time

device = "enp0s8"
b = BPF(src_file="monitor.c")
fn = b.load_func("monitor", BPF.XDP)
b.attach_xdp(device, fn, 0)
packet_cnt = b.get_table("packet_cnt")

prev = [0] * 256
print("Printing packet counts per IP protocol-number, hit CTRL+C to stop")
while 1:
    try:
        for k in packet_cnt.keys():
            val = packet_cnt.sum(k).value
            i = k.value
            if val:
                delta = val - prev[i]
                prev[i] = val
                print("{}: {} pkt/s".format(i, delta))
        time.sleep(1)
    except KeyboardInterrupt:
        print("Removing filter from device")
        break
b.remove_xdp(device, 0)

5.3 运行监控程序

使用 root 权限运行加载器:

# python monitor_loader.py

这样就可以实时监控不同 IP 协议的数据包流量。

5.4 监控流程的 mermaid 流程图

graph LR
    A[开始] --> B[编写 XDP 程序]
    B --> C[编写用户空间加载器];
    C --> D[使用 root 权限运行加载器];
    D --> E[实时监控数据包流量];
    E --> F{是否停止};
    F -- 否 --> E;
    F -- 是 --> G[移除 XDP 程序];
    G --> H[结束];

6. DDoS 缓解的详细实现

6.1 构建黑名单映射

在用户空间,我们需要构建一个黑名单映射,用于存储需要拦截的源 IP 地址。以下是一个简单的示例:

from bcc import BPF
import ctypes

device = "enp0s8"
b = BPF(src_file="ddos_mitigation.c")
fn = b.load_func("ddos_mitigation", BPF.XDP)
b.attach_xdp(device, fn, 0)
blacklist = b.get_table("blacklist")

# 添加需要拦截的源 IP 地址
ip = ctypes.c_uint32(0xc0a82101)  # 192.168.33.1
blacklist[ip] = ctypes.c_uint32(1)

6.2 编写 XDP 程序

以下是一个根据黑名单拦截数据包的 XDP 程序:

#define KBUILD_MODNAME "ddos_mitigation"
#include <linux/bpf.h>
#include <linux/in.h>
#include <linux/ip.h>
BPF_TABLE("hash", uint32_t, uint32_t, blacklist, 1024);

int ddos_mitigation(struct xdp_md *ctx) {
    int ipsize = 0;
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;
    struct ethhdr *eth = data;
    struct iphdr *ip;

    ipsize = sizeof(*eth);
    ip = data + ipsize;
    ipsize += sizeof(struct iphdr);

    if (data + ipsize > data_end) {
        return XDP_PASS;
    }

    uint32_t src_ip = ip->saddr;
    if (blacklist.lookup(&src_ip)) {
        return XDP_DROP;
    }

    return XDP_PASS;
}

6.3 DDoS 缓解流程的 mermaid 流程图

graph LR
    A[开始] --> B[构建黑名单映射];
    B --> C[编写 XDP 程序];
    C --> D[加载 XDP 程序到网卡];
    D --> E[数据包到达网卡];
    E --> F{源 IP 是否在黑名单中};
    F -- 是 --> G[丢弃数据包];
    F -- 否 --> H[放行数据包];
    G --> I[结束];
    H --> I;

7. 负载均衡的实现思路

7.1 负载均衡的基本原理

XDP 负载均衡的基本原理是根据一定的规则将数据包分发到不同的后端服务器。由于 XDP 只能在同一 NIC 上重新传输数据包,我们可以通过修改数据包的目标 IP 地址来实现负载均衡。

7.2 编写 XDP 程序

以下是一个简单的负载均衡 XDP 程序示例:

#define KBUILD_MODNAME "load_balancer"
#include <linux/bpf.h>
#include <linux/in.h>
#include <linux/ip.h>
BPF_TABLE("array", uint32_t, uint32_t, backend_ips, 2);

int load_balancer(struct xdp_md *ctx) {
    int ipsize = 0;
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;
    struct ethhdr *eth = data;
    struct iphdr *ip;

    ipsize = sizeof(*eth);
    ip = data + ipsize;
    ipsize += sizeof(struct iphdr);

    if (data + ipsize > data_end) {
        return XDP_PASS;
    }

    // 简单的轮询算法
    static uint32_t index = 0;
    uint32_t backend_ip = backend_ips.lookup(&index)->value;
    ip->daddr = backend_ip;
    index = (index + 1) % 2;

    return XDP_TX;
}

7.3 负载均衡流程的 mermaid 流程图

graph LR
    A[开始] --> B[编写 XDP 程序];
    B --> C[设置后端服务器 IP 地址];
    C --> D[加载 XDP 程序到网卡];
    D --> E[数据包到达网卡];
    E --> F[使用轮询算法选择后端服务器];
    F --> G[修改数据包目标 IP 地址];
    G --> H[重新传输数据包];
    H --> I[结束];

8. 总结与展望

通过以上详细的介绍,我们深入了解了 XDP 程序的加载、测试、应用场景以及具体的实现流程。XDP 在网络监控、DDoS 缓解和负载均衡等方面展现出了巨大的优势,能够帮助我们更高效地处理网络数据包。

在未来,随着网络技术的不断发展,XDP 有望在更多的领域得到应用,例如软件定义网络(SDN)、网络功能虚拟化(NFV)等。同时,我们也可以进一步优化 XDP 程序的性能,提高其处理数据包的效率。

希望本文能够为你在使用 XDP 进行网络编程方面提供有价值的参考。如果你有任何问题或建议,欢迎在评论区留言讨论。

内容概要:本文介绍了ENVI Deep Learning V1.0的操作教程,重点讲解了如何利用ENVI软件进行深度学习模型的训练与应用,以实现遥感图像中特定目标(如集装箱)的自动提取。教程涵盖了从数据准备、标签图像创建、模型初始化与训练,到执行分类及结果优化的完整流程,并介绍了精度评价与通过ENVI Modeler实现一键化建模的方法。系统基于TensorFlow框架,采用ENVINet5(U-Net变体)架构,支持通过点、线、面ROI或分类图生成标签数据,适用于多/高光谱影像的单一类别特征提取。; 适合人群:具备遥感图像处理基础,熟悉ENVI软件操作,从事地理信息、测绘、环境监测等相关领域的技术人员或研究人员,尤其是希望将深度学习技术应用于遥感目标识别的初学者与实践者。; 使用场景及目标:①在遥感影像中自动识别和提取特定地物目标(如车辆、建筑、道路、集装箱等);②掌握ENVI环境下深度学习模型的训练流程与关键参数设置(如Patch Size、Epochs、Class Weight等);③通过模型调优与结果反馈提升分类精度,实现高效自动化信息提取。; 阅读建议:建议结合实际遥感项目边学边练,重点关注标签数据制作、模型参数配置与结果后处理环节,充分利用ENVI Modeler进行自动化建模与参数优化,同时注意软硬件环境(特别是NVIDIA GPU)的配置要求以保障训练效率。
内容概要:本文系统阐述了企业新闻发稿在生成式引擎优化(GEO)时代下的全渠道策略与效果评估体系,涵盖当前企业传播面临的预算、资源、内容与效果评估四大挑战,并深入分析2025年新闻发稿行业五大趋势,包括AI驱动的智能化转型、精准化传播、首发内容价值提升、内容资产化及数据可视化。文章重点解析央媒、地方官媒、综合门户和自媒体四类媒体资源的特性、传播优势与发稿策略,提出基于内容适配性、时间节奏、话题设计的策略制定方法,并构建涵盖品牌价值、销售转化与GEO优化的多维评估框架。此外,结合“传声港”工具实操指南,提供AI智能投放、效果监测、自媒体管理与舆情应对的全流程解决方案,并针对科技、消费、B2B、区域品牌四大行业推出定制化发稿方案。; 适合人群:企业市场/公关负责人、品牌传播管理者、数字营销从业者及中小企业决策者,具备一定媒体传播经验并希望提升发稿效率与ROI的专业人士。; 使用场景及目标:①制定科学的新闻发稿策略,实现从“流量思维”向“价值思维”转型;②构建央媒定调、门户扩散、自媒体互动的立体化传播矩阵;③利用AI工具实现精准投放与GEO优化,提升品牌在AI搜索中的权威性与可见性;④通过数据驱动评估体系量化品牌影响力与销售转化效果。; 阅读建议:建议结合文中提供的实操清单、案例分析与工具指南进行系统学习,重点关注媒体适配性策略与GEO评估指标,在实际发稿中分阶段试点“AI+全渠道”组合策略,并定期复盘优化,以实现品牌传播的长期复利效应。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值