0%

BGP路由Bird介绍

简介

边界网关协议(BGP)是互联网涉及的核心技术之一,它使网络能够相互通信。了解如何使用此工具来定义网络拓扑,不仅可以让我们更好地了解互联网络,还可以将这种鲁棒性应用到自己的网络。

在本教程结束时,我们将熟悉BGP的核心概念,并能够把相关的术语传达给其他人。此外,我们还将学会使用BIRD的用户界面建立对等会话并开始发布路由。

本文使用docker容器实现这一目标。为了完成本教程,需要确保已安装dockerdocker-compose

准备

首先,需要克隆bird_examples_docker项目。

1
2
3
ncatelli@ofet> git clone https://github.com/ncatelli/bird_examples_docker.git
ncatelli@ofet> cd bird_examples_docker
ncatelli@ofet> docker-compose up -d

上述命令将创建三个容器(peer1peer2peer3),所有这些容器均已安装BIRD并已建立对等会话。如果还不知道这意味着什么,请不要担心,我们将在设置好BGP并准备就绪后对其进行介绍。

首先连接到peer1并检查所有设置是否正确。

1
2
3
4
5
6
7
8
9
ncatelli@ofet> docker-compose exec peer1 bash
root@peer1:/# birdc show protocols
BIRD 1.6.6 ready.
name proto table state since info
kernel1 Kernel master up 02:36:03
device1 Device master up 02:36:03
direct1 Direct master up 02:36:03
peer2 BGP master up 02:36:08 Established
peer3 BGP master up 02:36:07 Established

如果发现peer2peer3已经显示Established,则说明一切正常,我们已准备就绪。在开始使用之前,将简要介绍BGP的工作原理。

原理概述

边界网关协议(BGP)是一种外部网关协议,用于在自治系统之间交换路由信息。自治系统(AS)是路由前缀和策略的组织单位。这些AS由唯一的16位(后来扩展到32位)自治系统编号(ASN)标识。例如,Facebook的ASN为32934或通常显示为AS32934。BGP的强大之处在于其在成千上万个分散式AS之间传递路由协议和策略的能力。

互联网以及许多其他网络由相互之间进行通信的许多自治系统组成。对等会话促进了这种通信,该会话允许两个AS交换策略,路由和链接状态。所有这些信息都在两个BGP守护程序之间交换,这两个守护程序在TCP的179端口上进行侦听。

虽然BGP被认为是用于在Internet上的大型组织之间进行路由的外部网关协议,但它也可以在AS中使用,以使网络工程师能够控制其内部网络的拓扑。这是eBGP和iBGP术语的来源。iBGP将是本教程其余部分的重点。现在,我们将开始使用BIRD及其交互式命令行工具birdc尝试这些对等会话。

BIRD简介

BIRD是功能齐全的路由守护程序,它支持许多不同的路由协议,包括BGP。BIRD提供了一种简单的配置格式和命令行,用于与会话进行交互。BIRD还内置了对IPv4和IPv6的支持,以及与这两种协议一起使用的相应工具。

检查BGP会话

与我们验证是否正确配置了docker环境的方式类似,我们可以通过运行以下命令查看正在运行的会话:

1
2
3
4
5
6
7
8
root@peer1:/# birdc show protocols
BIRD 1.6.6 ready.
name proto table state since info
kernel1 Kernel master up 02:36:02
device1 Device master up 02:36:02
direct1 Direct master up 02:36:02
peer2 BGP master up 02:36:07 Established
peer3 BGP master up 02:36:06 Established

这给了我们很多信息。但是,让我们关注最后两个条目,peer2peer3。我们可以看到它们都是BGP协议,并且info字段显示已经Established。这些条目中的每一个都对应于peer1peer2peer3打开的BGP会话。为了演示这些值与正在运行的会话之间的关系,让我们在peer2上停止Bird服务。在新的终端窗口中,运行以下命令来停止peer2,模拟网络故障。

1
2
ncatelli@ofet> docker-compose stop peer2
Stopping bird_examples_peer2_1 ... done
1
2
3
4
5
6
7
8
root@peer1:/# birdc show protocols
BIRD 1.6.6 ready.
name proto table state since info
kernel1 Kernel master up 02:36:02
device1 Device master up 02:36:02
direct1 Direct master up 02:36:02
peer2 BGP master start 02:43:38 Connect Socket: Connection closed
peer3 BGP master up 02:36:06 Established

通过重新启动peer2,BIRD应该重新启动,并且随后应重新建立对等会话。

