PGW開発の時に考えていたこと
ネットワークのパケット処理プログラミングを昔チームでやっていました。その当時工夫してやっていたことを簡単に紹介しようと思います。当時作っていたものの1つは、PGW/GGSNでした。これはLTEや3GのEPC(Evolved Packet…
PGW開発の時に考えていたこと
ネットワークのパケット処理プログラミングを昔チームでやっていました。その当時工夫してやっていたことを簡単に紹介しようと思います。当時作っていたものの1つは、PGW/GGSNでした。これはLTEや3GのEPC(Evolved Packet Core)というモバイルコアを構成するものの1つで、端末から外部ネットワークに抜けていく時に通っていくものです。
https://github.com/mixigroup/mixi-pgw このソースコードはMITライセンスですが、依存でリンクすることになるソースコードのライセンスに留意して活用ください。
パケット処理は、トンネルのためのヘッダーのつけ外しです。コントロールプレーンでトンネルのIDや宛先を決めて、その状態をデータプレーンに持たせて、1パケットづつ処理していきます。14.8M lookup per second程度のREADとパケット処理が必要です。また、その最中にもコントロールプレーンでlookupするテーブルの書き換えが走ります。このテーブルのサイズとしても数万から数十万程度を性能を測るターゲットとしていました。
まずパケット処理のアーキテクチャとしてデザインの根底に考えたのは、2点です。(1)開発とテストがしやすいことと、(2)デプロイがしやすいことがそれにあたります。これは進化をしやすくするために必須であると考えます。
(1)開発とテストのしやすさ
データプレーンとコントロールプレーンが連携して動く必要があります。一方、データプレーンはDPDKなどを使い、またコントロールプレーンは単純なソケットプログラミングです。コントロールプレーンで受け取った変更要求に対して、データプレーンの更新を同期的に行う場合には悩みが発生します。
予備系を含めた複数系統のデータプレーン全てに対して変更が成功した場合に成功を応答するのか?という問題が生じます。
YESである場合には、全てのデータプレーンプロセスが動いて初めて成功になるので信頼性は下がる方向になります。全てのデータプレーンプロセスが正常に応答している必要があります。
NOである場合、ステートが同期されていないデータプレーンの存在を許すことになります。
またYES/NOどちらであったとしても、難しいところがあります。また、開発時はデータプレーンのモックを用意する必要があります。
この悩みを解決するために、データベースのレプリケーションの仕組みを活用した方式を考え、それを実装しました。普段使い慣れたMySQL系のデータベースのbinlogによるレプリケーションをデータプレーンのパケット書き換えを行うプロセスに組み込みました。

データベースのレプリケーションは、変更内容を順次バックアップサーバに送り同じ変更をすることで同一の内容を保とうとします。データプレーンのプロセス内にデータベースにあるトンネルに関する情報をメモリー内に全て保持し、mapとして処理することで高速にlookupを行うようにしました。
これは、同じデータプレーンのステートを持つプロセスを複数台同時運用しながらも、コントロールプレーンはデータベースを1つ書き換えれば良い状態を作ることができます。
開発時もコントロールプレーンはデータベースの更新のみに集中すれば良く、データプレーンのプロセスとは綺麗に分離されます。
データプレーンのテストについても、コントロールプレーンと独立してテストできるメリットが生まれました。
全てが幸せかというとそんなことはありません。
データベースのレプリケーションには、レプリケーション遅延というものがあります。同期的に動く設定をすれば遅延なく同じデータになりますが、書き込み性能が大きく落ちるので用途によりますがそのような設定をしないことが多いかと思います。
データベースを書き換えて成功を応答して、データプレーンにパケットが届くまでにレプリケーションが間に合ってない状況になるとデータプレーンにおいて不整合が発生します。
これを回避するための仕組みを持つ必要がありました。
そこで活用することにしたのが、GTP-Cにある、Private ExtensionというInformation Elementsの活用です。

即座に反映させたいベストパスには、コントロールプレーンの拡張領域に必要な情報を埋め込み、応答が返っていくパスの途中でデータプレーンが情報をスプーフィングすることでデータを即座に更新します。
アプリケーションの特性として、トンネルのIDや宛先を更新していくデータ更新になり、2度漬けができるので、このような処理が問題なくなります。
このような方法でベストパスには即時に変更を反映し、バックアップパスには若干(サブ秒)の遅延を持って反映していくという形でこれを解決しました。
デプロイのしやすさについて
データプレーンのプロセスの入れ替えに、レプリーケーションを活用した仕組みは大いに役立ちます。プロセスが起動する時に、binlogのポジション情報の取得後に必要な情報をselectします。その後に確保しておいたポジションからレプリケーションを行います。上にあるように2度漬けができるデータなので、結果的に不整合なくステートを遅延を持ちながら同期できます。binlog活用まわりの解説は別で記事にしたいと思います。
なので、プロセスに改良を加えた場合の更新において、ステートをロストしてコントロールプレーンを再構築する一連の負荷の高い処理から解放されることができます。
データプレーンの実装の仕方の工夫
データプレーンの処理を最小限にしてテストしやすくすることも大事な要素です。データプレーンの処理に不具合が発生した時にネットワークの自律性に任せて自動的にデータプレーンのプロセスが迂回されることが大事と考えています。せっかくのバックアップのデータプレーンにパケットが来ないのであれば意味がありません。
我々はよくデータプレーンのプロセスを仮想的な通信ケーブルとして実装します。

2台のルータに1台のサーバを挟み、サーバはデータプレーンでブリッジの如く振る舞います。
右から来たパケットは左に、左から来たパケットは右に送ります。
そのため、ARPやルーティングプロトコルを一切実装しません。
データプレーンのパケット処理が止まる=ルータ同士が話せなくなって自動的に迂回するという仕組みにしました。もちろんBFD等も活用できます。
そしてこの不思議なブリッジをするプロセスは、自分の関心のあるパケットだけを書き換えながら、右から左に、また左から右にパケットを伝送します。それ以外はパススルーするか、捨てるかです。
https://mixi-developers.mixi.co.jp/packet-processing-as-a-virtual-network-cable-f3cdc152de36
上の記事ではその点を深掘りしています。
テストすべき内容が、処理対象かを判定するロジックと書き換えロジックに集中できるので、プロセスが複雑に分岐しません。
ざっと当時考えていたことを記録しました。
메타데이터
- post_id
- ee70a712dd74
- slug
- pgw-development-overview-ee70a712dd74
- url
- https://mixi-developers.mixi.co.jp/pgw-development-overview-ee70a712dd74
- canonical_url
- https://mixi-developers.mixi.co.jp/pgw-development-overview-ee70a712dd74
- author_url
- https://medium.com/@junpei_y
- status
- ok
- fetched_at
- 2026-07-27 01:00:16