GoBGP 原理与实践
共 17366字,需浏览 35分钟
·
2022-04-12 23:55
GoBGP 是使用 Go 语言开发的,运行在 Linux 系统上的开源工具,可以提供 BGP 协议的控制平面功能。与 Quagga/FRRouting 相比,GoBGP 的性能更好,收敛时间更短,可以适用于更大规模的网络,比如充当 IXP 路由器。可以使用 Python、C++ 等多种语言,通过 gRPC API 对 GoBGP 进行配置,当然也支持 CLI。GoBGP 还支持 OpenConfig,其 YANG 模型符合 draft-ietf-idr-bgp-model-03[1]。因为 GoBGP 可以很方便地人工干涉路由,参与感更强,是一个很好的实验工具。本文将介绍 gobgp 的主要功能与实践。
背景介绍
安装与组成
GoBGP 的安装非常简单,从 https://github.com/osrg/gobgp/releases 下载 tar.gz 文件,解压即可。此处选择的是 v2.27.0。
$ tar -xzf gobgp_2.27.0_linux_amd64.tar.gz
gobgpd Gobgp 的 daemon 程序,完整的实现了 BGP 协议 可以通过 gRPC API 与 gobgpd 交互 也可以通过配置文件来配置 bgp gobgp Full-featured CLI 可以查看 BGP 相关信息,也可以配置 BGP 配置文件:支持多种格式 toml/yaml/json 等等
支持特性
Full-featured CLI Multiprotocol Support IPv4/Pv6 Labeled IPv4/IPv6 Labeled IPv4/IPv6 EVPN Flowspec IPv4/IPv6/L2 Flexible Policy Graceful Restart Both restarting/helper speak role Route Reflector Route Server MRT Dumping BMP RPKI Validation FIB manipulation gRPC API Standard configuration format
性能测试
与 Quagga/FRRouting 相比,GoBGP 的性能更好,收敛时间更短,可以适用于更大规模的网络,比如充当 IXP 路由器。更多关于 BGP 的性能测试,可以参考 Comparing Open Source BGP stacks with internet routes[2]
与 Quagga/Zebra 等集成
GoBGP 仅支持 BGP 这一种路由协议,但是它可以和 Zebra 集成,通过 API 的方式与 Quagga/FRR 协同工作,以支持多种路由协议。
GoBGP 可以集成到 Quagga/Zebra 体系中:
使用 GoBGP
Basic operation
我们可以启动 gobgpd
作为一个 bgp server,和交换机建立 BGP 连接。
此处有网络拓扑图
比如针对
[global.config]
as = 1002
router-id = "172.25.0.136"
[[neighbors]]
[neighbors.config]
peer-as = 1002
neighbor-address = "172.25.0.129"
auth-password: "xxxxx"
启动 gobgpd:
/ # ./gobgpd -t yaml -f gobgpd.yaml
{"level":"info","msg":"gobgpd started","time":"2022-03-03T07:28:56Z"}
{"Topic":"Config","level":"info","msg":"Finished reading the config file","time":"2022-03-03T07:28:56Z"}
{"level":"info","msg":"Peer 172.25.0.129 is added","time":"2022-03-03T07:28:56Z"}
{"Topic":"Peer","level":"info","msg":"Add a peer configuration for:172.25.0.129","time":"2022-03-03T07:28:56Z"}
{"Key":"172.25.0.129","State":"BGP_FSM_OPENCONFIRM","Topic":"Peer","level":"info","msg":"Peer Up","time":"2022-03-03T07:29:01Z"}
通过 gobgp 查看 peer 信息,这里 State 的 Establ
才表示连接已经建立,如果 State 是 Active
则需要查看交换机配置是否正确。
/ # ./gobgp neighbor
Peer AS Up/Down State |#Received Accepted
172.25.0.129 1002 00:01:29 Establ | 8 8
/ # ./gobgp neighbor 172.25.0.129
BGP neighbor is 172.25.0.129, remote AS 1002
BGP version 4, remote router ID 172.25.100.4
BGP state = ESTABLISHED, up for 00:01:34
BGP OutQ = 0, Flops = 0
Hold time is 90, keepalive interval is 30 seconds
Configured hold time is 90, keepalive interval is 30 seconds
Neighbor capabilities:
multiprotocol:
ipv4-unicast: advertised and received
route-refresh: advertised and received
extended-nexthop: advertised
Local: nlri: ipv4-unicast, nexthop: ipv6
4-octet-as: advertised and received
Message statistics:
Sent Rcvd
Opens: 1 1
Notifications: 0 0
Updates: 0 7
Keepalives: 4 4
Route Refresh: 0 0
Discarded: 0 0
Total: 5 12
Route statistics:
Advertised: 0
Received: 8
Accepted: 8
查看 global table
:
/ # ./gobgp global rib
Network Next Hop AS_PATH Age Attrs
*> 10.0.0.0/24 172.25.0.129 801 45090 45090 00:04:16 [{Origin: ?} {LocalPref: 100}]
*> 10.0.2.0/24 172.25.0.129 801 45090 45090 00:04:16 [{Origin: ?} {LocalPref: 100}]
*> 172.25.0.0/25 172.25.0.129 801 1001 00:04:16 [{Origin: i} {LocalPref: 100}]
*> 172.25.0.128/25 172.25.0.129 00:04:16 [{Origin: i} {Med: 0} {LocalPref: 100}]
*> 172.25.100.1/32 172.25.0.129 801 00:04:16 [{Origin: i} {Med: 0} {LocalPref: 100}]
*> 172.25.100.2/32 172.25.0.129 801 00:04:16 [{Origin: i} {Med: 0} {LocalPref: 100}]
*> 172.25.100.3/32 172.25.0.129 801 1001 00:04:16 [{Origin: i} {LocalPref: 100}]
*> 172.25.100.4/32 172.25.0.129 00:04:16 [{Origin: i} {Med: 0} {LocalPref: 100}]
查看 adjacent rib-in and rib-out
:
/ # ./gobgp neighbor 172.25.0.129 adj-in
ID Network Next Hop AS_PATH Age Attrs
0 10.0.0.0/24 172.25.0.129 801 45090 45090 00:07:18 [{Origin: ?} {LocalPref: 100}]
0 10.0.2.0/24 172.25.0.129 801 45090 45090 00:07:18 [{Origin: ?} {LocalPref: 100}]
0 172.25.0.0/25 172.25.0.129 801 1001 00:07:18 [{Origin: i} {LocalPref: 100}]
0 172.25.0.128/25 172.25.0.129 00:07:18 [{Origin: i} {Med: 0} {LocalPref: 100}]
0 172.25.100.1/32 172.25.0.129 801 00:07:18 [{Origin: i} {Med: 0} {LocalPref: 100}]
0 172.25.100.2/32 172.25.0.129 801 00:07:18 [{Origin: i} {Med: 0} {LocalPref: 100}]
0 172.25.100.3/32 172.25.0.129 801 1001 00:07:18 [{Origin: i} {LocalPref: 100}]
0 172.25.100.4/32 172.25.0.129 00:07:18 [{Origin: i} {Med: 0} {LocalPref: 100}]
/ # ./gobgp neighbor 172.25.0.129 adj-out
Network not in table
可以通过以下命令 宣告路由
gobgp global rib -a ipv4 add 192.168.1.0/24
Route Reflector
为保证 iBGP 对等体之间的连通性,需要在 IBGP 对等体之间建立全连接关系。随着集群规模扩大,Full Mesh 模式效率将急剧降低,Route Reflection[3] 模式是一种成熟的替代方案。RR 方案下允许一个 BGP Speaker (也即是 Route Reflector)向其他 BGP Peer 广播学习到的路由信息,大大减少了 BGP Peer 连接数量。
对于 gobgpd,可以通过修改配置文件,添加 RouteReflector.RouteReflectorConfig
配置来支持 BGP Server 作为 Route Reflector。如下所示:
节点 172.25.0.7 作为 RR 节点,与交换机 172.25.0.1
建立 bgp peer节点 172.25.0.6 作为 RR client 节点,与 RR 节点 172.25.0.7 建立 bgp peer 节点 172.25.0.8 作为 RR client 节点,与 RR 节点 172.25.0.7 建立 bgp peer
[global.config]
as = 1001
router-id = "172.25.0.7"
[[neighbors]]
[neighbors.config]
neighbor-address = "172.25.0.1"
peer-as = 1001
auth-password = "xxxxxx"
[[neighbors]]
[neighbors.config]
neighbor-address = "172.25.0.6"
peer-as = 1001
auth-password = "xxxxxx"
[neighbors.route-reflector.config]
route-reflector-client = true
route-reflector-cluster-id = "172.25.0.137"
[[neighbors]]
[neighbors.config]
neighbor-address = "172.25.0.8"
peer-as = 1001
auth-password = "xxxxxx"
[neighbors.route-reflector.config]
route-reflector-client = true
route-reflector-cluster-id = "172.25.0.137"
Route Server
现网中存在一些场景,为了达到网络流量互通的目的,通常需要通过 eBGP 方式进行全连接。边界设备之间的全连接,对于经费消耗、设备性能要求都是比较高的,并且不利于网络拓扑和设备数量的扩张。Route Server 类似于 IBGP 全连接使用路由反射器,是一台(或多台)用于进行路由服务的设备,其主要的功能是,向各个客户端(边界设备)传播路由,且向客户端发布的路由不修改 AS_PATH、Nexthop、MED 等路径属性,从而减轻边界路由器全连接的消耗。
如下图所示,一个 IX (Internet eXchange) 中,包含多个独立的 SP (service provider),这些网络想要实现流量互通。每个 SP 都有一个边界路由器连接到公共的交换网络。每个 SP 都有自己的 AS 号,BGP Router 的地址从 10.0.0.1 到 10.0.0.8。
这种情况下,要求这 8 个 BGP Peer 建立全连接,和 iBGP 一样,这种 full mesh 连接对于经费消耗、设备性能要求都是比较高的,并且不利于网络拓扑和设备数量的扩张。
BGP Route Server 可以简化 SP 的连接,如下所示:
下图展示了 route server 实现的透明路由传播:
更多关于 route server 的信息,可以参考 Route Server[4]。
对于 GoBGP 同样支持 Route Server:
[global.config]
as = 64512
router-id = "192.168.255.1"
[[neighbors]]
[neighbors.config]
neighbor-address = "10.0.255.1"
peer-as = 65001
auth-password = "hoge1"
[neighbors.transport.config]
passive-mode = true
[neighbors.route-server.config]
route-server-client = true
[[neighbors]]
[neighbors.config]
neighbor-address = "10.0.255.2"
peer-as = 65002
auth-password = "hoge2"
[neighbors.transport.config]
passive-mode = true
[neighbors.route-server.config]
route-server-client = true
BGP Policy
Policy 是一种控制 BGP 路由如何插入到 RIB 或者广播给 BGP Peer 的方法,分为两个部分 Condition
和 Action
。当 Policy 配置完成后,触发 Condition 条件后,会执行 Action 操作来修改路由。
Condition 包括 prefix
,neighbor
(source/destination of the route) 和aspath
等Action 包括 accept
,reject
,MED/aspath/community manipulation
等
Policy Model
Policy model 包括有 Import Policy
和 Export Policy
:
Import policy is invoked before best path calculation and pushing routes to RIB. Export policy is invoked after that.
可以通过以下命令查看 policy
$ gobgp global policy import
$ gobgp global policy export
Route Server Policy Model
对于 Route Server 模式,Import and Export policies 都是针对于一个 Peer 而言的:
The Import policy defines what routes will be imported into the master RIB. The Export policy defines what routes will be exported from the master RIB.
$ gobgp neighbor policy import
$ gobgp neighbor policy export
Policy Structure
一个 Policy 包含多个 Statement,每个 Statement 都有自己的 Condtions 和 Actions
Conditions 包括:
prefix neighbor aspath aspath length community extended community rpki validation result route type (internal/external/local) large community afi-safi in
Actions 包括:
accept or reject add/replace/remove community or remove all communities add/subtract or replace MED value set next-hop (specific address/own local address/don’t modify) set local-pref prepend AS number in the AS_PATH attribute
可以通过以下命令查看 Policy 配置
$ gobgp policy
$ gobgp policy statement
$ gobgp policy prefix
$ gobgp policy neighbor
$ gobgp policy as-path
$ gobgp policy community
$ gobgp policy ext-community
$ gobgp policy large-community
Policy Configuration
Policy 配置比较复杂,以下是配置的步骤,具体可以参考 这里[5]:
define defined-sets define prefix-sets define neighbor-sets define bgp-defined-sets define community-sets define ext-community-sets define as-path-setList define large-community-sets define policy-definitions attach policies to global rib (or neighbor local rib when neighbor is route-server-client[6]).
Graceful Restart
[global.config]
as = 64512
router-id = "192.168.255.1"
[[neighbors]]
[neighbors.config]
neighbor-address = "10.0.255.1"
peer-as = 65001
[neighbors.graceful-restart.config]
enabled = true
BMP
GoBGP 支持 BGP Monitoring Protocol (RFC 7854)[7] 对 BGP 会话的运行状态进行实时监控,包括对等体关系的建立与关闭、路由信息等。
[global.config]
as = 64512
router-id = "192.168.255.1"
[[bmp-servers]]
[bmp-servers.config]
address = "127.0.0.1"
port=11019
Dynamic Neighbors
在 BGP 网络中,当多个对等体经常发生变动时,如果采用静态配置对等体的方式,则需频繁地在本端进行增加或删除对等体的配置,维护工作量很大。此时可以配置 BGP 动态对等体功能,使 BGP 侦听指定网段的 BGP 连接请求并动态建立 BGP 对等体,同时将这些对等体加入到同一个对等体组中。这样当对等体发生变动时,无需在本端进行增加或删除 BGP 对等体的配置,减少网络维护的工作量。
交换机都一般都支持配置 Dynamic Neighbors,比如 这里是华为交换机配置 Dynamic Neighbors 方法[8],对于 gobgp 同样也支持 Dynamic Neighbors。
如下所示,主要需要配置两个部分:
创建一个 peer group,描述这个 peer group 的基本信息 配置 peer group 监听在 172.40.0.0/16
网段
[global.config]
as = 65001
router-id = "172.40.1.2"
[[peer-groups]]
[peer-groups.config]
peer-group-name = "sample-group"
peer-as = 65002
[[peer-groups.afi-safis]]
[peer-groups.afi-safis.config]
afi-safi-name = "ipv4-unicast"
[[peer-groups.afi-safis]]
[peer-groups.afi-safis.config]
afi-safi-name = "ipv4-flowspec"
[[dynamic-neighbors]]
[dynamic-neighbors.config]
prefix = "172.40.0.0/16"
peer-group = "sample-group"
Others
在 GitHub 中还有很多其他关于 MRT/BMP/EVPN 等特性的说明,此处不再赘述,如有需要可以直接查看文档。
GoBGP 编程
Basic Server
参考 gobgp 库 提供的文档[9],我们可以实现一个简单的 go bgp server,如下所示:
package main
import (
"context"
"time"
"github.com/sirupsen/logrus"
apb "google.golang.org/protobuf/types/known/anypb"
api "github.com/osrg/gobgp/v3/api"
"github.com/osrg/gobgp/v3/pkg/log"
"github.com/osrg/gobgp/v3/pkg/server"
)
func main() {
log := logrus.New()
// 创建 BGP Server 实例
s := server.NewBgpServer(server.LoggerOption(&myLogger{logger: log}))
go s.Serve()
// global configuration
if err := s.StartBgp(context.Background(), &api.StartBgpRequest{
Global: &api.Global{
Asn: 65003,
RouterId: "10.0.255.254",
ListenPort: -1, // gobgp won't listen on tcp:179
},
}); err != nil {
log.Fatal(err)
}
// monitor the change of the peer state
if err := s.MonitorPeer(ctx, &api.MonitorPeerRequest{}, func(p *api.Peer) { log.Print(p) }); err != nil {
log.Fatal(err)
}
// neighbor configuration
n := &api.Peer{
Conf: &api.PeerConf{
NeighborAddress: "172.17.0.2",
PeerAsn: 65002,
},
}
if err := s.AddPeer(context.Background(), &api.AddPeerRequest{
Peer: n,
}); err != nil {
log.Fatal(err)
}
// add routes
nlri, _ := apb.New(&api.IPAddressPrefix{
Prefix: "10.0.0.0",
PrefixLen: 24,
})
a1, _ := apb.New(&api.OriginAttribute{
Origin: 0,
})
a2, _ := apb.New(&api.NextHopAttribute{
NextHop: "10.0.0.1",
})
a3, _ := apb.New(&api.AsPathAttribute{
Segments: []*api.AsSegment{
{
Type: 2,
Numbers: []uint32{6762, 39919, 65000, 35753, 65000},
},
},
})
attrs := []*apb.Any{a1, a2, a3}
_, err := s.AddPath(context.Background(), &api.AddPathRequest{
Path: &api.Path{
Family: &api.Family{Afi: api.Family_AFI_IP, Safi: api.Family_SAFI_UNICAST},
Nlri: nlri,
Pattrs: attrs,
},
})
if err != nil {
log.Fatal(err)
}
v6Family := &api.Family{
Afi: api.Family_AFI_IP6,
Safi: api.Family_SAFI_UNICAST,
}
// add v6 route
nlri, _ = apb.New(&api.IPAddressPrefix{
PrefixLen: 64,
Prefix: "2001:db8:1::",
})
v6Attrs, _ := apb.New(&api.MpReachNLRIAttribute{
Family: v6Family,
NextHops: []string{"2001:db8::1"},
Nlris: []*apb.Any{nlri},
})
c, _ := apb.New(&api.CommunitiesAttribute{
Communities: []uint32{100, 200},
})
_, err = s.AddPath(context.Background(), &api.AddPathRequest{
Path: &api.Path{
Family: v6Family,
Nlri: nlri,
Pattrs: []*apb.Any{a1, v6Attrs, c},
},
})
if err != nil {
log.Fatal(err)
}
s.ListPath(context.Background(), &api.ListPathRequest{Family: v6Family}, func(p *api.Destination) {
log.Info(p)
})
// do something useful here instead of exiting
time.Sleep(time.Minute * 3)
}
// ...
可以看到,示例代码相对比较简单,主要使用了以下的 API:
// 创建 BGP Server 实例
func NewBgpServer(opt ...ServerOption) *BgpServer
// 启动 BGP Server
func (s *BgpServer) Serve()
// global 配置
// BGP Server 的 AS 是 65003,RouterId 是 10.0.255.254
api.Global{
Asn: 65003,
RouterId: "10.0.255.254",
ListenPort: -1, // gobgp won't listen on tcp:179
}
// 根据传入 global 配置,开启 BGP Server 的 BGP 协商
func (s *BgpServer) StartBgp(ctx context.Context, r *api.StartBgpRequest) error
// 观察 BGP Peer 状态变化
func (s *BgpServer) MonitorPeer(ctx context.Context, r *api.MonitorPeerRequest, fn func(*api.Peer)) error
// Peer 信息
api.Peer{
Conf: &api.PeerConf{
NeighborAddress: "172.17.0.2",
PeerAsn: 65002,
},
}
// 建立 BGP Peer 连接
func (s *BgpServer) AddPeer(ctx context.Context, r *api.AddPeerRequest) error
// 传递 BGP 路由信息
func (s *BgpServer) AddPath(ctx context.Context, r *api.AddPathRequest) (*api.AddPathResponse, error)
Route Reflector
可以通过对 api.Peer
这个结构进行更详细的配置,使得加入的 BGP Peer 是作为 RR client:
n := &api.Peer{
Conf: &api.PeerConf{
NeighborAddress: "172.25.0.6",
PeerAsn: 1001,
},
RouteReflector: &api.RouteReflector{
RouteReflectorClient: true,
RouteReflectorClusterId: "172.25.0.7",
}
}
BMP
这里列出了几种常见的 BMP Message:
type BMPMessage struct {
Header BMPHeader
PeerHeader BMPPeerHeader
Body BMPBody
}
type BMPRouteMonitoring struct {
BGPUpdate *bgp.BGPMessage
BGPUpdatePayload []byte
}
type BMPPeerDownNotification struct {
Reason uint8
BGPNotification *bgp.BGPMessage
Data []byte
}
type BMPPeerUpNotification struct {
LocalAddress net.IP
LocalPort uint16
RemotePort uint16
SentOpenMsg *bgp.BGPMessage
ReceivedOpenMsg *bgp.BGPMessage
}
// ...
参考资料
GoBGP 配置[10] GoBPG 命令行工具使用[11] IETF: Internet Exchange BGP Route Server[12] Tutorial: Using GoBGP as an IXP connecting router[13] GoBGP library[14] GoBGP slides[15] Comparing Open Source BGP stacks with internet routes[16] draft-ietf-idr-bgp-model-03[17] bgp perf[18] Route Server[19] https://www.twblogs.net/a/5db3af42bd9eee310da061d0
引用链接
draft-ietf-idr-bgp-model-03: https://datatracker.ietf.org/doc/html/draft-ietf-idr-bgp-model-03
[2]Comparing Open Source BGP stacks with internet routes: https://elegantnetwork.github.io/posts/comparing-open-source-bgp-internet-routes
[3]Route Reflection: https://datatracker.ietf.org/doc/html/rfc2796
[4]Route Server: https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/iproute_bgp/configuration/xe-3s/irg-xe-3s-book/irg-route-server.pdf
[5]这里: https://github.com/osrg/gobgp/blob/master/docs/sources/policy.md#examples
[6]route-server-client: https://github.com/osrg/gobgp/blob/master/docs/sources/route-server.md
[7]BGP Monitoring Protocol (RFC 7854): https://tools.ietf.org/html/rfc7854
[8]这里是华为交换机配置 Dynamic Neighbors 方法: https://support.huawei.com/enterprise/zh/doc/EDOC1100034251/f1ac6afd
[9]提供的文档: https://github.com/osrg/gobgp/blob/master/docs/sources/lib.md
[10]GoBGP 配置: https://github.com/osrg/gobgp/blob/master/docs/sources/configuration.md
[11]GoBPG 命令行工具使用: https://github.com/osrg/gobgp/blob/master/docs/sources/cli-command-syntax.md
[12]IETF: Internet Exchange BGP Route Server: https://datatracker.ietf.org/doc/html/rfc7947
[13]Tutorial: Using GoBGP as an IXP connecting router: http://www.slideshare.net/shusugimoto1986/tutorial-using-gobgp-as-an-ixp-connecting-router
[14]GoBGP library: https://pkg.go.dev/github.com/keshonok/gobgp@v0.0.0-20210807180423-09a4a8306818
[15]GoBGP slides: https://ripe71.ripe.net/presentations/135-RIPE71_GoBGP.pdf
[16]Comparing Open Source BGP stacks with internet routes: https://elegantnetwork.github.io/posts/comparing-open-source-bgp-internet-routes/
[17]draft-ietf-idr-bgp-model-03: https://datatracker.ietf.org/doc/html/draft-ietf-idr-bgp-model-03
[18]bgp perf: https://github.com/osrg/bgperf
[19]Route Server: https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/iproute_bgp/configuration/xe-3s/irg-xe-3s-book/irg-route-server.pdf
原文链接:https://houmin.cc/posts/23fb5364/
你可能还喜欢
点击下方图片即可阅读
云原生是一种信仰 🤘
关注公众号
后台回复◉k8s◉获取史上最方便快捷的 Kubernetes 高可用部署工具,只需一条命令,连 ssh 都不需要!
点击 "阅读原文" 获取更好的阅读体验!
发现朋友圈变“安静”了吗?