一覧NW-08-1 · ルーティングとNAT

最長一致によるルーティング

宛先に一致する経路を求め、往路と復路が成立する条件を確認します。

説明

ルーターは宛先IPに一致する経路を探し、通常はプレフィックスが最も長い経路を使います。小さなmetricのdefaultがあっても、より具体的な宛先経路が優先されます。

Pythonで一つの転送表を検索するモデルを作り、/16・/24・/32とdefaultの選択を確認します。後半では二つのルーターの往復経路を調べ、行き先に届いても返信が戻らない条件を直します。

1異なる長さの経路を用意する

各行は宛先プレフィックス、次ホップまたは出力先、metricです。この演習は単一の表を検索し、ポリシールーティングやECMPは含めません。

import ipaddressアドレス範囲の処理; routes=転送表を用意[("0.0.0.0/0","wan",1),("10.20.0.0/16","R2",10),("10.20.30.0/24","R3",100),("10.20.30.9/32","R4",500)]; routes経路の内容

/32は1アドレスだけ、/24はその周囲256アドレス、/16はさらに広い範囲に一致します。defaultの/0はすべての宛先に一致する最後の候補です。

2最長一致の検索を定義する

一致する候補のうち、プレフィックス長の降順、同じ長さならmetricの昇順で選びます。

def lookup(address, table):宛先と表を受け取る candidates=一致する経路を抽出[r for r in table if ipaddress.ip_address(address) in ipaddress.ip_network(r[0])] return min長いプレフィックスを優先(candidates,key=lambda r:(-ipaddress.ip_network(r[0]).prefixlen,r[2])) if candidates else None

最初にプレフィックス長を比較し、それが等しい場合だけmetricを使います。実機では経路情報の採用や複数表の検索にも規則がありますが、ここではこの表からの転送先選択を扱います。

3四つの宛先で選択を確かめる

末尾9だけでなく、同じ/24内、別の/24、表外の宛先を検索します。

[(a,lookup(a,routes))宛先ごとの選択結果 for a in検索する宛先 ["10.20.30.9","10.20.30.10","10.20.40.9","198.51.100.20"]]

順にR4、R3、R2、wanが選ばれます。/32のmetric500がdefaultの1より大きくても、宛先により具体的に一致する/32を使います。

4同じプレフィックスならmetricを比較する

同じ/24への別経路をmetric20で追加します。

routes.append同じ範囲への候補を追加(("10.20.30.0/24","R5",20)); lookupmetricの小さい候補を確認("10.20.30.10",routes)

/24同士ではmetric20のR5が選ばれます。この追加は/32のホスト経路を上書きしておらず、末尾9の選択はR4のままです。

5より具体的な経路を取り除く

/32を取り除いた後、末尾9の行き先を検索します。

routes=ホスト経路を取り除く[r for r in routes if r[0]!="10.20.30.9/32"]; lookup残った候補で再検索("10.20.30.9",routes)

次に長い/24が使われ、その中でR5が選ばれます。経路が一つ消えたとき、必ず到達不能になるのではなく、残った候補によって別経路へ変わることがあります。

6往路と復路を別々の表で調べる

端末10.1.0.10はR1、サーバー10.2.0.20はR2のLANにあります。R1からR2への経路だけが登録された状態です。

r1=R1の経路[("10.1.0.0/24","lan1",0),("10.2.0.0/24","R2",10)]; r2=R2の経路[("10.2.0.0/24","lan2",0)]; (lookup往路と復路の検索("10.2.0.20",r1),lookup("10.1.0.10",r2))

往路はR2ですが、返信先10.1.0.10へのR2の検索結果はNoneです。要求を送れる設定だけでは往復は成立しません。端末とサーバーはそれぞれ隣接ルーターを既定経路にしている条件です。

7戻り経路を追加する

R2へ10.1.0.0/24の次ホップR1を追加します。

r2.append返信先への経路を追加(("10.1.0.0/24","R1",10)); (lookup両方向を再確認("10.2.0.20",r1),lookup("10.1.0.10",r2))

往路はR1からR2、復路はR2からR1になりました。実際の転送には次ホップへのリンク、近隣解決、通信を許可する規則も必要です。経路の成立とアプリケーションの応答は段階を分けて確かめます。

8次ホップも接続できる範囲か確かめる

R1とR2の間を192.0.2.0/30とし、R1を.1、R2を.2とします。

transit=ルーター間の範囲ipaddress.ip_network("192.0.2.0/30"); [(a,次ホップ候補の所属ipaddress.ip_address(a) in list(transit.hosts())) for a in ["192.0.2.2","192.0.2.9"]]

.2はこのリンクで使えるホストですが、.9は同じリンク外です。単に次ホップ欄にIPを書いても、その宛先へ到達する仕組みがなければ転送できません。ここでは再帰解決を使わない直接隣接の構成です。

まとめ

宛先への最長一致を優先し、同じプレフィックスの候補でmetricを比較します。

往路・復路それぞれの転送表と、次ホップへ届く条件を確認します。

操作履歴と解説を振り返れます。

Python 3

1
import ipaddress; routes=[("0.0.0.0/0","wan",1),("10.20.0.0/16","R2",10),("10.20.30.0/24","R3",100),("10.20.30.9/32","R4",500)]; routes
[('0.0.0.0/0', 'wan', 1), ('10.20.0.0/16', 'R2', 10), ('10.20.30.0/24', 'R3', 100), ('10.20.30.9/32', 'R4', 500)]
2
def lookup(address, table): candidates=[r for r in table if ipaddress.ip_address(address) in ipaddress.ip_network(r[0])] return min(candidates,key=lambda r:(-ipaddress.ip_network(r[0]).prefixlen,r[2])) if candidates else None
3
[(a,lookup(a,routes)) for a in ["10.20.30.9","10.20.30.10","10.20.40.9","198.51.100.20"]]
[('10.20.30.9', ('10.20.30.9/32', 'R4', 500)), ('10.20.30.10', ('10.20.30.0/24', 'R3', 100)), ('10.20.40.9', ('10.20.0.0/16', 'R2', 10)), ('198.51.100.20', ('0.0.0.0/0', 'wan', 1))]
4
routes.append(("10.20.30.0/24","R5",20)); lookup("10.20.30.10",routes)
('10.20.30.0/24', 'R5', 20)
5
routes=[r for r in routes if r[0]!="10.20.30.9/32"]; lookup("10.20.30.9",routes)
('10.20.30.0/24', 'R5', 20)
6
r1=[("10.1.0.0/24","lan1",0),("10.2.0.0/24","R2",10)]; r2=[("10.2.0.0/24","lan2",0)]; (lookup("10.2.0.20",r1),lookup("10.1.0.10",r2))
(('10.2.0.0/24', 'R2', 10), None)
7
r2.append(("10.1.0.0/24","R1",10)); (lookup("10.2.0.20",r1),lookup("10.1.0.10",r2))
(('10.2.0.0/24', 'R2', 10), ('10.1.0.0/24', 'R1', 10))
8
transit=ipaddress.ip_network("192.0.2.0/30"); [(a,ipaddress.ip_address(a) in list(transit.hosts())) for a in ["192.0.2.2","192.0.2.9"]]
[('192.0.2.2', True), ('192.0.2.9', False)]

演習完了