0%

Polycube

Polycube是一个基于 eBPF 和 XDP 技术的开源软件框架,用于在Linux内核中构建快速、灵活的网络功能。

介绍

Polycube 是一个基于 Linux 的开源软件框架,提供快速且轻量级的网络功能,比如网桥(bridge)、路由器(router)、网络地址转换(NAT)、负载均衡器(load balancer)、防火墙(firewall)、DDoS 缓解器等。

  • 适用范围:命名空间(namespaces)、容器(containers)、虚拟机和物理主机。

  • 核心:Cube 网络功能模块,基于 Linux 内核最新的 BPF (Berkeley Packet Filter) 和 XDP (eXpress Data Path) 技术

架构:

  • 交互方式:
    • polycubectl,命令行工具,用于人工交互
    • polycubed,REST API 守护进程,接收 polycubectl 或其他外部指令,用于自动交互
  • 架构
    • 应用层:应用程序,如 pcn-k8s, pcn-iptables,通过调用 polycubed 或 CLI 来驱动底层 cubes
    • 控制与管理层:polycubectl/polycubed,人工/自动控制工具
    • 功能模块层:各种可用的 “Cubes” (具体的网络功能模块)
    • 底层:基于 BPF/XDP 的 Linux 内核支撑

Polycube architecture brief

eBPF

eBPF 程序可以加载到指定的内核代码路径(如 tc、XDP 等)中,当执行该代码路径时,则会执行对应的 eBPF 程序。

PolyCube 主要利用的 XDP 和 tc 挂载 eBPF 程序

XDP (eXpress Data Path) BPF

  • 位置: 挂载在网卡驱动层,是数据包进入的最早阶段
  • 能力: 性能极高,主要用于高速过滤、DDoS 防护和简单的负载均衡
  • 方向: 作用于 Ingress 方向
  • 返回值: 程序可以返回 XDP_DROP (丢弃)、XDP_PASS (放行到协议栈)、XDP_TX (原路返回)、XDP_REDIRECT (重定向到其他接口)

TC (Traffic Control) BPF

  • 位置: 挂载在内核网络协议栈的 TC 层,位于网络驱动层
  • 能力: 可以访问更丰富的数据包元数据,适合进行复杂的流量控制、分类、整形(QoS)、策略执行等
  • 方向: 同时支持 IngressEgress 两个方向
  • 返回值: 程序可以返回 TC_ACT_OK (放行)、TC_ACT_SHOT (丢弃)、TC_ACT_REDIRECT (重定向) 等动作

开发

以 pcn-iptables 为例

函数注册

  1. CMake 构建时,将 datapaths 下的代码内容加载成变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # takes 'Router_dp.c' and make it available in 'Router_dp.h'
    # as a variable called 'router_code'
    load_file_as_variable(pcn-iptables datapaths/Iptables_ActionLookup_dp.c iptables_code_actionlookup)
    load_file_as_variable(pcn-iptables datapaths/Iptables_BitScan_dp.c iptables_code_bitscan)
    load_file_as_variable(pcn-iptables datapaths/Iptables_ChainForwarder_dp.c iptables_code_chainforwarder)
    load_file_as_variable(pcn-iptables datapaths/Iptables_ChainSelector_dp.c iptables_code_chainselector)
    load_file_as_variable(pcn-iptables datapaths/Iptables_ConntrackLabel_dp.c iptables_code_conntracklabel)
    load_file_as_variable(pcn-iptables datapaths/Iptables_ConntrackMatch_dp.c iptables_code_conntrackmatch)
    load_file_as_variable(pcn-iptables datapaths/Iptables_ConntrackTableUpdate_dp.c iptables_code_conntracktableupdate)
    load_file_as_variable(pcn-iptables datapaths/Iptables_Horus_dp.c iptables_code_horus)
    load_file_as_variable(pcn-iptables datapaths/Iptables_IpLookup_dp.c iptables_code_iplookup)
    load_file_as_variable(pcn-iptables datapaths/Iptables_L4PortLookup_dp.c iptables_code_l4portlookup)
    load_file_as_variable(pcn-iptables datapaths/Iptables_InterfaceLookup_dp.c iptables_code_interfacelookup)
    load_file_as_variable(pcn-iptables datapaths/Iptables_L4ProtocolLookup_dp.c iptables_code_l4protolookup)
    load_file_as_variable(pcn-iptables datapaths/Iptables_Parser_dp.c iptables_code_parser)
    load_file_as_variable(pcn-iptables datapaths/Iptables_TcpFlagsLookup_dp.c iptables_code_tcpflagslookup)

