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

支持特性

性能測試

與 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。如下所示:

[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 操作來修改路由。

Policy Model

Policy model 包括有 Import Policy 和 Export Policy

可以通過以下命令查看 policy

$ gobgp global policy import
$ gobgp global policy export

Route Server Policy Model

對於 Route Server 模式,Import and Export policies 都是針對於一個 Peer 而言的:

$ gobgp neighbor <neighbor-addr> policy import
$ gobgp neighbor <neighbor-addr> policy export

Policy Structure

一個 Policy 包含多個 Statement,每個 Statement 都有自己的 Condtions 和 Actions

Conditions 包括:

Actions 包括:

可以通過以下命令查看 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]:

  1. define defined-sets

  2. define prefix-sets

  3. define neighbor-sets

  4. define bgp-defined-sets

  5. define community-sets

  6. define ext-community-sets

  7. define as-path-setList

  8. define large-community-sets

  9. define policy-definitions

  10. 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。

如下所示,主要需要配置兩個部分:

[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
}

// ...

參考資料

引用鏈接

[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