GoBGPのオプション機能"GoBGP as a Go Native BGP library"を試してみる

最近、GoBGPのドキュメンテーションに、オプション機能["GoBGP as a Go Native BGP library"](https://github.com/osrg/gobgp/blob/master/docs/sources/lib.md#gobgp-as-a-go-native-bgp-library)なるものが記載されていることに気が付きました。
「これは、何なんだろう？」という素朴な疑問を、私なりの解釈で解き明かしてみたいと思います。

# ◼︎ まずは、GoBGP構築準備から ...

次のようなGoBGP環境を作成します。なお、対向BGP Routerに、GoBGPを配備しても問題ありません。

```
                +---------+ (eth1)         iBGP                 +--------+
... ----------+ |  GoBGP  | +---------------------------------+ |  BGP   | +---- ...
                |         | 192.168.0.1/24       192.168.0.2/24 | Router |
                +---------+                                     +--------+
                < AS65000 >                                     < AS65000 >

             <---  Target  --->                            <-- out of scope -->
```

以下、"GoBGP"側の構築手順を記述します。

## (1) Linux基本設定

- Linuxネットワーク設定

```
$ sudo vi /etc/network/interfaces

...(snip)
auto eth1
iface eth1 inet static
address 192.168.0.1
netmask 255.255.255.0

```

- golangのインストール

```
$ vi $HOME/.profile

...(snip)
export GOPATH=$HOME/golang
export PATH=$GOPATH/bin:/usr/local/go/bin:$PATH

$ wget --no-check-certificate https://storage.googleapis.com/golang/go1.6.2.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.6.2.linux-amd64.tar.gz
$ mkdir $HOME/golang
$ source .profile

$ go version
go version go1.6.2 linux/amd64
```

## (2) GoBGPインストール

- GoBGPをインストールします。

```
$ sudo apt-get update
$ sudo apt-get install git
$ go get github.com/osrg/gobgp/gobgpd
$ go get github.com/osrg/gobgp/gobgp
```

# ◼︎ いろいろな方法で、GoBGPを動作させてみる
GoBGPを動作させたことがある方にとっては、GoBGP設定ファイルを用いたGoBGP起動方法を思い浮かべることだと思います。
ここでは、いろいろなGoBGP起動方法について、GoBGP動作結果を比較してみたいと思います。


## (GoBGP起動方法１) gobgp設定ファイルから、GoBGP起動してBGPピアを開設してみる

こちらは、オーソドックスなGoBGP起動方法ですよね。

- 事前に、GoBGP設定ファイルを作成しておきます

```text:$HOME/golang/bin/gobgpd.conf
[[neighbors]]
[neighbors.config]
 neighbor-address = "192.168.0.2"
 peer-as = 65000
 local-as = 65000

[global.config]
 as = 65000
 router-id = "192.168.0.1"
```

- gobgpdを起動します

```
$ cd $GOPATH/bin
$ sudo ./gobgpd -f gobgpd.conf -l debug -p
INFO[0000] gobgpd started
INFO[0000] finished reading the config file
INFO[0000] Peer 192.168.0.2 is added
INFO[0000] Add a peer configuration for 192.168.0.2
DEBU[0000] IdleHoldTimer expired                         Duration=0 Key=192.168.0.2 Topic=Peer
DEBU[0000] state changed                                 Key=192.168.0.2 Topic=Peer new=BGP_FSM_ACTIVE old=BGP_FSM_IDLE reason=idle-hold-timer-expired
DEBU[0013] state changed                                 Key=192.168.0.2 Topic=Peer new=BGP_FSM_OPENSENT old=BGP_FSM_ACTIVE reason=new-connection
DEBU[0013] state changed                                 Key=192.168.0.2 Topic=Peer new=BGP_FSM_OPENCONFIRM old=BGP_FSM_OPENSENT reason=open-msg-received
INFO[0013] Peer Up                                       Key=192.168.0.2 State=BGP_FSM_OPENCONFIRM Topic=Peer
DEBU[0013] state changed                                 Key=192.168.0.2 Topic=Peer new=BGP_FSM_ESTABLISHED old=BGP_FSM_OPENCONFIRM reason=open-msg-negotiated
DEBU[0013] received update                               Key=192.168.0.2 Topic=Peer attributes=[{Origin: ?}  {Nexthop: 192.168.0.2} {LocalPref: 100}] nlri=[20.20.20.0/24] withdrawals=[]
DEBU[0013] create Destination                            Key=20.20.20.0/24 Topic=Table
DEBU[0013] Processing destination                        Key=20.20.20.0/24 Topic=table
DEBU[0013] computeKnownBestPath known pathlist: 1
DEBU[0013] From same AS, ignore.                         Data={ 20.20.20.0/24 | src: { 192.168.0.2 | as: 65000, id: 192.168.0.2 }, nh: 192.168.0.2 } Key=192.168.0.2 Topic=Peer
DEBU[0043] sent                                          Key=192.168.0.2 State=BGP_FSM_ESTABLISHED Topic=Peer data=&{Header:{Marker:[] Len:19 Type:4} Body:0x1234d78}
DEBU[0073] sent                                          Key=192.168.0.2 State=BGP_FSM_ESTABLISHED Topic=Peer data=&{Header:{Marker:[] Len:19 Type:4} Body:0x1234d78}
DEBU[0103] sent                                          Key=192.168.0.2 State=BGP_FSM_ESTABLISHED Topic=Peer data=&{Header:{Marker:[] Len:19 Type:4} Body:0x1234d78}
```

- gobgpコマンドから、Peer情報を確認してみます

```
$ gobgp neighbor 192.168.0.2
BGP neighbor is 192.168.0.2, remote AS 65000
  BGP version 4, remote router ID 192.168.0.2
  BGP state = BGP_FSM_ESTABLISHED, up for 00:00:10
  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:
    BGP_CAP_MULTIPROTOCOL:
        ipv4-unicast:   advertised and received
        l2vpn-evpn:     received
    BGP_CAP_ROUTE_REFRESH:      advertised and received
    BGP_CAP_FOUR_OCTET_AS_NUMBER:       advertised and received
  Message statistics:
                         Sent       Rcvd
    Opens:                  1          1
    Notifications:          0          0
    Updates:                0          1
    Keepalives:             1          1
    Route Refesh:           0          0
    Discarded:              0          0
    Total:                  2          3
  Route statistics:
    Advertised:             0
    Received:               1
    Accepted:               1
```

- gobgpコマンドから、rib情報を確認してみます

```
$ gobgp global rib 
    Network             Next Hop             AS_PATH              Age        Attrs
*>  20.20.20.0/24       192.168.0.2                               00:00:38   [{Origin: ?} {LocalPref: 100}]
```

## (GoBGP起動方法２) gobgpコマンドから、BGPピアを開設してみる
続いての起動方法は、GoBGP設定ファイルを使用せずに、gobgpコマンドで、BGPピア開設に必要な設定情報を投入する方法です。

- gobgpdを起動します

```
$ cd $GOPATH/bin
$ sudo ./gobgpd -l debug -p
INFO[0000] gobgpd started
```

- gobgpコマンドから、Global情報を設定します

```
$ gobgp global as 65000 router-id 192.168.0.1
```

- gobgpコマンドから、BGP Peer情報を設定します

```
$ gobgp neighbor add 192.168.0.2 as 65000
```

- 先ほど起動したgobgpdプロセスのログ出力を確認しておきます


```
$ sudo ./gobgpd -l debug -p
INFO[0000] gobgpd started
INFO[0007] Add a peer configuration for 192.168.0.2
DEBU[0007] IdleHoldTimer expired                         Duration=0 Key=192.168.0.2 Topic=Peer
DEBU[0007] state changed                                 Key=192.168.0.2 Topic=Peer new=BGP_FSM_ACTIVE old=BGP_FSM_IDLE reason=idle-hold-timer-expired
DEBU[0012] accepted a new passive connection from 192.168.0.2
DEBU[0012] state changed                                 Key=192.168.0.2 Topic=Peer new=BGP_FSM_OPENSENT old=BGP_FSM_ACTIVE reason=new-connection
DEBU[0012] state changed                                 Key=192.168.0.2 Topic=Peer new=BGP_FSM_OPENCONFIRM old=BGP_FSM_OPENSENT reason=open-msg-received
INFO[0012] Peer Up                                       Key=192.168.0.2 State=BGP_FSM_OPENCONFIRM Topic=Peer
DEBU[0012] state changed                                 Key=192.168.0.2 Topic=Peer new=BGP_FSM_ESTABLISHED old=BGP_FSM_OPENCONFIRM reason=open-msg-negotiated
DEBU[0012] received update                               Key=192.168.0.2 Topic=Peer attributes=[{Origin: ?}  {Nexthop: 192.168.0.2} {LocalPref: 100}] nlri=[20.20.20.0/24] withdrawals=[]
DEBU[0012] create Destination                            Key=20.20.20.0/24 Topic=Table
DEBU[0012] Processing destination                        Key=20.20.20.0/24 Topic=table
DEBU[0012] computeKnownBestPath known pathlist: 1
DEBU[0012] From same AS, ignore.                         Data={ 20.20.20.0/24 | src: { 192.168.0.2 | as: 65000, id: 192.168.0.2 }, nh: 192.168.0.2 } Key=192.168.0.2 Topic=Peer
DEBU[0042] sent                                          Key=192.168.0.2 State=BGP_FSM_ESTABLISHED Topic=Peer data=&{Header:{Marker:[] Len:19 Type:4} Body:0x1234d78}
DEBU[0072] sent                                          Key=192.168.0.2 State=BGP_FSM_ESTABLISHED Topic=Peer data=&{Header:{Marker:[] Len:19 Type:4} Body:0x1234d78}
DEBU[0102] sent                                          Key=192.168.0.2 State=BGP_FSM_ESTABLISHED Topic=Peer data=&{Header:{Marker:[] Len:19 Type:4} Body:0x1234d78}
```

- gobgpコマンドから、Peer情報を確認してみます

```
$ gobgp neighbor 192.168.0.2
BGP neighbor is 192.168.0.2, remote AS 65000
  BGP version 4, remote router ID 192.168.0.2
  BGP state = BGP_FSM_ESTABLISHED, up for 00:03:29
  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:
    BGP_CAP_MULTIPROTOCOL:
        ipv4-unicast:   advertised and received
        l2vpn-evpn:     received
    BGP_CAP_ROUTE_REFRESH:      advertised and received
    BGP_CAP_FOUR_OCTET_AS_NUMBER:       advertised and received
  Message statistics:
                         Sent       Rcvd
    Opens:                  1          1
    Notifications:          0          0
    Updates:                0          1
    Keepalives:             7          7
    Route Refesh:           0          0
    Discarded:              0          0
    Total:                  8          9
  Route statistics:
    Advertised:             0
    Received:               1
    Accepted:               1
```

- gobgpコマンドから、rib情報を確認してみます

```
$ gobgp global rib 
    Network             Next Hop             AS_PATH              Age        Attrs
*>  20.20.20.0/24       192.168.0.2                               00:04:38   [{Origin: ?} {LocalPref: 100}]
```

## (GoBGP起動方法３) "GoBGP as a Go Native BGP library"活用により、BGPピアを開設してみる

最後の起動方法として、全く馴染みがない"GoBGP as a Go Native BGP library"を活用して、GoBGPを起動してみます。

- 事前に、サンプルコードを準備しておきます

```text:$HOME/golang/bin/example_code.go
package main

import (
    log "github.com/Sirupsen/logrus"
    api "github.com/osrg/gobgp/api"
    "github.com/osrg/gobgp/gobgp/cmd"
    "github.com/osrg/gobgp/packet/bgp"
    gobgp "github.com/osrg/gobgp/server"
)

func main() {
    log.SetLevel(log.DebugLevel)
    s := gobgp.NewBgpServer()
    go s.Serve()

    // start grpc api server. this is not mandatory
    // but you will be able to use `gobgp` cmd with this.
    g := gobgp.NewGrpcServer(":50051", s.GrpcReqCh)
    go g.Serve()

    // global configuration
    req := gobgp.NewGrpcRequest(gobgp.REQ_START_SERVER, "", bgp.RouteFamily(0), &api.StartServerRequest{
        Global: &api.Global{
            As:         65000,
            RouterId:   "192.168.0.1",
            ListenPort: -1, // gobgp won't listen on tcp:179
        },
    })
    s.GrpcReqCh <- req
    res := <-req.ResponseCh
    if err := res.Err(); err != nil {
        log.Fatal(err)
    }

    // neighbor configuration
    req = gobgp.NewGrpcRequest(gobgp.REQ_GRPC_ADD_NEIGHBOR, "", bgp.RouteFamily(0), &api.AddNeighborRequest{
        Peer: &api.Peer{
            Conf: &api.PeerConf{
                NeighborAddress: "192.168.0.2",
                PeerAs:          65000,
            },
            Transport: &api.Transport{
                LocalAddress: "192.168.0.1",
            },
        },
    })
    s.GrpcReqCh <- req
    res = <-req.ResponseCh
    if err := res.Err(); err != nil {
        log.Fatal(err)
    }

    // monitor new routes
    req = gobgp.NewGrpcRequest(gobgp.REQ_MONITOR_RIB, "", bgp.RF_IPv4_UC, &api.Table{
        Type: api.Resource_GLOBAL,
    })
    s.GrpcReqCh <- req
    for res := range req.ResponseCh {
        p, _ := cmd.ApiStruct2Path(res.Data.(*api.Destination).Paths[0])
        cmd.ShowRoute(p, false, false, false, true, false)
    }
}
```

- example codeを起動します

```
$ cd $GOPATH/bin
$ go run example_code.go 
INFO[0000] Add a peer configuration for 192.168.0.2     
DEBU[0000] IdleHoldTimer expired                         Duration=0 Key=192.168.0.2 Topic=Peer
DEBU[0000] state changed                                 Key=192.168.0.2 Topic=Peer new=BGP_FSM_ACTIVE old=BGP_FSM_IDLE reason=idle-hold-timer-expired
DEBU[0010] state changed                                 Key=192.168.0.2 Topic=Peer new=BGP_FSM_OPENSENT old=BGP_FSM_ACTIVE reason=new-connection
DEBU[0010] state changed                                 Key=192.168.0.2 Topic=Peer new=BGP_FSM_OPENCONFIRM old=BGP_FSM_OPENSENT reason=open-msg-received
INFO[0010] Peer Up                                       Key=192.168.0.2 State=BGP_FSM_OPENCONFIRM Topic=Peer
DEBU[0010] state changed                                 Key=192.168.0.2 Topic=Peer new=BGP_FSM_ESTABLISHED old=BGP_FSM_OPENCONFIRM reason=open-msg-negotiated
DEBU[0010] received update                               Key=192.168.0.2 Topic=Peer attributes=[{Origin: ?}  {Nexthop: 192.168.0.2} {LocalPref: 100}] nlri=[20.20.20.0/24] withdrawals=[]
DEBU[0010] create Destination                            Key=20.20.20.0/24 Topic=Table
DEBU[0010] Processing destination                        Key=20.20.20.0/24 Topic=table
DEBU[0010] computeKnownBestPath known pathlist: 1       
DEBU[0010] From same AS, ignore.                         Data={ 20.20.20.0/24 | src: { 192.168.0.2 | as: 65000, id: 192.168.0.2 }, nh: 192.168.0.2 } Key=192.168.0.2 Topic=Peer
[ROUTE] 20.20.20.0/24 via 192.168.0.2 aspath [] attrs [{Origin: ?} {LocalPref: 100}]
DEBU[0040] sent                                          Key=192.168.0.2 State=BGP_FSM_ESTABLISHED Topic=Peer data=&{Header:{Marker:[] Len:19 Type:4} Body:0x1148878}
DEBU[0070] sent                                          Key=192.168.0.2 State=BGP_FSM_ESTABLISHED Topic=Peer data=&{Header:{Marker:[] Len:19 Type:4} Body:0x1148878}
DEBU[0100] sent                                          Key=192.168.0.2 State=BGP_FSM_ESTABLISHED Topic=Peer data=&{Header:{Marker:[] Len:19 Type:4} Body:0x1148878}
```

- gobgpコマンドから、Peer情報を確認してみます

```
$ gobgp neighbor 192.168.0.2
BGP neighbor is 192.168.0.2, remote AS 65000
  BGP version 4, remote router ID 192.168.0.2
  BGP state = BGP_FSM_ESTABLISHED, up for 00:02:13
  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:
    BGP_CAP_MULTIPROTOCOL:
        ipv4-unicast:   advertised and received
        l2vpn-evpn:     received
    BGP_CAP_ROUTE_REFRESH:      advertised and received
    BGP_CAP_FOUR_OCTET_AS_NUMBER:       advertised and received
  Message statistics:
                         Sent       Rcvd
    Opens:                  1          1
    Notifications:          0          0
    Updates:                0          1
    Keepalives:             5          5
    Route Refesh:           0          0
    Discarded:              0          0
    Total:                  6          7
  Route statistics:
    Advertised:             0
    Received:               1
    Accepted:               1
```

- gobgpコマンドから、rib情報を確認してみます

```
$ gobgp global rib 
    Network             Next Hop             AS_PATH              Age        Attrs
*>  20.20.20.0/24       192.168.0.2                               00:02:48   [{Origin: ?} {LocalPref: 100}]
```

以上、３つのGoBGP起動方法を試してみました。GoBGP動作結果を比較してみると、概ね、どれも同じ結果になりました。

# ◼︎ GoBGP起動方法を、DeepDiveしてみる

gobgpdは、"grpcServer"と"bgpServer"から構成されていて、実際のBGP動作に関わる機能ブロックは、"bgpServer"が担っています。そして、BGPピア開設に必要な設定情報を"bgpServer"に伝えるためには、"GrpcReqCh"チャネルを経由することになります。

各々のGoBGP起動方法の全体動作イメージを、簡単にまとめてみました。

## (1) gobgp設定ファイルからのBGP Peer設定
GoBGP設定ファイルを、gobgpd起動時に取り込んで、"GrpcReqCh"チャネルに渡してあげると、"bgpServer"は、BGP開設処理が行わるようです。
なお、別途、"grpcServer"も起動しているので、gobgpコマンドから、bgpServer動作状態を確認することも可能です。

![goBGP Deep Dive.005.jpeg](https://qiita-image-store.s3.amazonaws.com/0/103595/a63db98a-060b-30df-087a-254584483975.jpeg)

## (2) gobgpコマンドからのBGP Peer設定
gobgpコマンドで投入された情報は、grpc経由で、gobgpdに伝えられることになります。
そして、"GrpcReqCh"チャネルに渡してあげると、"bgpServer"は、BGP開設処理が行わるようです。

![goBGP Deep Dive.006.jpeg](https://qiita-image-store.s3.amazonaws.com/0/103595/d2f0b909-5298-6ec7-38be-5aa2b502d0c1.jpeg)


## (3) "GoBGP as a Go Native BGP library"活用によるBGP Peer設定
こちらは、Example Codeから、直接"GrpcReqCh"チャネルに渡してあげると、"bgpServer"は、BGP開設処理が行わるようです。
なお、別途、"grpcServer"も起動しているので、gobgpコマンドから、bgpServer動作状態を確認することも可能です。

![goBGP Deep Dive.007.jpeg](https://qiita-image-store.s3.amazonaws.com/0/103595/7041ce91-06bc-26e4-b285-7e1c90e942cb.jpeg)

どのGoBGP起動方法でも、動作原理は、共通している印象がありますね。

# ◼︎ 最後に、、、
GoBGPのオプション機能"GoBGP as a Go Native BGP library"では、BGP処理に関わるAPIリファレンス的なドキュメントは、一切、提供されていません。GoBGPコードリーディングを通じて、内部動作を理解しながら、カスタマイズ方法を模索していく必要があるのが、難点ですね。
しかしながら、APIリファレンス的な作法を克服してしまえば、GoBGPを自分の好みに応じて、自由にカスタマイズできますというメリットがあります。
まぁ、使い手の判断に委ねられるところですかね。