pcn-iptables

Ingress 数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Packet in (TC/XDP ingress)

┌────────────────────┐
│ Parser_ingress │
│ Iptables_Parser_dp │
└─────────┬──────────┘

_HORUS_ENABLED?
├── YES ─────┐
↓ ↓
┌──────────────┐ ┌──────────────────┐
│ Horus_dp │ │ ChainSelector_dp │
└───────┬──────┘ └───────┬──────────┘
↓ ↓
┌────────────────────────────────────┐
│ ConntrackLabel_dp (ingress) │
└──────────────┬─────────────────────┘

┌───────────────────────────┐
│ ChainForwarder_dp │
└──────────────┬────────────┘

_NEXT_HOP_INPUT_1 / _NEXT_HOP_FORWARD_1

┌────────────────────────────────────┐
│ Rule chain subprograms │
│ BitScan → IpLookup → InterfaceLookup │
│ → ConntrackMatch → L4ProtocolLookup │
│ → L4PortLookup → TcpFlagsLookup │
│ → ActionLookup │
└──────────────┬─────────────────────┘

_CONNTRACKTABLEUPDATE_INGRESS

RX_OK / RX_DROP

1. 整体架构

1.1 系统分层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌─────────────────────────────────────────────────┐
│ 用户态 (Userspace API) │
│ REST API / CLI 接口 │
│ ├─ 添加/删除规则 │
│ ├─ 配置链(INPUT/FORWARD/OUTPUT) │
│ ├─ 查询统计信息 │
│ └─ 启用/禁用功能(Horus, Conntrack) │
└────────────────┬────────────────────────────────┘

┌───────┴────────┐
↓ ↓
Control Plane Data Plane
(Iptables.cpp) (BPF Programs)
│ │
┌────┴─────────┐ ┌───┴──────────────────┐
│ Chain │ │ Kernel (eBPF) │
│ Manager │ │ Parser/Lookup/... │
│ │ │ AttackPoint Hook │
│ Rule │ │ (TC/XDP) │
│ Compiler │ │ │
│ │ │ BPF Tables (Maps) │
│ BPF Code │ │ per_cpu_array │
│ Generation │ │ hash / lpm_trie │
└──────────────┘ └────────────────────────┘

1.2 关键类结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Iptables (主容器)
├─ 3 个 Chain 对象
│ ├─ INPUT
│ ├─ FORWARD
│ └─ OUTPUT

├─ 每个 Chain 包含 N 个 ChainRule 对象
│ ├─ Rule 0: src=10.0.0.1, dst=10.0.0.2, action=DROP
│ ├─ Rule 1: src=10.0.0.3, dst=*, action=ACCEPT
│ └─ Rule N: ...

└─ SessionTable (连接追踪表)
├─ (src, dst, proto, sport, dport) → 连接状态
└─ 状态:NEW / ESTABLISHED / RELATED / INVALID

2. 规则结构

2.1 ChainRule 的匹配维度

