香港特惠服务器
三网CN2带宽,提供30M至100M大带宽,保障CN2/CMIN2/CU/PCCW四大运营商线路稳定接入

| 类别 | 型号/版本 | 备注 |
|---|---|---|
| 机房 | 香港葵涌 DC | 双路市电+UPS |
| 服务器 | 1U 单路,AMD EPYC 7313P(16C/32T) | 睿频开,C-State 限制 |
| 内存 | 64GB DDR4 | NUMA 单路 |
| 网卡 | Mellanox ConnectX-5 10/25GbE(mlx5) | 原生支持 XDP DRV 模式 |
| 系统 | Ubuntu Server 24.04 LTS(内核 6.8) | 22.04/5.15 也可 |
| 线路 | CN2/GIA 出口(上联电信侧) | 业务多为小包、高并发 |
| 工具 | clang/llvm、bpftool、xdp-tools、ethtool | apt 可装 |
[eth0 下联] <—— 业务流量 —— 客户端
|
(XDP: LPM 匹配 + 重定向)
|
[eth1 上联] —— CN2/GIA ——> 运营商/内地侧
# 必备包(Ubuntu 24.04/22.04 通用)
sudo apt update
sudo apt install -y build-essential clang llvm libbpf-dev libelf-dev \
linux-tools-$(uname -r) bpftool git make xdp-tools ethtool
# 确认内核/驱动支持 XDP
ethtool -i eth0 # 下联口
ethtool -i eth1 # 上联口
# 关闭会干扰小包路径的聚合(内核栈内可留 GRO,但 XDP 在之前阶段)
sudo ethtool -K eth0 lro off gro off gso on tso on
sudo ethtool -K eth1 lro off gro off gso on tso on
# 增大 ring buffer 与队列数(按核数/NUMA 定)
sudo ethtool -G eth0 rx 4096 tx 4096
sudo ethtool -G eth1 rx 4096 tx 4096
# 开多队列 + RSS,分散到多个 CPU
sudo ethtool -L eth0 combined 16
sudo ethtool -L eth1 combined 16
# 绑定中断亲和(示例把 eth0 的 IRQ 绑到 CPU 0-7,eth1 绑 8-15)
# 可以配合 irqbalance --oneshot 或手写 /proc/irq/*/smp_affinity_list
# 示例(粗略):
for i in $(grep eth0 /proc/interrupts | awk '{print $1}' | tr -d :); do
echo 0-7 | sudo tee /proc/irq/$i/smp_affinity_list
done
for i in $(grep eth1 /proc/interrupts | awk '{print $1}' | tr -d :); do
echo 8-15 | sudo tee /proc/irq/$i/smp_affinity_list
done
# RPS/XPS(可选,结合实际内核栈)
echo ffff > /sys/class/net/eth0/queues/rx-*/rps_cpus
echo ffff > /sys/class/net/eth1/queues/rx-*/rps_cpus
# sysctl,注意只调与接入/跨境相关的关键项
sudo tee /etc/sysctl.d/99-xdp.conf >/dev/null <<'EOF'
net.core.netdev_max_backlog = 50000
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_mtu_probing = 1
# busy poll:仅在确认收益时开启
net.core.busy_poll = 50
net.core.busy_read = 50
EOF
sudo sysctl --system
// xdp_cn2_kern.c
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
struct lpm_v4_key {
__u32 prefixlen;
__u32 addr; // network byte order
};
struct {
__uint(type, BPF_MAP_TYPE_LPM_TRIE);
__uint(max_entries, 256);
__type(key, struct lpm_v4_key);
__type(value, __u32); // devmap 的 key(非 ifindex)
__uint(map_flags, BPF_F_NO_PREALLOC);
} lpm_v4 SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_DEVMAP);
__uint(max_entries, 256);
__type(key, __u32); // 逻辑端口号
__type(value, __u32); // ifindex
} tx_ports SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 4);
__type(key, __u32);
__type(value, __u64);
} stats SEC(".maps");
enum { STAT_RX=0, STAT_TX, STAT_PASS, STAT_DROP };
static __always_inline void stat_inc(__u32 idx) {
__u64 *c = bpf_map_lookup_elem(&stats, &idx);
if (c) __sync_fetch_and_add(c, 1);
}
SEC("xdp")
int xdp_cn2(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
if ((void*)(eth + 1) > data_end) return XDP_ABORTED;
stat_inc(STAT_RX);
if (bpf_ntohs(eth->h_proto) != ETH_P_IP)
goto pass;
struct iphdr *iph = (void*)(eth + 1);
if ((void*)(iph + 1) > data_end) goto pass;
if (iph->ihl < 5) goto drop;
// 只演示按目的地址前缀匹配,可按需扩展为源/五元组
struct lpm_v4_key key = {
.prefixlen = 32,
.addr = iph->daddr,
};
__u32 *dev_key = bpf_map_lookup_elem(&lpm_v4, &key);
if (dev_key) {
int rc = bpf_redirect_map(&tx_ports, *dev_key, 0);
if (rc == XDP_REDIRECT) {
stat_inc(STAT_TX);
return rc;
}
}
pass:
stat_inc(STAT_PASS);
return XDP_PASS;
drop:
stat_inc(STAT_DROP);
return XDP_DROP;
}
char _license[] SEC("license") = "GPL";
clang -O2 -g -Wall -target bpf -c xdp_cn2_kern.c -o xdp_cn2_kern.o
# 将 XDP 挂在下联口 eth0(驱动 native 模式优先)
sudo xdp-loader load -m drv -s xdp_cn2 -d eth0 xdp_cn2_kern.o
sudo xdp-loader status
cat /sys/class/net/eth0/ifindex # 假设输出 5(下联)
cat /sys/class/net/eth1/ifindex # 假设输出 6(上联,CN2/GIA)
sudo bpftool map show | egrep 'lpm_v4|tx_ports'
# 记下 lpm_v4 的 id=XXX,tx_ports 的 id=YYY
# key=0x00000000, value=0x06000000(小端:6)
printf '\x00\x00\x00\x00\x06\x00\x00\x00' | \
sudo bpftool map update id YYY key - value - # YYY 替换为 tx_ports 的 id
# key 结构:prefixlen(u32) + addr(u32, network byte order)
# 例如 203.0.113.0/24:prefixlen=24, addr=0xCB007100
printf '\x18\x00\x00\x00\xCB\x00\x71\x00\x00\x00\x00\x00' | hexdump -C # 自查
# 实际写入(更简单的方式是用 JSON,但这里用二进制示例更通用)
printf '\x18\x00\x00\x00\xCB\x00\x71\x00' | \
sudo bpftool map update id XXX key - value 0x00 0x00 0x00 0x00
# XXX 为 lpm_v4 的 id;value=0 表示 devmap 的“逻辑端口 0”
sudo bpftool map dump id $(sudo bpftool map show | awk '/stats/ {print $1}' | cut -d: -f2)
# 或写个小 userspace 读取 per-cpu stats 汇总
sudo xdp-loader unload -a # 卸载所有网卡上的 xdp
# 或:sudo xdp-loader unload -d eth0
| 指标 | 调整前(传统内核路径) | 调整后(XDP 前置+DEVMap) |
|---|---|---|
| 吞吐(Gbps) | 1.8–2.0 | 2.7–3.0 |
| 64B PPS(Mpps) | 2.8 | 4.2 |
| CPU softirq(%) | 85–90 | 55–60 |
| ksoftirqd 抢占 | 频繁 | 显著减少 |
| 丢包率(接口) | 0.5–1.2% | < 0.2% |
| 95 分位时延(ms) | 3.4 | 2.1 |
BPF_CLANG ?= clang
BPF_CFLAGS ?= -O2 -g -Wall -target bpf
all: xdp_cn2_kern.o
xdp_cn2_kern.o: xdp_cn2_kern.c
$(BPF_CLANG) $(BPF_CFLAGS) -c $< -o $@
clean:
rm -f xdp_cn2_kern.o
#!/usr/bin/env bash
# usage: ./add_prefix.sh <lpm_map_id> <dev_key> <cidr>
# e.g. ./add_prefix.sh 123 0 203.0.113.0/24
map_id=$1
dev_key=$2
cidr=$3
prefix=${cidr#*/} # 24
ip=${cidr%/*} # 203.0.113.0
# 转网络字节序
IFS=. read -r a b c d <<< "$ip"
printf -v addr '\\x%02x\\x%02x\\x%02x\\x%02x' $a $b $c $d
# 写 LPM:prefixlen(u32 little endian) + addr(be)
printf "\\x$(printf '%02x' $prefix)\\x00\\x00\\x00$addr" | \
sudo bpftool map update id "$map_id" key - value "$(printf '0x%02x 0x00 0x00 0x00' $dev_key)"