Thursday, April 16, 2020

C言語で配列の要素数を安全に数える話

C言語で配列の要素数を数えるイディオムってのがあって、
sizeof(array) / sizeof(array)
なんだけど、配列名が長くなって、たとえば
sizeof(var.that_has_an_array.as_a.member) /
    sizeof(var.that_has_an_array.as_a.member[0])
とかになるとカオス。

なので、ベンダーによっては、
#define _countof(array) (sizeof(array) / sizeof(array[0]))
みたいなマクロを提供していたりするんだけど、こうやって、何も考えずに使えるようにしていくと、配列ではなくポインタを引数に渡しちゃって、サイズ計算ミスって変な動作する懸念が増してくる。

なので、Twitterで

と聞いたところ、mattnさんから

と教えてもらったので、
#define PTLS_BUILD_ASSERT(cond) \
    ((void)sizeof(char[2 * !!(!__builtin_constant_p(cond) || (cond)) - 1]))

#define PTLS_ASSERT_IS_ARRAY(a) \
    PTLS_BUILD_ASSERT(
        __builtin_types_compatible_p(__typeof__(a[0])[], __typeof__(a)))

 #define PTLS_ELEMENTSOF(x) \
    (PTLS_ASSERT_IS_ARRAY(x), sizeof(x) / sizeof((x)[0]))
こんな感じにして取り込みました。これで、先の冗長な例も
PTLS_ELEMENTSOF(var.that_has_an_array.as_a.member)
と書くことができる。便利。

完全な変更は add PTLS_ELEMENTSOF for counting the number of elements in an array by kazuho · Pull Request #301 · h2o/picotls をご覧ください。


PS. 配列かポインタかの確認方法については、yuguiさんから下のようなコンパイラ非依存の解も教えてもらったのですが、残念ながらコンパイル時に動かすことが難しいようでした。実行時判定が必要なケースなら、この方法のほうがいいかも。

Wednesday, March 11, 2020

2020年にDSRロードバランス環境を作る方法

TCPの時代もそうだったんですが、QUICにおいても、Linux等でソフトウェアロードバランサをipvsadmとDSR (direct server return, a.k.a. direct routing) を実現することは有効な手段です。

なんだけど、ipvsadm界隈はHA前提の込み入った設定か古いドキュメントしか見つからなかったので、最低限の検証環境のセットアップ方法を、ここにまとめる次第です。

ロードバランサ:
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
sudo ifconfig eth0:0 $VIP netmask 255.255.255.255 broadcast $VIP up
sudo route add -host $VIP dev eth0:0
sudo ipvsadm -A -t $VIP:443 -s rr
sudo ipvsadm -a -t $VIP:443 -r $SERVER1 -g
sudo ipvsadm -a -t $VIP:443 -r $SERVER2 -g
sudo ipvsadm -a -t $VIP:443 -r $SERVER3 -g
...

要は、フォワーディングを有効にして、自分自身へのパケットを他のサーバにフォワードするような変態行為をするからspoofing対策フィルタをオフにして、次にipvsadmでテーブル作ってサーバ追加する。

サーバ:
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
echo 0 | sudo tee /proc/sys/net/ipv4/conf/eth0/rp_filter 
echo 2 | sudo tee /proc/sys/net/ipv4/conf/eth0/arp_announce
echo 1 | sudo tee /proc/sys/net/ipv4/conf/eth0/arp_ignore
sudo ifconfig lo:0 $VIP netmask 255.255.255.255 broadcast $VIP up
sudo route add -host $VIP dev lo:0

要は、フォワーディング有効にして、VIPがARPでアナウンスされたり、応答したりしないように設定して、loにVIPをつけて、eth0で受信したパケットがlo:0に転送されるように設定する。

これで動く。

Wednesday, August 14, 2019

H2O version 2.2.6, 2.3.0-beta2 released, includes security fixes

H2O version 2.2.6 and 2.3.0-beta2 have been released.

This release addresses a series of DoS attack vectors that have been recently found on a broad range of HTTP/2 stacks.

Specifically, H2O had been deemed vulnerable to the following, and fixed:

