Path MTU Discovery が原因の通信障害は分りづらい

普通、何らかの通信障害が発生すると、当然、通信の当事者に関する事を調べ出します。

サーバ側に何かログは出ていないか、パケットキャプチャをして相手からどんなパケットが届いているのか、Firewall 等で当該ホストに関するルールが引っかかっていないか....

ところが、Path MTU Discovery に起因する問題が発生すると、通信の当事者では、一見、相手が悪いように見えるし、実際、原因が当事者とは無関係のように見えるところにあります。

通信障害の最終手段として登場*1するパケットキャプチャをしても、単に、相手からのパケットが届かないだけに見えます。

そもそも、Path MTU Discovery とは?

まず、MTU

フレッツ網が出始めた頃、ネットワークスピードの改善方法として MTU のサイズを調整する話で有名になった MTU ですが、Maximum Transmission Unit の略で、1つのフレームで送信できる最大サイズの事を言います。

元々、MTU 自体は回線の種別によって決まるもので、一般的な Ethernet では 1500 バイトで、WAN 回線で使われる PPP の場合は可変ですが、Ethernet と同じ 1500 バイトになる事が多いようです。今はほとんど見かけない FDDI では 4352、IP over ATM で 9180 バイト、といった具合に、本来は様々な MTU を持つ回線があります。

フレッツ網が登場したときに MTU が話題になったのは、PPPoE というカプセル化技術を使ったためで、1500 バイトの Ethernet フレームの中に、ユーザ側の端末と契約先の ISP をつなぐ為の PPP を入れ、さらにその中に IP を入れる、という仕組みです。

そのため、通常の Ehternet に比べて PPP の分だけ、入れられる IP のサイズは小さくなります。

Path MTU とは

「Path」は「経路」を意味します。相手にパケットが届くまで、いくつものルータを渡っていきますが、その間の回線がどんな物が使われているか分りません。

ある相手と通信する時に、その経路全体での MTU が Path MTU です。

つまり、経路全体で、最も小さい MTU が Path MTU です。

Path MTU Discovery は、経路全体で最も小さい MTU をどうやったら分るか? という仕組みです。

ルータによるパケットの分割

ルータが受け取ったパケットを転送する際、転送先となる回線の MTU が受け取ったサイズより小さかった場合、パケットを分割して送ることがあります。

こうすれば、通信当事者は経路上の MTU など意識することなく、パケットを送信できますが、その代わり、ルータに取っては負担になります。

Path MTU Discovery の RFC 1191 が出されたのは 1990 年ですが、1990 年代に入ってから、ISP のルータのような、多くのネットワークとつながって、大量のパケットを裁くルータにかかる負荷が問題になっていました。

時期的に次期 IP(後の IPv6)の策定が行われていて、次期 IP のテーマの一つに、このルータの負荷問題もありました。

経路上の MTU が分ってしまえば、最初っからそれに合わせたサイズでパケットを送り出すことになるので、ルータでの分割は発生しません。

そのため、IPv6 では Path MTU Discovery が使われる事が前提になりました。

Path MTU Discovery の仕組み

