ネットワークのパケット処理プログラミングを昔チームでやっていました。その当時工夫してやっていたことを簡単に紹介しようと思います。当時作っていたものの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の活用です。