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に転送されるように設定する。

これで動く。