ROUTE_TABLE (APPL_DB)¶
概要¶
ROUTE_TABLE は FRR (zebra) から FPM プロトコル経由で受け取った経路情報を APPL_DB に保持するテーブル4。fpmsyncd が netlink メッセージを受信して書き込み、orchagent の RouteOrch が読み取って SAI route エントリとして実装する。
APPL_DB テーブル
ROUTE_TABLE は APPL_DB テーブルであり、CONFIG_DB には存在しない。静的経路は CONFIG_DB の STATIC_ROUTE テーブルで管理し、bgpcfgd / staticd を経由して最終的にこのテーブルに反映される。
データフロー¶
flowchart LR
FRR["FRR (zebra)<br/>rtm_protocol"]
FPM["fpmsyncd<br/>RouteSync"]
APPDB[("APPL_DB<br/>ROUTE_TABLE")]
OA["orchagent<br/>RouteOrch"]
SYNCD["syncd"]
SAI["SAI<br/>sai_route_api"]
FRR -->|FPM/netlink| FPM
FPM --> APPDB
APPDB --> OA
OA --> SYNCD
SYNCD --> SAI
凡例
FRR から SAI までの典型転送経路。詳細は本ページ本文と引用コードを参照。
key 構造¶
<prefix>は IPv4 / IPv6 プレフィックス(例:192.168.1.0/24、2001:db8::/32)。<vrf_name>はVrfプレフィックスで始まる VRF デバイス名(例:Vrf-RED)。- 管理 VRF (
mgmt) 向け経路は fpmsyncd がスキップするため、このテーブルには存在しない5。
主要フィールド¶
| フィールド | 型 | 暗黙デフォルト | 説明 |
|---|---|---|---|
protocol |
string | "" (フィールド不在) |
経路プロトコル名(bgp、static、kernel 等)。rtm_protocol 番号を iproute2 ライブラリで解決 |
blackhole |
string | "false" (フィールド不在) |
blackhole 経路の場合のみ "true" が書き込まれる |
nexthop |
string | "" (フィールド不在) |
カンマ区切り nexthop IP リスト。interface route では "0.0.0.0" / "::" |
ifname |
string | "" (フィールド不在) |
カンマ区切り出力インターフェース名リスト |
nexthop_group |
string | "" (フィールド不在) |
kernel nexthop group ID 文字列。nexthop / ifname と相互排他 |
mpls_nh |
string | "" (フィールド不在) |
MPLS nexthop ラベルスタック。MPLS 経路のみ設定 |
weight |
string | "1" per nexthop |
カンマ区切り ECMP 重みリスト。netlink weight=0 は 1 にフォールバック |
vni_label |
string | "" (フィールド不在) |
EVPN VNI のカンマ区切りリスト。EVPN 経路のみ |
router_mac |
string | "" (フィールド不在) |
EVPN 宛先 VTEP MAC アドレスのカンマ区切りリスト |
segment |
string | "" (フィールド不在) |
SRv6 SID リストキー。SRv6 steer route のみ |
seg_src |
string | "" (フィールド不在) |
SRv6 encapsulation source IPv6 アドレス。SRv6 route のみ |
コード由来デフォルト詳細¶
blackhole — 宣言デフォルト "false"¶
RouteTableFieldValueTupleWrapper の C++ メンバー宣言4:
non-ZMQ path では blackhole != "false" の場合のみ APPL_DB に書き込む。つまり通常経路(RTN_UNICAST)ではフィールド自体が存在しない。RTN_BLACKHOLE type の netlink メッセージを受け取った場合のみ "true" が書き込まれる4:
orchagent 側では blackhole = fvValue(i) == "true" と評価し、フィールド不在は false として処理する6。
protocol — iproute2 ライブラリ解決¶
rtnl_route_get_protocol() で取得した rtm_protocol 番号を rtnl_route_proto2str() で文字列変換4:
解決に失敗した場合は数値文字列(例: "186")を返す。/etc/iproute2/rt_protos が存在する場合はオーバーライド可能。
weight — netlink weight=0 → 1 フォールバック¶
getNextHopWt() 内でハードコード4:
uint8_t weight = rtnl_route_nh_get_weight(nexthop);
if (weight == 0)
{
weight = 1; // default weight is 1
}
FRR が weight を指定しない場合(weight=0)は 1 として APPL_DB に書き込まれる。ECMP の各 nexthop に最低 "1" が保証される。
nexthop_group と nexthop/ifname の相互排他¶
kernel の nexthop group ID が存在する場合は nexthop_group フィールドのみ設定し、nexthop / ifname は設定しない。orchagent は両方が存在する場合はエラーとして経路を棄却する6:
if (!nhg_index.empty() && (!ips.empty() || !aliases.empty()))
{
SWSS_LOG_ERROR("Route %s has both nexthop_group and ips/aliases", key.c_str());
// erases the entry
}
ZMQ path の差異¶
ORCH_NORTHBOND_ROUTE_ZMQ_ENABLED が有効な場合、全フィールド(空文字列のものを含む)を常に送信する。フィールド不在が発生しないため orchagent 側の「フィールド不在=デフォルト」ロジックは使われない。
書込み順依存 (Phase B)¶
ADD 時: VRF 経路は VRF エントリが先行必須¶
Vrf プレフィックスで始まる key(例: Vrf-RED:192.168.1.0/24)を持つ経路を書き込む前に、対応する VRF エントリが VRFOrch に登録されていなければならない。m_vrfOrch->isVRFexists(vrf_name) が false の場合、orchagent は経路を後回し(it++; continue)にして SAI プログラミングを行わない6。
ADD 時: nexthop_group 指定経路は NhgOrch 登録が先行必須¶
nexthop_group フィールドに NhgOrch 管理の NHG インデックスを指定する場合、gNhgOrch->hasNhg() / gCbfNhgOrch->hasNhg() がいずれも false なら addRoutePre が return false して後回しになる6。
ADD 時: 通常 IP nexthop は NeighOrch 登録が先行必須¶
nexthop が IP アドレスでインタフェース直結でない場合(非 isIntfNextHop())、m_neighOrch->hasNextHop() が false なら return false で後回し。fpmsyncd 経由の通常フローでは zebra がネイバー解決後に ROUTE_TABLE に書くため問題は生じないが、APPL_DB を直接操作する場合は ARP/NDP 解決を先に確認すること6。
ADD 時: EVPN overlay 経路は L3 VNI 登録が先行必須¶
vni_label フィールドを持つ経路では、各 VNI に対して m_vrfOrch->isL3VniVlan(vni) を検査し、L3 VNI として登録されていなければ it++; continue で後回し6。VXLAN_TUNNEL_MAP / VRF の L3 VNI 設定を先に完了してから EVPN Type-5 経路を書き込む。
ADD 時: インタフェース直結経路は RIF 登録が先行必須¶
nexthop が isIntfNextHop() の場合、m_intfsOrch->getRouterIntfsId(alias) が SAI_NULL_OBJECT_ID ならば return false で後回し。INTERFACE / PORTCHANNEL_INTERFACE テーブルへの IP アドレス設定 → IntfsOrch が RIF を SAI 登録 → ROUTE_TABLE 書き込み、の順を守る6。
DEL 時: 経路 DEL → 参照先オブジェクト DEL の順が必須¶
NHG・VRF を先に DEL しようとするとリファレンスカウントが詰まり DEL が遅延する。推奨順序:
制約¶
nexthop_groupとnexthop/ifnameは同時に存在できない(orchagent がエラー棄却)。- 管理 VRF (
mgmt) 向け経路は fpmsyncd がスキップ → テーブルに存在しない。 - eth0 / docker0 / eth1-midplane 向け経路は fpmsyncd が DEL 送信に変換(静的フィルタ)。
- EVPN Multipath SRv6 経路は未対応でサイレントスキップ。
購読者¶
orchagent(RouteOrch): APPL_DB のROUTE_TABLEを購読し、SAIsai_route_entry_tを作成・削除・更新する。
通信メカニズム (Redis PUBSUB / ZMQ)¶
ROUTE_TABLE は APPL_DB テーブルであり、CONFIG_DB からの keyspace notification は使用しない。FRR (zebra) が FPM (Forwarding Plane Manager) プロトコル経由で送る netlink メッセージを fpmsyncd が受信し、直接 APPL_DB に書き込む構成。
fpmsyncd → APPL_DB (ProducerStateTable / ZmqProducerStateTable)¶
RouteSync コンストラクタ (routesync.cpp:154-158) で m_routeTable を初期化する:
m_zmqClient(create_local_zmq_client(ORCH_NORTHBOND_ROUTE_ZMQ_ENABLED, false)),
m_routeTable(createProducerStateTable(pipeline, APP_ROUTE_TABLE_NAME, true, m_zmqClient)),
ORCH_NORTHBOND_ROUTE_ZMQ_ENABLED (DEVICE_METADATA|localhost の orch_northbond_route_zmq_enabled フィールド) の値で 2 パスが切り替わる:
| パス | m_routeTable 型 |
Transport | デフォルト |
|---|---|---|---|
| 通常 Redis | ProducerStateTable |
Redis EVALSHA: SADD KEY_SET + HSET + PUBLISH ROUTE_TABLE_CHANNEL@0 G |
◎ |
| ZMQ | ZmqProducerStateTable |
ZMQ PUSH → tcp://localhost:8100 + APPL_DB 永続化 |
— |
APPL_DB → orchagent RouteOrch (ConsumerStateTable / ZmqConsumerStateTable)¶
RouteOrch は ZmqOrch を継承する。orchagent 初期化 (orchdaemon.cpp:334-337) で ZMQ フラグを確認し、対応する Consumer を登録する:
auto enable_route_zmq = get_feature_status(ORCH_NORTHBOND_ROUTE_ZMQ_ENABLED, false);
auto route_zmq_server = enable_route_zmq ? m_zmqServer : nullptr;
gRouteOrch = new RouteOrch(m_applDb, route_tables, ..., route_zmq_server);
ZmqOrch::addConsumer() (zmqorch.cpp:59-68) が ZMQ 有無で Consumer を選択:
[ZMQ 無効] ConsumerStateTable → SUBSCRIBE ROUTE_TABLE_CHANNEL@0 → pops.lua
[ZMQ 有効] ZmqConsumerStateTable → ZMQ PULL ← tcp://localhost:8100
→ RouteOrch::doTask(ConsumerBase&)
ZMQ フィールド送信の差異¶
- 通常 Redis パス: 空値フィールドは APPL_DB に書き込まない(フィールド不在 = デフォルト値、orchagent 側でフォールバック)
- ZMQ パス: 全フィールドを常に送信(フィールド不在が発生しないため orchagent の「フィールド不在=デフォルト」ロジックは使われない)
STATE_DB 書き込み¶
RouteOrch は STATE_DB:ROUTE_TABLE にデフォルト経路の有無のみ書き込む (routeorch.cpp:294)。個別経路エントリのステータスは STATE_DB に書き込まれない。TTL は使用しない。
経路フィルタ(fpmsyncd がスキップする経路)¶
| 条件 | 動作 |
|---|---|
管理 VRF (mgmt プレフィックス) |
スキップ(APPL_DB に書き込まない) |
| nexthop が eth0 / docker0 / eth1-midplane | DEL メッセージに変換して送信(FRR 7.2→7.5 の挙動変化対策) |
| EVPN Multipath SRv6 | サイレントスキップ |
通信フロー全体図¶
FRR (zebra) --[FPM/netlink]--> fpmsyncd (RouteSync)
↓ [通常] ProducerStateTable (EVALSHA: SADD KEY_SET + HSET + PUBLISH CHANNEL@0)
↓ [ZMQ] ZmqProducerStateTable (ZMQ PUSH tcp://localhost:8100 + APPL_DB 永続化)
APPL_DB[ROUTE_TABLE|<prefix>]
↓ [通常] ConsumerStateTable (SUBSCRIBE ROUTE_TABLE_CHANNEL@0 → pops.lua)
↓ [ZMQ] ZmqConsumerStateTable (ZMQ PULL tcp://localhost:8100)
RouteOrch::doTask(ConsumerBase&) → SAI sai_route_api → ASIC
STATE_DB[ROUTE_TABLE|<default-route>]
← RouteOrch::set() (デフォルト経路の有無のみ、TTL なし)
SET/DEL 副次 DB 書込み¶
ROUTE_TABLE エントリの SET / DEL が引き起こす他 DB への書込み一覧。ROUTE_TABLE は APPL_DB テーブルであるため、CONFIG_DB 直接の副作用はなく、すべて orchagent (RouteOrch) 経由で発生する。
STATE_DB ROUTE_TABLE — デフォルト経路の有無 (routeorch.cpp:287-294)¶
RouteOrch::updateDefRouteState() がデフォルト経路 (0.0.0.0/0 / ::/0) の追加・削除時のみ書き込む1:
| 操作 | 対象 DB / テーブル | キー | フィールド | 条件 |
|---|---|---|---|---|
| SET | STATE_DB / ROUTE_TABLE |
0.0.0.0/0 または ::/0 |
state=ok |
デフォルト経路のみ |
| DEL | STATE_DB / ROUTE_TABLE |
0.0.0.0/0 または ::/0 |
state=na |
デフォルト経路のみ |
個別経路エントリのステータスは STATE_DB に書き込まれない。
APPL_STATE_DB ROUTE_TABLE — 経路処理ステータス (routeorch.cpp:3185-3201)¶
RouteOrch::publishRouteState() が SAI 操作の成否に関わらず全経路に対して書き込む1:
// ResponsePublisher m_publisher{"APPL_STATE_DB"}; (orch.h:382)
if (ctx.is_set) {
fvs.emplace_back("protocol", ctx.protocol);
}
m_publisher.publish(APP_ROUTE_TABLE_NAME, ctx.key, fvs, status, replace);
| 操作 | 対象 DB / テーブル | キー | フィールド | 条件 |
|---|---|---|---|---|
| SET | APPL_STATE_DB / ROUTE_TABLE |
<prefix> / <vrf>:<prefix> |
protocol=<proto> |
SAI 操作後 常時1 |
| DEL | APPL_STATE_DB / ROUTE_TABLE |
<prefix> / <vrf>:<prefix> |
(エントリ削除) | SAI 操作後 常時1 |
COUNTERS_DB — CRM リソースカウンタ (crmorch.cpp)¶
CrmOrch の定期タイマー (CRM_COUNTERS_POLL) が updateCrmCountersTable() を呼び出し、経路 SET/DEL 毎に incCrmResUsedCounter / decCrmResUsedCounter で更新されたメモリ内カウンタを COUNTERS_DB に反映する2:
| 操作 | 対象 DB / テーブル | キー | フィールド |
|---|---|---|---|
| SET (IPv4) | COUNTERS_DB / CRM |
STATS |
crm_stats_ipv4_route_used 増加 |
| SET (IPv6) | COUNTERS_DB / CRM |
STATS |
crm_stats_ipv6_route_used 増加 |
| DEL (IPv4) | COUNTERS_DB / CRM |
STATS |
crm_stats_ipv4_route_used 減少 |
| DEL (IPv6) | COUNTERS_DB / CRM |
STATS |
crm_stats_ipv6_route_used 減少 |
crm_stats_ipv{4,6}_route_available は SAI ポーリング (sai_object_type_get_availability) で別途更新される。
COUNTERS_DB — Flow Counter マッピング (flowcounterrouteorch.cpp)¶
ルートフロウカウンタが有効 (FLEX_COUNTER_TABLE でパターン設定済み) の場合のみ書き込む3:
| 操作 | 対象 DB / テーブル | キー | 条件 |
|---|---|---|---|
| SET | COUNTERS_DB / COUNTERS_ROUTE_NAME_MAP |
"" フィールド: <vrf>:<prefix> = <counter_oid> |
フロウカウンタ有効時のみ |
| SET | COUNTERS_DB / COUNTERS_ROUTE_TO_PATTERN_MAP |
"" フィールド: <vrf>:<prefix> = <pattern> |
フロウカウンタ有効時のみ |
| DEL | COUNTERS_DB / COUNTERS_ROUTE_NAME_MAP |
"" (該当フィールド削除) |
フロウカウンタ有効時のみ |
| DEL | COUNTERS_DB / COUNTERS_ROUTE_TO_PATTERN_MAP |
"" (該当フィールド削除) |
フロウカウンタ有効時のみ |
STATE_DB FLOW_COUNTER_CAPABILITY_TABLE — 起動時 1 回のみ (flowcounterrouteorch.cpp:174-178)¶
| 操作 | 対象 DB / テーブル | キー | フィールド | タイミング |
|---|---|---|---|---|
| SET | STATE_DB / FLOW_COUNTER_CAPABILITY_TABLE |
route |
support=true/false |
orchagent 起動時 1 回のみ3 |
副作用サマリ¶
| DB | テーブル | キー形式 | SET | DEL |
|---|---|---|---|---|
| STATE_DB | ROUTE_TABLE |
0.0.0.0/0 / ::/0 |
state=ok |
state=na |
| APPL_STATE_DB | ROUTE_TABLE |
<prefix> / <vrf>:<prefix> |
protocol=<proto> 書込 |
エントリ削除 |
| COUNTERS_DB | CRM |
STATS |
crm_stats_ipv{4,6}_route_used 増加 |
減少 |
| COUNTERS_DB | COUNTERS_ROUTE_NAME_MAP |
"" |
マップ追加 (条件付) | マップ削除 (条件付) |
| COUNTERS_DB | COUNTERS_ROUTE_TO_PATTERN_MAP |
"" |
マップ追加 (条件付) | マップ削除 (条件付) |
| STATE_DB | FLOW_COUNTER_CAPABILITY_TABLE |
route |
support=true/false (起動時のみ) |
— |
関連 CONFIG_DB / YANG / CLI¶
- 関連 CONFIG_DB:
STATIC_ROUTE(静的経路の設定元)、VRF - 関連 CLI:
show ip route、show ipv6 route、show bgp ipv4 unicast - 関連 YANG: 未定義(スキーマの正本は
routesync.h/routeorch.cpp)
関連リファレンス¶
- CONFIG_DB:
STATIC_ROUTE
引用元¶
運用ヒント¶
確認コマンド¶
# APPL_DB の ROUTE_TABLE エントリを確認
sonic-db-cli APPL_DB keys 'ROUTE_TABLE:*' | head -20
# 特定プレフィックスのエントリ詳細
sonic-db-cli APPL_DB hgetall 'ROUTE_TABLE:10.0.0.0/24'
# FRR の経路テーブルとの比較
vtysh -c 'show ip route'
show ip route
典型エントリ例¶
# BGP 学習経路(ECMP あり)
ROUTE_TABLE:10.1.0.0/24
protocol: bgp
nexthop: 192.168.0.1,192.168.0.2
ifname: Ethernet0,Ethernet4
weight: 1,1
# blackhole 経路
ROUTE_TABLE:192.0.2.0/24
protocol: bgp
blackhole: true
# EVPN Type-5 経路
ROUTE_TABLE:10.2.0.0/24
protocol: bgp
nexthop: 172.16.0.1
vni_label: 10000
router_mac: aa:bb:cc:dd:ee:ff
ifname: Brvxlan1000
よくある問題¶
show ip routeに表示されるが ASIC に反映されない →sonic-db-cli APPL_DB hgetall 'ROUTE_TABLE:<prefix>'でフィールドを確認。nexthop_groupとnexthopが両方存在すると orchagent がエラー棄却する。- デフォルト経路が eth0 に向いてしまう → fpmsyncd の eth0/docker0 フィルタが機能しているか確認(FRR の
show ip route 0.0.0.0/0)。
-
orchagent/routeorch.cpphttps://github.com/sonic-net/sonic-swss/blob/4305596156d70e9797e8a881b3d19b46de0bce0d/orchagent/routeorch.cpp ↩↩↩↩ -
orchagent/crmorch.cpphttps://github.com/sonic-net/sonic-swss/blob/4305596156d70e9797e8a881b3d19b46de0bce0d/orchagent/crmorch.cpp ↩ -
orchagent/flex_counter/flowcounterrouteorch.cpphttps://github.com/sonic-net/sonic-swss/blob/4305596156d70e9797e8a881b3d19b46de0bce0d/orchagent/flex_counter/flowcounterrouteorch.cpp ↩↩ -
RouteTableFieldValueTupleWrapper 宣言・実装:
fpmsyncd/routesync.h/routesync.cpp. https://github.com/sonic-net/sonic-swss/blob/4305596156d70e9797e8a881b3d19b46de0bce0d/fpmsyncd/routesync.h ↩↩↩↩↩ -
管理 VRF スキップ・テーブル名定数:
fpmsyncd/routesync.cpp,common/schema.h. https://github.com/sonic-net/sonic-swss-common/blob/158de8d3463ff4b841653f6d57190bb142b80d9c/common/schema.h ↩ -
orchagent フィールド消費:
orchagent/routeorch.cpp. https://github.com/sonic-net/sonic-swss/blob/4305596156d70e9797e8a881b3d19b46de0bce0d/orchagent/routeorch.cpp ↩↩↩↩↩↩↩