每条规则可以指定以下匹配条件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct ChainRule {
// Layer 3 (IP)
std::string src; // 源 IP (支持 CIDR)
std::string dst; // 目标 IP (支持 CIDR)

// Layer 4 (L4)
std::string l4proto; // 协议 (TCP/UDP/ICMP/...)
uint16_t sport; // 源端口
uint16_t dport; // 目标端口

// TCP 特定
std::string tcpflags; // TCP flags (SYN/ACK/FIN/...)

// 接口
std::string inIface; // 入站接口 (仅 INPUT/FORWARD)
std::string outIface; // 出站接口 (仅 FORWARD/OUTPUT)

// 连接追踪
ConntrackstatusEnum conntrack; // NEW/ESTABLISHED/RELATED/INVALID

// 动作
ActionEnum action; // DROP 或 ACCEPT
};

2.2 规则匹配过程

示例规则链:

1
2
3
4
5
6
Rule ID | Src IP      | Dst IP      | Proto | DPort | Action
--------|-------------|-------------|-------|-------|--------
0 | 10.0.0.0/24 | 192.168.0.1 | TCP | 443 | DROP
1 | * | 10.0.0.1 | ICMP | * | ACCEPT
2 | 172.16.0.0/16 | * | UDP | 53 | ACCEPT
3 | * | * | * | * | DEFAULT

匹配流程示例(数据包 10.0.0.5 → 192.168.0.1:443 TCP):

1
2
3
4
5
6
7
8
9
10
┌─ BitScan 初始化:bits = 0x111...1 (所有规则)

├─ Rule 0 匹配:src ✓, dst ✓, proto ✓, dport ✓ → 规则 0 "中奖"
│ bits 更新为:0 号位保留,其他置 0

├─ BitScan 扫描找到最低的 1 位 → 规则 0

├─ ActionLookup → 查询规则 0 的动作 = DROP

└─ 返回 DROP(数据包被丢弃)

3. 控制平面工作原理

3.1 规则编译流程

用户通过 REST API 添加规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
POST /iptables/chain/INPUT/rule
{
"id": 0,
"src": "10.0.0.0/24",
"dst": "192.168.0.1",
"l4proto": "tcp",
"dport": 443,
"action": "DROP"
}



Chain::addRule()

ChainRule 对象创建
├─ 解析 CIDR (10.0.0.0/24)
├─ 存储匹配条件
└─ 触发 Chain::updateChain()

Chain::updateChain()
├─ 获取所有规则
├─ 生成 BPF 规则代码
├─ 编译成 BitScan / IpLookup / ... 的 BPF 代码
└─ 调用 Parser::reload() / ChainSelector::reload() 等

Program::reload()
├─ 调用 iptables_.reload(code, index)
└─ 加载新的 eBPF 程序到内核

3.2 规则代码生成示例

规则 0:src=10.0.0.0/24, dst=192.168.0.1, proto=TCP, dport=443, action=DROP

控制平面生成对应的 BPF 匹配代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 在 IpLookup_dp 中生成
struct lpm_k key = {
.netmask_len = 24,
.ip = htonl(0x0A000000) // 10.0.0.0
};
struct elements *result = ip_srcTrie_INPUT.lookup(&key);
if (result != NULL) {
// 源 IP 匹配
__be32 dst_check = htonl(0xC0A80001); // 192.168.0.1
if (pkt->dstIp == dst_check) {
// 目标 IP 也匹配
if (pkt->l4proto == IPPROTO_TCP && pkt->dport == htons(443)) {
// 所有条件匹配 → 规则 0 中奖
(result->bits)[0] |= (1ULL << 0);
}
}
}

3.3 Interactive vs Batch Mode

Interactive Mode(交互模式,默认):

1
添加规则 → 立即编译 → 立即加载到内核 → 规则生效

Batch Mode(批处理模式):

1
2
3
4
添加规则 1 → 内存更新
添加规则 2 → 内存更新
...
调用 applyRules() → 编译所有规则 → 加载到内核


4. 数据平面工作原理

4.1 数据包入站处理(Ingress)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
网络包进入 NIC

