要約

  • RFC 3493では、AF_INET6 socketがIPv4-mapped IPv6 addressを通じてIPv4 peerと通信できた。IPV6_V6ONLYはそれをIPv6に限定したが、RFC上のdefaultは無効だった。
  • したがってsocket family名やwildcard bindの成功だけでは、到達できるpeer familyも別のIPv4 listenerが同じportを所有できたかも証明しない。optionの実効値、各bind結果、実際の受信を別々に確認する必要がある。

二つのserver processを用意した場面から考える。一つはIPv6、もう一つはIPv4、port番号は同じである。IPv6側が先にwildcard bindを行い、次のIPv4側がaddress in useで失敗することがある。名前上は二つの担当がいても、kernelが最初のlistenerへ与えた領域が二族分なら、実際の分離は成立していない。

RFC 3493の互換手段はIPv4-mapped IPv6 addressだった。IPv4 addressを固定の::ffff: prefixの下、128-bit構造の下位32 bitへ収める。applicationはAF_INET6 socketでIPv4 nodeへ接続でき、受信時にはkernelがmapped peerをsockaddr_in6として返せる。区別が必要ならIN6_IS_ADDR_V4MAPPED()を使う。

これはapplicationとkernelの間の表現である。remote hostがnative IPv6を使う証拠でも、pathがIPv6だけだった証拠でも、同じ認可policyが適用された証拠でもない。構造体のfamilyとnetwork realityは同じ層にない。

RFC 3493 section 5.3はIPV6_V6ONLYを明示した。有効にするとAF_INET6 socketはIPv6通信だけに限定される。文書はdefaultを無効と定めたので、当時の契約では一つのIPv6 wildcard listenerがnative IPv6とmapped IPv4の双方を扱えた。

RFCが示す利用例はportの分割である。optionを有効にすれば、同じserverの二つの版が同じportを使い、一方をIPv6、他方をIPv4にできる。つまりoptionは注釈ではない。一つのbindが二族を占めるか、別socketのために一族分を残すかを左右するresource controlだった。

このためprocess監視だけでは足りない。二つとも起動済みに見えても、一方のbindが失敗しているかもしれない。socket一覧もAF_INET6をIPv6-onlyと読むなら誤る。family、IPV6_V6ONLYのread-back値、bind address、namespace、return code、native/mapped peerの受信実績を一組にして初めて境界を説明できる。

mapped表現にも但し書きがある。RFC 3493は、SIITによって有効なIPv6通信としてnodeへ入るmapped addressにはこのoptionが影響しないと述べる。RFC 6052とRFC 6145のembedded addressやpacket translationはlocal dual-stack socketとは別の仕組みだ。見た目だけで変換地点を決めてはならない。

名前解決はさらに手前の段階である。getaddrinfo()はfamilyやflagに応じて候補を返す。RFC 6724はdefault selectionを、RFC 8305はIPv6とIPv4候補の競争を扱う。候補取得、listener所有、接続、認可、service完了は別のreceiptだ。

なお、default無効という記述は2003年の歴史的契約である。RFC 3493はInformationalでRFC 2553をobsoleteし、正式なsocket標準の所在も別に示した。現在のOS、runtime、container platformが同じdefaultやbind conflictを持つとは限らない。現在値は測定すべきで、歴史から推測してはならない。

scopeも別の証拠である。sockaddr_in6のsin6_scope_idは、link-localなどのaddressをどのinterface集合で解釈するかを実装へ渡す。一つの数値addressが見えても、適切なzoneを欠けば利用可能なpathは決まらない。family、mapping、scope、route、listenerは連続するが同一ではない。だから監査では、address文字列だけを保存せず、受信interfaceとlocal endpoint、remote endpoint、namespaceを同じeventに結び付ける必要がある。

また、成功したbindは外部からの到達性を証明しない。host firewall、containerのport公開、load balancer、routing、service discoveryが後段に残る。反対に外部probeの成功だけでは、どのdescriptorが接続を受け、mapped peerをどのpolicy branchへ渡したかは分からない。起動時の設定証跡と実通信の観測を結び、途中の変換点を明示して初めて、意図した分離が実在したと言える。

残る教訓は、宣言されたfamilyではなく、実効option、bind範囲、port所有、peer表現、application policy、service結果を順に記録することだ。「IPv6 listener」という一語ではIPv4入口も、起動できなかったIPv4 processも見えない。

出典