1
2
ncatelli@ofet> docker-compose start peer2
Starting peer2 ... done
1
2
3
4
5
6
7
8
root@peer1:/# birdc show protocols
BIRD 1.6.6 ready.
name proto table state since info
kernel1 Kernel master up 02:36:02
device1 Device master up 02:36:02
direct1 Direct master up 02:36:02
peer2 BGP master up 02:46:29 Established
peer3 BGP master up 02:36:06 Established

通过停止peer2上的bird守护程序,我们使端口179上的TCP连接在peer1peer2之间关闭。这样做会将我们的对等会话从Established更改为Connect。这两个状态对应于许多BGP状态中的两个,但是出于本教程的考虑,我们将仅关注Established,并将所有其他值视为未建立。对于那些更好奇的人,可以在Wikipedia上有关BGP的文章中找到有关会话状态的更多信息。

配置BGP会话

尽管现在知道了如何检查会话是否已经建立,但了解这些会话的配置也很重要。为此,我们需要深入研究bird配置文件。让我们看一下peer1上的/etc/bird下的配置文件。

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
root@peer1:~# cat /etc/bird/bird.conf
router id 10.0.0.10;

protocol kernel {
metric 0;
import none;
learn;
export all;
}

protocol device {
}

protocol direct {
}

protocol bgp peer2 {
local as 64512;
neighbor 10.0.0.11 as 64513;
import all;
export all;
}

