2011/11/19

syncookiesとListenバックログとパケットロスト

たまにはLinuxネタを。

Listenバックログは、伝統的なUNIXの実装だと、SYN_RCVDとESTABLISHEDの両方のソケット数を数えますが、LinuxのそれはESTABLISHEDな状態の数だけを数えるようになっています(manを見よ)。

これは何でかというと、いわゆるSYN Flooding攻撃への対応として、Linuxはsyncookieを実装したことの副作用なのだと思います。syncookieを実装していると、SYNに対してSYN_ACK(COOKIE)を返すコストがほぼゼロ(メモリコストとしては)になるので、来たSYNにすべてSYN+ACKを返すことが可能です。
したがって、SYN_RCVDの数は数えても意味がなくなったので、それはListenバックログの数としてカウントしないようにした、ということのようです(厳密に言うと、tcp_max_syn_backlog個までは SYN_RCVD で受けて普通のSYN+ACKを返し、それを超えた分についてはsyncookieを返すという挙動になりますが)。

syncookieの利点は、基本的に来たものは攻撃パケットだろうが正当な(legitimateな)パケットだろうがすべて食って返事が出来る、ということにあるのだと思われます。
すなわち、伝統的な実装の場合、攻撃パケットと正当なパケットは平等にキューから溢れますが、syncookieだと「とりあえず全員に応答してみて、handshake-completing-ACK (最後のACK)を返して来たものだけ処理をする」ということができるので、正当なコネクション要求を落としにくくなる、というメリットがあるということですね。

なお、syncookieはRFC4987で議論されていて、やり過ぎだとかTCPオプション使えないじゃないかとか、syncacheの方が良いとか毀誉褒貶?があるようですが、実装された以上は、まあ仕方がありません。



さて、前述のように「Listenバックログの数がESTABLISHEDの数だ」ということになっているので、ESTABLISHEDな受付待ちセッション数に上限を掛けなければならない都合上、Linuxはhandshake-completing-ACKを(能動的に)落とすことがあります。これは上の話からも明らかで、Listenバックログを超えてESTABLISHEDなセッションが生まれそうになったら、そのタイミングでACKを落とさざるを得ないからです。逆に伝統的な実装であれば、SYN+ACKを返した暁には、必ずhandshake-completing-ACKを受けることができます。

(余談ですが、LinuxはSYNも静かに、すなわちnetstatや/procに何らかの主張をすることなく落とすことがあって、ちょっと嫌ですね)

ここで問題になるのは、handshake-completing-ACKを落とすと、「クライアント側がESTABLISHEDなのに、サーバ側はSYN_RCVDのまま」といったことがあり得て、特に、クライアント側がread-sideになる場合は、(自分でkeep aliveなりselectなりでタイマーを掛けていない限り)無限に待ってしまうことがあり得ます。

さらに微妙なケースで、SYN+ACK再送とFIN再送のタイミングによっては、サーバ側も(read-sideの場合は、そして普通はサーバがread-sideになります)ESTABLISHEDのままになってしまうことがあり得ます。

一般論として、カーネルがパケットをいつ落とそうが知ったことではないわけですが、Linuxの場合、それが比較的、能動的な形で起きるので、そこはちょっとびっくりします。



何にせよ、
  • タイマは掛けよう。
  • tcp_abort_on_overflowやtcp_synack_retriesとかを使うのも良い考えかもしれない。
  • syncookieの有無にかかわらず、Listenバックログは適切に設定すべし。
といったことに注意が必要ですね、というお話でした。

とってんぱらりのぷう。

0 件のコメント: