GoBGP 中文入門指南
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 <neighbor-addr> policy import
$ gobgp neighbor <neighbor-addr> 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
引用鏈接
[1]
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/
本文轉載自:「雲原生實驗室」,原文:https://tinyurl.com/2p8f42uw,版權歸原作者所有。歡迎投稿,投稿郵箱: editor@hi-linux.com。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/9PLUQr8MhvIYK8nIWo-m8g