0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

動かしながらBGPを理解する

0
Last updated at Posted at 2025-11-02

背景

私は、研究室のベアメタルサーバーを用いてKubernetesクラスタを構築しており、その中で様々な技術的な検証を行なっています。

例えば、Longhornを用いた分散ストレージの検証を行なったり、VM間通信を行うためにVXLANによるオーバーレイネットワークを構築したりもしました。
参考:

以前はブリッジアダプタやVXLANなど、L2での通信技術を検証していましたが、今回はL3での通信を行う技術として、BGPというルーティングプロトコルを用いた通信を検証してみたいと思います。

BGPについて

概要

BGPは、異なるAS(Autonomous System: 自律システム)間で経路情報をやり取りする時に使われるルーティングプロトコルの一つです。

各ASは、IPセグメントとその宛先となるAS番号をテーブルとして保持しており、このテーブルを自動的に他のASと交換するためのプロトコルがBGPです。

VXLANとの比較

以前VXLANでのネットワーク構築も行ったため、BGPとVXLANの比較を簡単に行います。
なお、本内容については以下の書籍を参考にしています。

VXLANは、仮想的なL2ネットワークをL3ネットワークを超えて構築できる技術であり、場所に依存せずサーバ数の増大に対応でき、サーバの場所を移動したとしてもIPアドレスを変更する必要がありません。

なお、VXLANは通信をカプセル化を行うため、観測する場所によって通信データの中身が変化してしまい、可観測性が低いというデメリットがあります。

一方でBGPはL3ネットワークなので、送信先/送信元が場所によって変化したりせず観測しやすく、BGPはVXLAN等のオーバーレイ技術と比較すると、可観測性が高いというメリットがあります。
また、カプセル化のオーバーヘッドがないというメリットもあります。

一方でBGPのデメリットは、ルータのルーティングテーブルを書き換える管理が必要なので、ルーティングテーブルが肥大化した時に各ノードへの負担が増大するという点です。
また、ホストのサブネットに依存するためサーバが移動するとIP変更が必要になる点もVXLANとの違いとして挙げられます。

BGPを用いたネットワーク構築

構成図

最終的には、以下のようなネットワーク構成で通信を行いたいと考えています。
なお、事前にVirtualBoxにより4つのVMを計2台のホスト上に構築済みで、Kubernetesクラスタの構築を目的にしていたため、名前はk8s-master, k8s-worker-1~3としています。

image.png

なお、L2/L3を分離するためにBridgeはIPアドレスを持たずL2レイヤで動くようにしていますが、L3でVM外と通信できるようにするために、vethペアを使いBridgeとL3ルータを仮想ケーブルにより接続しています。Bridge側のインターフェースを veth-br とし、ルータ側のインターフェースを veth-gw としており、L2/L3の境界を明確にしています。

ここでは、L3ゲートウェイである veth-gw の背後にあるVMネットワーク全体( 10.10.10.0/2410.10.20.0/24 )が各ASのL3ネットワーク範囲となります。

BGPでは、各ASが自分の所有する上記のようなサブネットを広告します。それにより、互いに経路情報を学習することで、ルータ間で異なるサブネット間の到達経路を共有します。

このような広告・学習のプロセスにより、異なるL3ネットワーク間で経路選択・ルーティングが自動化され、VMが属する異なるAS間でもL3レベルでの通信が可能になります。

この通信を実現するための設定を以下に行っていきます。

設定(デフォルトゲートウェイ設定まで)

仮想Bridge br-vm 作成

各VMのネットワークインターフェースを束ね、VM間のL2接続として機能する仮想Bridge br-vm を作成します。
また、ホストOSでL3ルーティングを有効化する設定も追加します。

server-1, server-2
sudo ip link add br-vm type bridge
sudo ip link set br-vm up
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-forward.conf
sudo sysctl --system

仮想BridgeにVMを接続

各VMを、仮想Bridgeである br-vm に接続し、同一L2セグメント上に配置します。

server-1
VBoxManage controlvm "k8s-master" poweroff
VBoxManage controlvm "k8s-worker-1" poweroff

VBoxManage modifyvm "k8s-master" --nic2 bridged --bridgeadapter2 "br-vm"
VBoxManage modifyvm "k8s-worker-1" --nic2 bridged --bridgeadapter2 "br-vm"