もともと、IP パケットには DF(Don't Fragment)フラグというフラグがあり、このフラグがセットされているパケットは、ルータによる分割をしてはならない、ということになっています。

ルータがより小さい MTU を持つ回線を使ってパケットを転送しようとした時に、「分割できない」という事は、これ以上、パケットを転送する事ができない、という事を意味します。

こういった事態が発生していることを、パケットの送信元に伝えるために ICMP が使われます。

Type 3(Destination Unreachable Message)の Code 4(fragmentation needed and DF set)という ICMP がルータから送信元に送られ、この中に「○○○バイトだったら転送できるんだけど...」という情報が入っています。

送信元がこれを受け取ったら、そのサイズに合わせて IP パケットを再送します。

最終的に相手にパケットが届くまで、ICMP の情報に合わせて小さくしていけば、経路上の最小 MTU が見つかる、という仕組みです。

ICMP がフィルタされると Path MTU Discovery が成立しない

Path MTU Discovery は、パケットが分割できないときにルータが返す ICMP を利用して実現しています。ということは、もし ICMP がフィルタされている*2と、

  • ルータはこれ以上転送できないから、受け取ったパケットを捨てて ICMP を返す。
  • 送信元から見ると、送ったパケットに対して、相手から一切応答がない状態。

ということになります。

IPv6 では Path MTU Discovery が使われるので、ICMP をバッサリと Firewall 等で落としてしまうと、通信できない事態が発生する可能性があります。そのため、わざわざ RFC で、ICMPv6 のフィルタリングに関する文章が出されています。

RFC 4890: Recommendations for Filtering ICMPv6 Messages in Firewalls

考えなしにまるごとフィルタリングをしてはいけません。

実は IPv4 でも同じことが...

IPv6 の話だと思って、「そんなの使っていないし」と思っていると落とし穴があります。Path MTU Discovery 自体は IPv4 でも使える仕組みなので、原理的には同じことが発生しうることになります。

実は、割と以前から Linux では IPv4 でも Path MTU Discovery が有効になっていて、そのことに私が気がついたのは5年ほど前の CentOS Ver.6 系でした。CentOS 上で Squid を動かした HTTP Proxy を経由すると、繋がったり繋がらなかったり、みたいなことがあって、そのときにパケットキャプチャをして DF フラグが立っていることに気が付きました。

LinuxIPv4 の Path MTU Discovery が有効になっているかは sysctl コマンドで分ります。

$ sysctl net.ipv4.ip_no_pmtu_disc
net.ipv4.ip_no_pmtu_disc = 0

ちょっと分りづらいですが、ip_no_pmtu_disc なので、値が 0 なら有効です。

LinuxIPv4 の Path MTU Discovery が有効ということは...

今や Linux はサーバ以外にもいろいろなところで使われています。という事は、明確に「Linux」と認識していないものでも、Path MTU Discovery が有効になっている、ということになります。ネットワーク機器や IoT デバイスにも Linux は使われています。

私が実際に経験したのは、AWS のロードバランサである ELB です。

ELB に対する接続が VPN 経由だと NG、という事があったのですが、「ELB の実体って、Amazon Linuxインスタンスとかじゃないかなぁ」と思って、ICMP をブロックしていた Network ACL の設定を変更したら解消した、という事がありました。

IPv6 に限らず IPv4 に関しても、いたずらに「ICMP を丸ごとブロック」をするべきではありません。

Ping of Death*3Smurf 攻撃*4は、正直、過去の物です。ICMP に対して過大な恐怖心を持たず、フィルタするなら ICMP の「中身」も意識したいものです。

*1:個人的には、レイヤー 3 以下が原因になりそうな症状で、当事者が Linux だったら、最初っから tcpdump の登場させますが。

*2:Firewall 製品だと、意図的にブロックしない限り、許可している通信に対して発生した ICMP は適切に転送してくれる事が多いです。L3 スイッチの ACL のように、ステートレスにパケットを判断する場合、フィルタされてしまいがちです。

*3:Windows 95 が登場した頃、UNIX 系 OS がサイズの大きい ICMP Echo Request を送りつけられると、カーネルパニックに陥る欠陥があった。

*4:宛先がブロードキャストアドレスとなっている ICMP Echo Request に対して、該当するホストが応答する動作利用し、送信元アドレスを攻撃対象のアドレスに偽装してパケットを送りつけると、その何倍ものパケットが攻撃対象に向かう、という DDoS 攻撃の一種

せきゅぽろ 22

今回は、立命館大学の上原哲太郎先生による、マイナンバーのお話でした。

マイナンバーの話となると、

  • マイナンバーを管理するはめになった企業向けの話。
  • プライバシーに関するマイナンバー制度の問題。

といった2つのパターンのが多いと思いますが、もちろん上記の点も含め、非常に広範囲の話を聞くことが出来ました。

Slideshare で今日の資料が公開されています。

今さら聞けないマイナンバー

詳細は上記の資料に譲るとして、印象に残った話を少し書きます。

国民総背番号制」はすでに実施済み

厳密に言うと、住基ネットに参加しなかった一つの自治体を除けば、ですが、住民票があれば、住基ネットで使われている住民票コードが付与されているので、「国民総背番号制」という意味では、マイナンバーで導入されるものではないです。

言われてみれば、そうだよなぁ、という話なのですが、逆に、住基ネットがうまく活用出来なかったことを示唆している感じがします。

チェックデジットがダサい

マイナンバーの最後の数字はチェックデジットなのですが、計算式で得られる計算結果は 1 〜 11 の整数。チェックデジットは一桁の数字。で、どうしているかというと、

「計算結果が 10 以上の時は 0」

だそうです。つまり、末尾が 0 の人は、他の数字に比べて2倍の確率で存在することになります。

ちなみに、この計算式は官報にも載っていて、で、官報は縦書き、ということで、計算式がどう書かれているのか、気になる人はチェックしてみましょう*1

システムの仕組みは結構複雑

Slideshare 資料にもありますが、単純に中央のデータベースがあって、そこにアクセスするような仕組みではなく、基本的には、各自治体が組織ごとにデータベースがあって、中央では必要に応じてひも付けをするような仕組みだそうです。

なので、マイナンバーのシステムによって、現状の住基ネットよりセキュリティリスクが特に高くなるようなことはない感じです。

マイナンバーによるプラバシー問題の本質は「名寄せコストの減少」

個人情報の漏洩、ということがニュースで大きく取り扱われるご時世ですが、本当に問題なのは、各種のプライバシー情報が「名寄せ」されることによって、芋づる式に個人関する情報が取得できてしまうことで、マイナンバーは、その「名寄せ」のコストを劇的に下げる効果があります。

故に、マイナンバーの民間利用に関しては慎重になる必要があり、民間が使いたいと言っている「目的」に対しては、フェデレーションのような別の「手段」が適当ではないか、といったお話がありました。

マイナンバーは「アカウント」

マイナンバーはあくまで「識別」するための番号であって、「認証」に利用するものではなく、いわば「アカウント」のようなもの、という説明は分かりやすかったです。アカウントだけでは本人かどうかは判断できないのと同様に、マイナンバーの番号だけで本人性を確認できるわけはない、ということになります。


全体的に、メディアが煽るようなリスク拡大はないように思えます。ただ、上原先生もおっしゃっていましたが、今後、民間活用を含めた利用拡大が検討されているので、その部分で注視していく必要はありそうです。

*1:私も後で探してみます ... ありました! http://www.soumu.go.jp/main_content/000327387.pdf

AWS Cloudformation と IAM Policy での IP アドレス制限

AWS で、マネージメントコンソールや API 等で AWS 上のリソースの操作を特定の IP アドレスからしか実行できないようにするために、IAM ポリシーの aws:SourceIp を使って制限する、というのは常套手段として知られていると思います。

AWS管理コンソールへのアクセスをIPアドレスで制限したい | Developers.IO

このページの最後でも書かれていますが、この常套句の設定は CloudFormation を使ったスタックの生成、更新、削除といった時に問題になります。

AWS Identity and Access Management によるアクセスの制御 - AWS CloudFormation

aws:SourceIp 条件を使用しないでください。AWS CloudFormation はリクエストの送信元 IP アドレスではなく、独自の IP アドレスを使用してリソースをプロビジョニングします。たとえば、スタックを作成する際、Amazon EC2 インスタンスを起動したり Amazon S3 バケットを作成したりするために、CreateStack 呼び出しや aws cloudformation create-stack コマンドによって得られる IP アドレスではなく、スタックの IP アドレスからリクエストを行います。

(2015 年 3 月現在)

「独自の IP アドレスを使用してリソースをプロビジョニングします」といっている「独自の IP アドレス」というのが分かれば、それを許可してやれば、などと思ったのですが、公開されている AWS の IP アドレスの範囲*1を試したり、プライベートアドレスの範囲を指定したりして見ましたが、うまくいきませんでした。

Cloud Trail で見ても、Source IP は cloudformation.amazonaws.com となっていて、この名前で DNS に問い合わせても A レコードは持っていません。

AWS で su

UNIX 系 OS であれば、ホストのファイヤーウォール設定や、/etc/hosts.allow, /etc/hosts.deny を使って、特定の IP アドレスから接続を許可し、/etc/ssh/sshd_config で root でのログインを拒否して、一般ユーザでログインするようにしておいて、特権が必要な時には su で root になるか、sudo を使う、という運用をしているケースが多いと思います。

これと同じように、「通常は、制限されたアクセス権で、必要に応じて大きな権利を取得する」という手段が AWS に用意されています。それが「AssumeRole」です。

AssumeRole は、他の AWS アカウントを操作する為の権限を設定させたり、外部の認証基盤を使った認証処理を行う、という文脈で説明されることが多いです。

しかし、別の AWS アカウントじゃなくて、自分自身の AWS アカウントを指定しても OK?、と思ったきっかけがありました。

AWS CLIがAssumeRoleによる自動クレデンシャル取得とMFAに対応しました! | Developers.IO

作成した IAM アカウントに MFA を設定して、マネージメントコンソールに対しては MFA 無しでは利用できないことは確認したのですが、AWS CLI の時にはどうなるんだ? と思ってたどり着いたのが上記の記事です。

これを読むと、あるバージョンの AWS CLI からは、MFA が必要な時にはそのトークンを要求する、といった動きができるようになったことが分かります。

よく読むと、信頼ポリシー(trust plolicy)で「MFA での認証に成功している場合に AssumeRole を許可」としています。じゃぁ、この条件を「特定の IP アドレスからの場合だったら」としてみたら...

ということで、思いついたシナリオは以下の通りです。

  • IAM ユーザに、AssumeRole を許可したポリシーを割り当てる。
  • 信頼ポリシーで、先の「MFA での認証が〜」の条件の所を、「特定の IP アドレスだったら〜」に変えたものを設定する。

実際にやってみる

まず、IAM ロールを作成します。ロール名を入力して、ロールタイプを選択する画面が出たら、「Role for Cross-Account Access」の「Provide access between AWS accounts you own」を選択します。元々は「あなたが持っている AWS アカウント間でのアクセスを提供」という意味ですが、持っているアカウントが1つでもこれを選択します*2

次に、「どのアカウントに対してアクセスを提供するか?」を指定するのですが、本来のクロスアカウントでの利用であれば、アクセスを付与する先の AWS アカウント番号を入れる所に、自分自身のアカウント番号を入力します。

上記画面の「Require MFA」をチェックすれば、前述の「AWS CLI で MFA を使う」ための信頼ポリシーが生成されます。

後は、付与したい権利の内容を選択すれば、ロールの作成はひとまず完了です。

作成したロールの内容を確認すると、画面の下の方にある「Trust Relationship」で生成された信頼ポリシーが確認できます。

実際に生成されたポリシーは、こんな感じになります。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::自分の AWS Account ID:root"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

これを編集して、Condition 句で IP アドレスを指定します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::自分の AWS Account ID:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": "xxx.xxx.xxx.xxx/xx"
        }
      }
    }
  ]
}

