CEF(Cisco Express Forwarding)の動作検証メモ
はじめに
現在普及しているCisco機器では、以下のIPフォワーディング処理を、CEF(Cisco Express Forwarding)というCisco独自の方式で実現しています。
- パケット受信
- パケット送信先機器の決定(等コストのパスが複数ある場合、どれを使うかの選択を含む)
- パケット送信
本機能について、IOSで動作確認した結果をメモしておきます。
1. CEF概要
詳細はCiscoサイトや他のブログにお任せして、ポイントだけ記載しておきます。
①フォワーディング処理にあたり、以下テーブルを作成・参照。
テーブル | 説明 |
---|---|
FIBテーブル | ルーティングテーブルをベースに作成される。宛先ネットワークとネクストホップアドレス情報を含む。 |
Adjacencyテーブル | ARPテーブルをベースに作成される。ネクストホップアドレスに紐付くMACアドレス、出力インターフェースとそのMACアドレス情報を含む。 |
②等コストパスのロードバランシング方式は、以下の2種類がある。
方式 | 説明 |
---|---|
宛先単位 (Per-destination) | デフォルトで採用。名前が紛らわしいが送信元と宛先ホストの組み合わせでパスが決定される。 |
パケット単位 (Per-packet) | パケット毎にパスを決定。1つのセッションが複数パスを通り得るため、パケット到着順序逆転により、音声やビデオの通信品質に影響が出る可能性あり。 |
③ロードバランシングアルゴリズムとして、代表的なものは以下。
アルゴリズム | 説明 |
---|---|
Original | 送信元、宛先アドレスから計算したハッシュ値をもとに、パスを決定。名前の通り当初からあるアルゴリズムで、冗長パスが全く使用されなくなるCEFの極性(※)が課題。 |
Universal | デフォルトで採用。CEFの極性を防ぐため、送信元、宛先アドレスに加え、機器に割り振られた4バイト長のユニバーサルIDの組み合わせでハッシュ値を計算。ユニバーサルIDの採番方法は非公開。手動でも設定可能。 |
Include-ports | Universalアルゴリズムに加え、さらに送信元、宛先ポート番号のいずれかもしくは両方をハッシュ計算に利用。 |
Tunnel | こちらもUniversalアルゴリズムの改良版で、トンネルをはっていて、送信元、宛先ペアが少ないケースで利用。 |
※ CEFの極性(Polarization)の詳細はこちら
2. 事前準備
2-1. 検証構成
iosv-0
とiosv-1
(IOSv、15.8(3)M2)を3本のリンクで互いに接続し、desktop-0
からdesktop-1
(Alpine Linux)向けの通信が、3つのパスでバランシングされるようにしました。
2-2. ルーティング設定、CEFテーブル確認
以下のStaticルーティングにより、等コストを実現しています。
ip route 10.2.2.0 255.255.255.0 10.3.3.2
ip route 10.2.2.0 255.255.255.0 10.4.4.2
ip route 10.2.2.0 255.255.255.0 10.5.5.2
ip route 10.1.1.0 255.255.255.0 10.3.3.1
ip route 10.1.1.0 255.255.255.0 10.4.4.1
ip route 10.1.1.0 255.255.255.0 10.5.5.1
iosv-0
のルーティングテーブルを見ると、10.2.2.0/24
宛てのネクストホップが3つ表示されています。
iosv-0#show ip route 10.2.2.0
Routing entry for 10.2.2.0/24
Known via "static", distance 1, metric 0
Routing Descriptor Blocks:
10.5.5.2
Route metric is 0, traffic share count is 1
10.4.4.2
Route metric is 0, traffic share count is 1
* 10.3.3.2
Route metric is 0, traffic share count is 1
CEFはデフォルトで有効化されていますので、このタイミングでFIBテーブルにも反映されていました。
iosv-0#show ip cef 10.2.2.0/24
10.2.2.0/24
nexthop 10.3.3.2 GigabitEthernet0/1
nexthop 10.4.4.2 GigabitEthernet0/2
nexthop 10.5.5.2 GigabitEthernet0/3
2-3. Adjacencyテーブル確認
こちらも事前に確認しておきます。ARPテーブルの中で、自分自身(Ageが-のエントリ)以外は、Adjacencyテーブルに反映されている事が分かります。
iosv-0#show ip arp
Protocol Address Age (min) Hardware Addr Type Interface
Internet 10.1.1.1 - 5254.0015.00ca ARPA GigabitEthernet0/0
Internet 10.1.1.100 4 5254.0017.2fe6 ARPA GigabitEthernet0/0
Internet 10.3.3.1 - 5254.0007.cf14 ARPA GigabitEthernet0/1
Internet 10.3.3.2 117 5254.0015.29b4 ARPA GigabitEthernet0/1
Internet 10.4.4.1 - 5254.0003.aafb ARPA GigabitEthernet0/2
Internet 10.4.4.2 116 5254.001b.27e2 ARPA GigabitEthernet0/2
Internet 10.5.5.1 - 5254.0014.86d7 ARPA GigabitEthernet0/3
Internet 10.5.5.2 83 5254.0011.03b7 ARPA GigabitEthernet0/3
iosv-0#show adjacency
Protocol Interface Address
IP GigabitEthernet0/0 10.1.1.100(7)
IP GigabitEthernet0/1 10.3.3.2(13)
IP GigabitEthernet0/2 10.4.4.2(13)
IP GigabitEthernet0/3 10.5.5.2(13)
2-4. CEF統計情報の有効化
以下設定により、CEFロードバランシングに関する統計情報を収集可能です。具体的には、各パス(ハッシュバケット)へフォワーディングされたパケット数、バイト数、割合などを閲覧できます。
ip cef accounting load-balance-hash
3. Universalアルゴリズム(デフォルト)の動作
3-1. CEF設定内容
設定項目 | 値 |
---|---|
ロードバランシング方式 | 宛先単位 |
アルゴリズム | Universal |
show cef state
コマンドの※部分で設定が確認できます。ユニバーサルIDは「C9C9DAD7」になっています。
iosv-0#show cef state
CEF Status:
RP instance
common CEF enabled
IPv4 CEF Status:
CEF enabled/running
dCEF disabled/not running
CEF switching enabled/running
universal per-destination load sharing algorithm, id C9C9DAD7 ※
IPv6 CEF Status:
CEF disabled/not running
dCEF disabled/not running
universal per-destination load sharing algorithm, id C9C9DAD7
3-2. ロードバランシングの内部データ構造
show ip cef <宛先アドレス> internal
コマンドで確認できます。10.2.2.0/24
の結果を見ると、0~14のハッシュバケットがあり、1:1:1で3つのパスが割り振られています。具体的には、0,3,6,9,12は10.3.3.2
、1,4,7,10,13は10.4.4.2
、2,5,8,11,14は10.5.5.2
をそれぞれネクストホップとしています。
送信元、宛先ホスト情報から計算したハッシュ値に従い、どのバケットに割り振られるか決定されます。
下の方のHash Bucket Usageで、ハッシュバケット単位の使用状況が確認できます。今回はバケット4,6,10,12でそれぞれ8,1,1,1パケットが使われています。
また、Choice usageでChoice(パス)単位の使用状況も確認可能です。今回はChoice0(10.3.3.2
宛て)、Choice1(10.4.4.2
宛て)でそれぞれ2,9パケットが使われています。
iosv-0#show ip cef 10.2.2.0 internal
10.2.2.0/24, epoch 0, RIB[S], refcnt 5, per-destination sharing
sources: RIB
feature space:
IPRM: 0x00048000
ifnums:
GigabitEthernet0/1(3): 10.3.3.2
GigabitEthernet0/2(4): 10.4.4.2
GigabitEthernet0/3(5): 10.5.5.2
path list 0FA962EC, 3 locks, per-destination, flags 0xE9 [shble, rif, rcrsv, hwcn, hwmod]
path 0FA96484, share 1/1, type recursive, for IPv4
recursive via 10.3.3.2[IPv4:Default], fib 0F642708, 1 terminal fib, v4:Default:10.3.3.2/32
path list 0EE08CE4, 2 locks, per-destination, flags 0x49 [shble, rif, hwcn]
path 10E99260, share 1/1, type adjacency prefix, for IPv4
attached to GigabitEthernet0/1, IP adj out of GigabitEthernet0/1, addr 10.3.3.2 0FCDC728
path 0FA964F0, share 1/1, type recursive, for IPv4
recursive via 10.4.4.2[IPv4:Default], fib 0F642684, 1 terminal fib, v4:Default:10.4.4.2/32
path list 0FA9624C, 2 locks, per-destination, flags 0x49 [shble, rif, hwcn]
path 10E991F4, share 1/1, type adjacency prefix, for IPv4
attached to GigabitEthernet0/2, IP adj out of GigabitEthernet0/2, addr 10.4.4.2 0FCDC5F8
path 0FA9655C, share 0/1, type recursive, for IPv4
recursive via 10.5.5.2[IPv4:Default], fib 0F642600, 1 terminal fib, v4:Default:10.5.5.2/32
path list 0FA9629C, 2 locks, per-destination, flags 0x49 [shble, rif, hwcn]
path 10E99188, share 1/1, type adjacency prefix, for IPv4
attached to GigabitEthernet0/3, IP adj out of GigabitEthernet0/3, addr 10.5.5.2 0E7526A0
output chain:
loadinfo 0F640BA8, per-session, 3 choices, flags 0003, 5 locks
flags [Per-session, for-rx-IPv4]
15 hash buckets
< 0 > IP adj out of GigabitEthernet0/1, addr 10.3.3.2 0FCDC728
< 1 > IP adj out of GigabitEthernet0/2, addr 10.4.4.2 0FCDC5F8
< 2 > IP adj out of GigabitEthernet0/3, addr 10.5.5.2 0E7526A0
< 3 > IP adj out of GigabitEthernet0/1, addr 10.3.3.2 0FCDC728
< 4 > IP adj out of GigabitEthernet0/2, addr 10.4.4.2 0FCDC5F8
< 5 > IP adj out of GigabitEthernet0/3, addr 10.5.5.2 0E7526A0
< 6 > IP adj out of GigabitEthernet0/1, addr 10.3.3.2 0FCDC728
< 7 > IP adj out of GigabitEthernet0/2, addr 10.4.4.2 0FCDC5F8
< 8 > IP adj out of GigabitEthernet0/3, addr 10.5.5.2 0E7526A0
< 9 > IP adj out of GigabitEthernet0/1, addr 10.3.3.2 0FCDC728
<10 > IP adj out of GigabitEthernet0/2, addr 10.4.4.2 0FCDC5F8
<11 > IP adj out of GigabitEthernet0/3, addr 10.5.5.2 0E7526A0
<12 > IP adj out of GigabitEthernet0/1, addr 10.3.3.2 0FCDC728
<13 > IP adj out of GigabitEthernet0/2, addr 10.4.4.2 0FCDC5F8
<14 > IP adj out of GigabitEthernet0/3, addr 10.5.5.2 0E7526A0
Subblocks:
Hash Bucket Usage:
Bucket/Choice Packets Bytes Usage Histogram
< 0/0 > 0 0 0.0%
< 1/1 > 0 0 0.0%
< 2/2 > 0 0 0.0%
< 3/0 > 0 0 0.0%
< 4/1 > 8 634 72.7% ==============-
< 5/2 > 0 0 0.0%
< 6/0 > 1 46 9.0% =-
< 7/1 > 0 0 0.0%
< 8/2 > 0 0 0.0%
< 9/0 > 0 0 0.0%
<10/1 > 1 46 9.0% =-
<11/2 > 0 0 0.0%
<12/0 > 1 46 9.0% =-
<13/1 > 0 0 0.0%
<14/2 > 0 0 0.0%
-----------------------------------------------
Totals 11 772 100.0% (ideal:6.6%)
Choice usage:
Choice/Share Packets Bytes Usage Histogram
< 0/1 > 2 92 18.1% ===-
< 1/1 > 9 680 81.8% ================
< 2/1 > 0 0 0.0%
-----------------------------------------------
Totals 11 772 100.0%
ちなみに、等コストパスが2つの場合、0~15の計16個のハッシュバケットを1:1で分割します。(3つの場合は最後が端数のため計15個しか使用されない。)
3-3. ロードバランシング例
以下パケットを例に、どのパスが採用されるか確認してみます。
設定項目 | 値 |
---|---|
送信元アドレス | 10.1.1.100 |
宛先アドレス | 10.2.2.100 |
show ip cef exact-route <送信元アドレス> <宛先アドレス>
コマンドで、どのパスが採用されるか確認可能です。今回の例では、10.5.5.2
がネクストホップとなっています。
iosv-0#show ip cef exact-route 10.1.1.100 10.2.2.100
10.1.1.100 -> 10.2.2.100 =>IP adj out of GigabitEthernet0/3, addr 10.5.5.2
実際にdesktop-0
からTracerouteを実行してみると、上記結果と一致していました。
desktop-0:~$ traceroute 10.2.2.100
traceroute to 10.2.2.100 (10.2.2.100), 30 hops max, 46 byte packets
1 10.1.1.1 (10.1.1.1) 2.601 ms 3.580 ms 2.445 ms
2 10.5.5.2 (10.5.5.2) 3.582 ms 4.952 ms 4.937 ms
3 10.2.2.100 (10.2.2.100) 3.475 ms 3.613 ms 4.632 ms
※IOS-XEベースの仮想プラットフォームCSR1000vで確認した時は、なぜか一致しませんでした。。
4. Include-portsアルゴリズムの動作
4-1. CEF設定内容
設定項目 | 値 |
---|---|
ロードバランシング方式 | 宛先単位 |
アルゴリズム | Include-ports |
以下設定でアルゴリズムをInclude-portsに変更し、送信元と宛先ポート番号をハッシュ計算に含めるようにします。ロードバランシング方式はインターフェース毎に設定しますが、アルゴリズムはグローバル設定となります。
ip cef load-sharing algorithm include-ports source destination
show cef state
コマンドの※部分を見ると、設定が反映されています。
iosv-0#sh cef state
CEF Status:
RP instance
common CEF enabled
IPv4 CEF Status:
CEF enabled/running
dCEF disabled/not running
CEF switching enabled/running
include-ports source destination per-destination load sharing algorithm, id C9C9DAD7 ※
IPv6 CEF Status:
CEF disabled/not running
dCEF disabled/not running
universal per-destination load sharing algorithm, id C9C9DAD7
4-2. ロードバランシング例
以下パケットを例に、どのパスが採用されるか確認してみます。
設定項目 | 値 |
---|---|
送信元アドレス | 10.1.1.100 |
宛先アドレス | 10.2.2.100 |
プロトコル | UDP |
送信元ポート | 49188 |
宛先ポート | 33435から1ずつインクリメント |
show ip cef exact-route <送信元アドレス> src-port <送信元ポート> <宛先アドレス> dest-port <宛先ポート>
コマンドで、どのパスが採用されるか確認可能です。
LinuxのTracerouteはデフォルトでUDPが使われます。各ホップで3回実行する中で、宛先ポート番号が1つずつインクリメントされる動作のため、2ホップ目にあたる33438,33439,33440で確認しました。今回のパターンでは、10.5.5.2
、10.4.4.2
、10.3.3.2
の3つに分散されました。
iosv-0#show ip cef exact-route 10.1.1.100 src-port 49188 10.2.2.100 dest-port 33438
10.1.1.100 -> 10.2.2.100 =>IP adj out of GigabitEthernet0/3, addr 10.5.5.2
iosv-0#show ip cef exact-route 10.1.1.100 src-port 49188 10.2.2.100 dest-port 33439
10.1.1.100 -> 10.2.2.100 =>IP adj out of GigabitEthernet0/2, addr 10.4.4.2
iosv-0#show ip cef exact-route 10.1.1.100 src-port 49188 10.2.2.100 dest-port 33440
10.1.1.100 -> 10.2.2.100 =>IP adj out of GigabitEthernet0/1, addr 10.3.3.2
Traceroute結果は上記と一致しています。
desktop-0:~$ traceroute 10.2.2.100
traceroute to 10.2.2.100 (10.2.2.100), 30 hops max, 46 byte packets
1 10.1.1.1 (10.1.1.1) 2.681 ms 3.021 ms 3.516 ms
2 10.5.5.2 (10.5.5.2) 6.206 ms 10.4.4.2 (10.4.4.2) 6.241 ms 10.3.3.2 (10.3.3.2) 4.941 ms
3 10.2.2.100 (10.2.2.100) 6.918 ms 8.867 ms 6.341 ms
TracerouteのプロトコルをICMPに変更すると、ポート番号情報が無いためパスは1種類だけになりました。WindowsもデフォルトでICMPを利用するため、同様の結果になると思います。
desktop-0:~$ traceroute -I 10.2.2.100
traceroute to 10.2.2.100 (10.2.2.100), 30 hops max, 46 byte packets
1 10.1.1.1 (10.1.1.1) 5.041 ms 3.424 ms 4.228 ms
2 10.4.4.2 (10.4.4.2) 4.637 ms 5.575 ms 5.683 ms
3 10.2.2.100 (10.2.2.100) 11.712 ms 8.464 ms 9.388 ms
5. (参考) Cisco機器発通信の動作
注意が必要なのは、CEFが適用されるのはあくまでも受信してフォワーディングするパケットのみという点です。自身発の通信は、送信元、宛先が同じでもラウンドロビン方式でネクストホップがパケットごとに変わります。
実際に、iosv-0
からdesktop-1
宛てにTracerouteを2回実行してみると、1回目は10.3.3.2
、10.4.4.2
、10.5.5.2
...の順番でネクストホップが変わり、4回試行×2ホップ=計8パケット送信(2と2/3周)した後、2回目は10.5.5.2
から始まっています。
iosv-0#traceroute 10.2.2.100 probe 4
Type escape sequence to abort.
Tracing the route to 10.2.2.100
VRF info: (vrf in name/id, vrf out name/id)
1 10.3.3.2 4 msec
10.4.4.2 5 msec
10.5.5.2 4 msec
10.3.3.2 4 msec
2 10.2.2.100 7 msec 7 msec 9 msec 8 msec
iosv-0#traceroute 10.2.2.100 probe 4
Type escape sequence to abort.
Tracing the route to 10.2.2.100
VRF info: (vrf in name/id, vrf out name/id)
1 10.5.5.2 5 msec
10.3.3.2 3 msec
10.4.4.2 5 msec
10.5.5.2 5 msec
2 10.2.2.100 12 msec 12 msec 11 msec 10 msec
Discussion