VBoxManage startvm "k8s-master" --type headless
VBoxManage startvm "k8s-worker-1" --type headless
server-2
VBoxManage controlvm "k8s-worker-2" poweroff
VBoxManage controlvm "k8s-worker-3" poweroff

VBoxManage modifyvm "k8s-worker-2" --nic2 bridged --bridgeadapter2 "br-vm"
VBoxManage modifyvm "k8s-worker-3" --nic2 bridged --bridgeadapter2 "br-vm"


VBoxManage startvm "k8s-worker-2" --type headless
VBoxManage startvm "k8s-worker-3" --type headless

veth作成

2つで1組の仮想LANケーブルのように動作するインターフェースである veth を設定します。 このペアを利用し、L2ブリッジ br-vm と L3ゲートウェイ veth-gw を分離したまま接続します。

veth-br 側を br-vm (L2ブリッジ)に接続し、veth-gw 側にL3ゲートウェイとしてのIPアドレスを割り当てます。

server-1
sudo ip link add veth-gw type veth peer name veth-br
sudo ip link set veth-br master br-vm
sudo ip link set veth-gw up
sudo ip link set veth-br up
sudo ip addr add 10.10.10.1/24 dev veth-gw
server-2
sudo ip link add veth-gw type veth peer name veth-br
sudo ip link set veth-br master br-vm
sudo ip link set veth-gw up
sudo ip link set veth-br up
sudo ip addr add 10.10.20.1/24 dev veth-gw

デフォルトゲートウェイ設定

VMが別サブネットへの通信を行う際、まず自サブネットのゲートウェイである veth-gw にパケットを転送できるように設定します。

sudo vim /etc/netplan/01-bridged.yamlで以下の設定を追加後、sudo netplan apply で反映させました。

k8s-master
network:
  version: 2
  renderer: networkd
  ethernets:
    enp0s8:
      dhcp4: no
      addresses: [10.10.10.101/24] 
      routes:
        - to: 10.10.20.0/24
          via: 10.10.10.1         # veth-gw IP
k8s-worker-1
network:
  version: 2
  renderer: networkd
  ethernets:
    enp0s8:
      dhcp4: no
      addresses: [10.10.10.103/24] 
      routes:
        - to: 10.10.20.0/24
          via: 10.10.10.1         # veth-gw IP
k8s-worker-2
network:
  version: 2
  renderer: networkd
  ethernets:
    enp0s8:
      dhcp4: no
      addresses: [10.10.20.105/24] 
      routes:
        - to: 10.10.10.0/24
          via: 10.10.20.1          # veth-gw IP
k8s-worker-3
network:
  version: 2
  renderer: networkd
  ethernets:
    enp0s8:
      dhcp4: no
      addresses: [10.10.20.107/24] 
      routes:
        - to: 10.10.10.0/24
          via: 10.10.20.1          # veth-gw IP

設定後、各VMが別サブネットへの通信を行う際に veth-gw を経由するようになることをルートテーブルで確認します。

k8s-master
$ ip route
default via 10.0.4.2 dev enp0s9 proto dhcp src 10.0.4.15 metric 100 
10.10.10.0/24 dev enp0s8 proto kernel scope link src 10.10.10.101 
10.10.20.0/24 via 10.10.10.1 dev enp0s8 proto static

ここまでの設定を確認

br-vm に接続されているインターフェースを確認し、veth-br が接続されていることを確認します。

$ bridge link show master br-vm
19: veth-br@veth-gw: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br-vm state forwarding priority 32 cost 2 

スイッチ br-vm が正しく起動していることを確認します。

$ ip -br link show br-vm
br-vm            UP             be:70:0a:f7:2a:98 <BROADCAST,MULTICAST,UP,LOWER_UP>

veth-gw がルータとして機能するためのIPが正しく設定されているかを確認します。
10.10.10.1/24 が問題なく設定されていることが確認できます。

$ ip addr show veth-gw
20: veth-gw@veth-br: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether d2:a4:d0:50:1b:25 brd ff:ff:ff:ff:ff:ff
    inet 10.10.10.1/24 scope global veth-gw
       valid_lft forever preferred_lft forever
    inet6 fe80::d0a4:d0ff:fe50:1b25/64 scope link 
       valid_lft forever preferred_lft forever

