BIRD 1 (The BIRD Internet Routing Daemon) はルーティングプロトコルを実装した OSS のひとつ。 今回は、そんな BIRD を Network Namespace と組み合わせて BGP-4 を使ったダイナミックルーティングを設定をしてみる。 なお、現在 (2023-06-22) の BIRD はバージョン 1 系と 2 系が平行してメンテナンスされているが、今回使うのはバージョン 2 系になる。
今回のエントリは、以下のエントリの BGP-4 を使ったバージョンになっている。 そのため、こちらに先に目を通しておくと内容を理解しやすい。
BGP-4 には異なる AS (Autonomous System) 間での経路交換に使われる eBGP と、同じ AS 内での経路交換に使われる iBGP の 2 種類の使い方がある。 今回扱うのは前者の eBGP の方だけ。
使った環境は次のとおり。
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=22.04 DISTRIB_CODENAME=jammy DISTRIB_DESCRIPTION="Ubuntu 22.04.2 LTS" $ uname -srm Linux 5.15.0-75-generic x86_64 $ bird --version BIRD version 2.0.8
もくじ
下準備
まずは BIRD のバージョン 2 系をインストールする。
$ sudo apt-get update $ sudo apt-get install bird2
インストールすると同時に BIRD のサービスが動作し始める。 しかし、今回は systemd 経由では BIRD を使わないため止めておく。
$ sudo systemctl stop bird
$ sudo systemctl disable bird
ネットワークを構築する
はじめに Network Namespace を使ってネットワークを構築する。 今回作るのは、以下のようなネットワークになる。 先のスタティックルーティングの構成と変わらない。
上記で、router1
は 198.51.100.0/24
と直接はつながっていないため経路を知らない。
同様に、router2
は 192.0.2.0/24
と直接はつながっていないため経路を知らない。
そこで、router1
と router2
が BIRD の BPG-4 を使って経路を交換するのが今回の目的になる。
まずは Network Namespace を用意する。
$ sudo ip netns add ns1 $ sudo ip netns add router1 $ sudo ip netns add router2 $ sudo ip netns add ns2
Network Namespace 間をつなぐ Virtual Ethernet Device のインターフェイスを用意する。
$ sudo ip link add ns1-veth0 type veth peer name gw1-veth0 $ sudo ip link add gw1-veth1 type veth peer name gw2-veth0 $ sudo ip link add gw2-veth1 type veth peer name ns2-veth0
作成したインターフェイスを Network Namespace に所属させる。
$ sudo ip link set ns1-veth0 netns ns1 $ sudo ip link set gw1-veth0 netns router1 $ sudo ip link set gw1-veth1 netns router1 $ sudo ip link set gw2-veth0 netns router2 $ sudo ip link set gw2-veth1 netns router2 $ sudo ip link set ns2-veth0 netns ns2
それぞれのインターフェイスをリンクアップさせる。
$ sudo ip netns exec ns1 ip link set ns1-veth0 up $ sudo ip netns exec router1 ip link set gw1-veth0 up $ sudo ip netns exec router1 ip link set gw1-veth1 up $ sudo ip netns exec router2 ip link set gw2-veth0 up $ sudo ip netns exec router2 ip link set gw2-veth1 up $ sudo ip netns exec ns2 ip link set ns2-veth0 up
それぞれのインターフェイスに IP アドレスを付与する。
$ sudo ip netns exec ns1 ip address add 192.0.2.1/24 dev ns1-veth0 $ sudo ip netns exec router1 ip address add 192.0.2.254/24 dev gw1-veth0 $ sudo ip netns exec router1 ip address add 203.0.113.1/24 dev gw1-veth1 $ sudo ip netns exec router2 ip address add 203.0.113.2/24 dev gw2-veth0 $ sudo ip netns exec router2 ip address add 198.51.100.254/24 dev gw2-veth1 $ sudo ip netns exec ns2 ip address add 198.51.100.1/24 dev ns2-veth0
ホストに相当する Network Namespace にはデフォルトルートを設定しておく。
$ sudo ip netns exec ns1 ip route add default via 192.0.2.254 $ sudo ip netns exec ns2 ip route add default via 198.51.100.254
そして、ルータになる Network Namespace は IPv4 のルーティングを有効にする。
$ sudo ip netns exec router1 sysctl net.ipv4.ip_forward=1 $ sudo ip netns exec router2 sysctl net.ipv4.ip_forward=1
この状態で、それぞれのルータは自身が直接つながっているセグメントへの経路は把握している。
$ sudo ip netns exec router1 ip route show 192.0.2.0/24 dev gw1-veth0 proto kernel scope link src 192.0.2.254 203.0.113.0/24 dev gw1-veth1 proto kernel scope link src 203.0.113.1
しかし、直接つながっていないセグメントへの経路がないため ns1
から ns2
の間で ping(8) は通らない。
$ sudo ip netns exec ns1 ping -c 3 198.51.100.1 -I 192.0.2.1 PING 198.51.100.1 (198.51.100.1) from 192.0.2.1 : 56(84) bytes of data. From 192.0.2.254 icmp_seq=1 Destination Net Unreachable From 192.0.2.254 icmp_seq=2 Destination Net Unreachable From 192.0.2.254 icmp_seq=3 Destination Net Unreachable --- 198.51.100.1 ping statistics --- 3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2041ms
router1 の BIRD を設定する
まずは router1
の BIRD を設定していく。
以下が BIRD の設定ファイルになる。
$ cat << 'EOF' > router1.conf log syslog all; log stderr all; log "/var/log/bird-router1.log" all; debug protocols all; router id 203.0.113.1; protocol device { } protocol direct { ipv4; } protocol bgp { local as 65001; source address 203.0.113.1; neighbor 203.0.113.2 as 65002; ipv4 { import all; export all; }; } protocol kernel { ipv4 { export filter { if proto = "direct1" then reject; accept; }; }; } EOF
スタティックルーティングの場合から変わっている部分として、まず direct プロトコルがある。 これは、自身が直接つながって知っているネットワークに関する情報を BIRD のルーティングテーブルに取り込む機能になっている。 デフォルトでは disabled;
が設定されており、有効にするときは ipv4;
や ipv6;
を指定して使う。 今回は直接つながっているネットワークの中で IPv4 の情報を取り込んで BGP-4 で広告したいので ipv4;
を設定している。
protocol direct { ipv4; }
そして、今回の主題となる BGP-4 を設定しているのが以下の部分。
BGP が動作する上では必ず AS 番号を設定する必要があるため local as 65001;
の部分で設定している。
AS 番号は世界的に一意な番号で、経路制御をしたい組織ごとに割り振りを受けて使うもの。
ただし、今回は動作確認なのでプライベート AS 番号のレンジ (64512 ~ 65534) から番号を使っている。
プライベート AS 番号は組織内で自由に割り振って使うことができる。
また、BGP のセッションを張る際の送信元 IP アドレスを source address 203.0.113.1;
で設定している。
そして、対向の BGP ピアを neighbor 203.0.113.2 as 65002;
で設定している。
このとき、対向の AS 番号が自身と同じ場合には、自動で iBGP として動作する。
ipv4
の import all;
は BGP で得られるすべての経路を BIRD のルーティングテーブルに取り込むという意味になる。
同様に export all
は、BIRD の知っているすべての経路を BGP で他のルータに広告するという意味になる。
protocol bgp { local as 65001; source address 203.0.113.1; neighbor 203.0.113.2 as 65002; ipv4 { import all; export all; }; }
設定が終わったら BIRD を実行する。
-d
オプションをつけるとデバッグログを有効にした状態で、フォアグラウンドで動作する。
オプションをつけなければ端末を切り離してデーモンとして動作する。
$ sudo ip netns exec router1 bird -d \ -c router1.conf \ -s router1.ctl \ -P router1.pid
router2 の BIRD を設定する
同様に router2
も設定ファイルを用意する。
$ cat << 'EOF' > router2.conf log syslog all; log stderr all; log "/var/log/bird-router2.log" all; debug protocols all; router id 203.0.113.2; protocol device { } protocol direct { ipv4; } protocol bgp { local as 65002; source address 203.0.113.2; neighbor 203.0.113.1 as 65001; ipv4 { import all; export all; }; } protocol kernel { ipv4 { export filter { if proto = "direct1" then reject; accept; }; }; } EOF
もし経路交換の様子を観察したいときは router2
の BIRD を起動する前に tcpdump(1) を実行しておいたほうが良い。
BGP-4 は TCP の 179 番ポートで動作するため、次のようにする。
$ sudo ip netns exec router1 tcpdump -tnlvvv -i gw1-veth1 "tcp port 179"
準備ができたら、新しいターミナルで BIRD を実行する。
$ sudo ip netns exec router2 bird -d \ -c router2.conf \ -s router2.ctl \ -P router2.pid
動作を確認する
両方のルータで BIRD を実行してしばらくすると経路交換が終わる。
試しに router1
の方で BIRD のルーティングテーブルを確認してみよう。
$ sudo birdc show route -s router1.ctl BIRD 2.0.8 ready. Table master4: 198.51.100.0/24 unicast [bgp1 08:55:16.462] * (100) [AS65002i] via 203.0.113.2 on gw1-veth1 192.0.2.0/24 unicast [direct1 08:55:08.802] * (240) dev gw1-veth0 203.0.113.0/24 unicast [direct1 08:55:08.802] * (240) dev gw1-veth1 unicast [bgp1 08:55:16.462] (100) [AS65002i] via 203.0.113.2 on gw1-veth1
198.51.100.0/24
の経路が追加されていることが確認できる。
また、同じ経路はカーネルのルーティングテーブルにも追加されている。
$ sudo ip netns exec router1 ip route show 192.0.2.0/24 dev gw1-veth0 proto kernel scope link src 192.0.2.254 198.51.100.0/24 via 203.0.113.2 dev gw1-veth1 proto bird metric 32 203.0.113.0/24 dev gw1-veth1 proto kernel scope link src 203.0.113.1
BIRD の各プロトコルの稼働状況は次のとおり。
$ sudo birdc show protocols all -s router1.ctl BIRD 2.0.8 ready. Name Proto Table State Since Info device1 Device --- up 08:55:08.802 direct1 Direct --- up 08:55:08.802 Channel ipv4 State: UP Table: master4 Preference: 240 Input filter: ACCEPT Output filter: REJECT Routes: 2 imported, 0 exported, 2 preferred Route change stats: received rejected filtered ignored accepted Import updates: 2 0 0 0 2 Import withdraws: 0 0 --- 0 0 Export updates: 0 0 0 --- 0 Export withdraws: 0 --- --- --- 0 bgp1 BGP --- up 08:55:13.573 Established BGP state: Established Neighbor address: 203.0.113.2 Neighbor AS: 65002 Local AS: 65001 Neighbor ID: 203.0.113.2 Local capabilities Multiprotocol AF announced: ipv4 Route refresh Graceful restart 4-octet AS numbers Enhanced refresh Long-lived graceful restart Neighbor capabilities Multiprotocol AF announced: ipv4 Route refresh Graceful restart 4-octet AS numbers Enhanced refresh Long-lived graceful restart Session: external AS4 Source address: 203.0.113.1 Hold timer: 173.117/240 Keepalive timer: 14.466/80 Channel ipv4 State: UP Table: master4 Preference: 100 Input filter: ACCEPT Output filter: ACCEPT Routes: 2 imported, 2 exported, 1 preferred Route change stats: received rejected filtered ignored accepted Import updates: 2 0 0 0 2 Import withdraws: 0 0 --- 0 0 Export updates: 3 1 0 --- 2 Export withdraws: 0 --- --- --- 0 BGP Next hop: 203.0.113.1 kernel1 Kernel master4 up 08:55:08.802 Channel ipv4 State: UP Table: master4 Preference: 10 Input filter: ACCEPT Output filter: (unnamed) Routes: 0 imported, 1 exported, 0 preferred Route change stats: received rejected filtered ignored accepted Import updates: 0 0 0 0 0 Import withdraws: 0 0 --- 0 0 Export updates: 5 0 4 --- 1 Export withdraws: 0 --- --- --- 0
さて、経路交換の様子はどうなっただろうか。 tcpdump(1) の結果を確認してみよう。
$ sudo ip netns exec router1 tcpdump -tnlvvv -i gw1-veth1 "tcp port 179"
まず、BGP スピーカー同士で TCP のセッションを張る。 以下は、そのスリーウェイハンドシェイクの部分。
IP (tos 0xc0, ttl 1, id 27581, offset 0, flags [DF], proto TCP (6), length 60) 203.0.113.1.52957 > 203.0.113.2.179: Flags [S], cksum 0x7833 (incorrect -> 0x0fb2), seq 147705789, win 64240, options [mss 1460,sackOK,TS val 1023139905 ecr 0,nop,wscale 7], length 0 IP (tos 0xc0, ttl 255, id 0, offset 0, flags [DF], proto TCP (6), length 60) 203.0.113.2.179 > 203.0.113.1.52957: Flags [S.], cksum 0x7833 (incorrect -> 0xb7ef), seq 1708540707, ack 147705790, win 65160, options [mss 1460,sackOK,TS val 1883847382 ecr 1023139905,nop,wscale 7], length 0 IP (tos 0xc0, ttl 1, id 27582, offset 0, flags [DF], proto TCP (6), length 52) 203.0.113.1.52957 > 203.0.113.2.179: Flags [.], cksum 0x782b (incorrect -> 0xe34d), seq 1, ack 1, win 502, options [nop,nop,TS val 1023139906 ecr 1883847382], length 0
最初に Open メッセージがやり取りされる。
このメッセージで、自分の素性やどういった機能を有しているか示す。
BGP-4 は機能拡張がたくさんあるプロトコルなので、こういったやり取りが必要になる。
ここでやり取りした内容は、先ほど birdc show protocols all
した内容の Local / Neighbor capabilities で確認できる。
IP (tos 0xc0, ttl 1, id 27583, offset 0, flags [DF], proto TCP (6), length 105) 203.0.113.1.52957 > 203.0.113.2.179: Flags [P.], cksum 0x7860 (incorrect -> 0x09ea), seq 1:54, ack 1, win 502, options [nop,nop,TS val 1023139906 ecr 1883847382], length 53: BGP Open Message (1), length: 53 Version 4, my AS 65001, Holdtime 240s, ID 203.0.113.1 Optional parameters, length: 24 Option Capabilities Advertisement (2), length: 22 Multiprotocol Extensions (1), length: 4 AFI IPv4 (1), SAFI Unicast (1) 0x0000: 0001 0001 Route Refresh (2), length: 0 Graceful Restart (64), length: 2 Restart Flags: [none], Restart Time 120s 0x0000: 0078 32-Bit AS Number (65), length: 4 4 Byte AS 65001 0x0000: 0000 fde9 Enhanced Route Refresh (70), length: 0 no decoder for Capability 70 Long-lived Graceful Restart (71), length: 0 IP (tos 0xc0, ttl 255, id 3014, offset 0, flags [DF], proto TCP (6), length 52) 203.0.113.2.179 > 203.0.113.1.52957: Flags [.], cksum 0x782b (incorrect -> 0xe311), seq 1, ack 54, win 509, options [nop,nop,TS val 1883847382 ecr 1023139906], length 0 IP (tos 0xc0, ttl 1, id 3015, offset 0, flags [DF], proto TCP (6), length 105) 203.0.113.2.179 > 203.0.113.1.52957: Flags [P.], cksum 0x7860 (incorrect -> 0x08ab), seq 1:54, ack 54, win 509, options [nop,nop,TS val 1883847383 ecr 1023139906], length 53: BGP Open Message (1), length: 53 Version 4, my AS 65002, Holdtime 240s, ID 203.0.113.2 Optional parameters, length: 24 Option Capabilities Advertisement (2), length: 22 Multiprotocol Extensions (1), length: 4 AFI IPv4 (1), SAFI Unicast (1) 0x0000: 0001 0001 Route Refresh (2), length: 0 Graceful Restart (64), length: 2 Restart Flags: [none], Restart Time 120s 0x0000: 0078 32-Bit AS Number (65), length: 4 4 Byte AS 65002 0x0000: 0000 fdea Enhanced Route Refresh (70), length: 0 no decoder for Capability 70 Long-lived Graceful Restart (71), length: 0 IP (tos 0xc0, ttl 1, id 27584, offset 0, flags [DF], proto TCP (6), length 52) 203.0.113.1.52957 > 203.0.113.2.179: Flags [.], cksum 0x782b (incorrect -> 0xe2e1), seq 54, ack 54, win 502, options [nop,nop,TS val 1023139907 ecr 1883847383], length 0
TCP のセッションが切れたりしないように定期的に Keepalive メッセージを流す。
IP (tos 0xc0, ttl 1, id 27585, offset 0, flags [DF], proto TCP (6), length 71) 203.0.113.1.52957 > 203.0.113.2.179: Flags [P.], cksum 0x783e (incorrect -> 0xdeb3), seq 54:73, ack 54, win 502, options [nop,nop,TS val 1023139907 ecr 1883847383], length 19: BGP Keepalive Message (4), length: 19 IP (tos 0xc0, ttl 1, id 3016, offset 0, flags [DF], proto TCP (6), length 71) 203.0.113.2.179 > 203.0.113.1.52957: Flags [P.], cksum 0x783e (incorrect -> 0xde98), seq 54:73, ack 73, win 509, options [nop,nop,TS val 1883847384 ecr 1023139907], length 19: BGP Keepalive Message (4), length: 19 IP (tos 0xc0, ttl 1, id 27586, offset 0, flags [DF], proto TCP (6), length 52) 203.0.113.1.52957 > 203.0.113.2.179: Flags [.], cksum 0x782b (incorrect -> 0xe291), seq 73, ack 73, win 502, options [nop,nop,TS val 1023139948 ecr 1883847384], length 0
そして、実際の経路交換が Update メッセージで行われる。
IP (tos 0xc0, ttl 1, id 27587, offset 0, flags [DF], proto TCP (6), length 103) 203.0.113.1.52957 > 203.0.113.2.179: Flags [P.], cksum 0x785e (incorrect -> 0x78a6), seq 73:124, ack 73, win 502, options [nop,nop,TS val 1023142918 ecr 1883847384], length 51: BGP Update Message (2), length: 51 Origin (1), length: 1, Flags [T]: IGP 0x0000: 00 AS Path (2), length: 6, Flags [T]: 65001 0x0000: 0201 0000 fde9 Next Hop (3), length: 4, Flags [T]: 203.0.113.1 0x0000: cb00 7101 Updated routes: 192.0.2.0/24 203.0.113.0/24 IP (tos 0xc0, ttl 1, id 3017, offset 0, flags [DF], proto TCP (6), length 103) 203.0.113.2.179 > 203.0.113.1.52957: Flags [P.], cksum 0x785e (incorrect -> 0x0376), seq 73:124, ack 124, win 509, options [nop,nop,TS val 1883850394 ecr 1023142918], length 51: BGP Update Message (2), length: 51 Origin (1), length: 1, Flags [T]: IGP 0x0000: 00 AS Path (2), length: 6, Flags [T]: 65002 0x0000: 0201 0000 fdea Next Hop (3), length: 4, Flags [T]: 203.0.113.2 0x0000: cb00 7102 Updated routes: 198.51.100.0/24 203.0.113.0/24 IP (tos 0xc0, ttl 1, id 27588, offset 0, flags [DF], proto TCP (6), length 75) 203.0.113.1.52957 > 203.0.113.2.179: Flags [P.], cksum 0x7842 (incorrect -> 0xc899), seq 124:147, ack 124, win 502, options [nop,nop,TS val 1023142918 ecr 1883850394], length 23: BGP Update Message (2), length: 23 End-of-Rib Marker (empty NLRI) IP (tos 0xc0, ttl 1, id 3018, offset 0, flags [DF], proto TCP (6), length 75) 203.0.113.2.179 > 203.0.113.1.52957: Flags [P.], cksum 0x7842 (incorrect -> 0xc87b), seq 124:147, ack 147, win 509, options [nop,nop,TS val 1883850394 ecr 1023142918], length 23: BGP Update Message (2), length: 23 End-of-Rib Marker (empty NLRI) IP (tos 0xc0, ttl 1, id 27589, offset 0, flags [DF], proto TCP (6), length 52) 203.0.113.1.52957 > 203.0.113.2.179: Flags [.], cksum 0x782b (incorrect -> 0xca6f), seq 147, ack 147, win 502, options [nop,nop,TS val 1023142968 ecr 1883850394], length 0
さて、それでは最初に動作しなかった ns1
と ns2
の間で ping を打ってみよう。
$ sudo ip netns exec ns1 ping -c 3 198.51.100.1 -I 192.0.2.1 PING 198.51.100.1 (198.51.100.1) from 192.0.2.1 : 56(84) bytes of data. 64 bytes from 198.51.100.1: icmp_seq=1 ttl=62 time=0.123 ms 64 bytes from 198.51.100.1: icmp_seq=2 ttl=62 time=0.096 ms 64 bytes from 198.51.100.1: icmp_seq=3 ttl=62 time=0.094 ms --- 198.51.100.1 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2093ms rtt min/avg/max/mdev = 0.094/0.104/0.123/0.013 ms
ちゃんと疎通が得られた。
まとめ
今回は Network Namespace を使って構築したネットワーク上で、BIRD の BGP-4 を動かしてダイナミックルーティングによる経路交換を試した。