TC/XDP Hook 触发(attach 点)
↓ [1] Parser
├─ 解析以太网/IP/L4 头
├─ 填充 packetHeaders 结构
└─ 调用 _CHAINSELECTOR 或 _HORUS(如果启用)

↓ [2] ChainSelector (如果没启用 Horus)
├─ 查看包的目标 IP
├─ 检查是否为本机地址?
│ ├─ YES → goto INPUT chain
│ └─ NO → goto FORWARD chain
└─ 初始化 sharedEle (规则位向量)

↓ [3] ConntrackLabel
├─ 查询连接追踪表
├─ 确定连接状态 (NEW/ESTABLISHED/...)
└─ 调用规则链处理

↓ [4] 规则链处理 (_NEXT_HOP_INPUT_1 或 _NEXT_HOP_FORWARD_1)
├─ BitScan → 找最低匹配规则
├─ IpLookup → 检查 src/dst IP
├─ InterfaceLookup → 检查入站接口
├─ ConntrackMatch → 检查连接状态
├─ L4ProtocolLookup → 检查 L4 协议
├─ L4PortLookup → 检查源/目标端口
├─ TcpFlagsLookup → 检查 TCP flags
└─ ActionLookup → 查询动作 (DROP/ACCEPT)

↓ [5] ChainForwarder
├─ 根据 forwardingDecision
│ ├─ INPUT_LABELING → return RX_OK 或 RX_DROP
│ └─ FORWARD_LABELING → return RX_OK 或 RX_DROP
└─ 如果是 ACCEPT,调用 ConntrackTableUpdate

↓ [6] ConntrackTableUpdate
├─ 如果是 ACCEPT,更新连接追踪表
├─ 记录连接信息 (src/dst/sport/dport/proto)
├─ 设置 TTL 和状态
└─ 返回 RX_OK


数据包发往应用或转发

4.2 数据包出站处理(Egress)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
应用发送包 / 转发包出去

TC/XDP Hook 触发(egress 点)
↓ [1] Parser (egress)
├─ 解析包头
├─ 填充 packetHeaders
└─ 调用 _CHAINSELECTOR

↓ [2] ChainSelector (egress)
├─ 查看包的源 IP
├─ 检查是否为本机地址?
│ ├─ YES → goto OUTPUT chain
│ └─ NO → goto PASS (直接转发)
└─ 初始化 sharedEle

↓ [3] 规则链处理 (如果是 OUTPUT 链)
└─ 同 INPUT/FORWARD 链


数据包送往网络

4.3 关键数据结构(Kernel-Side)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 规则位向量
struct elements {
uint64_t bits[_MAXRULES]; // 每 64 条规则一个 uint64_t
};

// 解析的包头
struct packetHeaders {
uint32_t srcIp; // 源 IP
uint32_t dstIp; // 目标 IP
uint8_t l4proto; // L4 协议
uint16_t srcPort; // 源端口
uint16_t dstPort; // 目标端口
uint8_t flags; // TCP flags
uint32_t seqN; // TCP seq (仅用于连接追踪)
uint32_t ackN; // TCP ack (仅用于连接追踪)
uint8_t connStatus; // 连接状态
};

// 连接追踪表键
struct ct_k {
uint32_t srcIp;
uint32_t dstIp;
uint8_t l4proto;
uint16_t srcPort;
uint16_t dstPort;
};

// 连接追踪表值
struct ct_v {
uint64_t ttl; // 连接超时时间
uint8_t state; // NEW / ESTABLISHED / ...
uint8_t ipRev; // IP 反向映射标志(用于 NAT)
uint8_t portRev; // 端口反向映射标志(用于 NAT)
uint32_t sequence; // TCP 序列号
};

4.4 BPF Maps 映射表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
BPF_TABLE("extern", int, struct elements, sharedEle, 1)

└─ 当前包的匹配位向量(与 control plane 同步)

BPF_TABLE("extern", int, struct packetHeaders, packet, 1)