protocol bgp peer3 {
local as 64512;
neighbor 10.0.100.11 as 64514;
import all;
export all;

我们可以看到建立这些初始会话所需的配置非常少。为了更深入地了解这项工作的真正作用,我们将专注于一个特定块:bgp peer2

1
2
3
4
5
6
protocol bgp peer2 {
local as 64512;
neighbor 10.0.0.11 as 64513;
import all;
export none;
}

在本教程的前面,我们讨论了eBGP和iBGP之间的区别以及大型AS如何使用唯一的ASN标识自己。但是,一小部分可用的ASN已保留给专用iBGP使用。这个范围是64512-65534。知道了这一点,我们可以看到我们已经将私有范围中的ASN分配给了peer2peer1被分配了ASN 64512。

查看下一条语句,我们可以看到具有IP和附加AS的邻居语句。该IP对应于我们尝试与之建立会话的主机或BGP术语中的邻居,而64513对应于我们分配给peer2主机的AS。可以通过查看peer2上的配置文件来确认这一点。

1
2
3
4
5
6
root@peer2:/# grep -A4 peer1 /etc/bird/bird.conf
protocol bgp peer1 {
local as 64513;
neighbor 10.0.0.10 as 64512;
export none;
}

协议BGP块中的这两个指令处理会话的初始建立。

虽然建立和维护会话对于BGP的运行至关重要,但仅建立会话无法路由任何流量。在下一节中,我们将探讨配置文件中的其他一些元素,以及如何使用它们来发现和宣布节点之间的路由。在继续进行此操作之前,先回顾一下我们当前的拓扑。

当前,我们的网络中有三个节点,peer1(AS64512),peer2(AS64513)和peer3(AS64514)。这些配置在同一广播域中,但是对等的结构类似于peer3 <-> peer1 <-> peer2。这种结构允许通过我们的路由服务器peer1peer2peer3进行路由通信。在继续进行本教程的下一步,即发布路由时,请牢记此拓扑。

IP地址 节点名 AS号
10.0.0.10 peer1 64512
10.0.0.11 peer2 64513
10.0.100.11 peer3 64514

BGP发布路由

内核协议

在开始发布Bird守护程序之间的路由之前,我们应该首先了解BIRD如何在Linux内核和BIRD守护程序之间传递路由。这就是我们前面看到的内核协议块起作用的地方。

1
2
3
4
5
6
protocol kernel {
metric 0;
learn;
import none;
export all;
}

kernel块中可以指定许多选项,并且可以在此处找到关于这些选项的更多信息,但是我们要执行的大部分操作由导入/导出定义。

1
import none;

告诉BIRD不要将路由从内核路由表中读取到BIRD中。我们将通过直接协议(即配置)获取路由。

1
export all;

告诉BIRD将其他公告了解的所有路由导出到内核的路由表中。这使我们可以实际利用此主机上的任何已获取的路由。

1
metric 0;

度量标准值由内核用来确定路由的优先级,并选择优先级最低的路由。在这种情况下,我们将其设置为0或未定义,以便我们首选本地路由。

1
learn;

最后,我们将设置学习指令,该指令将允许其他守护程序从内核路由表中学习路由。

发现直接路由

现在,我们已经配置了BIRD守护程序以将路由直接推送到内核路由表,我们将需要配置对等端以发现本地直接路由。由于我们会将这些路由直接添加到我们的环回接口,因此在选择的编辑器中,将直接协议配置为仅使用lo接口。

1
2
3
4
5
6
ncatelli@ofet> grep -A2 direct conf/peer2/etc/bird/bird.conf
protocol direct {
interface "lo";
}
ncatelli@ofet> docker-compose restart peer2
Restarting bird_examples_peer2_1 ... done

由于我们的网络上也有peer3,因此在此主机上进行相同操作,以防止宣布其他任何路由。

1
2
3
4
5
6
ncatelli@ofet> grep -A2 direct conf/peer3/etc/bird/bird.conf
protocol direct {
interface "lo";
}
ncatelli@ofet> docker-compose restart peer3
Restarting bird_examples_peer3_1 ... done

此时,除了默认的10.0.0.0子网(可以使用birdc进行验证)以外,我们将没有其他路由学习和发布。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@peer2:/# birdc show route all
BIRD 1.6.6 ready.
10.0.0.0/24 via 10.0.0.10 on eth0 [peer1 03:05:02] ! (100) [AS64512i]
Type: BGP unicast univ
BGP.origin: IGP
BGP.as_path: 64512
BGP.next_hop: 10.0.0.10
BGP.local_pref: 100
10.0.100.0/24 via 10.0.0.10 on eth0 [peer1 03:05:02] * (100) [AS64512i]
Type: BGP unicast univ
BGP.origin: IGP
BGP.as_path: 64512
BGP.next_hop: 10.0.0.10
BGP.local_pref: 100

过滤引用和导出

与内核模块类似,导出和导入可用于控制BGP对等方导入和导出的内容。首先,我们探讨过滤的概念以及如何将其用于控制将宣布或导出哪些路由。

BIRD中的过滤器基本上是在路由上执行的函数,返回接受或拒绝。这使我们能够应用一种简单的编程语言来为我们的路由策略添加逻辑。过滤器可以包含任何内容,从单个语句到非常复杂的逻辑。首先,让我们将none和all指令重新实现为过滤器,然后将它们添加到include指令上方的bird.conf文件中。

1
2
3
filter accept_all {
accept;
};
1
2
3
filter reject_all {
reject;
};

现在我们已经有了过滤器,让我们在我们的协议块之一的导入/导出指令中实现它们。在主机peer1上,让我们看一下peer2块。

1
2
3
4
5
6
protocol bgp peer2 {
local as 64512;
neighbor 10.0.0.11 as 64513;
import filter accept_all;
export filter accept_all;
}

从功能上讲,这与我们的原始配置相同,但是现在我们可以使用进一步的逻辑扩展这些设置。通过进一步研究过滤器脚本语言,可以了解这些过滤器的功能。为了扩展我们所学的知识,让我们在peer2上的bird.conf中创建一个过滤器,以控制要向peer1发布的路由。

1
2
3
4
5
6
filter export_subnets {
if net ~ [ 192.168.5.5/32 ] then {
accept;
}
reject;
}

最后,我们需要在peer2上更新peer1以使用此导出过滤器。

1
2
3
4
5
6
root@peer2:/# grep -A4 peer1 /etc/bird/bird.conf
protocol bgp peer1 {
local as 64513;
neighbor 10.0.0.10 as 64512;
export filter export_subnets;
}
1
2
3
ncatelli@ofet> docker-compose restart peer1 peer2
Restarting bird_examples_peer2_1 ... done
Restarting bird_examples_peer1_1 ... done

发布路由

现在,我们有了开始发布peer1peer2之间的路由所需的所有构造块。在此之前,让我们回顾一下我们所做的事情。首先,我们已将BIRD守护程序配置为使用我们的内核协议在其内部路由表和内核路由表之间进行通信。我们已将BIRD守护程序配置为从具有直接协议的环回接口中学习路由。我们还配置了peer1从其他对等节点导入路由并导出这些路由。最终,我们将peer2配置为仅使用export_subnets过滤器将192.168.5.5/32导出到peer1。但是,目前我们还没有从peer2通告到peer1的路由。

1
2
3
4
root@peer1:/# ip route  
default via 10.0.0.1 dev eth0
10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.10
10.0.100.0/24 dev eth1 proto kernel scope link src 10.0.100.10

此时,所有设置已经完成,因此可以从环回接口中学习路由。通过将IP添加到peer2的环回接口上,我们应该能够看到路由的发布。

1
root@peer2:/# ip a add 192.168.5.5/32 dev lo

现在,如果我们同时查看peer1上的birdc和内核路由表,我们应该可以看到peer1上新IP的路由。

1
2
3
4
5
6
root@peer1:~# ip route
default viia 10.0.2.2 dev eth0
10.0.0.0/24 dev eth1 proto kernel scope link src 10.0.0.10
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15
10.0.100.0/24 dev eth2 proto kernel scope link src 10.0.100.10
192.168.5.5 via 10.0.0.11 dev eth1 proto bird
1
2
3
4
5
6
7
8
9
10
11
12
root@peer1:/# birdc show route all
BIRD 1.6.6 ready.
10.0.0.0/24 dev eth0 [direct1 03:10:33] * (240)
Type: device unicast univ
10.0.100.0/24 dev eth1 [direct1 03:10:33] * (240)
Type: device unicast univ
192.168.5.5/32 via 10.0.0.11 on eth0 [peer2 03:12:39] * (100) [AS64513i]
Type: BGP unicast univ
BGP.origin: IGP
BGP.as_path: 64513
BGP.next_hop: 10.0.0.11
BGP.local_pref: 100

ping将显示我们现在可以从peer1向该主机发送流量。

1
2
3
4
5
6
7
root@peer1:/# ping -c 1 192.168.5.5
PING 192.168.5.5 (192.168.5.5) 56(84) bytes of data.
64 bytes from 192.168.5.5: icmp_seq=1 ttl=64 time=0.135 ms

--- 192.168.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.135/0.135/0.135/0.000 ms

现在可以看到这些路由已通过peer1通告到peer3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
root@peer3:/# birdc show route all
BIRD 1.6.6 ready.
10.0.0.0/24 via 10.0.100.10 on eth0 [peer3 03:10:37] * (100) [AS64512i]
Type: BGP unicast univ
BGP.origin: IGP
BGP.as_path: 64512
BGP.next_hop: 10.0.100.10
BGP.local_pref: 100
10.0.100.0/24 via 10.0.100.10 on eth0 [peer3 03:10:37] ! (100) [AS64512i]
Type: BGP unicast univ
BGP.origin: IGP
BGP.as_path: 64512
BGP.next_hop: 10.0.100.10
BGP.local_pref: 100
192.168.5.5/32 via 10.0.100.10 on eth0 [peer3 03:12:38] * (100) [AS64513i]
Type: BGP unicast univ
BGP.origin: IGP
BGP.as_path: 64512 64513
BGP.next_hop: 10.0.100.10
BGP.local_pref: 100
1
2
3
4
5
6
7
root@peer3:/# ping -c 1 192.168.5.5
PING 192.168.5.5 (192.168.5.5) 56(84) bytes of data.
64 bytes from 192.168.5.5: icmp_seq=1 ttl=63 time=0.082 ms

--- 192.168.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.082/0.082/0.082/0.000 ms
1
2
3
4
root@peer3:/# traceroute 192.168.5.5
traceroute to 192.168.5.5 (192.168.5.5), 64 hops max
1 10.0.100.10 0.005ms 0.003ms 0.003ms
2 192.168.5.5 0.003ms 0.003ms 0.003ms

我们也可以通过查看AS PATH来了解情况。通过查看与birdc中的路由关联的AS PATH,可以看到从64513到64512的路由在到达peer3之前发布。

因为将peer1配置为将路由导出到peer3,并且由于peer3配置为从peer1导入路由,所以我们能够将此路由获取到peer3的BIRD路由表中。然后,由于我们已将内核协议配置为在BIRD中导出路由,因此这些路由会将其放入peer3的内核路由表中。

下一步

在这个简单的教程中,我们探讨了许多概念,但是,我们几乎没有涉及bird的概念。随时使用此示例,以发布和过滤路由的方式进一步探索。在以后的教程中,我们将更深入地探讨BGP的工作方式以及用于确定路由的过程,包括如何通信和本地优先级以及BGP守护程序如何使用它们来选择通往服务器的最佳路径。我们还将探讨什么是任播IP,以及如何使用BGP配置高可用性,以及如何使用过滤策略代替直接接口策略来控制向每个节点声明哪些前缀。BGP可以为我们提供对网络拓扑的大量控制,并且了解如何使用BGP可以更好地调整网络以适应自己的需求。