tag:blogger.com,1999:blog-42786881655848667332024-03-17T18:14:23.352+09:00Kazuho's WeblogKazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.comBlogger143125tag:blogger.com,1999:blog-4278688165584866733.post-54133096616042877422020-12-02T23:16:00.009+09:002020-12-02T23:58:51.954+09:00komake: Make の -j オプションに潜む罠とその解決策<p>ビルドツールの<s>ダジャレの</s>大家と言えば <a href="https://twitter.com/shinh">@shinh</a> さんですが、それはさておき、皆さんは今でも Make を使ってビルドすることが多いと思います。かく言う私も、その一人。</p>
<p>最近は CPU のコア数も多いですから、当然 -j 16 とか、やりたいわけです。大きいプロジェクトになればなるほど、威力絶大ですね。</p>
<p>ですが、ここで問題がひとつ。<b>大規模プロジェクトでは Makefile が別の Makefile を呼び出すような依存関係が良く見受けられます</b>。この際、ターゲット間の依存関係で菱形が存在すると(例: ターゲット sub1 と sub2 が shared に依存)、make shared が make sub1 と make sub2 から同時に起動されることが起こりえます。CMake で生成した Makefile の場合も、ターゲット毎に make を起動しますね。</p>
<p><b>二重起動が発生すると、ほとんどの Makefile は、同時起動されることを想定していませんから、ビルドは失敗したり、成果物が壊れたりしてしまいます。</b></p>
<p>実際に、以下のような Makefile を書いてみると、make shared が同時に動いてしまうことを確認できます(sleep 1が2回表示されていることに注目)。</p>
<pre style="border: 1px solid gray; margin: 1em 0; padding: 0.5em; background: #eee">
$ cat Makefile
all: proj
proj: sub1 sub2
touch $@
sub1:
$(MAKE) shared
touch $@
sub2:
$(MAKE) shared
touch $@
shared:
sleep 1
touch $@
clean:
rm -f proj sub1 sub2 shared
$ make -j2
/Applications/Xcode.app/Contents/Developer/usr/bin/make shared
/Applications/Xcode.app/Contents/Developer/usr/bin/make shared
sleep 1
sleep 1
touch shared
touch shared
touch sub1
touch sub2
touch proj
</pre>
<p>どうしたらいいでしょう。</p>
<p>先に書いたように、問題は同一ターゲットを引数にとる make が重複起動されてしまうところです。ならば、make の並走度を別途制限すればいいのではないでしょうか。ある make が終了するためには、その make が呼び出した make が全て終了している必要があることを加味すると、単純に、呼び出し階層毎の make の同時実行数を1に制限してしまえば、この問題が解決するはずです。</p>
<p style="font-weight: bold; color: red;">ジャジャーン!!! と言うわけで書いたのが make のラッパースクリプト <a href="https://github.com/kazuho/komake">komake</a> です!</p>
<p><b>わずか41行のスクリプトにして、求められる機能を実現しています。</b>komake を使って先の Makefile を実行すると、ほら、このとおり!!!</p>
<pre style="border: 1px solid gray; margin: 1em 0; padding: 0.5em; background: #eee">
$ komake -j2
komake shared
komake shared
sleep 1
touch shared
touch sub1
make[1]: `shared' is up to date.
touch sub2
touch proj
</pre>
<p>komake shared は2回呼び出されていますが、実際のビルド作業である sleep 1 と touch shared の呼び出しが1回に減少したことがわかります。これは、1回目の komake shared が終了した後に2回目の komake shared が呼び出され、既に「shared」ファイルが存在したから、ビルド作業は何も走らなかったからです。</p>
<p><b>完璧ですね!! すばらしい!!!!</b></p>
<p>なお、注意点として、多重起動されロックを取ろうとしている komake も make のジョブスロットを消費します。ですので、komake を使う場合には、-j オプションに渡す引数を CPU コア数 + ロックにひっかかりそうな make の数(たとえば4)等にセットするとよいでしょう。</p>
<p>ちなみに、名前の komake は、「こまけー問題なおしてるな」というセルフツッコミに由来します。細かな問題なのですが、<b>大規模プロジェクトにおいて切実な make の待ち時間問題を解決する、かゆいところに手の届くツール</b>になっていると思います。</p>
<p>。。。。。とか書いてるうちに、同僚の <a href="https://twitter.com/gfx">@gfx</a> が、<a href="https://github.com/h2o/h2o/pull/2504">問題となっていたプロジェクトのビルドツールを Ninja に移行する作業</a>を終わらせてしまいました。完敗だ。<b>これは<font color="red">KO負け</font>ですね。</b></p>Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com0tag:blogger.com,1999:blog-4278688165584866733.post-61062725952188210312020-06-18T15:52:00.000+09:002020-06-18T15:52:18.379+09:00QUICむけにAES-GCM実装を最適化した話 (2/2)<a href="http://blog.kazuhooku.com/2020/06/quicaes-gcm-12.html">前半</a>で述べたように、OpenSSLのAEAD暗号器は、長いAEADブロックの処理を前提に作られています。平文の暗号化処理においては理論上の上限にあたる速度を叩き出す一方、事前処理と事後処理、および呼出オーバーヘッドについては、あまり最適化が図られているとは言えません。これは、AEAD暗号の主な使用用途が、これまでTLSという長いAEADブロックを使う(ことが一般的な)プロトコルであったことを反映していると言えるでしょう。<br />
<br />
一方、QUICにおいては、UDPパケット毎に独立した、短いAEADブロックを暗号化する必要があり、したがって、次のような速度向上の機会があることが分かります。<br />
<br />
<blockquote>AEAD処理をひとつの関数にまとめ、事前処理と事後処理を、パイプライン化されスティッチングされた暗号処理と並行に走らせることができれば、AEADブロックが短くても、理論値に近いスループットを発揮するような、AES-GCM実装を作ることができる(<a href="http://blog.kazuhooku.com/2020/06/quicaes-gcm-12.html">前半</a>より引用)<br />
</blockquote><br />
この条件を満たすような関数を実装し、ボトルネックをつぶしていって速度向上を図るというのは一案です。しかし、往々にして、そのような対症療法的なプログラミングスタイルでは、何回もの変更に伴う手戻りが発生したり、必ずしも最適でないコードが成果物の一部に残ったりしがちです。<br />
<br />
より効率的な設計手法はないものでしょうか。<br />
<br />
<b>■QUIC向けAES-GCM実装「fusion」の設計方針</b><br />
<br />
幸いなことに、AES-GCMについては、第9世代Core CPUにおけるボトルネックがAES-NIであり、そのスループットの理論上の上限が64バイト/40クロックであることが分かっています。スティッチングを用いたAES-GCM実装が、暗号化処理中、AES-NIを最高速度で回しつつ、他の演算ユニットを用いてGCMのハッシュ計算を行うという手法であることも、先に述べたとおりです。<br />
<br />
ならば、<b>AES-NIを常時実行しつつ、その合間をぬって、AEADの事前処理、事後処理を含む他のあらゆる処理を行う</b>ようにすれば、理論上の上限値に迫るようなAES-GCM実装が作れるのではないでしょうか。<br />
<br />
このような考えに基づき、以下のような特徴をもつAES-GCM暗号ライブラリ「<a href="https://github.com/h2o/picotls/pull/310">fusion</a>」を作成することにしました:<br />
<ul><li>できるだけ長い間、6*16バイト単位でAES-NIを実行する</li>
<li>その間に、AAD(=事前処理)を含む、任意の長さのGCMハッシュ計算を行う</li>
<li>複雑な設計をメンテ可能とするために、アセンブリではなくCで記述する</li>
<li>AEADブロック全体にわたって、GCMハッシュの事前計算を行う。それにより、reductionの負荷を下げる</li>
<li>パケットヘッダ暗号化(パケット番号暗号化)に必要なAES演算を重畳する</li>
</ul><br />
AES-GCM暗号化の典型的なデータフローを可視化してみましょう。第一の図が、古典的な(OpenSSLのような)暗号化部分に注力したアプローチです。第二の図が、fusionのアプローチです。横軸が時間軸で、縦に並んでいる処理は同時実行(スティッチング)されています。fusionでは、より多くの処理がスティッチングされることがわかります。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgw0PJepm3MVEQ92bGGpsDdeTBomfmTkVvCFz9uBpGl7mQc057ECL8xi42xx34UjcP7Hm4nssZ_iaLB2LMvIILa56gFEbaKONLGG4P3JjJrrriZ5AOMTbpeERkvS70aWBvqBCA1FkdHwcWe/s1600/%2522fusion%2522+AES-GCM+engine+for+QUIC.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgw0PJepm3MVEQ92bGGpsDdeTBomfmTkVvCFz9uBpGl7mQc057ECL8xi42xx34UjcP7Hm4nssZ_iaLB2LMvIILa56gFEbaKONLGG4P3JjJrrriZ5AOMTbpeERkvS70aWBvqBCA1FkdHwcWe/s320/%2522fusion%2522+AES-GCM+engine+for+QUIC.png" width="320" height="180" data-original-width="960" data-original-height="540" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjifQ9kKg722L1C8mj4wWz_9TVHafbFQOnaMM5JT5-yxunaYFRFWICKIOCqIA5_4fXe9TisrtrdM5NWnDHGT_bRuur2hZnP4kxxoz1cOYXIRW4CUGbWKQ3crX2D9tUFmeAj-ffxjHAfSWP9/s1600/%2522fusion%2522+AES-GCM+engine+for+QUIC-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjifQ9kKg722L1C8mj4wWz_9TVHafbFQOnaMM5JT5-yxunaYFRFWICKIOCqIA5_4fXe9TisrtrdM5NWnDHGT_bRuur2hZnP4kxxoz1cOYXIRW4CUGbWKQ3crX2D9tUFmeAj-ffxjHAfSWP9/s320/%2522fusion%2522+AES-GCM+engine+for+QUIC-2.png" width="320" height="180" data-original-width="960" data-original-height="540" /></a></div><br />
以下が、<a href="https://github.com/h2o/picotls/blob/2ab530c5517615fd033cd5a2bd4753b2c1b95dfd/lib/fusion.c">fusion.c</a>の暗号化のホットループです。gfmul_onestepは、1ブロック分のGCMハッシュの乗算演算を行うインライン関数です。6ブロック分(bits0〜bits5)のAES計算をする間に、gdata_cntで指定された回数だけgfmul_onestepを呼び出していることがわかります。<br />
<pre style="border: 1px solid #666; background: #eee; margin: 1em 0; padding: 0.5em; font-size: 80%;">#define AESECB6_UPDATE(i) \
do { \
__m128i k = ctx->ecb.keys[i]; \
bits0 = _mm_aesenc_si128(bits0, k); \
bits1 = _mm_aesenc_si128(bits1, k); \
bits2 = _mm_aesenc_si128(bits2, k); \
bits3 = _mm_aesenc_si128(bits3, k); \
bits4 = _mm_aesenc_si128(bits4, bits4keys[i]); \
bits5 = _mm_aesenc_si128(bits5, k); \
} while (0)
#define AESECB6_FINAL(i) \
do { \
__m128i k = ctx->ecb.keys[i]; \
bits0 = _mm_aesenclast_si128(bits0, k); \
bits1 = _mm_aesenclast_si128(bits1, k); \
bits2 = _mm_aesenclast_si128(bits2, k); \
bits3 = _mm_aesenclast_si128(bits3, k); \
bits4 = _mm_aesenclast_si128(bits4, bits4keys[i]); \
bits5 = _mm_aesenclast_si128(bits5, k); \
} while (0)
/* run AES and multiplication in parallel */
size_t i;
for (i = 2; i < gdata_cnt + 2; ++i) {
AESECB6_UPDATE(i);
gfmul_onestep(&gstate, _mm_loadu_si128(gdata++),
--ghash_precompute);
}
for (; i < ctx->ecb.rounds; ++i)
AESECB6_UPDATE(i);
AESECB6_FINAL(i);
</pre><br />
コードを注意深く読んだ方は、bits4の計算だけ、異なる鍵を使うようになっていることに気づいたかもしれません。これが、パケットヘッダ暗号化のためのAES計算を重畳するための工夫です。<br />
<br />
<b>■パケットヘッダ暗号化</b><br />
<br />
<b><a href="http://blog.kazuhooku.com/2018/09/quic-builderscon.html">パケットヘッダ(パケット番号)の暗号化</a>は、QUICやDTLS 1.3といった新世代のトランスポートプロトコルに見られる機能</b>です。パケットヘッダを暗号化することで、傍受者による通信内容の推測をより難しくしたり、中継装置(ルータ)が特定の通信パターンを前提にしてしまうことによりトランスポートプロトコルの改良が困難になること(ossification)を防ぐ効果が期待されています。<br />
<br />
なぜ、パケットヘッダ暗号化のAES計算を重畳するのか。それは、6ブロック分のAES計算を一度に行う以上、パケット長を96で割った余りが65から80の間にならない限り、使われないスロットが発生するためです。<b>その余ったスロットをパケットヘッダ暗号化のAES演算に使うことで、パケットヘッダ暗号化のコストを隠蔽する</b>のが目的です。<br />
<br />
パケットヘッダ暗号化を重畳した場合のデータフローを、以下に示します。<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyZjGytY16Y8pGjmMDtkAp8KUEGs4AQd93wYZEl_WvOnoC41dkq4ppC_ck6noCauggkVKMOFS-VmxkjosuYtmNNUU9i8fAZsfFrTRQYV9UnEAIHsQbOlFT5ZmnnE_Yj4Nft9ocsO8xZ2iQ/s1600/%2522fusion%2522+AES-GCM+engine+for+QUIC-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyZjGytY16Y8pGjmMDtkAp8KUEGs4AQd93wYZEl_WvOnoC41dkq4ppC_ck6noCauggkVKMOFS-VmxkjosuYtmNNUU9i8fAZsfFrTRQYV9UnEAIHsQbOlFT5ZmnnE_Yj4Nft9ocsO8xZ2iQ/s320/%2522fusion%2522+AES-GCM+engine+for+QUIC-3.png" width="320" height="180" data-original-width="960" data-original-height="540" /></a></div><br />
<b>■ベンチマーク</b><br />
<br />
では、ベンチマーク結果を見てみましょう。<br />
<br />
青の棒は、OpenSSLのAES-GCM処理のうち、事前処理と事後処理を含まないスループットを、赤の棒は、両者を含んだトータルでのスループットを表しています。黄色はfusionのトータルスループット、緑は、パケットヘッダ暗号化に必要な演算を重畳した場合の値です。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyXcICLyj8WcSc7q1OE_UC4WTQedSnbC1ukZLH0Irm8nkw67u8P4ef0bSArZ5RFmPSO8uPP-_gNKsHVJ7J3a5thmvL68kuNcDOSOfeRcMP6pzEzwQc34q6dtIkskvVCXIcOsSrvSMcx3DA/s1600/AEAD+throughput-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyXcICLyj8WcSc7q1OE_UC4WTQedSnbC1ukZLH0Irm8nkw67u8P4ef0bSArZ5RFmPSO8uPP-_gNKsHVJ7J3a5thmvL68kuNcDOSOfeRcMP6pzEzwQc34q6dtIkskvVCXIcOsSrvSMcx3DA/s320/AEAD+throughput-2.png" width="320" height="205" data-original-width="968" data-original-height="619" /></a></div><br />
まずは、最近のIntel製CPUである、Core i5 9400の値を見てみましょう。<br />
<br />
AEADブロックサイズが16KBの場合、OpenSSLの事前事後処理を含まないスループットとfusionのスループットが、いずれも6.4GB/sという理論上の上限に達していることが分かります(微妙なズレは、CPUクロック制御の精度に起因するものです)。OpenSSLの事前事後処理を含むスループットは若干遅い6.2GB/sですが、TLSにおいて、事前事後処理を最適化しないオーバーヘッドは3%以下である、という風に読むこともできます。<br />
<br />
一方で、<b>AEADブロックサイズが1440バイトの場合、差は顕著</b>です。OpenSSLのトータルスループットが4.4GB/sと、理論値の約70%にまで落ち込むのに対し、<b>fusionは理論値の90%を超えるスループット</b>を発揮します。また、<b>パケットヘッダ暗号化によるオーバーヘッドが1%以下</b>なのも見てとることができます。<br />
<br />
AMD Ryzenに目を向けると、AEADブロックサイズ1440バイトの場合のみならず16KBの場合でも、fusionが勝っていることが読み取れます。これは、RyzenのAES-NIのスループットがPCLMULと比較して高いため、ボトルネックがPCLMULに代表されるGCMハッシュ計算の側に移動したものと考えられます。fusionは、想定されるAEADブロック全体にわたって事前計算を行うことで、GCMハッシュ演算のうちreductionの回数を削減しているので、ブロックサイズ16KBの場合にも差がついたと考えることができます。<br />
<br />
<b>■考察</b><br />
<br />
カーネル・ネットワークカードのUDP処理が最適化された場合、暗号処理のコスト差が問題となって、TLSよりもQUICのほうがCPU負荷が高くなる、という問題がありました。この問題について、QUICを始めとする暗号化トランスポート向けに最適化したAES-GCM実装を準備することで、大幅な改善が可能であることを示しました。fusionをQUICの暗号ライブラリとして使った場合の詳細は本稿では紹介しませんが、TCPとUDPでGSOハードウェアオフロードがある環境において、パケットサイズ9KBならQUICが優位、パケットサイズ1.5KBでもQUICのオーバーヘッドはTLS+5%程度だという測定結果を得ています(参照: <a href="https://github.com/h2o/quicly/pull/359">h2o/quicly PR #359</a>)。<br />
<br />
あわせて、<br />
<ul><li>パケットヘッダ暗号化のコストは(少なくとも送信側においては)特に問題視するレベルではないこと</li>
<li>アセンブリを用いる場合と比較して、C言語を用いることで、最善ケースのスループットを保ったまま、より高度な設計による暗号ライブラリが開発可能であること</li>
</ul>を示しました。<br />
<br />
今回開発したAES-GCM実装「fusion」は、昨日、我々が管理するTLSスタックである<a href="https://github.com/h2o/picotls/">picotls</a>にマージされ、使用可能になっています。fusion、あるいはそれに類する実装手法を用いることで、インターネット上の通信が、より低コストに、より安全になっていくことを期待します。<br />
<br />
末筆ですが、fusionを開発するにあたり、<a href="https://twitter.com/herumi">光成(@herumi)さん</a>にアドバイスを、<a href="https://twitter.com/syohex">吉田(@syohex)さん</a>にベンチマークでご協力をいただきました。この場を借りて御礼申し上げます。Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com0tag:blogger.com,1999:blog-4278688165584866733.post-74260045468561514532020-06-15T15:57:00.000+09:002020-06-15T16:14:41.080+09:00QUICむけにAES-GCM実装を最適化した話 (1/2)4月末に、会社のほうで「<a href="https://www.fastly.com/blog/measuring-quic-vs-tcp-computational-efficiency">Can QUIC match TCP’s computational efficiency?</a>」というブログエントリを書きました。我々が開発中のQUIC実装である<a href="https://github.com/h2o/quicly/">quicly</a>のチューニングを通して、QUICのCPU負荷はTLS over TCP並に低減可能であろうと推論した記事です。この記事を書く際には、Stay Homeという状況の中で、手元にあった安いハードウェアを使ったのですが、その後、10gbe NICを入手し、<b>ハードウェアによるUDP GSOオフロード環境でのパフォーマンスを確認していくと、OpenSSLのAES-GCM実装がボトルネック</b>になることがわかってきました。<br />
<br />
TCP上で通信するTLSでは、一般に、データを16KB単位でAEADブロックに分割して、AES-GCMを用いてAEAD暗号化します<sup>注</sup>。一方、UDPを用いるQUICでは、パケット毎にAES-GCMを適用することになります。インターネットを通過することができるパケットサイズは高々1.5KBなので、<b>QUICのAEADブロックサイズはTLSと比較して1/10以下</b>となります。<br />
<br />
両条件について、OpenSSLのAES-GCM実装のスループットを比較したところ、4GHzの第9世代Intel Core CPUを使った場合、AEADブロックサイズ16KBにおいては約6.4GB/sなのに対し、AEADブロックサイズ1440バイトにおいては、4.4GB/s程度しか出ないことが分かりました。ハードウェアGSOオフロードが可能な環境ではCPU負荷の半分弱が暗号処理コストになるので、<b>暗号処理で7割のスループットしか出ない</b>のは、QUICの足かせになります。<br />
<br />
それにしても、なぜ、これほど大きな速度差が発生するのでしょう。<br />
<br />
その答えを理解するには、最適化されたAES-GCM実装が、一般に、どのようなものかを知っておく必要があります。<br />
<br />
<b>■AES-NIとパイプライン処理</b><br />
<br />
まず、AES-GCMのうち、暗号処理である<a href="https://ja.wikipedia.org/wiki/Advanced_Encryption_Standard">AES</a>実装の最適化手法を見てみましょう。<br />
<br />
最近のx86 CPUは、たいてい、<a href="https://ja.wikipedia.org/wiki/AES-NI">AES-NI</a>というAES処理専用の命令に対応しています。128bitのAES暗号化においては、AES-NI命令を10回発行することで、16バイトの暗号化を行うことが可能です。第9世代のIntel Core CPUにおいては、<b>AES-NI命令のレイテンシは4クロックなので、10*4=40クロックで16バイトの暗号化が可能</b>になります。<br />
<br />
4GHzでのスループットを計算してみると、4GHz / 40clock * 16byte = 1.6GB/sになります。<br />
<br />
あれ、先ほどの6.4GB/sと比べると1/4の値です。なぜでしょう?<br />
<br />
実は、x86 CPUはAES-NIを<a href="https://ja.wikipedia.org/wiki/パイプライン処理">パイプライン処理</a>します。そのため、依存関係のない(=別のAESブロックを処理する)AES-NI命令を1クロックごとに1個発行することが可能なのです。<br />
<br />
つまり、16バイトの基本ブロック単位で処理するのではなく、AES-NI(ブロック1用)、AES-NI(ブロック2用)、AES-NI(ブロック3用)、AES-NI(ブロック4用)、AES-NI(ブロック1用)、...のように、<b>16バイトのブロック4つ分のAES-NI命令を並行に発行し続けることで、64バイト分の暗号化を4*10=40クロックで終えていくことができる</b>のです。<br />
<br />
4GHzでのスループットを再び計算すると、$GHz / 40clock * 16byte * 4 = 6.4GB/s。<br />
<br />
毎クロック1命令発行、16バイトの暗号化に10命令必要ですから、<b>これが理論上の上限値</b>になります。<br />
<br />
でも、ちょっと待ってください。6.4GB/sは、暗号処理であるAESのスループットであって、認証符号であるGCMの負荷を含んでいません。<b>GCMの負荷は一体どこに行ってしまったのでしょう</b>。<br />
<br />
<b>■AES-GCMのスティッチング</b><br />
<br />
<a href="https://ja.wikipedia.org/wiki/Galois/Counter_Mode">GCM</a>はガロア体における乗算を用いる認証符号で、x86 CPUでは、PCLMULQDQというキャリーレス乗算命令を利用する最適化が知られています。さらに、式を変形することで、16バイトあたりのPCLMULQDQ命令発行回数を5回に減らせることが、また、事前計算を行えば、16*nバイトあたりのPCLMULQDQ命令発行回数を3*n+2回まで減らせることが知られています(参照: <a href="https://crypto.stanford.edu/RealWorldCrypto/slides/gueron.pdf">https://crypto.stanford.edu/RealWorldCrypto/slides/gueron.pdf</a>)。<br />
<br />
PCLMULQDQ命令は、7クロックのレイテンシがありますが、AES-NIとは<a href="https://ja.wikipedia.org/wiki/スーパースカラー">同時に発行可能</a>なので、<b>AES命令とPCLMULQDQをほどよく織り交ぜるようなプログラムを書くことで、AESとGCMを並列に計算することが可能</b>です。<br />
<br />
IntelのCPUにおいては、若干GCMの方がAESよりも軽いので、ボトルネックはAESになり、AES-GCMでもAES同様のスループット6.4GB/sが期待できます。<br />
<br />
<b>■OpenSSLのaesni_ctr32_ghash_6x関数</b><br />
<br />
以上を踏まえ、OpenSSLのAES-GCM実装である<a href="https://github.com/openssl/openssl/blob/openssl-3.0.0-alpha3/crypto/modes/asm/aesni-gcm-x86_64.pl#L94">aesni_ctr32_ghash_6x</a>関数を見てみましょう。<br />
<br />
この関数は、perlスクリプトを用いて生成されるアセンブリコードですが、以下のような、AES-NI命令とPCLMULQDQ命令が織り混ざる構成になっています。また、AES-NI命令について、同じラウンドキーを違う引数($inout)に適用している、つまり、複数ブロックの暗号化を同時に行なっていることが分かります。命令の目的によってインデントを変えるなどの工夫も興味深いところです。<br />
<br />
<pre>vpclmulqdq \$0x01,$Hkey,$Ii,$T2
lea ($in0,%r12),$in0
vaesenc $rndkey,$inout0,$inout0
vpxor 16+8(%rsp),$Xi,$Xi # modulo-scheduled [vpxor $Z3,$Xi,$Xi]
vpclmulqdq \$0x11,$Hkey,$Ii,$Hkey
vmovdqu 0x40+8(%rsp),$Ii # I[3]
vaesenc $rndkey,$inout1,$inout1
movbe 0x58($in0),%r13
vaesenc $rndkey,$inout2,$inout2
movbe 0x50($in0),%r12
vaesenc $rndkey,$inout3,$inout3
mov %r13,0x20+8(%rsp)
vaesenc $rndkey,$inout4,$inout4
mov %r12,0x28+8(%rsp)
vmovdqu 0x30-0x20($Xip),$Z1 # borrow $Z1 for $Hkey^3
vaesenc $rndkey,$inout5,$inout5
</pre>注意深い方は既にお気づきかもしれませんが、関数名の6xは、128bitのブロックを6ブロック単位で(つまり、96バイト単位で)AES-GCM符号化を行なっていることに由来します。<br />
<br />
<b>■なぜOpenSSLは短いAEADブロックの処理が苦手なのか</b><br />
<br />
このように、丁寧に最適化されたコードであるにもかかわらず、なぜ、OpenSSLは短いAEADブロックの処理が苦手なのでしょうか。2つの要因が考えられます。<br />
<br />
第一の要因は、関数呼び出しのオーバーヘッドです。OpenSSLのAEAD処理は、EVPと呼ばれるレイヤで抽象化されています。ひとつのAEADブロックを暗号化するには、EVP_EncryptInit_ex、EVP_EncryptUpdate、ENP_EncryptFinal、という3つの関数を介して、AES-GCM固有の処理を呼び出す必要があります。<br />
<br />
第二の要因は、AES-GCMの事前処理と事後処理がパイプライン化されていない点です。先に紹介したaesni_ctr32_ghash_6x関数は、6.4GB/sという理論値を叩き出す、文句のつけどころのない関数です。しかし、AES-GCMにおいては、暗号化以外にも、AEADブロック毎に、AAD(認証つき平文)をGCMのコンテクストに入力したり、最終的なタグを計算するなどの処理が必要です。これらの付随する処理の負荷は、AEADブロックサイズが小さくのればなるほど、相対的に大きくなります。<br />
<br />
これらの問題を指摘することは簡単です。<br />
<br />
なるほど、<b>AEAD処理をひとつの関数にまとめ、事前処理と事後処理を、パイプライン化されスティッチングされた暗号処理と並行に走らせることができれば、AEADブロックが短くても、理論値に近いスループットを発揮するような、AES-GCM実装を作ることができる</b>でしょう。<br />
<br />
しかし、上述したように、パイプライン化・スティッチングされたコードは、<b>既に相当複雑です。ここにさらに、事前処理や事後処理を重畳することなどできるでしょうか。できたとして、保守可能なプログラムになるでしょうか</b>。<br />
<br />
<br />
次回に続きます。<br />
<br />
<br />
<br />
注: スロースタート時には、より小さなブロックサイズを使って実効レイテンシを改善する場合もあります(参照: <a href="https://www.slideshare.net/kazuho/programming-tcp-for-responsiveness">https://www.slideshare.net/kazuho/programming-tcp-for-responsiveness</a>)Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com0tag:blogger.com,1999:blog-4278688165584866733.post-39297302952982228112020-04-16T12:09:00.000+09:002020-04-16T12:09:04.248+09:00C言語で配列の要素数を安全に数える話C言語で配列の要素数を数えるイディオムってのがあって、<br />
<pre style="margin: 1em 0; padding: 0.5em; background: #eee; border: 1px solid #ccc;">sizeof(array) / sizeof(array)</pre>なんだけど、配列名が長くなって、たとえば<br />
<pre style="margin: 1em 0; padding: 0.5em; background: #eee; border: 1px solid #ccc;">sizeof(var.that_has_an_array.as_a.member) /
sizeof(var.that_has_an_array.as_a.member[0])</pre>とかになるとカオス。<br />
<br />
なので、ベンダーによっては、<br />
<pre style="margin: 1em 0; padding: 0.5em; background: #eee; border: 1px solid #ccc;">#define _countof(array) (sizeof(array) / sizeof(array[0]))</pre>みたいな<a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/countof-macro?view=vs-2019">マクロを提供</a>していたりするんだけど、こうやって、何も考えずに使えるようにしていくと、配列ではなくポインタを引数に渡しちゃって、サイズ計算ミスって変な動作する懸念が増してくる。<br />
<br />
なので、Twitterで<br />
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">C言語で、ある値がポインタなのか配列なのかを知る方法ってあるのかなぁ(gcc/clang拡張でも可)。意図としては countof(array) みたいなマクロで、引数arrayに渡されるものがポインタではなく配列であることをビルド時に保証したい<br><br>cf. <a href="https://t.co/izurmOdiTl">https://t.co/izurmOdiTl</a></p>— Kazuho Oku (@kazuho) <a href="https://twitter.com/kazuho/status/1250201992368025600?ref_src=twsrc%5Etfw">April 14, 2020</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><br />
と聞いたところ、mattnさんから<br />
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">gcc だとこのマクロがうまく動きそうです。<br><br># define IS_ARRAY(arg) __builtin_choose_expr(__builtin_types_compatible_p(typeof(arg[0]) [], typeof(arg)), 1, 0)</p>— mattn (@mattn_jp) <a href="https://twitter.com/mattn_jp/status/1250205966286467073?ref_src=twsrc%5Etfw">April 14, 2020</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script><br />
と教えてもらったので、<br />
<pre style="margin: 1em 0; padding: 0.5em; background: #eee; border: 1px solid #ccc;">#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]))</pre>こんな感じにして取り込みました。これで、先の冗長な例も<br />
<pre style="margin: 1em 0; padding: 0.5em; background: #eee; border: 1px solid #ccc;">PTLS_ELEMENTSOF(var.that_has_an_array.as_a.member)</pre>と書くことができる。便利。<br />
<br />
完全な変更は <a href="https://github.com/h2o/picotls/pull/301/files">add PTLS_ELEMENTSOF for counting the number of elements in an array by kazuho · Pull Request #301 · h2o/picotls</a> をご覧ください。<br />
<br />
<br />
PS. 配列かポインタかの確認方法については、yuguiさんから下のようなコンパイラ非依存の解も教えてもらったのですが、残念ながらコンパイル時に動かすことが難しいようでした。実行時判定が必要なケースなら、この方法のほうがいいかも。<br />
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">&してからintptr_tにキャストして、元の値と比較したらイケませんか?<br>もっと移植性のある方法もあった気はするんですが</p>— Yuki Yugui Sonoda (@yugui) <a href="https://twitter.com/yugui/status/1250204306151620610?ref_src=twsrc%5Etfw">April 14, 2020</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com0tag:blogger.com,1999:blog-4278688165584866733.post-68249870679949164352020-03-11T15:34:00.002+09:002020-03-17T13:57:09.472+09:002020年にDSRロードバランス環境を作る方法TCPの時代もそうだったんですが、QUICにおいても、Linux等でソフトウェアロードバランサをipvsadmとDSR (direct server return, a.k.a. direct routing) を実現することは有効な手段です。<br />
<br />
なんだけど、ipvsadm界隈はHA前提の込み入った設定か古いドキュメントしか見つからなかったので、最低限の検証環境のセットアップ方法を、ここにまとめる次第です。<br />
<br />
ロードバランサ:<br />
<pre style="border: 1px solid gray; margin: 1em 0; padding: 0.5em;">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
...
</pre><br />
要は、フォワーディングを有効にして、自分自身へのパケットを他のサーバにフォワードするような変態行為をするからspoofing対策フィルタをオフにして、次にipvsadmでテーブル作ってサーバ追加する。<br />
<br />
サーバ:<br />
<pre style="border: 1px solid gray; margin: 1em 0; padding: 0.5em;">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
</pre><br />
要は、フォワーディング有効にして、VIPがARPでアナウンスされたり、応答したりしないように設定して、loにVIPをつけて、eth0で受信したパケットがlo:0に転送されるように設定する。<br />
<br />
これで動く。Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com0tag:blogger.com,1999:blog-4278688165584866733.post-40931969560116484872019-08-14T02:26:00.001+09:002019-08-14T02:26:39.660+09:00H2O version 2.2.6, 2.3.0-beta2 released, includes security fixesH2O version <a href="https://github.com/h2o/h2o/releases/tag/v2.2.6">2.2.6</a> and <a href="https://github.com/h2o/h2o/releases/tag/v2.3.0-beta2">2.3.0-beta2</a> have been released.<br />
<br />
This release addresses a series of DoS attack vectors that have been recently found on a broad range of HTTP/2 stacks.<br />
<br />
Specifically, H2O had been deemed vulnerable to the following, and fixed:<br />
<br />
* CVE-2019-9512 (Ping Flood)<br />
* CVE-2019-9514 (Reset Flood)<br />
* CVE-2019-9515 (Settings Flood)<br />
<br />
Users of previous versions of H2O are advised to update to the recent versions.<br />
<br />
For more information, please refer to <a href="https://github.com/h2o/h2o/issues/2090">issue 2090: HTTP/2 DoS attack vulnerabilities CVE-2019-9512 CVE-2019-9514 CVE-2019-9515</a>.Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com0tag:blogger.com,1999:blog-4278688165584866733.post-31610250420257581402019-07-30T11:01:00.000+09:002019-07-30T11:01:31.542+09:00HTTP のプライオリティが大きく変わろうとしている話(その他 IETF 105 雑感)先週、モントリオールで開催された <a href="https://www.ietf.org/how/meetings/105/">IETF 105</a> に参加してきました。<br />
<br />
いろんなことがあったのですが、個人的に一番大きかったのは、HTTP/3 からプライオリティ(優先度制御)まわりの仕様を落とすことが決定したこと。<br />
<br />
<a href="https://quicwg.org/base-drafts/draft-ietf-quic-http.html">HTTP/3</a> は、トランスポートプロトコルである <a href="https://quicwg.org/base-drafts/draft-ietf-quic-transport.html">QUIC</a> の上で動作する、次世代の HTTP プロトコルです。その設計は、<a href="https://datatracker.ietf.org/wg/quic/about/">QUIC ワーキングググループ</a>が、<a href="https://datatracker.ietf.org/wg/httpbis/about/">HTTP ワーキンググループ</a>から委託され、<a href="https://httpwg.org/specs/rfc7540.html">HTTP/2</a> の機能を移植する、という形式を取っています。<br />
<br />
ところが、5月にロンドンで開催された QUIC ワーキンググループの中間会議で、一部参加者から <a href="https://github.com/quicwg/wg-materials/blob/master/interim-19-05/minutes.md#http3-priorities">HTTP/3 の優先度制御に対する不満が表明された</a>のです<sup>注1</sup>。それを受けて、QUIC ワーキンググループでは、HTTP/3 の優先度制御にあった HTTP/2 のそれとの差異を少なくする作業を進める一方、HTTP ワーキンググループでは、IETF 105 において、プライオリティをどうするか、議論することになっていました。<br />
<br />
HTTP/2 の優先度制御を十分にサポートしていないサーバが多いことは事実です。まともに実装したサーバを動かしている大規模事業者はFで始まる2社くらいではないでしょうか。一方で、実装しようと思えばできるものを「嫌だから変えたい」と言って変えようとするのは、信頼感のある話ではありません。HTTP/2 の仕様策定に関わった当事者が主張するのであれば、なおさらです。<br />
<br />
変えるのであれば、皆が納得できるようなものにすべきです。<br />
<br />
という考えを踏まえ、今月初頭、僕は Cloudflare 社の <a href=https://twitter.com/simmervigor">Lucas Pardue</a> 氏とともに、HTTP ヘッダベースの優先度制御手法を提案しました(<a href="https://datatracker.ietf.org/doc/draft-kazuho-httpbis-priority/">提案仕様</a>, <a href="https://lists.w3.org/Archives/Public/ietf-http-wg/2019JulSep/att-0095/The_Priority_Header_Field.pdf">発表資料</a>)。具体的には、以下のような特徴をもつ提案です。<br />
<br />
<ul><li>HTTP/2 よりも単純な、各リクエストが独立した優先度を保持する方式</li>
<li>たった8種類の優先度</li>
<li>サーバとクライアントによる優先度の協調制御</li>
<li>HTTP ヘッダを利用することで、優先度制御を HTTP のバージョン非依存にするとともに、将来の拡張性を確保</li>
</ul><br />
HTTP/2 方式の堅持を主張するプレーヤーが減ったことで、焦点は、QUIC と HTTP/3 への影響を最小限に抑えつつ、新しい優先度制御手法に如何に軟着陸するか、という点に移りました。<br />
<br />
結果、QUIC と HTTP の両ワーキンググループで HTTP/3 から優先度制御に関する規定を取り除くことが決定しました<sup>注2</sup>(<a href="https://github.com/quicwg/wg-materials/blob/master/ietf105/minutes.md#h3-priorities">QUIC WG 議事録</a>, <a href="https://github.com/httpwg/wg-materials/blob/gh-pages/ietf105/minutes.md#priorities">HTTP WG 議事録</a>)。HTTP/2 についても、現行の優先度制御を廃止するか、「優先度制御しない」ことを表明する機能を追加するかが議論され、後者が採択されました。また、新しい優先度制御手法を議論するサイドミーティングも開かれ、我々の提案した手法を中心に議論が行われました。<br />
<br />
今後は、HTTP ワーキンググループにおいて組織された、新しい優先度制御手法を議論する小グループにおいて、迅速な合意が得られるかが焦点になります。<br />
<br />
その他、僕の仕事に関係しているところでいうと、共著者の一人を務めている <a href="https://datatracker.ietf.org/doc/draft-ietf-tls-esni/">Encrypted SNI</a> は徐々に進展し、また、maprg(測定と解析に関するリサーチグループ)では、Fastly で開発を進めている <a href="https://github.com/h2o/quicly/">quicly</a> を使った、<a href="https://datatracker.ietf.org/meeting/105/materials/slides-105-maprg-measuring-quic-dynamics-over-a-high-delay-path-01">衛星ネットワークでの QUICの パフォーマンス試験</a>や、<a href="https://datatracker.ietf.org/meeting/105/materials/slides-105-maprg-packet-loss-signaling-for-encrypted-protocols-01">エンドポイントが露出するパケットあたり2ビットの情報を使って、プライバシー問題を抑えつつネットワーク事業者が輻輳の分析を行えるようにする手法の検証報告</a>がありました。<br />
<br />
感想:つかれた。<br />
<br />
注1: 4月に開催された <a href="https://github.com/HTTPWorkshop/workshop2019">HTTP Workshop</a> でも話があったのですが、僕は欠席したので雰囲気を知りません。<br />
注2: 優先度を伝達するプロトコルと、その関連規定を取り除くのであって、サーバ独自の優先度制御など、プロトコルによらない手段を否定するものではありません。<br />
Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com0tag:blogger.com,1999:blog-4278688165584866733.post-48854993510306900762019-07-22T17:11:00.001+09:002019-07-22T17:14:16.791+09:00pthread_once が嫌いすぎて再実装した話pthread_once が嫌いです。なぜ嫌いかって言うと、こんな感じで、ファイルレベルのグローバル変数やグローバル関数が出現し、また、値を使う場所と初期化コードの位置が離れがちで可読性が下がるから。<br />
<br />
<pre style="background: #eee; margin: 0.5em 0; padding: 0.5em; font-family: monospace;">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);
...
}
</pre><br />
<br />
一方、pthread_onceを使う煩雑さを避けようとすると、自前でダブルチェックロックを書くことになるのですが、ダブルチェックロックをちゃんと書くのは難しい(参考:<a href="https://www.jpcert.or.jp/java-rules/lck10-j.html">LCK10-J. ダブルチェックロック手法を誤用しない</a>)し、実際間違えるし、毎回、間違えないように書こうとするのはストレスなんです。<br />
<br />
というわけで、一念発起して、マクロを使って自分が本当にほしかった「once」を実装しました。<br />
<br />
こんな感じで使います。<br />
<br />
<pre style="background: #eee; margin: 0.5em 0; padding: 0.5em; font-family: monospace;">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);
...
}
</pre><br />
グローバル変数やコールバック関数はなくなったし、初期化コードと利用コードが隣同士になって可読性が上がりました。<br />
<br />
実際のコードは <a href="https://github.com/h2o/h2o/pull/2086">https://github.com/h2o/h2o/pull/2086</a> にありますので、ご参照ください。これで正しくダブルチェックロック実装できてるはず。<br />
<br />
最後に一言。Cのマクロにブロック渡すのは超便利。Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com0tag:blogger.com,1999:blog-4278688165584866733.post-16651334248379037592019-06-12T15:01:00.000+09:002019-06-12T15:01:04.350+09:00#doh_study で Encrypted SNI について話しました6月11日に開催された <a href="https://web-study.connpass.com/event/131577/">#doh_study</a> で登壇する機会をいただきました。<br />
<br />
発表では、<a href="https://datatracker.ietf.org/doc/draft-ietf-tls-esni/">Encrypt SNI</a> の設計と最近の変更を説明するとともに、背景としてインターネット上のプロトコルに求められる要件がどう変わってきているかを説明しました。<br />
<br />
<script async class="speakerdeck-embed" data-id="485a56d3e18b4c4eb9665275a91907bd" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script><br />
<br />
暗号化とプライバシー保護は通信プロトコルの前提条件となりつつありますが、それにともない、企業内の機器やペアレンタルコントロール等において通信内容をどのように管理していくのか。インターネットをより安全で便利なものにするために、ステークホルダーをまたいだ協力が求められています。<br />
<br />
参考: <a href="https://blog.apnic.net/2019/06/11/moving-control-to-the-endpoints-motivations-challenges-and-the-path-forward/"> Moving control to the endpoints: Motivations, challenges, and the path forward | APNIC Blog</a>Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com4tag:blogger.com,1999:blog-4278688165584866733.post-85783365830453715032018-09-08T11:41:00.001+09:002018-09-08T14:20:12.908+09:00次世代プロトコル(QUIC etc.)のセキュリティとプライバシー @ #builderscon9月6日より開催中の <a href="https://builderscon.io/tokyo/2018">builderscon 2018</a> において、登壇の機会をいただき、インターネットのトランスポート層プロトコルについてセキュリティやプライバシーに関わる設計がどのように進めてられているか、TLS と QUIC を中心に発表しました。<br />
<br />
QUIC のハンドシェイクプロトコルとパケット番号暗号化、TLS の Encrypted SNI 拡張は、いずれも僕が提案した機能あるいは方式が採用される予定のものなので、背景にある動機や意義を含め、整理して発表する機会をもらえたことをありがたく感じています。<br />
<br />
聴講いただいた方々、また、スライドをご覧になる方々と、次世代プロトコルの暗号応用の手法のみならず意義を含め共有し、理解と議論を深めることができれば、これに勝る喜びはありません。<br />
<br />
<script async class="speakerdeck-embed" data-id="012e302acf3b4aefae0454c9bd86ec34" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script><br />
<br />
PS. QUIC のハンドシェイクプロトコルと Encrypted SNI 拡張については、以下のブログ記事もあわせてご覧いただけます。<br />
<br />
<a href="http://blog.kazuhooku.com/2018/06/quictls.html">QUICハンドシェイクの再設計、もしくはTLSレイヤの終焉</a><br />
<a href="http://blog.kazuhooku.com/2018/07/tls-sni-internet-draft.html">TLS の SNI 暗号化に関する Internet Draft を共同提出しました</a><br />
Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com93tag:blogger.com,1999:blog-4278688165584866733.post-10237585597835886182018-07-03T11:08:00.000+09:002018-07-03T11:10:43.368+09:00TLS の SNI 暗号化に関する Internet Draft を共同提出しましたEric Rescorla (RTFM), Nick Sullivan (Cloudflare), Christopher Wood (Apple) の各氏とともに、SNI を暗号化する TLS 拡張を提案する Internet Draft を提出しました。<br />
<br />
<div align="center"><a href="https://tools.ietf.org/html/draft-rescorla-tls-esni-00">Encrypted Server Name Indication for TLS 1.3</a></div><br />
<a href="https://www.ietf.org/mail-archive/web/tls/current/msg26468.html">アナウンスのメール</a>にあるとおり、すでに NSS / Firefox と picotls / H2O で実装作業が開始されており、今月開催される <a href="https://www.ietf.org/how/meetings/102/">IETF 102</a> で相互運用試験を行うとともに、標準化にむけた議論を深める予定です。<br />
<br />
スノーデン事件以降、広範囲におよぶトラフィックモニタリングによるプライバシー侵害の懸念が明らかになるとともに、できるだけ多くのインターネット上の通信プロトコルを暗号化することが求められるようになってきました (参考: <a href="https://tools.ietf.org/html/rfc7258">RFC 7258 - Pervasive Monitoring Is an Attack</a>)。<br />
<br />
その結果として、ウェブサイトの HTTPS 化が進み、DNS over TLS (<a href="https://tools.ietf.org/html/rfc7858">RFC 7858</a>)と <a href="https://datatracker.ietf.org/doc/draft-ietf-doh-dns-over-https/">DNS over HTTPS</a> の標準化により DNS プロトコルの暗号化にも目処が立ち、残る課題は、クライアントがサーバと TLS ハンドシェイクを開始する際に平文で流れるサーバ名<sup>注1</sup>をどう暗号化するか、という点となっていました。<br />
<br />
今回、共同提案した方式は、<a href="https://tools.ietf.org/html/draft-kazuho-protected-sni-00">私が昨年提案した方式</a>を簡略化したもので、ひとことでいうと「木を森の中に隠す」アプローチです。<br />
<br />
具体的には、CDN のように、多数のサーバ名を同一の IP アドレスから配信しているという前提<sup>注2</sup>において、その IP アドレスに属する全てのサーバ名について、同一の ECDH 鍵を DNS を用いて配信し、クライアントはその鍵を利用して TLS ハンドシェイク内のサーバ名を暗号化する、という形になります。<br />
<br />
クライアントはサーバのアドレス解決と同時に ECDH 鍵も DNS に問い合わせすることになりますが、HTTP/2 を前提とする DNS over HTTPS では複数の DNS 問い合わせを並列に実行可能なので、接続確立までのレイテンシは従来と変わらないと考えられています。<br />
<br />
<a href="https://blog.nightly.mozilla.org/2018/06/01/improving-dns-privacy-in-firefox/">Firefox が DNS over HTTPS に対応</a>するのに続き、<a href="https://developers-jp.googleblog.com/2018/05/dns-over-tls-support-in-android-p.html">Android が DNS over TLS をサポートする</a>という流れにある今、あとは SNI 暗号化が実装されれば、通信内容を第三者に傍受されたとしても、どのサーバと通信しているのかが漏洩する可能性はとても小さくなります<sup>注2</sup>。<br />
<br />
インターネットを利用する人々のプライバシー保護の観点からは前進である一方、海賊版サイトのブロッキングにおいて、ISP レベルでの対応が難しくなることは事実ですが、こちらについては、立法や司法の判断に基づいて配信を停止するという、ユーザのプライバシーを侵害しない形式での手法を検討していただきたいと考える次第です。<br />
<br />
<br />
<div style="font-size: 80%">注1: TLS では、ひとつの IP アドレスで動作するサーバが、クライアントが指定するサーバ名にあわせて異なるサーバ証明書を配信し、それに基づいて暗号方式を確立します<br />
注2: サーバ名ごとに異なる IP アドレスを使っているケースでは、IP アドレスからどのサーバにアクセスしているか判明するため、サーバ名を暗号化する意味がありません</div>Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com38tag:blogger.com,1999:blog-4278688165584866733.post-11805257454341937012018-06-14T15:05:00.001+09:002018-06-14T15:05:50.754+09:00Git で全ブランチから検索ググったけどmacOSでぱっと動くのがなかったのでメモがてら書く。<br />
<br />
<div style="background: #eee; margin: 0.5em 0; padding: 0.5em; font-family: monospace;">全ローカルブランチから検索<br />
% git grep keyword $(git branch | colrm 1 2)<br />
<br />
リモート含む全ブランチから検索<br />
% git grep keyword $(git branch -a | colrm 1 2)<br />
</div>Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com22tag:blogger.com,1999:blog-4278688165584866733.post-7965360725210095502018-06-12T14:06:00.003+09:002018-06-13T15:29:29.268+09:00QUICハンドシェイクの再設計、もしくはTLSレイヤの終焉先週スウェーデンのKistaで開催された<a href="https://github.com/quicwg/wg-materials/blob/master/interim-18-06/agenda.md">第5回QUIC Interim</a>で、<a href="https://docs.google.com/presentation/d/176bVI27bRJrfahf8RR89hbZ_owMs54ipxXALNUUsV1c/edit">ハンドシェイクプロトコルの再設計案</a>の採用が決まりました。<br />
<br />
提案者として、その背景にある考え方を整理したいと思います。<br />
<br />
<b>▪️提案内容</b><br />
<br />
詳しくは<a href="https://docs.google.com/document/d/1fRsJqPinJl8N3b-bflDRV6auojfJLkxddT93j6SwHY8/edit">Design Doc</a>を見てもらえばいいとして、ざっくりいうと、<ul><li>TLSスタックをふたつに分割し</li>
<li>パケットはQUICがレイアウトしたバイト列をTLSスタックが提供するAPIを使って暗号化<sup>注1</sup>して生成</li>
<li>ハンドシェイクメッセージについては、平文のメッセージをTLSスタックとQUICスタックとの間で交換し、QUICスタック側で上記手法によるパケット化暗号化を行う</li>
</ul>というものです。<br />
<br />
これにより、たとえばサーバがハンドシェイク時に送出するパケットの構造は以下のようにかわります。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">図1. 従来方式<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjof0WmD9LwVqoHNsF6q11wvpjTnvtQ380XGuIJSdfCrPoWxpiMSGfvrCbcCrAy6JIGBz30JT9iZKbU02oIH55WqInsM7FD6A3LEos8T-fvgr_DZKtUavkxC3GNEVr-xRiE5MegPw5NDc25/s1600/Screen+Shot+2018-06-12+at+1.54.22+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjof0WmD9LwVqoHNsF6q11wvpjTnvtQ380XGuIJSdfCrPoWxpiMSGfvrCbcCrAy6JIGBz30JT9iZKbU02oIH55WqInsM7FD6A3LEos8T-fvgr_DZKtUavkxC3GNEVr-xRiE5MegPw5NDc25/s320/Screen+Shot+2018-06-12+at+1.54.22+PM.png" width="320" height="84" data-original-width="558" data-original-height="147" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;">図2. 新方式<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHBF0bQ_Kc3p3HYg1kP75skiDkK2CYcQAx92pw0Sc0HB5AVul2ug41cYCQdUDlen_m5S42F2uNXUko7tv-xNQ24gO80vyuGBeOqIdmwgXGf2ncLyhFWEece0dJoXe2iRDMkTf2eHvcsG7u/s1600/Screen+Shot+2018-06-12+at+1.54.38+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHBF0bQ_Kc3p3HYg1kP75skiDkK2CYcQAx92pw0Sc0HB5AVul2ug41cYCQdUDlen_m5S42F2uNXUko7tv-xNQ24gO80vyuGBeOqIdmwgXGf2ncLyhFWEece0dJoXe2iRDMkTf2eHvcsG7u/s320/Screen+Shot+2018-06-12+at+1.54.38+PM.png" width="320" height="58" data-original-width="562" data-original-height="101" /></a></div><br />
赤は難読化(つまり正当なパケットと攻撃との区別がつかない)、黄は未認証の暗号化(通信は暗号化されているが、誰と話しているかはわからない)、青は認証済の暗号化を示しています。従来は5層のレイヤで2重の暗号化をしていたのが、4層のレイヤで1重の暗号化を行うようににかわることがわかります。<br />
<br />
なぜ、このような変更を採用することになったのでしょうか。<br />
<br />
<b>▪️「レイヤ化」アプローチの限界</b><br />
<br />
伝統的なTLSスタックにおいて、TLS接続はTCP/IP接続の拡張として表現されます。たとえば、Javaの<a href="https://docs.oracle.com/javase/jp/7/api/javax/net/ssl/SSLSocket.html">SSLSocket</a>はSocketを拡張したクラスです。<br />
<br />
書き込みや読み込み命令はSSLSocketに対して同期命令として発行され、SSLSocketの中で適宜Socket I/Oに変換されます。<br />
<br />
この手法の問題は、ソケットの管理が困難になりがちなことです。<br />
<br />
たとえば、イベントドリブンなプログラムにおいては、同期I/Oではなく非同期I/Oが求められます。このことは、TLSスタックに非同期I/O用のモードを別途設けるか、あるいは、TLSスタックに対し、アプリケーション側からソケットのようなAPIをもつバッファを提供してやる必要がでてくることを意味します。<br />
<br />
また、ヘッドオブラインブロッキングの影響を制御するために、暗号化するブロックサイズを制御し、ブロック単位で送信しようにも、そのような制御を可能にするインターフェイスが存在しないという問題があります<sup>注2</sup>。<br />
<br />
これらの点に鑑み、<a href="https://h2o.examp1e.net">H2O</a>では、レイヤ化ではなく、入力バッファと出力バッファを引数としてハンドシェイクや暗号化関数を呼び出すというコーデックスタイルのAPIをもつ独自のTLSスタック「<a href="https://github.com/h2o/picotls">picotls</a>」を開発し、TLS 1.3むけに採用してきました。<br />
<br />
<b>▪️「トランスポート層による暗号化」の必要性</b><br />
<br />
TCP上でTLSを使う上での問題点のひとつが、第三者がいつでも接続をリセットできるという点です。このような攻撃が可能なのは、TCP自体が認証付き暗号によって保護されておらず、リセットを引き起こすようなパケットを誰でも生成できてしまうからです。<br />
<br />
このようなトランスポート層への攻撃に対処するため、QUICのこれまでのドラフトでは、QUICの上で動くTLSスタックから鍵を「エクスポート」し、この鍵を使ってパケットを暗号化してきました。<br />
<br />
この点を指して、picotlsの共同開発者である<a href="https://huitema.wordpress.com">Christian Huitema氏</a>は「TLSの利用形態はレイヤからコンポーネントへ変化している」と指摘してきました。プログラミング上の都合のみならず、プロトコル設計上の都合からも、従来のTLSをレイヤとして利用するアプローチが限界に来ていたことがわかります。<br />
<br />
利用形態の変化はともかく、これまでのドラフトのアプローチは以下の各点の問題を抱えるものでした。<br />
<ul><li>暗号化をTLSとQUICという2つのソフトウェアスタックで行うという二重暗号化</li>
<li>鍵交換後もハンドシェイク完了まで攻撃に対し脆弱</li>
<li>鍵の使用開始タイミングが不明確</li>
</ul><br />
これらの問題を解決しようとする動きとしては、先行して<a href="https://github.com/quicwg/wg-materials/blob/master/ietf101/Stream0-EKR.pdf">IETF 101におけるEric Rescorla氏によるDTLSへの移行提案</a>がありましたが、DTLSのパケットフォーマットがQUICが必要する機能を提供できない点や、再送制御がハンドシェイク前と後と異なってしまう点などが問題視されていました。<br />
<br />
それを踏まえ、DTLSという、QUICとは異なるプロトコルを組み合わせるくらいなら、いっそTLSスタックを分割して、QUICのパケットフォーマットをそのまま使おう、というのが、今回の提案の骨子だったわけです。<br />
<br />
従来のドラフトと比較した利点としては、<br />
<ul><li>二重暗号化がなくなる</li>
<li>TLS 1.3と同じタイミングで暗号鍵を変更するため、ハンドシェイク中の攻撃耐性が向上する<sup>注3</sup></li>
<li>鍵管理をTLSスタックに依存できるようになるので、QUICスタックの品質向上に寄与する</li>
<li>パケットフォーマットは引き続きQUICワーキンググループで設計管理、拡張される</li>
</ul>といった点があげられることになります。<br />
<br />
また、この提案が受け入れられた背景として、TLSスタックの従来のAPIに限界があることが共通理解となっていた、という点もあげられるでしょう。<br />
<br />
<b>▪️まとめ</b><br />
<br />
QUICではTLSをハンドシェイクと暗号化という2つの機能に分割して使用することになりました。このことは品質向上につながります。<br />
<br />
TLSから見ると、TCP上での使用を前提としたTLS 1.3、UDP上での使用を前提に異なるパケットフォーマットを採用したDTLS 1.3に加え、第3のパケットフォーマットをもつプロトコルが誕生することを意味します。<br />
<br />
今後策定されるプロトコルにおいて暗号化が必須であることを考えると、TLSのハンドシェイクメッセージを使いつつ、プロトコル毎に異なるパケットフォーマットを定義する流れが一般化するかもしれません。<br />
<br />
長期的にみると、TLSの解体と再構成につながるかもしれませんね。<br />
<br />
<br />
<div style="font-size: 80%">注1: TLSスタックが共通鍵を提供しQUICスタック側で暗号化を行ってもよい<br />
注2: TLSスタックの下にあるソケットAPIはあくまでバイト列をやりとりするためのものであって、レコードの切れ目がどこにあるかを表現する前提になっていない、ということ<br />
注3: 図2において、赤で示される、パケット注入攻撃が可能なタイミングが短くなっていることが確認できます<br />
</div>Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com20tag:blogger.com,1999:blog-4278688165584866733.post-80275375220452914052018-06-02T16:20:00.000+09:002018-06-02T16:22:19.622+09:00H2O version 2.3.0-beta1 released, improvements presented at Rubykaigi 2018Today, I am happy to announce the release of H2O version 2.3.0-beta1.<br />
<br />
Version 2.3 is going to be the largest release in the history of H2O. Beta-1 already includes more than 50 changes contributed by more than 10 developers.<br />
<br />
Improvements include:<br />
<ul><li>more powerful mruby handler with Rack and Rack middleware support</li>
<li>load balancing in the reverse proxy handler (<a href="https://github.com/h2o/h2o/pulls/1277">#1277</a>, <a href="https://github.com/h2o/h2o/pulls/1361">#1361</a>)</li>
<li>more flexible configuration through the use of !env and stash directives (<a href="https://github.com/h2o/h2o/pulls/1524">#1524</a>, <a href="https://github.com/h2o/h2o/pulls/1739">1739</a>)</li>
<li>support for new and upcoming HTTP extensions: Server-Timing (<a href="https://github.com/h2o/h2o/pulls/1646">#1646</a>, <a href="https://github.com/h2o/h2o/pulls/1717">#1717</a>), 103 Early Hints (<a href="https://github.com/h2o/h2o/pulls/1727">#1727</a>, <a href="https://github.com/h2o/h2o/pulls/1767">#1767</a>), 425 Too Early (<a href="https://github.com/h2o/h2o/pulls/1344">#1344</a>)</li>
</ul><br />
The improvements related to mruby and HTTP extensions were covered in today's our talk at <a href="http://rubykaigi.org/2018/">RubyKaigi 2018</a> and the slides are below. Please enjoy!<br />
<br />
<div align="center"><iframe src="//www.slideshare.net/slideshow/embed_code/key/fw1Q9bht4ZkkDt" width="510" height="420" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"><strong> <a href="//www.slideshare.net/ichitonagata/how-happy-they-became-with-h2omruby-and-the-future-of-http" title="How happy they became with H2O/mruby and the future of HTTP" target="_blank">How happy they became with H2O/mruby and the future of HTTP</a> </strong> from <strong><a href="//www.slideshare.net/ichitonagata" target="_blank">Ichito Nagata</a></strong> </div></div>Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com55tag:blogger.com,1999:blog-4278688165584866733.post-76839153656486264662018-06-01T17:16:00.000+09:002018-06-01T17:16:28.276+09:00H2O version 2.2.5 released with a vulnerability fixToday, we have released H2O <a href="https://github.com/h2o/h2o/releases/tag/v2.2.5">version 2.2.5</a>.<br />
<br />
This is a bug-fix release, including one security-related fix.<br />
<br />
The detail of the vulnerability is explained in <a href="https://github.com/h2o/h2o/issues/1775">#1775</a>. Users of H2O are advised to upgrade immediately to version 2.2.5 or to disable access logging.<br />
<br />
We would like to thank Marlies Ruck, ForAllSecure for finding the issue.<br />
<br />
List of other changes can be found <a href="https://github.com/h2o/h2o/releases/tag/v2.2.5">here</a>.<br />
Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com28tag:blogger.com,1999:blog-4278688165584866733.post-24548334238825056692018-05-01T14:40:00.000+09:002018-05-01T14:40:46.278+09:00IETF報告会で「TLS 1.3とその周辺の標準化動向」について発表してきた先週金曜日に開催された<a href="https://www.isoc.jp/wiki.cgi?page=IETF101Update">IETF報告会</a>にて、「TLS 1.3とその周辺の標準化動向」について発表する機会をいただきました。その際のスライドが下のものになります。<br />
<br />
<script async class="speakerdeck-embed" data-id="bcaf3fd693ce4518bf16c565e408fcc5" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script><br />
<br />
TLS 1.3や関連する提案の技術的特徴とともに、<a href="https://tools.ietf.org/html/rfc7258">スノーデン事件以来のテーマ</a>である通信内容のプライバシー保護とオシフィケーション(硬化)対策がどのように進んでいるか、ご理解いただける一助になれば幸いです。<br />
<br />
なお、本スライドは会社のテンプレートを使用していますが、会社としての見解を表明するものではありません。Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com30tag:blogger.com,1999:blog-4278688165584866733.post-23586585658709289112018-04-26T12:12:00.003+09:002018-04-26T12:27:28.183+09:00海賊版サイトのブロッキングについてアンケートをとってみたら興味深い結果が出た<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">政府がISPに対し対し海賊版サイトのブロッキングを要請し、議論になっています。あなたは以下のどの対策が正しいと思いますか?</p>— Kazuho Oku (@kazuho) <a href="https://twitter.com/kazuho/status/988975504291086337?ref_src=twsrc%5Etfw">April 25, 2018</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <br />
<br />
832票もの回答をいただきました。ありがとうございます。結果をみて、いくつか感想を述べさせていただきたいと思います。<br />
<br />
<b>▪️海賊版サイトに対し、なんらかの新たな対策が必要かどうかについて</b><br />
<br />
83%の方々が、なんらかの新しい対策を取ることに積極的賛成、あるいは消極的賛成という立場を取られていることがわかりました。一方で、17%の方々が、少なくとも現時点では新たな対策は不要であり、出版社等の権利者は現行法に基づき、刑事告発、民事訴訟、DMCA Takedownなどの手法を用いて戦うべきだと考えていらっしゃることもわかりました。<br />
<br />
<b>▪️ブロッキングという手法について</b><br />
<br />
意見が綺麗に割れました。<br />
<br />
42%の方々が、緊急の、あるいは法整備に基づいたブロッキングが適当だとされました。一方、58%の方々が、ブロッキング以外の手法を用いた対策を整備すべき、あるいは整備は不要であると回答されました。<br />
このことからは、「通信の秘密」が単なる法律の条文ではなく、守るべき価値のあるものだと考えていらっしゃる方々が相当数いらっしゃることも読み取れるのかと思います。<br />
<br />
<b>▪️ISPが政府の要請に従うべきかについて</b><br />
<br />
5%の方々が、「ISP各社は政府の要請に従ってブロッキングすべき」と答えられました。私のフォロワーには(ブロッキングに従事することに法的リスクがあったり、通信の秘密を重要視したりしがちな)情報通信産業の関係者が多いことを考えると、緊急のブロッキングを求める声は実際にはもっと大きいと考えられます。<br />
<br />
<br />
皆さんは、この結果を見て、どのように考えられますか?<br />
<br />
<b>▪️その他</b><br />
<br />
ツイッターのアンケート機能の、各選択肢が25字制限というのは辛かった。「その他、わからない」という選択肢を足せなかったも悲しかった。<br />
<br />
<b>▪️免責事項</b><br />
<br />
私はCDN企業に勤める従業員です(ただし、著作権を侵害するコンテンツの配布を禁止している企業であるため、海賊版サイトとの利害関係はない)。また、IETFにおいて、通信の傍受(と傍受によって可能になるブロッキング)が困難あるいは不可能になるようなプロトコル設計に従事しています。通信の秘密の保護はIETFのプロトコル設計の要件のひとつ(<a href="https://tools.ietf.org/html/rfc7258">RFC 7258</a>)なのですが、その価値について、他の方々がどのように考えているか、お伺いすることができて、うれしく思っています。Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com13tag:blogger.com,1999:blog-4278688165584866733.post-26121872148531614582018-04-17T15:50:00.000+09:002018-04-18T10:24:27.845+09:00Fastlyのプログラマから見たCDN4月13日に開催された<a href="https://http2study.connpass.com/event/81469/">CDN Study (Akamai/Fastly)</a>で使用したスライドをアップロードしました。Fastlyでミドルウェアを書いているプログラマから見た、CDNの面白さやFastlyの特徴について伝わればいいなと思います。<br />
<br />
<script async class="speakerdeck-embed" data-id="d05488bf312640d5a5fb3b631c06778a" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script><a href="http://b.hatena.ne.jp/entry/s/speakerdeck.com/kazuho/fastlyfalsepuroguramakarajian-tacdn" class="hatena-bookmark-button" data-hatena-bookmark-layout="basic-counter" title="このエントリーをはてなブックマークに追加"><img src="https://b.st-hatena.com/images/entry-button/button-only@2x.png" alt="このエントリーをはてなブックマークに追加" width="20" height="20" style="border: none;" /></a><script type="text/javascript" src="https://b.st-hatena.com/js/bookmark_button.js" charset="utf-8" async="async"></script><br />
<br />
当日は、リクルートさんに会場をご提供いただき、多くの方々、またAkamaiの方々ともと触れ合うことができる、とても貴重な機会となりました。この場を借りて、皆さんに御礼申し上げる次第です。Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com10tag:blogger.com,1999:blog-4278688165584866733.post-62099036171465983332018-04-17T14:03:00.001+09:002018-04-17T14:03:52.014+09:00HTTP/2で 速くなるとき ならないときたいへん遅ればせながら、<a href="http://yapcjapan.org/2018okinawa/">YAPC::Okinawa 2018 ONNNASON</a>で使用したスライドを、こちらにて公開する次第です。<br />
<br />
ベンチマークの難しさとチューニングの奥深さ、楽しさを共有できた結果がベストトーク賞につながったのかなと考えています。ありがとうございました&今後ともよろしくお願いいたします。<br />
<br />
<iframe src="//www.slideshare.net/slideshow/embed_code/key/bBYIxCweGa8GhE" width="595" height="435" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"><strong> <a href="//www.slideshare.net/kazuho/http2-94049492" title="HTTP/2で 速くなるとき ならないとき" target="_blank">HTTP/2で 速くなるとき ならないとき</a> </strong> from <strong><a href="https://www.slideshare.net/kazuho" target="_blank">Kazuho Oku</a></strong> </div>Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com33tag:blogger.com,1999:blog-4278688165584866733.post-43467617140415742662017-12-28T07:37:00.001+09:002017-12-28T08:04:37.157+09:00CI 用 Docker イメージ作成におけるベストプラクティス<a href="https://github.com/h2o/h2o">H2O</a> の CI では長らく、秘伝のタレ的な .travis.yml を使い続けてきたのですが、なにぶん依存関係が多いもので、だいぶメンテナンスが辛い感じになってきていました。また、CI テストで発生したエラーの調査の度に、時間のかかる CI を回さなければならないことが、開発者のストレスとなっていました。<br />
<br />
そこで一念発起して、<a href="https://docker.com/">Docker</a> イメージを使った CI に切り替えることにしました(実行環境としては引き続き <a href="https://travis-ci.org">Travis</a> を使います)。<br />
<br />
その際に、要件として以下のようなことを考えました。<br />
<ul><li>CI以外に、コミット前のテストにも使えるようなイメージにすること</li>
<li>コマンド一発でビルドとテストが動作すること</li>
<li>無駄な処理をしないこと</li>
</ul><br />
その結果、以下のような実装になりました。<br />
<ul><li>テストに必要なソフトウェア群をインストールしたイメージを <a href="https://hub.docker.com/">Docker Hub</a> にアップロードしておく</li>
<li>テストには、1. テスト用コンテナにコードのあるディレクトリをマウントし、2. アウトオブトリービルド<sup>注1</sup>とテストを実行するスクリプトを書く</li>
<li>CI環境では、そのスクリプトを実行する</li>
<li>開発者がローカルでそのスクリプトを実行してテストしてもよい</li>
</ul><br />
Dockerfile および CI 実行用のスクリプトは、<a href="https://github.com/h2o/h2o">H2O のレポジトリ</a>にある以下のファイルです。<br />
<ul><li><a href="https://github.com/h2o/h2o/blob/master/misc/docker-ci/Dockerfile">misc/docker-ci/Dockerfile</a></li>
<li><a href="https://github.com/h2o/h2o/blob/master/misc/docker-ci/Dockerfile">misc/docker-ci/check.mk</a></li>
</ul><br />
結果、Docker 導入前に90行あった .travis.yml は23行にまで短くなり、また、CI に必要な時間も2割ほど減少しました(CI を実行するたびに、必要なソフトウェアをビルドする手間が不要になったため)。<br />
<br />
また、開発者が各個人の環境において <code>make -f misc/docker-ci/check.mk</code> と <code>make</code> を実行するだけで、いつでも(コミット前でも)CI 環境と同様のテストが実行できるようになりました。テストが失敗したら、コンテナを立ち上げっぱなしにして、ローカルでコード修正&特定のテスト再実行を繰り返すことも可能なので、バグ修正が捗ります。<br />
<br />
まとめ:CI 用の Docker イメージを作るのではなく、テスト用の CI イメージを作り、それを CI に展開すべき<br />
<br />
言わずもがなかもしれませんが、備忘録としてまとめておきます。<br />
<br />
<br />
注1: ソースディレクトリ以外の場所に生成物を配置するビルド手法のこと。アウトオブトリービルドができない場合はaufsを使っても良いのかもしれないKazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com24tag:blogger.com,1999:blog-4278688165584866733.post-79549856260116723972017-12-27T10:39:00.001+09:002017-12-27T10:50:02.186+09:00git blameでプルリクエストの番号を表示する<a href="https://github.com/">GitHub</a>でプルリクエスト前提の開発をしていると、<code>git blame</code>で「なぜ、このコードがこうなっているのか」調べる際に、commit idではなくプルリクエストの番号を表示してほしくなります。<br />
<br />
というわけで書いたのが <a href="https://gist.github.com/kazuho/eab551e5527cb465847d6b0796d64a39">git-blame-pr.pl</a>。<br />
<br />
以下のような感じで表示されるので、調査がはかどります。<br />
<pre style="border: 1px solid gray; margin: 1em 0; padding: 0.5em; overflow-x: auto; white-space: pre !important; background: #eee;">$ git-blame-pr.pl lib/core/request.c
(中略)
PR #446
PR #606 h2o_iovec_t h2o_get_redirect_method(h2o_iovec_t method, int status)
PR #606 {
PR #606 if (h2o_memis(method.base, method.len, H2O_STRLIT("POST")) && !(status == 307 || status == 308))
PR #606 method = h2o_iovec_init(H2O_STRLIT("GET"));
PR #606 return method;
PR #606 }
PR #606
PR #1436 static void do_push_path(void *_req, const char *path, size_t path_len, int is_critical)
PR #1436 {
PR #1436 h2o_req_t *req = _req;
PR #1436
PR #1436 if (req->conn->callbacks->push_path != NULL)
PR #1436 req->conn->callbacks->push_path(req, path, path_len, is_critical);
PR #1436 }
PR #1436
PR #1169 h2o_iovec_t h2o_push_path_in_link_header(h2o_req_t *req, const char *value, size_t value_len)
PR #446 {
PR #1169 h2o_iovec_t ret = h2o_iovec_init(value, value_len);
PR #446
PR #1436 h2o_extract_push_path_from_link_header(&req->pool, value, value_len, req->path_normalized, req->input.scheme,
PR #1436 req->input.authority, req->res_is_delegated ? req->scheme : NULL,
PR #1436 req->res_is_delegated ? &req->authority : NULL, do_push_path, req, &ret);
PR #446
PR #1169 return ret;
PR #446 }
</pre>Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com12tag:blogger.com,1999:blog-4278688165584866733.post-24650619487072796822017-12-15T16:39:00.000+09:002017-12-15T16:39:08.869+09:00H2O version 2.2.4 released, incl. vulnerability fixesToday, we have released H2O <a href="https://github.com/h2o/h2o/releases/tag/v2.2.4">version 2.2.4</a>.<br />
<br />
This is a bug-fix release. Some of the fixes are security-related.<br />
<br />
The details of the vulnerabilities being fixed can be found in the links below. Users are encouraged to upgrade to 2.2.4 if they are affected or unsure.<br />
<ul><li><a href="https://github.com/h2o/h2o/issues/1543">fix crash when logging TLS 1.3 properties (CVE-2017-10872)</a> (reported by MITSUNARI Shigeo)</li>
<li><a href="https://github.com/h2o/h2o/issues/1544">fix crash when handling malformed HTTP/2 request (CVE-2017-10908)</a> (reported by Eiichi Tsukata)</li>
</ul>We would like to thank the people for reporting the issues.Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com16tag:blogger.com,1999:blog-4278688165584866733.post-86642925140104782972017-11-07T15:41:00.002+09:002017-11-07T15:47:27.480+09:00最高速のfizzbuzzを実装する話この前、Twitterで誰かが「コンパイラ言語でFizzbuzz書くなら、コンパイル時に全ての演算を済ませ、実行コストはI/O命令1個になるように最適化しないと」という話をしていた。いいこと言うな、と思ってスルーしていたのだが、体調不良で頭だけ動いている状態だったのでC++11でトライしてみることに。<br />
<br />
案ずるより産むが易しというもので、割と簡単に綺麗に書けた。こんな感じ。<br />
<br />
char配列を可変長のテンプレート引数として結合していって、文字列定数を生成するというテクニックは実際に使い所があるかもと思った。最近C++書いてないけど。<br />
<pre style="border: 1px solid gray; margin: 1em 0; padding: 0.5em; white-space: pre-wrap; background: #eee;">#include <cstdio>
template <typename LHS, int N> struct numstr {
template <char... Args> struct append {
typedef typename numstr<LHS, N / 10>::template append<'0' + N % 10, Args...>::result result;
};
};
template <typename LHS> struct numstr<LHS, 0> {
template <char... Args> struct append {
typedef typename LHS::template append<Args...>::result result;
};
};
template <int N, int Mod3 = N % 3, int Mod5 = N % 5> struct fizzbuzz {
template <char... Args> struct append {
typedef typename numstr<fizzbuzz<N - 1>, N>::template append<'\n', Args...>::result result;
};
};
template <> struct fizzbuzz<0> {
template <char... Args> struct append {
struct result {
const char data[sizeof...(Args)];
constexpr result() : data{Args...} {}
};
};
};
template <int N> struct fizzbuzz<N, 0, 0> {
template <char... Args> struct append {
typedef typename fizzbuzz<N - 1>::template append<'F', 'i', 'z', 'z', 'B', 'u', 'z', 'z', '\n', Args...>::result result;
};
};
template <int N, int Mod5> struct fizzbuzz<N, 0, Mod5> {
template <char... Args> struct append {
typedef typename fizzbuzz<N - 1>::template append<'F', 'i', 'z', 'z', '\n', Args...>::result result;
};
};
template <int N, int Mod3> struct fizzbuzz<N, Mod3, 0> {
template <char... Args> struct append {
typedef typename fizzbuzz<N - 1>::template append<'B', 'u', 'z', 'z', '\n', Args...>::result result;
};
};
int main() {
constexpr fizzbuzz<100>::append<>::result s;
fwrite(s.data, 1, sizeof(s.data), stdout);
return 0;
}
</pre>コンパイルしてディスアセンブルすると、コンパイル時に生成された文字列定数をfwriteするだけのmain関数が生成されていることが確認できる。<br />
<pre style="border: 1px solid gray; margin: 1em 0; padding: 0.5em; white-space: pre-wrap; background: #eee;">$ clang++ -O2 -std=c++11 -pedantic -Wall fizzbuzz11.cc && otool -tV a.out
a.out:
(__TEXT,__text) section
_main:
0000000100000dd0 pushq %rbp
0000000100000dd1 movq %rsp, %rbp
0000000100000dd4 movq 0x225(%rip), %rax ## literal pool symbol address: ___stdoutp
0000000100000ddb movq (%rax), %rcx
0000000100000dde leaq __ZZ4mainE1s(%rip), %rdi ## main::s
0000000100000de5 movl $0x1, %esi
0000000100000dea movl $0x19d, %edx
0000000100000def callq 0x100000df8 ## symbol stub for: _fwrite
0000000100000df4 xorl %eax, %eax
0000000100000df6 popq %rbp
0000000100000df7 retq
</pre><br />
ちなみに、<a href="https://gist.github.com/kazuho/b4d810a89f82bbca65aa42cc77954342">C++14だと、いたるところにconstexprがつけられるので、もっと普通のコードになる</a>。Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com6tag:blogger.com,1999:blog-4278688165584866733.post-21184733145454967372017-10-19T15:33:00.001+09:002017-10-19T15:34:43.495+09:00H2O version 2.2.3 released, incl. vulnerability fixesToday, we have released <a href="https://h2o.examp1e.net">H2O</a> version 2.2.3.<br />
<br />
This is a bug-fix release, including <a href="https://github.com/h2o/h2o/releases/tag/v2.2.3">two security fixes and 14 bug fixes from 7 people</a>. Please consult the <a href="https://github.com/h2o/h2o/releases/tag/v2.2.3">release page</a> for details.<br />
<br />
The vulnerabilities being fixed are <a href="https://github.com/h2o/h2o/issues/1459">#1459 (CVE-2017-10868)</a> and <a href="https://github.com/h2o/h2o/issues/1460">#1460 (CVE-2017-10869)</a>. Both are vulnerabilities against DoS attacks. It is recommended that the users of H2O update their deployments to the newest release.<br />
<br />
We would like to thank the developers for working on the fixes and for users reporting the issues.Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com48tag:blogger.com,1999:blog-4278688165584866733.post-6619552345427000982017-04-05T17:42:00.000+09:002017-04-05T17:42:12.815+09:00H2O version 2.2.0 releasedToday I am happy to announce the release of <a href="https://h2o.examp1e.net/">H2O HTTP/2 server</a> version 2.2.0.<br />
<br />
The release includes over ten new features (show below) as well as bug fixes.<br />
<br />
<ul><li>[core] add <code>crash-handler.wait-pipe-close</code> parameter <a href="https://github.com/h2o/h2o/issues/1092">#1092</a></li>
<li>[core] introduce an option to bypass the <code>server</code> header sent from upstream <a href="https://github.com/h2o/h2o/issues/1226">#1226</a></li>
<li>[access-log] add <code>%{remote}p</code> for logging the remote port <a href="https://github.com/h2o/h2o/issues/1166">#1166</a></li>
<li>[access-log] JSON logging <a href="https://github.com/h2o/h2o/issues/1208">#1208</a></li>
<li>[access-log] add specifier for logging per-request environment variables <a href="https://github.com/h2o/h2o/issues/1221">#1221</a></li>
<li>[access-log] add support for <code><</code>, <code>></code> modifiers for logging either the original or the final response <a href="https://github.com/h2o/h2o/issues/1238">#1238</a></li>
<li>[file] add directive for serving gzipped files, decompressing them on-the-fly <a href="https://github.com/h2o/h2o/issues/1140">#1140</a></li>
<li>[http2] recognize <code>x-http2-push-only</code> attribute on <code>link</code> header <a href="https://github.com/h2o/h2o/issues/1169">#1169</a></li>
<li>[http2] add optional timeout for closing connections upon graceful shutdown <a href="https://github.com/h2o/h2o/issues/1108">#1108</a></li>
<li>[proxy] add directives for tweaking headers sent to upstream <a href="https://github.com/h2o/h2o/issues/1126">#1126</a></li>
<li>[proxy] add directive for controlling the <code>via</code> request header <a href="https://github.com/h2o/h2o/issues/1225">#1225</a></li>
<li>[ssl] add directive for logging session ID <a href="https://github.com/h2o/h2o/issues/1164">#1164</a></li>
</ul><br />
Some notable changes are covered in separate blogposts: <a href="http://blog.kazuhooku.com/2017/02/h2o-version-22-beta-released-with-tls.html">H2O version 2.2 beta released with TLS 1.3 support and other improvements</a>, <a href="http://blog.kazuhooku.com/2017/03/json-logging-support-is-added-to-h2o.html">JSON logging support is added to H2O HTTP/2 server version 2.2.0-beta3</a>.<br />
<br />
Full list of changes can be found <a href="https://github.com/h2o/h2o/releases/tag/v2.2.0">here</a>.<br />
<br />
The release also comes with the up-to-date version of <a href="https://github.com/mruby/mruby">mruby</a>. Recently, <a href="https://mruby.sh/201703270126.html">a series of security defects have been reported for the language runtime</a>. Our understanding is that many of the vulnerabilities rely on an attacker writing the script (a model that does not apply to how mruby is used in H2O). However, you can turn off mruby support by providing <code>-DWITH_MRUBY=OFF</code> as an argument to CMake, or update mruby to the latest version simply by replacing the contents of <code>deps/mruby</code> with that of <a href="https://github.com/mruby/mruby/">github.com/mruby/mruby</a>.Kazuho Okuhttp://www.blogger.com/profile/16216892063018163156noreply@blogger.com88