これで、ロールの準備は OK です。

次に、IAM ユーザに割り当てる為のポリシーを用意します。任意のロールに切り替えられるのを過剰な権利なので、Resource に先に作ったロールを指定します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": "arn:aws:iam::自分の AWS Account ID:role/先に作ったロール名"
        }
    ]
}

後は、このポリシーをユーザに適用すれば OK です。

使ってみる

分かりやすいように、前述の AssumeRole を許可したポリシーだけをユーザに付与した状態にしてみます。AssumeRole が許可されているだけなので、信頼ポリシーで許可されているアドレスか否かにかかわらず、AWS 上のリソースに対する一切の操作、一切の情報取得ができない状態になっています。

で、許可された IP アドレスからアクセスすると、ロールを切り替えることができます。

マネージメントコンソールでロールを切り替えるのは、画面右上のログイン中のアカウント情報を表示している所からメニューを表示させて、「Switch Role」を選択します。

ロールの切り替え画面では、切り替え先の AWS アカウントと、先に作ったロールの名称を入力します。

*3

「表示名」の欄は、アカウントとロールを入力すれば「ロール@アカウント」の形式になります。

で、実際に切り替わると、下図のように、それまで「IAM ユーザ名@アカウント」だったのが、「ロール名@アカウント」になっています*4。実際に、ロールを切り替える前にでは軒並み「権利がない」と言われていたのが、切り替え後はきちんと表示されています。

  • ロール切り替え前:
  • ロール切り替え後:

信頼ポリシーで指定した IP アドレスとは違うところからログインしてロールを切り替えると、下図のように切り替えに失敗します。

ロールを切り替えられれば、そのロールで付与されているポリシーが適用されます。この時、ロールに付与したポリシーに aws:SourceIp を使わなければ、CloudFormation でスタック生成も問題なく実行できます。

AWS CLI での利用

先に紹介した、AWS CLI で MFA を使うケースに似ています。

AWS CLI のプロファイルで

  • 元になるプロファイル(source_profile)
  • 切り替え先のロール(role_arn)

を指定した物を作成し、実際の処理の際に「--profile プロファイル名」を付けて実行する事になります。MFA との例の違いは MFA デバイスを指定する「mfa_serial」の有無だけです。

直接、ファイルを編集しても良いですが、AWS CLI を使って必要な設定を書き込む事もできます。

今、AssumeRole が許可されたユーザのアクセスキー、シークレットキーがデフォルトで使われるように設定されているとします。

ここに「admin」というプロファイルを追加して、AdministrationRole という名前のロールへ切り替えるようにするには、下記のように AWS CLI を実行します。なお、AWS Account ID が 111122223333 だったとします。

aws configure set profile.admin.region ap-northeast-1
aws configure set profile.admin.role_arn arn:aws:iam::111122223333:role/AdministrationRole
aws configure set profile.admin.source_profile default

これで、config ファイルの中身は下記のようになります。

[default]
region = ap-northeast-1
[profile admin]
region = ap-northeast-1
source_profile = default
role_arn = arn:aws:iam::111122223333:role/AdminRole

この状態で「aws cloudformation --profile admin create-stack --stack-name ......」とすれば、スタックの生成ができます。

注意点

AssumeRole で切り替えた後の権限は、切り替え先のロールに全面的に切り替わります。

aws:SourceIp を使ったポリシーを設定する際、「このアドレスに合致したら許可する」ではなく「このアドレスに合致しなかったら、全てを拒否する」というポリシーを適用するのが良い、と言われます。複数のポリシーが割り当てられている場合、先に「拒否」のポリシーを評価するので、「このアドレスに合致しなかったらすべてを拒否する」というポリシーがあれば、他のポリシーで IP アドレス制限がなくても、確実に制限できます。

故に、CloudFormation の実行の際に問題になった訳ですが、切り替え先のロールに全面的に切り替わるからこそ、元々のポリシーに制限があっても、CloudFormation のスタック操作に成功する事になります。

ですので、切替後の操作に制限を加えたければ、あくまでも、ロールの中身で制限する必要があります。切替前のポリシーは切替後には反映されない事を十分に注意する必要があります。

*1:AWS IP アドレスの範囲 - アマゾン ウェブ サービス

*2:実はドキュメントにこのケースがきちんと書かれていました。IAM ユーザーにアクセス権限を委任するロールの作成 - AWS Identity and Access Management「このオプションは、ユーザー、ロール、アクセスされるリソースがすべて同じアカウントに属している場合にも選択します。」

*3:なぜかこの画面は日本語...

*4:切り替え先のロールを指定する画面で「表示名」に設定したものになる、はずです(試してない)。

せきゅぽろ 19

久しぶりに参加したせきゅぽろの勉強会は、Google に名前を入れると、広島出身のテクノポップアイドルの名前がサジェスチョンされる、辻 伸弘さんでした。辻さんは何度かせきゅぽろにいらっしゃっているはずですが、私個人は初めてでした。

テーマはまず最初に、昨今の不正ログインに関する話でした。

かつては総当たりや辞書攻撃、その応用のリバース・ブルートフォース(パスワードを固定にして ID の方を次々にかえる)が主流だったと思いますが、この1、2年で急速に脚光を浴びたのが、あらかじめ入手されているアカウントとパスワードの組み合わせのリストを利用したリスト型攻撃です。

ブルートフォース型の攻撃だと、パスワードが十分な長さで複雑であれば、現実的な時間で突破される事はない、という「時間軸」での問題だったのに対し、リスト型攻撃の場合、あるサイトで使っていたパスワードを使い回していると、他のサイトで突破される、いわば「横展開」の問題に変化したのが特徴だと思います。

辻さんがある人から持ちかけられた相談で、1000 万件のリストを 400 万円で買わないか? という話があったそうです。ただ、そのリストのサンプルを辻さんが眺めていて、似たようなパスワードがあったり、いわゆる脆弱なパスワードが見当たらない、など、本物かどうか怪しい感じ、そのリストにあるメールアドレスで検索したら、パスワードはハッシュ化されているけど、同じメールアドレスの一覧が見つかって、リストのパスワードと、検索して見つかったパスワードのハッシュ値を突き合わせたら、どうやらそのリストは偽物だった、ということがあったそうです。

2つめのテーマは、Phishing に関して。実際に Phishing サイトを作るツールを使って作られた、全く同じ画面のサイトにアカウント・パスワードを入力すると、その Phishing サイトのログにパスワードが記録されるところを、実際のデモで見せてもらえました。

辻さんは最近、Phishing サイトに引っかかるのが趣味で、下記のページで、その様子が読めます。

(n) – life is penetration. geeks cheer. geeks be ambitious.

Phishing は、特定の組織を狙えば標的型攻撃になる訳ですが、そういった標的型攻撃メールの対応訓練に関して、間違った訓練になっている、という指摘がありました。単純に開封率やクリック率を見て一喜一憂するのは無駄で、誰が開いたかが重要だし、そもそも、開いてしまった時にどうするのか、という訓練にならないと意味がない、というお話はその通りだと思いました。

3つめは、自分自身のプライバシーを守るために、利用できるツールの紹介でした。匿名化や暗号化ツールは、他方で犯罪者に使われるのでダーティなイメージがあるけど、包丁と同じでツール自体はどっちにも使える、というお話がありました。

最後に、実際に自分のセキュリティを守るために、専門家だけが啓蒙活動するだけはなく、草の根的な広がりが大切、という事を話されていました。自分もその端くれになっていれば良いなぁ、という思いを強くしました。

余談:
最後の質疑応答のところで、「徳丸本」が意外に知られていない事が判明。徳丸さん、せきゅぽろに来ませんか?
ハッシュ値、ソルト、ストレッチングに関する話題の場面でしたが、手前味噌で良ければ、私も書いたことがあるので、参考になれば幸いです。

塩加減は重要? - JULYの日記
調べてみると、結構複雑 - JULYの日記

IPv6 ネタの落ち穂拾い

IPv4IPv6 の用語の違い

IPv6 に慣れてしまえば、IPv6 用語を使った方がより正しい表現になるのは分かっていても、IPv4 で慣れ親しんだ用語を使わないと、なかなかイメージしづらい事が多いです。今回、意図的に IPv6 用語を最小限にした書き方にしたのも、自分自身で IPv6 用語が身についていない、というのもあります。

ということで、ちょっと用語を比較してみます。

グローバルアドレス、プライベートアドレス

IPv4 のプライベートアドレスに関しては、ULA*1が該当します。これは今回書いた内容にも多用しています。

若干違うのは、ULA は最初から、「保証はできないけど Unique であるように」という事を意識して、アドレスの決め方を用意している点です。IPv4 のプライベートアドレスで、「192.168.1.0/24」とかが選ばれがちで、そういうときに企業合併などで内部ネットワークをマージしなきゃいけなくなった時に面倒な事になる、という話があったのですが、ULA をきちんと計算して決めると、アドレスが衝突する事はほとんど無くなります。

一方、グローバルアドレスに関しては、GUA(Global Unicast Address)という用語が使われます。

アドレスのネットワーク部、ホスト部

アドレスでネットワークを表す部分とホストを表す部分は、IPv6 ではプレフィックスとインタフェース ID になります。ただし、プレフィックスが必ずしもネットワークという概念と結びつかないケースもあります。

例えば、サイトプレフィックスと呼ばれる 48bit 長のプレフィックスがあって、これは組織単位に割り当てられる、という意味合いになっています。ULA のプレフィックスの決め方も、このサイトプレフィックスの 48bit を決めるもので、このプレフィクスを分割して、通常は 64 bit のプレフィックス長でネットワークアドレスを作る、ということになります。

一方、ホスト部はインタフェース ID と呼ばれ、通常は下位 64 bit のアドレスの事を言います。正直、この「インタフェース ID」という用語は、なかなか身につかないです。

ARP と ND

用語、というよりは、仕組みそのものが違うのですが、イーサネット上の IPv4 で、MAC アドレスと IP アドレスの関係を解決するために ARP というプロトコルがありましたが、IPv6 では ARP は使いません。その代わり、ICMPv6 の一つとして実装される、ND(Neighbor Discovery: 近隣探索)という仕組みを使います。

なので、IP アドレスと MAC アドレスの関係を確認するために「arp -a」などとコマンドを打っても、IPv6 アドレスは出てきません。Windows では netsh で、Linux では ip コマンドで、ND キャッシュの状態を表示する事ができます*2

IPv4 IPv6
Windows arp -a netsh interface ipv6 show neighbors
Linux arp -a ip -6 neigh

Linux の ip コマンドの場合、「-6」を付けずに「ip neigh」とすれば、IPv4 に関しては ARP キャッシュ、IPv6 に関しては ND キャッシュの両方が表示されます。

IPv6 には無いもの

NAT

NAT が無いのは最初にも書きました。原理的にできないことはないですが、CentOS Ver.6 の ip6tables では nat テーブルが使えません*3。NAPT、いわゆる、IP マスカレードも使えません*4。ルータでも IPv6 の NAT をサポートしているものは少ないでしょう。現時点では NAT は無いと考えた方がよいです。

IPv4 では当たり前のように NAPT をしていて、それが結果的に「意図的な設定をしなければ、インターネット側からはアクセスできない」という状態を作りだし、セキュリティに貢献していた事は事実です*5

NAT / NAPT が無いということは、普通にルータに繋いだパソコンもグローバルアドレスを持ち、インターネット側からアクセスできる事を意味します。実際、その2で紹介しているフレッツ光ネクストでの接続設定だと、インターネット側からアクセス可能です*6。なので、一般的な IPv6 対応ルータの場合、インターネット側から内部への接続は落とすようなフィルタをデフォルトで有効になっている、という事になっています。

個人で ISP と契約して、64 bit のプレフィックスが与えられることから、「少ないアドレスを分け合うための NAPT」という役割は終わったと言えます。あるとすれば、セキュリティ面からの要望で、IPv4 の時のように、ULA のみ端末からインターネットを利用できるようにしたい、ということはあるかもしれません。その場合の NAT は NAPT のような「1 対 N」ではなく、「N 対 M」で、動的に GUA と ULA をマッピングするようなものになるかもしれません。

ただ、実際につながる環境ができてしまうと、グローバルなアドレスが直接ホストに割り当てられるのは、シンプルで良い感じがします。端末側から見ると、私みたいに ULA を使おうとすると、一つのインタフェースに複数のアドレスが付与されて、なんだか面倒な感じになりますが、インターネット側からアクセスできるかどうかは、純粋にルータフィルタで穴が空いているかどうか、になり、IPv4 の NAPT を使っている環境で、「このプロトコルは大丈夫だけど、これはダメで...」みたいな事はずっと少なくなるはずです。

ブロードキャストアドレス

IPv4 アドレスで、ホスト部が All 1 のアドレスはブロードキャストアドレス、という決まりがありましたが、IPv6 アドレスにブロードキャストアドレスはありません。

ただ、ブロードキャストと同じ役割をするマルチキャストアドレス、というのがあって、ff02::1 というアドレスを使うと、同一セグメントの全てのノード宛、という意味になります。これを「オールノーマルチキャストアドレス」(もしくは「全ノードマルチキャストアドレス」)といいます。

もっとも、使う場面は、何かプロトコルを実装でもしない限り、ほとんど無いと思いますが...。

まとめ

我が家の IPv6 導入顛末は、これでひとまず終わりです。まぁ、IPv6 が使えるようになったと言っても、「おぉ、GoogleIPv6 でつながっている!。気持ち、レスポンス良いかも」という程度で、実益は何もありませんが、でも、こうして実際に苦労してみると、「勘所」が IPv4IPv6 で違うのを実感しました。

もっとも、「おうち LAN に IPv6」なんて事をしなければ、今以上に「ルータを繋ぐだけ」ですが(^^;

*1:Unique Local Address かと思うと、RFC 的には Unique Local IPv6 Unicast Address

*2:厳密には、キャッシュだけじゃなくて、固定でマッピングしているものがあれば、それらも表示されます。

*3:kernel 3.7.0 から使えるようになったらしいです。

*4:少なくとも kernel 3.9.0 からは使えるらしいです。

*5:それが油断を生んでいた側面もあるとは思いますが、一般の人が電気屋さんで買ってきたルータを使うことで、直接攻撃から回避できる、というのは、セキュリティ的には都合が良かったと思います。

*6:Web インタフェースで IPv6 の設定をすると、フィルタを使う/使わないの設定があって、簡単に「外からはつながらない」フィルタを設定する事が可能です。もっとも、それに気づかずに半月ぐらい放置していても、IPv6 での SSH に対するブルートフォースアタックはゼロでした。IPv6 アドレスのアドレス空間が広大なので、仮にプローブしても、相手となるホストがほとんど見つからなくて、効率が悪そうですが。

RFC 3484 の呪い

昔、DNS ラウンドロビンが当てにならなくなってきたのは RFC 3484 の影響、という事を書きました。

IPv6 と RFC3484 - JULYの日記

この RFC は 6724 で更新されています。

RFC 6724 - Default Address Selection for Internet Protocol Version 6 (IPv6)

この RFC は、割り当てられた複数のアドレスから、どのアドレスを使って相手との通信を行うか、というもので、それを実装すると、名前解決の結果がソートされ、結果、DNS ラウンドロビンを期待して複数の A レコードを設定している場合、片方にアクセスが寄ってしまう、という現象がありました*1

RFC 3484、6724 自体は、IPv6 上でより近い経路になるアドレスを選択するためのもので、DNS ラウンドロビンの問題は、その結果の巻き添えを食った感じですが、今度は、本当にこの RFC にまつわる問題に遭遇しました。

ソースアドレスの選択

RFC 6724 のポイントの一つに、どのアドレスを発信元のアドレスとして選択するのか、というのがあります。これまでの試行錯誤の結果、

  • グローバルアドレス(MAC アドレスからの生成)
  • グローバル一時アドレス
  • ULA

の3つのアドレスがホストに割り当てられている状態になっています。この状態で実際に通信を行う時に、このアドレスのどれを発信元とするか、というルールを決める必要があります。そのルールが RFC 6724 の「5. Source Address Selection」にある、Rule 1 〜 8 です。

このルールを一つ一つ、確認してみます。

Rule 1: 宛先とアドレスと同じアドレスを優先する。

自分自身へ接続する場合には、宛先アドレスと同じ発信元アドレスを使う、ということになります。

Rule 2: 適切なスコープを選択する。

ざっくり言うとグローバルアドレス相手にはグローバルアドレスを、リンクローカルアドレス相手にはリンクローカルアドレスを、ということのようです。

Rule 3: 非推奨アドレスを避ける。

自動構成のアドレスの有効期限が近づいているアドレスが非推奨アドレスです。自動構成アドレスは時間とともに「優先(Preferred)」「非推奨(Deprecated)」「無効(Invalid)」というようにステータスが変化します。新たな通信を開始する時には、非推奨アドレスを利用せず、非推奨アドレスをつかった既存の通信が無くなるのを待つ、という形で、スムーズなアドレスの更新を実現します。

Rule 4: ホームアドレスを優先する。

ホームアドレスは、Mobaile IPで使われるアドレスです。

Rule 5: 宛先に向かうインタフェース上にあるアドレスを優先する。

複数のインタフェースがあって、宛先アドレスからルーティングテーブルにしたがってパケットを送出するインタフェースが決まると、そのインタフェースに割り当てられているアドレスを優先する、という話です。一見、当たり前ですが、例えば、ルータ自身が LAN 側のホストへ繋ごうとする際に、WAN 側のアドレスを使わない、という話になります。

Rule 5.5: Next-hop 側のインタフェース上にあるアドレスを優先する。

基本的には 5. と同じ話で、宛先がさらに別のゲートウェイの向こうになる場合、一番近いゲートウェイへ向かうインタフェース上のアドレスを優先する、という話です。

Rule 6: ラベルが一致する物を優先する。

NGN 閉域網の問題*2の時に話題になったポリシーテーブルで割り当てられるラベルが一致しているアドレスを優先します。

Rule 7: 一時アドレスを優先する。

一時アドレスがある場合には一時アドレスを優先して使います。

Rule 8: アドレスのプレフィックスを比較して、一致している長さが長いものを優先する。

IPv6 アドレスのネットワーク部であるプレフィクスを比較して、先頭ビットから何ビット一致しているかをチェックし、一致しているビット数の多い方を優先することで、ルーティング経路の有利なアドレスを選択する、という意図があります。

ULA のスコープ

RFC 6724 のルールを見ていくと、Rule 2 を除く 1 〜 5.5 に関しては、割と当たり前のルールです。Rule 4 は Mobile IP を使っている場合のケースで、かなり特殊で縁がない感じで、あとは、「まぁ、そうだよなぁ」という話です。

問題は、Rule 2 です。

IPv6 アドレスのスコープは、

  • グローバル
  • サイトローカル
  • リンクローカル

と言われていました。しかし、サイトローカルアドレスは廃止*3され、その代わりに ULA が作られました。

ULA のスコープがサイトローカルとして定義されていれば分かりやすかったのですが、実は、ULA のスコープはグローバルです。そのことが ULA を定義している RFC 4193 に書かれています。

http://tools.ietf.org/html/rfc4193

3.3 Scope Definition
By default, the scope of these addresses is global.

という事は、本当にグローバルなアドレスだろうが、ULA だろうが、Rule 2 では優先順位に差がない、ということになります。

最長一致より一時アドレス

前回までで、

  • グローバルなアドレスは、MAC アドレスを元にした自動構成アドレスと一時アドレス
  • ULA は DHCPv6 で割り当てられたアドレス

という3つのアドレスを持つ構成になっています。この状態で同じ ULA を持つおうちサーバに繋ごうとすると、Rule 6 より前のルールでは決着が付きません。

Rule 6 はポリシーテーブルで割り当てられるラベルで、デフォルトのポリシーテーブル*4では3つのアドレスに差はありません。

次に Rule 7 で評価されると、3つのアドレスのうち、グローバルな一時アドレスが優先される事が決定します。

つまり、これまで苦労して3つのアドレスが割り当てられるようにした結果、おうちサーバに繋ぐ時に選択されるアドレスは、グローバルなアドレスの一時アドレス、という事になってしまいます。Rule 8 にたどり着けば、プレフィックスの最長一致で、当然、ULA が選択されます。しかし、たとえ相手と同一プレフィックスを持っていても、つまり、相手が同一セグメントにあっても、別のプレフィックスを持つ一時アドレスが選択され、わざわざルータ経由で通信することになります。

屈辱のポリシーテーブル

RFC 3484 が 6724 で更新されたポイントの中に、3484 以降に制定された ULA に対する記述*5と聞いていたのですが、それがどこかというと「10.6. Configuring ULA Preference」で、そこで書かれているのは単に「ポリシーテーブルで ULA の優先順位を上げてね」という話*6で、少なくともデフォルトポリシーテーブルでは、ULA はグローバルアドレスと同じ扱いになってしまいます。

一時アドレスを一切使わない、ということであれば、今回のケースは Rule 8 のプレフィックス最長一致で救われることになりますが、一時アドレスの有無をプレフィックスによって変えると、たとえ全く同じプレフィックスを持つ、隣のホストに対するアクセスでも、一時アドレスを利用してルータ越しの通信を行うことになります。

ということで、結局、ポリシーテーブルを設定する以外に解決策がない、ということになりました。

Windows でのポリシーテーブルの設定方法に関しては、「netsh prefixpolicies」あたりで検索すれば、「IPv4 を優先させる」という文脈でたくさん見つかるでしょう。

手元の Windows 7 でのデフォルトでは下記のようになっています。

優先順位   ラベル  プレフィックス
----------  -----  --------------------------------
        50      0  ::1/128
        40      1  ::/0
        30      2  2002::/16
        20      3  ::/96
        10      4  ::ffff:0:0/96
         5      5  2001::/32

今回のケースだと、宛先アドレスの IPv6 アドレスは ULA のみ*7なので、Rule 6 で宛先とラベルが一致さえしてしまえば、発信元のアドレスとして ULA が選択されることになります。

なので、ULA のプレフィックスのラベルを定義し、優先順位は通常の IPv6 アドレスと同じ 40 で設定しました*8

優先順位   ラベル  プレフィックス
----------  -----  --------------------------------
        50      0  ::1/128
        40      6  fd00::/8
        40      1  ::/0
        30      2  2002::/16
        20      3  ::/96
        10      4  ::ffff:0:0/96
         5      5  2001::/32

こうすることで、ULA を持つおうちサーバに対して、ULA のアドレスを使ってアクセスするようになりました*9

そして、手動設定が残った...

このポリシーテーブルをクライアントへ配信する方法は、Active Directory を使って、参加しているホストに適用する事は出来るみたいですが、そもそも、ドメインコントローラに繋ぐ時に、一時アドレスが選択されたらどうなるのか? という疑問があります。

DHCPv6 で stateful な「管理された自動構成」を実現するなら、このポリシーテーブルも DHCPv6 で配布できないか、と思うのですが、どうも、そういったオプションは無さそうです*10

ポリシーテーブルの設定という手動設定が残る、という屈辱の結果になってしまいました。ULA のプレフィックスがデフォルトのポリシーとして設定されるようになるか、RFC 6724 が更新されて、ULA の宛先の時に、グローバルの一時アドレスより ULA が優先されるようなルールになって欲しいと思うのですが...。

*1:複数の A レコードで、アドレスの上位ビットがある程度等しいと、「行き先のネットワークは同じだろう」ということで、ソートされずに済むケースがあるみたいです。

*2:参照: NTT IPv6閉域網フォールバック問題:Geekなぺーじ

*3:RFC 3879 に廃止する理由などが書かれています

*4:RFC 6724 の「2.1. Policy Table」にデフォルトがあります

*5:RFC6724: Default Address Selection for Internet Protocol Version 6 (IPv6):Geekなぺーじ

*6:RFC 6724 「10.6. Configuring ULA Preference」にある例は、発信元、宛先ともにグローバルと ULA の2つのアドレスを持っている時に、ULA が優先されない、という文脈なので、今回のケースとはちょっと違ってます。

*7:もし、おうちサーバの名前解決の結果にグローバルなアドレスが含まれるのであれば、ULA の優先順位を上げた方が良いでしょう。

*8:実際に設定するときに、netsh の interface ipv6 add prefixpolicies で、デフォルトのポリシーテーブルに追加したつもりでいて、再起動して確認すると、自分が追加したポリシーだけが入っている状態になりました。なので、一度、デフォルトのポリシーを削除し、自分が追加したいポリシーと一緒に、元のデフォルトポリシーも追加してやる必要があるみたいです。

*9:「fd00::/8」よりも「fc00::/7」の方が正しいかもしれませんが、とりあえず「fd00::/8」で実際に利用可能な状態にある ULA をカバーしていることになるはず。

*10:2014-4-28 追記: RFC 7078 で、DHCPv6 を使った配布方法が定義されたようです。但し、発行したのが今年の1月。一般的に実装されるのは、まだ先の話ですね。

Stateless と Stateful の狭間

IPv6 アドレスの stateless な自動設定に関する RFC は 2014 年 4 月現在、4862 です。

RFC 4862 - IPv6 Stateless Address Autoconfiguration

ところが、RFC の中に明示的に stateful な自動設定に関して記述しているものは見つかりませんでした。強いていうと、DHCPv6 は 3315 です。

http://tools.ietf.org/html/rfc3315

RFC 4862 のタイトルは明確に「IPv6 Stateless Address Autoconfiguration」と言っているのとは対照的です。しかし、RFC 4862 をよく読むと、こう書かれています。

Major clarifications:

(中略)

Avoided the wording of "stateful configuration", which is known to be quite confusing, and simply used "DHCPv6" wherever appropriate.

「"stateful な設定"という言葉は、ちょっと混乱を招くので、"DHCPv6" に置き換えた」ということのようです。あれこれ調べ回っていると、下記の記事では、自動設定の形式として「Stateless Configuration」「Stateful Configuration」「Both Configurations」の3つがある、という書き方をしています。

How to configure IPv6 address in Windows

どうも、単純に stateless、stateful の2つに分割できる話じゃなさそうです。

autonomous address-configuration flag

今の目標は、

  • グローバルなアドレスは、RA による自動構成まかせ。
  • ULA は、DHCPv6 からのアドレスのみで、一時アドレスを作らないようにする。

です。DHCPv6 を使うときの RA の設定で、M フラグや O フラグは RA 全体に対するフラグで、プレフィックス毎に設定する物ではありません。

じゃぁ、プレフィックス毎に何か設定できる事はないか、と思い、かつて、おうちサーバで動かしていた radvd の設定ファイルを眺めていると、「AdvAutonomous」という設定項目がありました。

radvd.conf(5) - Linux man page

AdvAutonomous on|off
When set, indicates that this prefix can be used for autonomous address configuration as specified in RFC 4862.
Default: on

「セットされていれば、このプレフィクスが RFC 4862 で定義されている自立的なアドレス設定に利用できる事を意味する」とあります。

RFC 4862 を読み返すと、「4. Protocol Overview」にこのような記述があります。

One Prefix Information option field, the "autonomous address-configuration flag", indicates whether or not the option even applies to stateless autoconfiguration

プレフィックスが stateless な自動構成を適用するかどうかを表すオプションだと言っています。また、「5.5 Creation of Global Addresses」の「5.5.3 Router Advertisement Processing」では、

a) If the Autonomous flag is not set, silently ignore the Prefix Information option.

