Linux VPS上配置BIRD2 BGP路由
在Debian 12或Ubuntu 24.04上安装BIRD2并配置BGP会话来宣告自有IP前缀。涵盖双栈配置、导出过滤器、持久化dummy接口、nftables规则和birdc验证。
BIRD是由CZ.NIC维护的路由守护进程。版本2将IPv4和IPv6统一到单一守护进程中,取代了1.x时期分离的BIRD/BIRD6架构,并新增了强大的过滤器语言。本指南涵盖在Linux VPS上安装BIRD2、配置与上游提供商的BGP会话、宣告你的IP前缀,以及验证所有配置是否正常工作。
所有示例均为双栈(IPv4 + IPv6)。请将文中所有占位ASN、IP和前缀替换为你自己的实际值。
配置BIRD2 BGP前需要准备什么?
在修改bird.conf之前,你需要一个ASN、已分配的IP地址空间,以及一个在VPS上提供BGP会话的服务商。三者缺一不可,否则BIRD2既没有前缀可宣告,也没有对端可以建立对等关系。
从你的服务商或RIR分配中收集以下信息:
| 项目 | 示例值 | 获取途径 |
|---|---|---|
| 你的ASN | AS65400 | RIPE NCC、ARIN或你的LIR |
| 你的IPv4前缀 | 203.0.113.0/24 | RIR分配或服务商的PA地址 |
| 你的IPv6前缀 | 2001:db8:1000::/48 | RIR分配或服务商的PA地址 |
| 上游ASN | AS64496 | 服务商的BGP会话详情 |
| 上游对等IPv4 | 198.51.100.1 | 服务商的BGP会话详情 |
| 上游对等IPv6 | 2001:db8::1 | 服务商的BGP会话详情 |
| 你的对等IPv4 | 198.51.100.2 | 服务商的BGP会话详情 |
| 你的对等IPv6 | 2001:db8::2 | 服务商的BGP会话详情 |
| MD5密码 | (共享密钥) | 与服务商协商确定 |
你还需要在下游网络对你的路由执行RPKI验证之前,发布你前缀的ROA记录。相关设置参见。
确认你的服务商已将你的前缀加入其IRR过滤器。否则即使BGP会话建立成功,你的宣告也会被过滤掉。
一台至少1 GB内存的VPS足以运行BIRD2并承载全表(超过100万条IPv4路由)。如果你只接受上游的默认路由,内存占用会低于100 MB。
如何在Debian 12和Ubuntu 24.04上安装BIRD2?
两个发行版的默认仓库都包含BIRD2。Debian 12提供2.0.12版本,Ubuntu 24.04提供2.14版本。两者都能满足基本BGP需求,但CZ.NIC官方仓库提供的BIRD 2.18(2026年1月发布)支持BGP动态无编号功能、性能改进和最新bug修复。生产环境建议使用CZ.NIC仓库。
注意: CZ.NIC以bird2项目名发布BIRD2软件包。不要与bird项目(仅覆盖Debian)或bird3(BIRD 3.x)混淆。
从CZ.NIC仓库安装(推荐)
添加官方仓库后安装:
sudo apt-get update
sudo apt-get -y install apt-transport-https ca-certificates wget
导入CZ.NIC GPG密钥:
sudo wget -O /usr/share/keyrings/cznic-labs-pkg.gpg https://pkg.labs.nic.cz/gpg
添加仓库。Debian 12:
echo "deb [signed-by=/usr/share/keyrings/cznic-labs-pkg.gpg] https://pkg.labs.nic.cz/bird2 bookworm main" | sudo tee /etc/apt/sources.list.d/cznic-labs-bird.list
Ubuntu 24.04:
echo "deb [signed-by=/usr/share/keyrings/cznic-labs-pkg.gpg] https://pkg.labs.nic.cz/bird2 noble main" | sudo tee /etc/apt/sources.list.d/cznic-labs-bird.list
安装BIRD2:
sudo apt-get update
sudo apt-get -y install bird2
从默认仓库安装
如果你偏好使用发行版自带的软件包:
sudo apt-get update
sudo apt-get -y install bird2
验证安装
bird --version
预期输出(CZ.NIC仓库):
BIRD version 2.18
检查服务是否正在运行:
sudo systemctl status bird
BIRD在安装后会自动启动。/etc/bird/bird.conf中的默认配置只是一个骨架。下一节你将完全替换它。
bird.conf的BGP配置结构是怎样的?
面向BGP的bird.conf包含五个部分: 全局设置和四个协议段(device、direct、kernel、bgp)。每个协议独立运行,通过路由表相互通信。
以下是双栈BGP配置的完整bird.conf。先通读一遍,随后各小节会逐块讲解。
sudo cp /etc/bird/bird.conf /etc/bird/bird.conf.bak
sudo tee /etc/bird/bird.conf > /dev/null << 'BIRDCONF'
# /etc/bird/bird.conf - BIRD2 BGP configuration
# Replace all placeholder values with your own
log syslog all;
router id 198.51.100.2;
# Watch interface state changes
protocol device {
scan time 10;
}
# Import connected routes (needed for next-hop resolution)
protocol direct {
ipv4;
ipv6;
interface "dummy0";
interface "eth0";
}
# Sync BIRD routes to kernel routing table
protocol kernel {
ipv4 {
export all;
import all;
};
learn;
scan time 15;
merge paths on;
}
protocol kernel {
ipv6 {
export all;
import all;
};
learn;
scan time 15;
merge paths on;
}
# Define your prefixes
define OWN_V4_PREFIX = 203.0.113.0/24;
define OWN_V6_PREFIX = 2001:db8:1000::/48;
# Export filter: only announce your own prefixes
filter export_bgp_v4 {
if net = OWN_V4_PREFIX then accept;
reject;
}
filter export_bgp_v6 {
if net = OWN_V6_PREFIX then accept;
reject;
}
# Static routes for prefix origination (point to dummy0)
protocol static static_v4 {
ipv4;
route 203.0.113.0/24 via "dummy0";
}
protocol static static_v6 {
ipv6;
route 2001:db8:1000::/48 via "dummy0";
}
# BGP session with upstream provider
protocol bgp upstream1 {
description "Upstream Provider";
local 198.51.100.2 as 65400;
neighbor 198.51.100.1 as 64496;
source address 198.51.100.2;
hold time 90;
keepalive time 30;
password "your-md5-secret";
ipv4 {
import all;
export filter export_bgp_v4;
next hop self;
};
ipv6 {
import all;
export filter export_bgp_v6;
next hop self;
};
}
BIRDCONF
设置配置文件权限。只有root和bird用户需要访问:
sudo chown root:bird /etc/bird/bird.conf
sudo chmod 640 /etc/bird/bird.conf
验证权限:
ls -la /etc/bird/bird.conf
预期输出:
-rw-r----- 1 root bird 1247 Mar 19 12:00 /etc/bird/bird.conf
全局设置
log syslog all;
router id 198.51.100.2;
router id必须是全局唯一的IPv4地址。使用你分配到的IP之一。BIRD可以从接口自动检测,但显式指定更可靠。
log syslog all将所有日志发送到系统日志。通过journalctl -u bird -f查看。如果调试某个协议时需要详细输出,可以临时添加debug protocols all;。会话正常后记得删除它。Debug级别的日志量很大,可能会占满日志存储空间。
Protocol device
protocol device {
scan time 10;
}
每10秒扫描接口状态变化。这是必需的。没有它,BIRD无法感知接口的上线或下线。
Protocol direct
protocol direct {
ipv4;
ipv6;
interface "dummy0";
interface "eth0";
}
从列出的接口导入直连路由。BIRD需要这些路由来解析下一跳。只列出与BGP相关的接口即可。
Protocol kernel
需要两个kernel协议块: 一个用于IPv4,一个用于IPv6。各自在BIRD路由表和Linux内核路由表之间同步对应的地址族。
merge paths on在你有多个上游时启用ECMP(等价多路径)。learn将已有的内核路由导入BIRD。
protocol bgp块的作用是什么?
protocol bgp块定义与一个邻居的对等会话。它指定你是谁(local ... as)、你的对端是谁(neighbor ... as)、认证方式(password),以及交换哪些路由(channel块)。
关键指令:
| 指令 | 用途 |
|---|---|
local <ip> as <ASN> |
你的对等IP和ASN |
neighbor <ip> as <ASN> |
上游的对等IP和ASN |
source address <ip> |
TCP连接的源IP |
hold time 90 |
多少秒后宣布对端失联(keepalive的3倍) |
keepalive time 30 |
keepalive消息发送间隔 |
password "..." |
MD5认证(RFC 2385) |
MD5认证保护BGP会话免受伪造TCP RST和注入攻击。双方必须配置相同的密码。向你的服务商索取共享密钥。
如何配置双栈IPv4和IPv6通道?
BIRD2将IPv4和IPv6作为同一协议块内的独立通道(channel)处理。每个通道有自己的导入/导出策略:
ipv4 {
import all;
export filter export_bgp_v4;
next hop self;
};
ipv6 {
import all;
export filter export_bgp_v6;
next hop self;
};
import all接受上游的所有路由。在生产环境中,你也应该过滤导入的路由。参见获取导入过滤器示例。
next hop self将下一跳属性重写为你的对等IP。当上游期望流量通过你的地址转发时需要此设置,这是VPS上的标准配置。
如果你的服务商为IPv4和IPv6运行独立的BGP会话(每个地址族使用不同的邻居IP),则应拆分为两个protocol bgp块。
如何编写导出过滤器只宣告自己的前缀?
导出过滤器(export filter)控制BIRD向上游发送哪些路由。配置错误的导出过滤器可能会泄露你不拥有的路由,这将导致你的会话被关闭,甚至可能收到上游NOC的电话。
上述配置中的过滤器使用简单的前缀匹配:
define OWN_V4_PREFIX = 203.0.113.0/24;
filter export_bgp_v4 {
if net = OWN_V4_PREFIX then accept;
reject;
}
这只接受精确的/24。其他一律拒绝。末尾的reject是默认拒绝策略。
对于多个前缀,使用集合(set):
define OWN_V4_PREFIXES = [ 203.0.113.0/24, 198.51.100.0/24 ];
filter export_bgp_v4 {
if net ~ OWN_V4_PREFIXES then accept;
reject;
}
~运算符对前缀集合进行匹配。它还支持范围表示法:
define OWN_V4_PREFIXES = [ 203.0.113.0/24{24,24} ];
{24,24}将匹配限制为恰好是/24。如果你需要为流量工程宣告更具体的路由,可以用{24,28}允许分解到/28。
IPv6的模式完全相同:
define OWN_V6_PREFIXES = [ 2001:db8:1000::/48{48,48} ];
filter export_bgp_v6 {
if net ~ OWN_V6_PREFIXES then accept;
reject;
}
始终保持导出过滤器尽可能严格。只宣告你拥有的前缀,使用你计划的精确前缀长度。如果过滤器末尾缺少reject,BIRD将使用通道的默认动作,而默认动作可能是accept。始终以显式的reject结尾。
你可以在不应用配置的情况下测试过滤器:
sudo birdc eval '203.0.113.0/24 ~ [ 203.0.113.0/24 ]'
这会返回TRUE或FALSE,帮助你在重载配置前调试过滤器逻辑。
如何创建持久化dummy接口用于前缀起源?
BGP宣告路由表中存在的前缀。你需要一个分配了你的前缀的接口,这样BIRD才能生成路由。dummy接口就是为此设计的: 它始终处于UP状态,没有物理链路,通过systemd-networkd配置后可以在重启后保留。
临时的ip link add命令会在重启后丢失。请改用systemd-networkd。
创建.netdev文件
sudo tee /etc/systemd/network/10-dummy0.netdev > /dev/null << 'EOF'
[NetDev]
Name=dummy0
Kind=dummy
EOF
创建.network文件
sudo tee /etc/systemd/network/10-dummy0.network > /dev/null << 'EOF'
[Match]
Name=dummy0
[Network]
Address=203.0.113.1/32
Address=2001:db8:1000::1/128
EOF
从你分配的前缀中选一个/32(IPv6选/128)分配给dummy接口。这个地址不需要从外部可达。它存在的唯一目的是让BIRD拥有该前缀的直连路由。
应用并验证
sudo systemctl restart systemd-networkd
等待几秒后检查:
ip addr show dummy0
预期输出:
3: dummy0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff
inet 203.0.113.1/32 scope global dummy0
valid_lft forever preferred_lft forever
inet6 2001:db8:1000::1/128 scope global
valid_lft forever preferred_lft forever
dummy接口显示state UNKNOWN是正常的。这表示链路层没有物理载波可跟踪,但接口处于UP状态且可以转发数据。MAC地址由systemd-networkd随机生成,你的系统上会不同。
如果你使用的是ifupdown而不是systemd-networkd(通过networkctl status检查),可能需要先启用systemd-networkd:
sudo systemctl enable --now systemd-networkd
验证文件已就位,确保下次重启后生效:
ls -la /etc/systemd/network/10-dummy0.*
-rw-r--r-- 1 root root 42 Mar 19 12:00 /etc/systemd/network/10-dummy0.netdev
-rw-r--r-- 1 root root 89 Mar 19 12:00 /etc/systemd/network/10-dummy0.network
如何用nftables防火墙规则保护BGP?
BGP运行在TCP端口179上。没有防火墙规则的话,互联网上任何主机都可以尝试与你的路由器建立BGP会话。自动扫描工具会在VPS上线数小时内发现开放的179端口。必须将179端口限制为仅允许你的对等IP访问。
Ubuntu 24.04默认可能未安装nftables。先安装:
sudo apt-get -y install nftables
创建nftables规则集
sudo tee /etc/nftables.d/bgp.conf > /dev/null << 'EOF'
table inet bgp_filter {
set bgp_peers_v4 {
type ipv4_addr
elements = { 198.51.100.1 }
}
set bgp_peers_v6 {
type ipv6_addr
elements = { 2001:db8::1 }
}
chain input {
type filter hook input priority 0; policy accept;
# Allow BGP from known peers only
tcp dport 179 ip saddr @bgp_peers_v4 accept
tcp dport 179 ip6 saddr @bgp_peers_v6 accept
# Drop BGP from everyone else
tcp dport 179 drop
}
}
EOF
添加更多上游时,只需向集合中添加对等IP。命名集合(named set)让你无需重写规则即可更新对等列表。
集成到现有nftables配置
如果你已有/etc/nftables.conf,引入BGP规则:
echo 'include "/etc/nftables.d/bgp.conf"' | sudo tee -a /etc/nftables.conf
如果你还没有设置nftables,创建目录并加载配置:
sudo mkdir -p /etc/nftables.d
sudo systemctl enable --now nftables
enable --now会立即启动nftables并确保每次开机自动加载。
应用规则:
sudo nft -f /etc/nftables.d/bgp.conf
验证规则
sudo nft list table inet bgp_filter
预期输出:
table inet bgp_filter {
set bgp_peers_v4 {
type ipv4_addr
elements = { 198.51.100.1 }
}
set bgp_peers_v6 {
type ipv6_addr
elements = { 2001:db8::1 }
}
chain input {
type filter hook input priority filter; policy accept;
tcp dport 179 ip saddr @bgp_peers_v4 accept
tcp dport 179 ip6 saddr @bgp_peers_v6 accept
tcp dport 179 drop
}
}
nft list会将priority 0标准化为priority filter并去除注释。这是正常行为。
测试规则是否生效,从VPS上检查:
sudo nft list ruleset | grep -c "179"
返回值应与你创建的179端口规则数量一致。
同时确认BGP流量在你和对端之间没有被阻断。上述规则只影响入站连接。BIRD发起的到对端的出站连接由established/related conntrack条目处理。如果你有严格的出站策略,添加:
sudo nft add rule inet bgp_filter input ct state established,related accept
这条规则通常已存在于你的基础防火墙规则集中。如果没有,将它添加在BGP规则之前。
如何应用BIRD2配置?
加载新配置前先验证:
sudo birdc configure check
预期输出:
BIRD 2.18 ready.
Reading configuration from /etc/bird/bird.conf
Configuration OK
如果有语法错误,BIRD会报告行号和问题。修正后重新检查。
应用配置:
sudo birdc configure
BIRD 2.18 ready.
Reading configuration from /etc/bird/bird.conf
Reconfigured
你可能看到Reconfiguration in progress而不是Reconfigured。当BIRD仍在处理协议变更(如建立新BGP会话)时会出现这种情况。两者都是正常的。
BIRD在不重启的情况下应用新配置。现有会话会尽可能进行软重配置(soft reconfiguration)。如果你修改了邻居IP或本地AS,BIRD会自动重启受影响的协议。
对于不应中断运行中会话的配置变更(过滤器修改、静态路由添加),使用:
sudo birdc reload upstream1
这会对现有路由重新应用导入/导出过滤器,而不拆除TCP会话。
如何验证BGP会话和路由宣告?
应用配置后,使用birdc检查会话是否已建立以及路由是否正在被宣告。这些命令是你的主要调试工具。
| 命令 | 显示内容 | 使用时机 |
|---|---|---|
show protocols |
协议状态摘要(Established、Active等) | 配置变更后的首次检查 |
show protocols all upstream1 |
完整的会话详情、计数器、定时器 | 调试特定会话 |
show route |
BIRD表中的所有路由 | 验证接收到的路由和静态路由 |
show route export upstream1 |
正在发送给上游的路由 | 确认你的前缀已被宣告 |
show route for 203.0.113.0/24 |
特定前缀的最佳路由 | 追踪路径选择 |
show route protocol static_v4 |
来自特定协议的路由 | 验证静态路由已加载 |
检查会话状态
sudo birdc show protocols
正常工作时的预期输出:
BIRD 2.18 ready.
Name Proto Table State Since Info
device1 Device --- up 12:00:00.000
direct1 Direct --- up 12:00:00.000
kernel1 Kernel master4 up 12:00:00.000
kernel2 Kernel master6 up 12:00:00.000
static_v4 Static master4 up 12:00:00.000
static_v6 Static master6 up 12:00:00.000
upstream1 BGP --- up 12:00:05.000 Established
Established状态表示BGP会话已建立并可以交换路由。其他任何状态都意味着存在问题。
检查导出的路由
sudo birdc show route export upstream1
BIRD 2.18 ready.
Table master4:
203.0.113.0/24 unicast [static_v4 12:00:00.000] * (200)
dev dummy0
Table master6:
2001:db8:1000::/48 unicast [static_v6 12:00:00.000] * (200)
dev dummy0
你的IPv4和IPv6前缀都应该出现在这里。如果某个前缀缺失,说明导出过滤器将其拒绝了。
查看完整会话详情
sudo birdc show protocols all upstream1
查找Routes:行:
Routes: 2 imported, 0 filtered, 2 exported, 0 preferred
exported计数应与你计划宣告的前缀数量一致。
从外部验证
使用公共Looking Glass确认你的前缀在互联网上可见。NLNOG Looking Glass或PeeringDB可以帮助你。搜索你的前缀并验证:
- Origin AS与你的一致
- AS路径经过你的上游
- 没有出现RPKI invalid状态
同时检查内核路由表:
ip route show | grep 203.0.113
ip -6 route show | grep 2001:db8:1000
你应该看到路由指向dummy0接口。
如何排查常见的BIRD2 BGP问题?
大多数BGP问题归结为几类。按以下顺序排查。
BGP会话状态参考
| 状态 | 含义 | 排查方向 |
|---|---|---|
| Idle | BIRD未尝试连接 | 配置错误、协议被禁用 |
| Connect | TCP连接进行中 | 防火墙阻断179端口、邻居IP错误 |
| Active | TCP连接失败,正在重试 | 对端不可达、防火墙、源地址错误 |
| OpenSent | TCP已连接,等待OPEN回复 | MD5密码不匹配、对端ASN配置错误 |
| OpenConfirm | 已收到OPEN,等待KEEPALIVE | 很少停留在此状态,通常快速转换 |
| Established | 会话已建立,正在交换路由 | 正常工作 |
会话停留在Connect或Active状态
这表示TCP无法到达对端的179端口。排查:
# Can you reach the peer at all?
ping -c 3 198.51.100.1
# Is port 179 open on the peer?
nc -zv 198.51.100.1 179
# Are your nftables rules blocking outbound?
sudo nft list ruleset | grep 179
# Check BIRD logs
journalctl -u bird --since "5 minutes ago" --no-pager
常见原因: BGP块中source address配置错误、对端尚未完成其侧的配置,或你的防火墙阻断了出站连接。
会话停留在OpenSent状态
TCP连接正常但BGP OPEN消息被拒绝。几乎可以确定是MD5密码不匹配。验证:
- bird.conf中的
password与服务商提供的完全一致(区分大小写,注意末尾空格) - 双方就ASN达成一致
查看日志获取详细信息:
journalctl -u bird | grep -i "error\|md5\|password"
前缀未出现在show route export中
你的前缀存在于BIRD的路由表中,但导出过滤器未接受它。调试过滤器:
sudo birdc show route 203.0.113.0/24 all
检查路由来源。如果显示的是[device1]或[direct1]而不是[static_v4],说明静态路由配置不正确。导出过滤器匹配的是路由本身,因此路由必须来自正确的协议。
sudo birdc show route noexport upstream1
这会显示被过滤器明确拒绝的路由。如果你的前缀出现在这里,说明过滤器逻辑有bug。
前缀已宣告但在Looking Glass上不可见
你的导出正常工作,但路由未在传播。可能的原因:
- 没有ROA记录: 你的上游或其对等方会过滤RPKI invalid的路由。先发布ROA记录
- IRR过滤: 你的上游基于IRR对象(RIPE DB、RADB)进行过滤。创建或更新你的route/route6对象
- 最大前缀限制: 你的上游可能配置了前缀限制。如果达到限制,联系他们
- 传播延迟: BGP收敛需要几分钟。等待5-10分钟后再检查
查看日志
BIRD将日志写入系统日志。实时排障:
journalctl -u bird -f
查看过去一小时的事件:
journalctl -u bird --since "1 hour ago" --no-pager
查找包含Error、BGP、session或Received的行。它们会告诉你BIRD正在做什么以及出了什么问题。
BIRD2与BIRD 1.x: 迁移注意事项
如果你从BIRD 1.x迁移,主要区别如下:
- 单一守护进程: BIRD2用一个进程替代了
bird和bird6 - 通道语法: IPv4和IPv6作为协议块内的通道配置,而非独立的协议
- 过滤器语法: 大部分兼容,但部分函数改了名。查阅BIRD2文档获取完整的过滤器参考
- 配置文件: 默认路径从
/etc/bird/bird.conf和/etc/bird/bird6.conf合并为单一的/etc/bird/bird.conf - birdc套接字: 使用
/run/bird/bird.ctl单一套接字,而非分离的IPv4/IPv6套接字
后续步骤
BGP会话已建立并开始宣告前缀后,还有几项配置需要完成:
- RPKI验证: 设置RTR协议,基于ROA数据验证收到的路由
- 导入过滤器: 过滤收到的路由,防止路由泄露和劫持
- 多上游冗余: 添加第二个
protocol bgp块实现冗余。使用preference设置主备优先级 - BFD: 启用Bidirectional Forwarding Detection(双向转发检测),实现上游之间亚秒级故障切换
- 监控: 使用bird_exporter将BIRD指标导出到Prometheus
关于替代路由守护进程,参见。完整的BYOIP流程请从BYOIP指南开始。
Copyright 2026 Virtua.Cloud. All rights reserved.