* CVE-2019-9512 (Ping Flood)
* CVE-2019-9514 (Reset Flood)
* CVE-2019-9515 (Settings Flood)

Users of previous versions of H2O are advised to update to the recent versions.

For more information, please refer to issue 2090: HTTP/2 DoS attack vulnerabilities CVE-2019-9512 CVE-2019-9514 CVE-2019-9515.

Tuesday, July 30, 2019

HTTP のプライオリティが大きく変わろうとしている話(その他 IETF 105 雑感)

先週、モントリオールで開催された IETF 105 に参加してきました。

いろんなことがあったのですが、個人的に一番大きかったのは、HTTP/3 からプライオリティ(優先度制御)まわりの仕様を落とすことが決定したこと。

HTTP/3 は、トランスポートプロトコルである QUIC の上で動作する、次世代の HTTP プロトコルです。その設計は、QUIC ワーキングググループが、HTTP ワーキンググループから委託され、HTTP/2 の機能を移植する、という形式を取っています。

ところが、5月にロンドンで開催された QUIC ワーキンググループの中間会議で、一部参加者から HTTP/3 の優先度制御に対する不満が表明されたのです注1。それを受けて、QUIC ワーキンググループでは、HTTP/3 の優先度制御にあった HTTP/2 のそれとの差異を少なくする作業を進める一方、HTTP ワーキンググループでは、IETF 105 において、プライオリティをどうするか、議論することになっていました。

HTTP/2 の優先度制御を十分にサポートしていないサーバが多いことは事実です。まともに実装したサーバを動かしている大規模事業者はFで始まる2社くらいではないでしょうか。一方で、実装しようと思えばできるものを「嫌だから変えたい」と言って変えようとするのは、信頼感のある話ではありません。HTTP/2 の仕様策定に関わった当事者が主張するのであれば、なおさらです。

変えるのであれば、皆が納得できるようなものにすべきです。

という考えを踏まえ、今月初頭、僕は Cloudflare 社の Lucas Pardue 氏とともに、HTTP ヘッダベースの優先度制御手法を提案しました(提案仕様, 発表資料)。具体的には、以下のような特徴をもつ提案です。

  • HTTP/2 よりも単純な、各リクエストが独立した優先度を保持する方式
  • たった8種類の優先度
  • サーバとクライアントによる優先度の協調制御
  • HTTP ヘッダを利用することで、優先度制御を HTTP のバージョン非依存にするとともに、将来の拡張性を確保

HTTP/2 方式の堅持を主張するプレーヤーが減ったことで、焦点は、QUIC と HTTP/3 への影響を最小限に抑えつつ、新しい優先度制御手法に如何に軟着陸するか、という点に移りました。

結果、QUIC と HTTP の両ワーキンググループで HTTP/3 から優先度制御に関する規定を取り除くことが決定しました注2QUIC WG 議事録, HTTP WG 議事録)。HTTP/2 についても、現行の優先度制御を廃止するか、「優先度制御しない」ことを表明する機能を追加するかが議論され、後者が採択されました。また、新しい優先度制御手法を議論するサイドミーティングも開かれ、我々の提案した手法を中心に議論が行われました。

今後は、HTTP ワーキンググループにおいて組織された、新しい優先度制御手法を議論する小グループにおいて、迅速な合意が得られるかが焦点になります。

その他、僕の仕事に関係しているところでいうと、共著者の一人を務めている Encrypted SNI は徐々に進展し、また、maprg(測定と解析に関するリサーチグループ)では、Fastly で開発を進めている quicly を使った、衛星ネットワークでの QUICの パフォーマンス試験や、エンドポイントが露出するパケットあたり2ビットの情報を使って、プライバシー問題を抑えつつネットワーク事業者が輻輳の分析を行えるようにする手法の検証報告がありました。

感想:つかれた。

注1: 4月に開催された HTTP Workshop でも話があったのですが、僕は欠席したので雰囲気を知りません。
注2: 優先度を伝達するプロトコルと、その関連規定を取り除くのであって、サーバ独自の優先度制御など、プロトコルによらない手段を否定するものではありません。

Monday, July 22, 2019

pthread_once が嫌いすぎて再実装した話