└─ 已解析的包头(由 Parser 填充)

BPF_TABLE("extern", int, int, forwardingDecision, 1)

└─ 转发决策(INPUT_LABELING / FORWARD_LABELING / ...)

BPF_TABLE("lpm_trie", struct lpm_k, struct elements,
ip_srcTrie_INPUT, 1024, BPF_F_NO_PREALLOC)

└─ INPUT 链源 IP 前缀树(支持 CIDR 匹配)

BPF_TABLE("lpm_trie", struct lpm_k, struct elements,
ip_dstTrie_INPUT, 1024, BPF_F_NO_PREALLOC)

└─ INPUT 链目标 IP 前缀树

BPF_TABLE("hash", struct ct_k, struct ct_v, connections, 65536)

└─ 连接追踪表(最多 65536 个连接)

BPF_TABLE("percpu_array", int, u64, pkts_INPUT_default, 1)

└─ INPUT 链默认动作计数(包数量)

BPF_TABLE("percpu_array", int, u64, bytes_INPUT_default, 1)

└─ INPUT 链默认动作计数(字节数)

BPF_TABLE("percpu_array", int, u64, pkts_action_0, 8000)

└─ 规则 0 的包计数(支持最多 8000 条规则)

BPF_TABLE("percpu_array", int, u64, bytes_action_0, 8000)

└─ 规则 0 的字节计数

5. 关键机制

5.1 BitScan 规则扫描

目的: 从位向量中高效找到最低的 1 位

代码:

1
2
3
4
5
6
7
uint64_t bits = (ele->bits)[0];
if (bits != 0) {
// De Bruijn 序列技巧:快速找到最低位的位置
int index = (int)(((bits ^ (bits - 1)) * 0x03f79d71b4cb0a89) >> 58);
matchingResult = index64.lookup(&index);
// index 现在是规则 ID(0-63)
}

例子:

1
2
3
4
bits = 0b00010101 = 0x15
bits - 1 = 0b00010100 = 0x14
bits ^ (bits - 1) = 0b00000001 = 0x01
最低位位置 = 0(第 0 条规则)

5.2 LPM Trie 前缀匹配

用途: 支持 CIDR 表示的 IP 范围匹配

例子:

1
2
3
4
5
6
7
8
9
10
11
规则中的 src = "10.0.0.0/24"

在 BPF 中构建 LPM Trie:
key.netmask_len = 24
key.ip = 10.0.0.0
value = 规则匹配位向量

查询时:
查询包的 srcIp = 10.0.0.100
→ Trie 会自动匹配 10.0.0.0/24
→ 返回对应的规则位向量

5.3 连接追踪状态机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
┌─────────┐
│ START │
└────┬────┘

├──[TCP SYN]──→ ┌──────┐
│ │ NEW │
│ └──┬───┘
│ │
│ ┌┴─────────────────┐
│ │[TCP SYN+ACK] │
│ ↓ ↓
│ ┌────────────┐ ┌──────────┐
│ │ESTABLISHED │ │ TIMEOUT │
│ └──┬─────────┘ └──────────┘
│ │
│ ┌┴──────────────┐
│ │ [TCP FIN/RST] │
│ ↓ ↓
│ ┌─────────┐ ┌────────────┐
│ │ CLOSE │ │ TIMEOUT │
│ └─────────┘ └────────────┘

├──[UDP SYN]──→ ┌──────┐
│ │ NEW │ (UDP 无连接,快速过期)
│ └──┬───┘
│ │[TIMEOUT]
│ ↓
│ ┌─────────────┐
└──────────────│ INVALID │
└─────────────┘

状态对应的规则动作:

1
2
3
4
5
Rules with conntrack:
- conntrack=NEW → 仅匹配新连接
- conntrack=ESTABLISHED → 仅匹配已建立的连接
- conntrack=RELATED → 匹配相关连接 (如 ICMP error)
- conntrack=INVALID → 匹配无效状态(通常用来 DROP)

