IPv6とTCP MSSの調整

本日リリースされたSEIL/X,B1 3.50, SEIL/x86 2.00から、IPv6の通信でもTCP MSS調整機能が利用できるようになりました。今回は主にTCP MSS調整機能とは何なのか、またその必要性についてIPv6の仕様を絡めて解説致します。

記事一覧

TCP MSS調整機能とは

TCP MSS調整機能とは、SEILを通過するTCP通信に含まれるMSS(Maximum Segment Size)オプションの内容を調整(書き換え・追加)する機能です。IPv4では以前から利用できましたが、この度IPv6にも対応しました。

以下、TCP MSSについて、関連する用語を含めてご説明致します。

TCP(Transmission Control Protocol)

TCPは再送制御などを元に、通信に信頼性を持たせることができるプロトコルです。HTTPやFTPなどの下位プロトコルとして、インターネットで幅広く 使われています。TCPでは、通信をTCPセグメントという単位でやり取りします。TCPセグメントのサイズは送るデータ(ペイロード)の大きさによって 可変です。

MSS(Maximum Segment Size)

TCPの通信でやり取りされるTCPセグメントの最大サイズをMSSと呼びます。端末は送りたい内容がMSSよりも大きい場合、内容を幾つかのTCPセグ メントに分割して送ります。MSSは主にTCPの通信を始めるための最初のやり取り(3ウェイハンドシェイク)で決定します。

MSSオプション

TCPの通信を始める際、端末はお互いにSYNフラグの立ったTCPパケットを送り合います。その際、MSSオプションという内容をパケットに含め ることができます。MSSオプションでは、端末は自身が受け取れるTCPセグメントの最大サイズを相手に通知できます。MSSオプションが無い場合には、 どんなサイズでも良いことになります。一般的な実装では、常にMSSオプションを付けて通信を始めます。

付与されるMSS オプションのデフォルト値には、一般にMTU(Maximum Transmission Unit)からTCPのペイロードまでのヘッダのサイズを引いた値が使われます。例えば一般的なLANの環境でIPv6を使って通信をする場合、 EthernetのMTUは1500オクテットなので、そこからIPv6(40オクテット)とTCP(20オクテット)のヘッダ分を引いた1440オク テットが使われます。(*1)

MTU(Maximum Transmission Unit)

MTUはデータリンクの間で転送できるデータの最大サイズです。MTUを超える大きさのデータを一回で送ることはできないため、幾つかに分割して送る必要があります。

機能の必要性

TCP MSS調整機能が存在しない、または有効でない場合に考えられるリスクは以下があります。

TCP MSS調整機能は、IPv6では特に必要とされる機能になると思われます。IPv6はプロトコルの仕様から、全く通信できない状況に陥る可能性がIPv4に比べて高いためです。

IPv4の場合

前述する問題が発生する典型例について、SEILをフレッツ網に繋いでブロードバンドルータとして利用することを想定して考えてみます。SEILはlan1インタフェースをフレッツ網に繋いでPPPoEでインターネットと接続します。lan0にはスイッチを挟んで幾つかの端末が設置されて、端末はSEILをデフォルトルートとして利用します。

端末がインターネット上にあるWebサーバと通信する場合を考えてみます。端末がWebサーバとのTCPのハンドシェイクで付与するMSSオプションの値は自身が繋がっているデータリンク(Ethernet)のMTUから1460オクテットが使われます。前述したIPv6の場合よりも値が大きいのは、IPv4パケットのヘッダはIPv6に比べて20オクテット小さいためです。ハンドシェイクが完了した後に、端末はWebページの内容を取得するべくHTTPのGETメソッドをWebサーバに送信します。端末からWebサーバへの「行き」の通信はおそらくMSSに比べて小さいため、フレッツ網を抜けてすぐにWebサーバに到達します。前述した3つの問題が発生するのは「戻り」の通信の可能性が高いと言えます。

通信効率が低下するパターン (ルータがパケットをフラグメント)

Webサーバは端末が通知したMSSオプションの値を元にWebページの内容を1460オクテットのTCPセグメントに分割して送信します。しかし、一部のサービスを除いてPPPoEを使用する場合のフレッツ網のMTUは1454オクテットということが知られています。1460オクテットのTCPセグメントを元にIPv4ヘッダとTCPヘッダが付与された1500オクテットのパケットは転送できません。そのため、フレッツ網のルータはパケットをフラグメント(分割)します。フラグメントはIPのレイヤーで行われるため、パケットを受信した端末はIPとTCPの両方でリアセンブル(再結合)する必要があり、通信効率が低下します。

通信に遅延が発生するパターン (Path MTU Discoveryに成功)