pthread_once が嫌いです。なぜ嫌いかって言うと、こんな感じで、ファイルレベルのグローバル変数やグローバル関数が出現し、また、値を使う場所と初期化コードの位置が離れがちで可読性が下がるから。

static volatile BIO_METHODS *biom = NULL;

static void init_biom(void)
{
    biom = BIO_meth_new(BIO_TYPE_FD, "h2o_socket");
    BIO_meth_set_write(biom, write_bio);
    BIO_meth_set_read(biom, read_bio);
    BIO_meth_set_puts(biom, puts_bio);
    BIO_meth_set_ctrl(biom, ctrl_bio);
}

static void setup_connection(...)
{
    (いろいろ省略)

    // BIOを初期化
    static pthread_once_t init_biom_once = PTHREAD_ONCE_INIT;
    pthread_once(&init_biom_once, init_biom);
    BIO *bio = BIO_new(biom);
    ...
}


一方、pthread_onceを使う煩雑さを避けようとすると、自前でダブルチェックロックを書くことになるのですが、ダブルチェックロックをちゃんと書くのは難しい(参考:LCK10-J. ダブルチェックロック手法を誤用しない)し、実際間違えるし、毎回、間違えないように書こうとするのはストレスなんです。

というわけで、一念発起して、マクロを使って自分が本当にほしかった「once」を実装しました。

こんな感じで使います。

static void setup_connection(...)
{
    (いろいろ省略)

    // BIOを初期化
    static volatile BIO_METHODS *biom = NULL;
    H2O_MULTITHREAD_ONCE({
        biom = BIO_meth_new(BIO_TYPE_FD, "h2o_socket");
        BIO_meth_set_write(biom, write_bio);
        BIO_meth_set_read(biom, read_bio);
        BIO_meth_set_puts(biom, puts_bio);
        BIO_meth_set_ctrl(biom, ctrl_bio);
    });
    BIO *bio = BIO_new(biom);
    ...
}

グローバル変数やコールバック関数はなくなったし、初期化コードと利用コードが隣同士になって可読性が上がりました。

実際のコードは https://github.com/h2o/h2o/pull/2086 にありますので、ご参照ください。これで正しくダブルチェックロック実装できてるはず。

最後に一言。Cのマクロにブロック渡すのは超便利。

Wednesday, June 12, 2019

#doh_study で Encrypted SNI について話しました

6月11日に開催された #doh_study で登壇する機会をいただきました。

発表では、Encrypt SNI の設計と最近の変更を説明するとともに、背景としてインターネット上のプロトコルに求められる要件がどう変わってきているかを説明しました。



暗号化とプライバシー保護は通信プロトコルの前提条件となりつつありますが、それにともない、企業内の機器やペアレンタルコントロール等において通信内容をどのように管理していくのか。インターネットをより安全で便利なものにするために、ステークホルダーをまたいだ協力が求められています。

参考: Moving control to the endpoints: Motivations, challenges, and the path forward | APNIC Blog

Saturday, September 8, 2018

次世代プロトコル(QUIC etc.)のセキュリティとプライバシー @ #builderscon

9月6日より開催中の builderscon 2018 において、登壇の機会をいただき、インターネットのトランスポート層プロトコルについてセキュリティやプライバシーに関わる設計がどのように進めてられているか、TLS と QUIC を中心に発表しました。

QUIC のハンドシェイクプロトコルとパケット番号暗号化、TLS の Encrypted SNI 拡張は、いずれも僕が提案した機能あるいは方式が採用される予定のものなので、背景にある動機や意義を含め、整理して発表する機会をもらえたことをありがたく感じています。

聴講いただいた方々、また、スライドをご覧になる方々と、次世代プロトコルの暗号応用の手法のみならず意義を含め共有し、理解と議論を深めることができれば、これに勝る喜びはありません。



PS. QUIC のハンドシェイクプロトコルと Encrypted SNI 拡張については、以下のブログ記事もあわせてご覧いただけます。

QUICハンドシェイクの再設計、もしくはTLSレイヤの終焉
TLS の SNI 暗号化に関する Internet Draft を共同提出しました