5.4 Horus 优化

问题: 逐条规则扫描效率低

解决: 用哈希表快速跳过无关规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 在 Horus_dp.c 中
struct horusKey {
uint32_t srcIp;
uint32_t dstIp;
uint8_t l4proto;
uint16_t srcPort;
uint16_t dstPort;
};

struct horusValue {
uint32_t ruleID; // 直接指向匹配的规则
};

// 查询哈希表
BPF_TABLE("hash", struct horusKey, struct horusValue, horusTable, 2048);

// 如果命中,跳过 BitScan/IpLookup 等阶段
struct horusValue *result = horusTable.lookup(&key);
if (result != NULL) {
// 直接跳到 ConntrackLabel,规则 ID 已知
}

适用场景: 规则数量 ≥ 2048 时启用


6. 统计信息收集

6.1 计数器类型

1
2
3
4
5
6
7
每条规则的计数器:
├─ 数据包数 (pkts_action_<rule_id>)
└─ 字节数 (bytes_action_<rule_id>)

每条链的默认动作计数器:
├─ 数据包数 (pkts_<chain>_default)
└─ 字节数 (bytes_<chain>_default)

6.2 计数器更新时机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 在 ActionLookup 中
incrementCounters(&key, md->packet_len);

// 此函数的实现
static void incrementCounters(int *action, u32 bytes) {
u64 *value;
value = pkts_DIRECTION.lookup(action);
if (value) {
*value += 1; // ++count
}
value = bytes_DIRECTION.lookup(action);
if (value) {
*value += bytes; // += packet_len
}
}

7. 端口与接口绑定

7.1 端口概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Iptables 需要 attach 到具体的网络接口:

用户 API:
POST /iptables/ports/eth0
{
"type": "physical"
}

内部实现:
Port 对象被创建

port->setPolycubeAttachPoint(
polycube::service::CubeIface::TC_INGRESS,
polycube::service::CubeIface::TC_EGRESS
)

eBPF 程序被 attach 到 eth0 的 TC ingress/egress hook

7.2 多端口处理

1
2
3
4
如果有多个端口(eth0, eth1, eth2):
├─ 每个端口独立 attach TC hook
├─ 所有端口共享同一份 BPF 规则代码
└─ 包元数据包含端口号 (md->in_port)

8. 完整交互流程示例

场景:用户添加一条 DROP 规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
[用户侧]
POST /iptables/chains/INPUT/rules
{
"id": 100,
"src": "192.168.1.0/24",
"dport": 22,
"l4proto": "tcp",
"action": "DROP"
}



[REST API 处理]
IptablesApi::create_iptables_chain_rule_handler()



[Control Plane]
Chain::addRule(100, conf)
├─ 创建 ChainRule 对象
├─ 存储规则参数
└─ 调用 Chain::updateChain()
├─ 遍历所有规则(0-100)
├─ 生成 BPF 代码:
│ ├─ BitScan: 初始化位向量,扫描找到规则 100
│ ├─ IpLookup: 匹配 src = 192.168.1.0/24
│ ├─ L4ProtocolLookup: 匹配 proto = TCP
│ ├─ L4PortLookup: 匹配 dport = 22
│ └─ ActionLookup: 返回 DROP
├─ 编译 eBPF 代码(BCC)
└─ 调用 Parser::reload(), ChainSelector::reload() 等
├─ 逐个加载新程序到内核
└─ BPF 程序被替换



[Kernel]
新的 eBPF 程序被加载到 TC hook



[下次包进入]
TC ingress hook 触发:
├─ Parser 解析包
├─ ChainSelector 识别为 INPUT 链
├─ BitScan 扫描规则,找到规则 100
├─ IpLookup 检查 src (192.168.1.100 ∈ 192.168.1.0/24) → 匹配
├─ L4ProtocolLookup 检查 proto=TCP → 匹配
├─ L4PortLookup 检查 dport=22 → 匹配
├─ ActionLookup 返回 DROP
└─ 包被丢弃(RX_DROP)