autonomous フラグがセットされていなかったら、そのプレフィックス情報は黙って無視する(つまり、アドレスを作らない)と書かれています。

どうも、このフラグが肝となりそうです。このフラグが off なら、stateless な自動構成アドレスの対象から外され、その結果、一時アドレス(stateless な自動構成アドレスの一種)も作られないのではないか。

NVR500 だと、「ipv6 prefix」にある「a_flag」が該当するので、

ipv6 prefix 2 fdfe:dcba:9876:5432::/64 a_flag=off

としてみます*1

すると、見事に

  • グローバルなアドレスは、MAC アドレスから生成されたアドレスと一時アドレスの2つ。
  • ULA は DHCPv6 から払い出されたアドレスの1つ。

という構成になりました。

stateless の本当の意味

RFC 4862 は stateless なアドレスの自動構成に関して書いています。で、RA のプレフィックス情報に含まれる "autonomous address-configuration flag" が on でないと、そもそも自動構成の対象とせずに無視される、という事が書かれています。

という事は、stateless なアドレスの自動構成は、

  • RA の M フラグが off
  • RA のプレフィックス情報で "autonomous address-configuration flag" が on

という、2つの条件を満たす必要がある、という事になります。この2つのフラグはともにデフォルトで、自動構成が行われる値(M フラグは off、"autonomous address-configuration flag" は on)なので、結果的に「RA だけがある状態なら stateless」ということになります。

ところが...

割り当てられるアドレスは狙ったとおりになりました。しかし、おうちサーバへの接続には、まだ問題が...。ということで、まだ話は続きます。

*1:引き続き、ULA のプレフィックスを fdfe:dcba:9876:5432::/64 としています。