IPv4パケットのヘッダにはDF(Don't Fragment)フラグという領域が存在します。このフラグは、途中のルータでパケットをフラグメントしても良いかを示しています。上記の、通信効率が低下するパターンでは、フレッツ網のルータでパケットがフラグメントされることを想定していました。しかし、Webサーバが送信するパケットにDFフラグが立っているなど、フラグメントできない場合もあります。

途中のルータでパケットをフラグメントできない場合、ルータは転送しようとしていたパケットを破棄して、パケットの送信元にICMP(Internet Control Message Protocol)パケットを送信します。送信されるICMPパケットのタイプは3(Destination Unreachable Message)で、コードは4(Fragmentation Needed and DF set)です。パケットにはフラグメントが必要になったリンクのMTUがNext-Hop MTUとして含まれます。ICMPパケットを受信したWebサーバは、パケットに含まれるNext-Hop MTUを元にパケットを小さくして再送します。パケットの再送が必要になるため、通信に遅延が発生します。

全く通信できないパターン(Path MTU Discoveryに失敗)

通信に遅延が発生するパターンでは、ICMPパケットを使って転送できないリンクの存在を知ることができました。では、このパケットがもし送信元に届かなければどうなるでしょうか。

例えば、運用上ICMPパケットはフィルタリングされることがあります。仮にICMPのタイプ3、コード4が途中のフィルタで破棄されてしまった場合、Webサーバは途中に転送できないリンクが存在することを知ることができません。TCPパケットを送るものの、送信先には届かないため、何度かの再送の後にいずれTCPのセッションがタイムアウトしてしまい、全く通信できなくなります。

経路のMTU(Path MTU)を知るためのICMPを生成しない、または破棄する存在はICMP Black HoleやPMTU Black Holeと呼ばれます。ネットワークにこうしたブラックホールは存在すべきではありません。

他にも、ルータが未対応または何らかの不具合によってICMPパケットを生成・転送できない場合や、ICMPを使った外部からの攻撃に対する対処として生成・転送量を制限(レートリミテーション)している場合などにもブラックホールが生じる可能性があります。

IPv6の場合

IPv6の場合、ブラックホールの存在はより深刻です。IPv4ではパケットがPath MTUよりも大きい場合に2つの復旧方法がありました。

  • 経路上のルータがパケットをフラグメントする
  • 端末がICMPパケットを元にパケットを適切に小さくして再送する

IPv6の仕様では、ルータがパケットをフラグメントできないため、最初の方法は使えません。IPv6では、必ず端末がパケットをPath MTUよりも適切に小さくして送る必要があります。IPv4ではフラグメントによって回避できていた場面も、IPv6では通信できない状況に陥る可能性があるわけです。

IPv6の場合、パケットが大きすぎて転送できないリンクがある時に、ルータが送信するのはICMPv6(Internet Control Message Protocol for IPv6)パケットです。コードは2(Packet too Big)で、IPv4のICMPと同様に転送できなかったリンクのMTUが含まれます。

また、IPv6固有の注意点としてルータの持つアドレスがあります。IPv6ではルーティングにグローバルアドレスが不要なため、ルータがリンクローカルアドレスしか持たない状況を作ることも可能です。しかし、リンクローカルアドレスで通信できるのは同一リンク上に限られるため、ルータを越えてICMPv6パケットを送ることができません。ルータがグローバルアドレスを持たない場合にもPMTU Black Holeが生じます。

TCP MSSの調整

上記の問題を解決するための手段として、ネットワークのエッジ側の機器(ルータ)でTCP MSSのサイズを調整する方法があります。ネットワークの途中に小さなMTUのリンクが存在する場合にも、エッジ側の機器でTCPのハンドシェイクに含まれるMSSオプションの値をそのMTUに基づいた値に書き換えることで、TCPセグメントのサイズが小さくなるためパケットがPath MTUよりも小さくなり、転送できないという状況をそもそも生まなくなります。ただし、この方法ではTCP以外のプロトコルについては救えません。

その他、ブラックホールが存在する場合の端末側の対処方法として、RFC2923(TCP Problems with Path MTU Discovery)ではTCPの通信に失敗した際にIPv6の最小MTU(1280オクテット)までパケットを小さくして再送する方法を提案しています。ただし、この方法は今のところそれほど一般的ではないようです。

MTUの調整

TCP MSSの調整以外には、MTUを小さくするという方法もあります。これは端末が直接繋がっているリンクのMTUをPath MTUの中で最も小さくすることで、MTUから計算されるMSSを小さくして転送できない状況を防ぐというものです。この方法には以下のような特徴があります。

  • TCP以外のプロトコルに対しても有効
  • LAN内の通信でもMTUが小さくなるため通信の効率が落ちる
  • 変更の影響がIPv4/IPv6両方に及ぶ

幾つかのデメリットはありますが、使用する機器(ルータ)でTCP MSSの書き換えができない場合などの回避策としては有効です。

MSSの値

通信効率が最大になるMSSは、MTUからTCPのペイロードまでのヘッダを引いた値です。これは通信するインタフェースやネットワークに依存します。例えばEthernet上でPPPoEを用いたIPv6の通信では以下のようになります。

※単位はオクテット
1500(EtnernetのMTU) - 6(PPPoEヘッダ) - 2(PPPヘッダ) - 40(IPv6ヘッダ) - 20(TCPヘッダ) = 1432

しかし、前述したように接続先がフレッツ網であれば同じPPPoEの通信でもMTUが1454オクテットになるためMSSは異なります。MTUが小さくなるのはPPPoEヘッダとPPPヘッダが外れた後なので更に引く必要はないようです。

1454(フレッツ網のMTU) - 40(IPv6ヘッダ) - 20(TCPヘッダ) = 1394

IPv4の場合はヘッダがIPv6よりも小さくなりますので、サイズは20オクテットで計算してください。

MSSとIP,TCPオプション

ここまでの説明で、書き換えるMSSとして使用する値はMTUからIPとTCPの基本ヘッダのサイズだけを引いてきました。ここで一つ疑問が生じます。IPヘッダやTCPヘッダにオプションが付いている場合はどうなるのでしょうか。例えばTCPヘッダにはTimestampオプションがほとんどの場合に付与されます。これを考慮せずに書き換えるMSSの値を設定すると、パケットがTimestampオプションのサイズ(4オクテット)だけMTUより大きくなって転送できなくなりそうです。また、MSSオプションは受信側が送信側に通知する値ですが、実際に送信側がどういったオプションを付けてパケットを送ってくるのか、受信側からは分かりません。

しかし、実際にはほとんどの場合にこれは問題となりません。draft-ietf-tcpm-TCP MSS-03(TCP Options and MSS)には、以下のように書かれています。

  • 受信側はMTUからIP, TCPの基本ヘッダ分だけ引いたものをMSSオプションとして通知すべき (SHOULD)
  • 受信側はMTUからIP, TCPのオプションまで引いたものをMSSオプションとして通知すべきでない (SHOULD NOT)
  • 送信側は送信するTCPデータ(ペイロード)のサイズからIP, TCPのオプションのサイズを引かなければならない (MUST)

ネットワークスタックの実装がこのI-Dに準じていれば、送信側であらかじめオプションのサイズ分を減らして送ってくれるため、パケットの大きさがMTUを越える心配はありません。

現行の主な実装(Windows7, Linux2.6.32, FreeBSD8.2, Mac OS X 10.6)で調べたところ、いずれもI-Dに準じていました。MSSの値は単純にMTUからIPとTCPの基本ヘッダのサイズを引いたものを採用すれば良さそうです。

SEILを設定する

SEIL/X,B1 3.50, SEIL/x86 2.00で追加されたIPv6のTCP MSS調整機能は、IPv4のそれとはコマンドが異なります。これは前述したようにIPv4とIPv6ではヘッダのサイズが異なるのと、IPv4の通信とIPv6の通信ではPath MTUが異なる可能性があるためです。

SEILの仕様

SEILのTCP MSS調整機能では、パケットがインタフェースから出入りするタイミングでMSSオプションの値を書き換えます。ハンドシェイクに含まれるMSSオプションがユーザが指定した値より小さい場合には書き換えません。ハンドシェイクにMSSオプションが含まれない場合にはユーザが指定した値でMSSオプションを追加します。

PPP/PPPoEの設定

前述したフレッツ網でSEILをブロードバンドルータとして利用する場合のコンフィグは以下の通りです。

ppp add ipv6 keepalive none ipcp disable ipcp-address off ipcp-dns off ipv6cp \
  enable authentication-method chap identifier <ppp account> passphrase \
  <ppp password> tcp-mss auto tcp-mss6 auto
interface pppoe0 ppp-configuration ipv6
interface pppoe0 over lan1

ポイントはpppコマンドのtcp-mss6オプションです。この設定によって、ppp設定"ipv6"を使用しているpppoe0インタフェース上のIPv6/TCPの通信でTCP MSS調整機能が動作するようになります。尚、pppコマンドにおいてtcp-mss6オプションはデフォルトで有効になります。

パラメータとして"auto"が指定された場合、書き換えるMSSの値はインタフェースのMTUから適切なMSSを求めます(*2)。

その他のインタフェースの設定

lan1インタフェースでインターネットと通信する場合に、フレッツ網の先にあるホストと通信することを想定してみます。

interface lan1 tcp-mss6 1394

パラメータとして"auto"を使用するとデフォルトではEtnernetのMTU1500からMSSの値が1440になってしまうため、手動で1394(オクテット)を指定します。1394(オクテット)はフレッツ網のMTU1454オクテットからIPv6ヘッダ(40オクテット)とTCPヘッダ(20オクテット)を引いた値です。デフォルト値は"none"で、機能は動作しません。lan0インタフェースに繋がっている端末(2001:db8:1::1)がlan1インタフェースの先にいるWebサーバ(2001:db8:2::1)からHTTPでWebページを取得する通信を観測して動作を確認してみます。動作の確認にはtcpdumpコマンドを使用します。まずはlan1インタフェースからです。

# tcpdump interface lan1 verbose-level 3 expression "ip6 and tcp"
################################################################
tcpdump: when you want to exit this program, please type Ctrl-C.
################################################################
09:07:49.496187 2001:db8:1::1.65534 > 2001:db8:2::1.80: S [tcp sum ok] 2875063776:
2875063776(0) win 32768 <mss 1394,nop,wscale 0,sackOK,nop,nop,nop,nop,timestamp 0
 0> [flowlabel 0xcb331] (len 44, hlim 63)
09:07:49.496674 2001:db8:2::1.80 > 2001:db8:1::1.65534: S [tcp sum ok] 2794657148:
2794657148(0) ack 2875063777 win 32768 <mss 1440,nop,wscale 0,nop,nop,timestamp 0
 0,sackOK,nop,nop> (len 44, hlim 64)
(...省略)
^C

lan1インタフェースからWebサーバに向かって転送されるTCPパケットに含まれるMSSオプションの値は1394オクテットに書き換えられています。Webサーバから端末宛のTCPパケットに含まれるMSSオプションの値は1440オクテットのままです。次にlan0インタフェースも見てみましょう。

# tcpdump interface lan0 verbose-level 3 expression "ip6 and tcp"
################################################################
tcpdump: when you want to exit this program, please type Ctrl-C.
################################################################
09:08:13.312466 2001:db8:1::1.65533 > 2001:db8:2::1.80: S [tcp sum ok] 3675877394:
3675877394(0) win 32768 <mss 1440,nop,wscale 0,sackOK,nop,nop,nop,nop,timestamp 0 
0> [flowlabel 0xb2322] (len 44, hlim 64)
09:08:13.313095 2001:db8:2::1.80 > 2001:db8:1::1.65533: S [tcp sum ok] 3606063147:
3606063147(0) ack 3675877395 win 32768 <mss 1394,nop,wscale 0,nop,nop,timestamp 0
 0,sackOK,nop,nop> (len 44, hlim 63)
(...省略)
^C

lan0インタフェースではlan1インタフェースとは反対に、WebサーバからのTCPパケットに含まれるMSSオプションが1394(オクテット)に書き換えられています。これで端末もWebサーバも、相手から通知されたMSSオプションの値を1394(オクテット)と認識するため、途中のリンクのMTUが1454になっている場合にも、パケットのサイズがMTUを超えることはなくなります。

おわりに

今回は、SEILを使ったIPv6の通信でTCP MSSを調整する方法と、その際のポイントについてご紹介しました。TCP MSS調整機能を使うことで、TCPの通信の効率を上げたり、PMTU Black Holeによって発生する問題を回避できます。

ブラックホールが存在する場合にも、MTUを変更したりTCP MSS調整機能を用いることで、全く通信できないような状況は回避できます。しかし、そもそもネットワーク上にブラックホールが存在すること自体が誤りですから、そもそも作りこまない、あったら直すというのが大事だと思います。

最後に、IPv6のTCP MSSについて言及されたドキュメントを幾つかご紹介します。

  • 頑張れフォールバック
    • 弊社の松崎がJANOG26で発表したIPv6のフォールバックに関する資料です。13ページから「Path MTU discovery」の記述があり、その中でTCP MSSの調整(書き換え)について言及されています。
  • IPv6家庭用ルータガイドライン (2.0版 2010年7月29日発行)
    • IPv6普及・高度化推進協議会のIPv4/IPv6共存WG IPv6家庭用ルータSWGで作成された、家庭用ルータが実装すべきIPv6の機能について言及された資料です。60ページ「7.4 特殊なフォワーディング」の中で「要件55: 家庭用ルータを通過するTCP 通信に対して、MSS(Maximum Segment Size)オプションを適切に調整する機能を有すること」の記述があります。

*1: 実際に使われる値は端末のネットワークスタックの実装に依存します。

*2: IPv6の場合は、前述するI-Dに基づいてIPv6とTCPの基本ヘッダのサイズ分(60オクテット)を引いた値です。IPv4の場合は、SEILに機能が実装された当時I-Dに基づかない実装が存在したことからIPv4とTCPの基本ヘッダのサイズ(40オクテット)に加えて固定の16オクテットが引かれた値になります。