VMにsshし、NIC enp0s8 の状態を確認します。
割り振られているIPアドレスが 10.10.10.101/24 であり、veth-gw と同一サブネットであることが確認できます。

k8s-master
$ ip addr show enp0s8
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:92:62:c2 brd ff:ff:ff:ff:ff:ff
    inet 10.10.10.101/24 brd 10.10.10.255 scope global enp0s8
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe92:62c2/64 scope link 
       valid_lft forever preferred_lft forever

ここで、VMからデフォルトゲートウェイである veth-gw にpingが通ることを確認します。

k8s-master
$ ping -c1 10.10.10.1
PING 10.10.10.1 (10.10.10.1) 56(84) bytes of data.
64 bytes from 10.10.10.1: icmp_seq=1 ttl=64 time=0.970 ms

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

BGPによる広告設定

ここまでで各ASが自身のサブネットを持ち、L3で通信できる状態になりました。
次に、各ASが自分の所有するサブネットをBGPで広告し、相互に経路情報を交換できるようにします。

server-1側で、自ASの所有するサブネット 10.10.10.0/24 を広告します。

server-1
sudo tee /etc/frr/frr.conf >/dev/null <<'EOF'
frr defaults traditional
hostname nakazato5-1
service integrated-vtysh-config
!
router bgp 65001
 bgp router-id 192.168.80.10
 neighbor 192.168.80.11 remote-as 65002
 !
 address-family ipv4 unicast
  network 10.10.10.0/24
  neighbor 192.168.80.11 route-map ACCEPT-ALL in
  neighbor 192.168.80.11 route-map ACCEPT-ALL out
 exit-address-family
exit
!
route-map ACCEPT-ALL permit 10
!
EOF
sudo systemctl restart frr
sudo vtysh -c 'write memory'

server-2側で、自ASの所有するサブネット 10.10.20.0/24 を広告します。

server-2
sudo tee /etc/frr/frr.conf >/dev/null <<'EOF'
frr defaults traditional
hostname nakazato5-3
service integrated-vtysh-config
!
router bgp 65002
 bgp router-id 192.168.80.11
 neighbor 192.168.80.10 remote-as 65001
 !
 address-family ipv4 unicast
  network 10.10.20.0/24
  neighbor 192.168.80.10 route-map ACCEPT-ALL in
  neighbor 192.168.80.10 route-map ACCEPT-ALL out
 exit-address-family
exit
!
route-map ACCEPT-ALL permit 10
!
EOF
sudo systemctl restart frr
sudo vtysh -c 'write memory'

また、BGPデーモンを有効化するために、sudo vim /etc/frr/daemons で、bgpd=yes に変更します。

設定後、FRRを再起動します。

sudo systemctl restart frr

広告と学習の確認

BGPセッションの確立状況と経路学習結果を確認していきます。

経路情報の確認

まず、自AS( AS65001 ) が現在認識しているすべての経路情報(自分で広告したネットワーク+他ASから学習したネットワーク) を確認します。

以下の結果から、BGPセッションが確立しており、AS65002 から 10.10.20.0/24 の経路情報を正常に学習できていることがわかります。
これにより、AS65001 配下のVM(k8s-masterなど)が AS65002 配下のVM(k8s-worker-3など)宛てに L3通信できるようになります。

server-1
$ sudo vtysh -c 'show ip bgp'
BGP table version is 2, local router ID is 192.168.80.10, vrf id 0
Default local pref 100, local AS 65001
Status codes:  s suppressed, d damped, h history, * valid, > best, = multipath,
               i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes:  i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

   Network          Next Hop            Metric LocPrf Weight Path
*  10.10.10.0/24    192.168.80.11                           0 65002 65001 i
*>                  0.0.0.0                  0         32768 i
*> 10.10.20.0/24    192.168.80.11             0             0 65002 i

Displayed  2 routes and 3 total paths

Path Attributeの確認

sudo vtysh -c 'show bgp ipv4 unicast 10.10.20.0/24 json' を実行し、BGP経路ごとの詳細な属性情報を確認します。
なお、出力は長いので、jsonから適宜要素を抽出しながら確認を行なっていきます。

server-1 (AS65001) のBGPルータが、server-2 (AS65002) というピアに対して経路を広告している:

server-1
"advertisedTo": {
  "192.168.80.11": {
    "hostname": "server-2"
  }
}

10.10.20.0/24 の経路をserver-2 (AS65002) から学習し、有効かつ最適経路(bestpath)として選んでいる:

server-1
"paths": [
  {
    "aspath": {
      "string": "65002",
      "segments": [
        {
          "type": "as-sequence",
          "list": [65002]
        }
      ],
      "length": 1
    },
    "origin": "IGP",
    "valid": true,
    "bestpath": {
      "overall": true,
      "selectionReason": "First path received"
    }
  }
]

次ホップを 192.168.80.11(server-2)とすることで、宛先へ到達する:

server-1
"nexthops": [
  {
    "ip": "192.168.80.11",
    "hostname": "server-2",
    "accessible": true,
    "used": true
  }
]

経路は外部BGPピアである、192.168.80.11 (AS65002) から学習されたことを示す:

server-1
"peer": {
  "peerId": "192.168.80.11",
  "routerId": "192.168.80.11",
  "hostname": "server-2",
  "type": "external"
}

ルートテーブルの確認

BGP経由で学習した経路が、Linuxカーネルのルートテーブルに反映されていることを確認します。

ここでは、AS65001AS65002 からネットワーク 10.10.20.0/24 の経路情報を学習し、
その結果として 「宛先 10.10.20.0/24 の通信は 192.168.80.11( AS65002 側ルータ)を経由する」 というルートが追加されています。

server-1
$ ip route show proto bgp
10.10.20.0/24 nhid 38 via 192.168.80.11 dev eno1 metric 20 

通信の流れの整理

ここまでの設定により、各ホストOSがBGPルータとして動作し、互いにサブネット情報を交換する状態になっています。
たとえば、k8s-worker-2 ( 10.10.20.105 ) から k8s-master ( 10.10.10.101 ) へパケットを送信する場合、次のような流れで通信が行われます。

  • k8s-worker-2 → server-2の veth-gw
    • k8s-worker-2 は宛先 10.10.10.101 が異なるサブネットであるため、デフォルトゲートウェイ 10.10.20.1(veth-gw) へパケットを転送する。
  • server-2の veth-gw → server-1
    • veth-gw は BGP により AS65001 側 (server-1) から 10.10.10.0/24 の経路情報を学習しており、「宛先 10.10.10.0/24 は次ホップ 192.168.80.10(server-1)である」というルートを保持していいる。
    • そのため、パケットは L3 ルーティングにより物理NIC eno1 経由で server-1 へ転送される。
  • server-1 → k8s-master
    • server-1 に到着したパケットは、veth-gw が受け取り、宛先 10.10.10.101 が自サブネット( 10.10.10.0/24 )であるため、L2転送により直接 k8s-master へ届けられる。
  • k8s-master → k8s-worker-2の応答
    • k8s-master からのICMP応答も、同様にBGP経路を用いて逆方向に返送される。

動作確認

pingによる疎通確認

AS65002 配下の k8s-worker-2( 10.10.20.105 ) から AS65001 配下の k8s-master( 10.10.10.101 )に対して pingが通ることから、BGP経由のルーティングが正常に機能していることがわかります。

k8s-worker-3
# k8s-master IPへping
$ ping 10.10.10.101
PING 10.10.10.101 (10.10.10.101) 56(84) bytes of data.
64 bytes from 10.10.10.101: icmp_seq=1 ttl=62 time=1.12 ms

tcpdumpによるパケットの確認

次に、server-1 側で tcpdump を用いて ICMP パケットを観測します。
VXLAN のようなカプセル化が行われておらず、物理NIC(eno1)上でL3ルーティングにより直接転送されていることが確認できます。

server-1
$ sudo tcpdump -i eno1 -n icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eno1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
14:23:31.154815 IP 10.10.20.105 > 10.10.10.101: ICMP echo request, id 1, seq 198, length 64
14:23:31.154982 IP 10.10.10.101 > 10.10.20.105: ICMP echo reply, id 1, seq 198, length 64
^C
2 packets captured
2 packets received by filter
0 packets dropped by kernel

まとめ

本記事では、BGPを用いたVM間通信を、実際に動かしながら検証してみました。パケットの中身を見ることでVXLANとの違いについても理解を深めることができ、良かったです。

引き続き、興味のある技術について検証していきたいと考えています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?