[统计]
规则 100 的计数器 ++
pkts_action_100 += 1
bytes_action_100 += packet_length

9. 性能优化

9.1 为什么使用 BPF?

1
2
3
4
5
6
7
8
9
10
11
12
13
传统 Netfilter (内核):
├─ 在内核态执行,无上下文切换
├─ 但规则处理在内核协议栈中,开销较大
└─ 性能:~100k pps

eBPF (Polycube):
├─ 在 TC/XDP hook 处理(比 Netfilter 更早)
├─ 可 JIT 编译到 CPU 指令
├─ 支持高效的 LPM Trie 和 Hash 表
└─ 性能:~1M+ pps

用户态处理:
└─ 太慢,每个包都需要陷入用户态

9.2 规则优化顺序

系统自动优化规则的执行顺序以提高性能:

1
2
3
4
5
6
7
输入规则(按添加顺序):
Rule 0: src=10.0.0.0/8, action=DROP
Rule 1: dst=192.168.0.0/16, action=ACCEPT
Rule 2: dport=443, action=ACCEPT

优化后(频繁匹配的规则前置):
可能会调整顺序以减少平均匹配步数

9.3 Horus 表的大小

1
2
3
4
Horus 优化适用于规则数量 ≥ 2048 时
├─ 对于较少规则(<100),BitScan 更快
├─ 对于大量规则 (>1000),哈希表查询更快
└─ 系统自动选择最优方式

10. 故障排查

10.1 常见问题

问题:规则不生效

1
2
3
4
5
排查步骤:
1. 检查是否调用了 applyRules()(非 interactive 模式)
2. 检查 rule ID 是否正确
3. 查看规则的 action 字段
4. 检查包是否真的到达了该链(INPUT/FORWARD/OUTPUT)

问题:性能下降

1
2
3
4
5
排查步骤:
1. 检查规则数量是否过多
2. 启用 Horus 优化
3. 查看连接追踪表是否溢出
4. 检查包是否过于复杂(IPv6/隧道等)

问题:连接追踪失效

1
2
3
4
排查步骤:
1. 检查是否启用了 conntrack 功能
2. 查看连接追踪表的状态
3. 确保规则中的 conntrack 字段正确设置

10.2 调试工具

1
2
3
4
5
6
7
8
查看内核日志:
dmesg | grep -i iptables

查看 BPF 统计信息:
bpftool map dump name pkts_action_0

查看规则编译后的代码:
llvm-objdump -d <bpf_obj>

总结

pcn-iptables 的核心设计思想:

  1. 分层架构:用户态管理规则,内核态执行转发
  2. 动态编译:规则改变时动态生成 eBPF 代码
  3. 高效匹配:利用位扫描、LPM Trie、哈希表等数据结构
  4. 连接追踪:维护连接状态,支持有状态防火墙
  5. 可观测性:详细的计数器和统计信息
  6. 灵活扩展:支持链选择器、规则链式处理等机制

这种设计使得 pcn-iptables 能够在用户态实现高性能的防火墙功能,同时保持良好的可维护性和可观测性。

开发

安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sudo apt install wget unzip build-essential golang-go

wget https://github.com/polycube-network/polycube/releases/download/v0.9.0/polycube-v0.9.0.zip

unzip polycube-v0.9.0.zip

cd polycube-v0.9.0/


sudo apt-get -y install git build-essential cmake bison flex \
libelf-dev libllvm5.0 llvm-5.0-dev libclang-5.0-dev libpcap-dev \
libnl-route-3-dev libnl-genl-3-dev uuid-dev pkg-config \
autoconf libtool m4 automake libssl-dev kmod jq bash-completion \
gnupg2chmod +x llvm.sh

参考链接