Protocol in Code · BGP Session 01

What A BGP Neighbor Needs

BGP neighbor を張るとき、私たちはふつう設定コマンドを並べて覚えます。ここでは GitHub 上の session.py を開いて、それを関数の入力と state machine として読み替えます。

Intermediate Session 01 BGP first RFC 4271 Sections 1.1, 3, 4.2

Session Goal

このセッションで終わらせたいこと

Session 01 のゴールは、BGP neighbor を「設定コマンドの束」としてではなく、 「入力が足りると state が進み、足りないと途中で止まるロジック」として説明できるようになることです。

  • `peer_ip` だけでは足りない理由を言える
  • TCP failure と OPEN failure の違いを言える
  • `Established` に至るまでの state 名を追える
  • どの `if` で止まったかを source から説明できる

Toy Model Boundary

この lesson で実際に state を進める field

hold_timekeepalive_timecapabilities は realistic な session surface として config に置いています。ただし、この lesson の最小 state machine が実際に branch しているのは peer_iptcp_reachable、AS 番号、open_message_okkeepalive_received です。

Core Idea

BGPSessionConfig が neighbor に必要な入力をまとめる

`session.py` では、neighbor を張るための入力が `BGPSessionConfig` に集められています。 単に `peer_ip` を知っているだけでは成立せず、local AS と remote AS、TCP 到達性、OPEN の成否、KEEPALIVE 受信まで 揃って初めて `Established` まで進みます。

`neighbor` を 1 行の設定として覚えるより、「どの field が足りないと session が進まないか」を意識したほうが、 エラー時の切り分けがずっとしやすくなります。

Read The Source

まずは dataclass の field を読む

src/protocol_in_code/bgp/session.py
@dataclass(frozen=True)
class BGPSessionConfig:
    peer_ip: str
    peer_as: int
    local_as: int
    tcp_reachable: bool
    hold_time: int = 180
    keepalive_time: int = 60
    capabilities: tuple[BGPCapability, ...] = field(default_factory=tuple)
    open_message_ok: bool = True
    keepalive_received: bool = True

ここでは pseudo-code を捨てて、実際の field をそのまま読めば十分です。 各入力が欠けたときに、どこで止まるかを考えます。

field なぜ必要か
peer_ip TCP の接続先が必要。そもそも相手に届かなければ OPEN まで進まない。
peer_as 相手が想定した AS かどうかを判断するために必要。eBGP / iBGP の区別にも直結する。
local_as 自分が誰として名乗るかが必要。OPEN message に含まれる。
tcp_reachable BGP は TCP の上に乗る。TCP が張れなければ BGP state machine は進まない。
hold_time / keepalive_time session を維持するための時間条件。交渉と監視の両方に関わる。
capabilities Multiprotocol extensions など、何を話せるかの前提を揃えるために必要。

Read Order

この順番で読むと迷いにくい

  1. BGPSessionConfig の field を見て、必要入力を先に掴む
  2. SessionState を見て、どんな停止地点があるか知る
  3. establish_neighbor() を上から追って early return を確認する
  4. walkthrough script を実行して、各 scenario がどの state で終わるか見る

State Machine

establish_neighbor() は state の遷移として書ける

src/protocol_in_code/bgp/session.py
def establish_neighbor(config: BGPSessionConfig) -> SessionState:
    state = SessionState.IDLE

    if not config.peer_ip:
        return state

    state = SessionState.CONNECT
    if not config.tcp_reachable:
        return SessionState.ACTIVE

    state = SessionState.OPEN_SENT
    if config.peer_as <= 0 or config.local_as <= 0:
        return state
    if not config.open_message_ok:
        return state

    state = SessionState.OPEN_CONFIRM
    if not config.keepalive_received:
        return state

    return SessionState.ESTABLISHED

「neighbor が張れない」と言うとき、本当はどこで return しているかを見ないといけません。 `ACTIVE` で返るなら TCP 到達性、`OPEN_SENT` で返るなら AS 番号や OPEN negotiation、`OPEN_CONFIRM` で返るなら KEEPALIVE まであと一歩、 という読み方になります。

Failure Reading

if 文のどこで落ちるかを説明できるようにする

reading checklist
1. peer_ip が空ではないか
2. tcp_reachable が true か
3. peer_as と local_as が妥当か
4. open_message_ok が true か
5. keepalive_received が true か

ここで大事なのは、session 失敗を「BGP が壊れた」でまとめないことです。 どの条件が満たされず、どの `if` で止まったかを分けて読むのが `Protocol in Code` の中級コースの狙いです。

Walkthrough

手元で 6 つの scenario を流す

GitHub repo 側には Session 01 用の walkthrough script を置いてあります。 これを流すと、`Idle`、`Active`、`OpenSent`、`OpenConfirm`、`Established` のどこで止まるかをまとめて確認できます。

run locally
cd protocol-in-code
PYTHONPATH=src python3 examples/bgp/session_01_walkthrough.py
実行結果を読むときは、設定が正しいかどうかではなく、 「この scenario はどの入力が足りず、どの state で返ったか」を見ます。

Done Check

Session 01 を終えたと言える条件

  • BGP は TCP 到達性の前に始まらない、と説明できる
  • AS mismatch や OPEN failure は `OpenSent` 付近の問題だと説明できる
  • KEEPALIVE 未達は `OpenConfirm` から `Established` へ進めない問題だと説明できる
  • `neighbor が張れない` を 1 つの曖昧な言い方で終わらせず、停止地点を言い分けられる

Next

次は UPDATE を state change として読む

neighbor が `Established` になったあと、何が route table を変えるのかを見るのが Module 02 です。 次は GitHub 上の `update.py` を開いて、UPDATE を `apply_update()` と `withdraw()` の 2 種類の操作として読みます。