Network Working Group
Request for Comments: 2553
Obsoletes: 2133
Category: Informational
R. Gilligan
FreeGate
S. Thomson
Bellcore
J. Bound
Compaq
W. Stevens
Consultant
March 1999

Basic Socket Interface Extensions for IPv6

Status of This Memo

このメモは、インターネット社会のための情報を提供する。それは、如何なる種類のインターネット標準も規定しない。このメモの配布は制限されない。

Copyright Notice

Copyright (C) The Internet Society (1999). All Rights Reserved.

Abstract

TCP/IP アプリケーションのデファクト標準のアプリケーションプログラムインターフェース (API) は "ソケット" インターフェースである。この API は 1980年代前半に Unix のために開発されたが、Unix ではないシステムにも広範囲に渡って実装されてきた。ソケット API を使用して書かれた TCP/IP アプリケーションは、過去に高度な移植性を享受しており、IPv6 アプリケーションも同様な移植性を持たせたい。しかし、IPv6 をサポートするためにソケット API に変更が必要であり、このメモはこれらの変更について説明している。この変更には、IPv6 アドレスを伝えるためのソケットアドレス構造体や、新しいアドレス変換機能、新しいソケットオプションが含まれる。これらの拡張は、システムへの変更を最小限に押え、既存の IPv4 アプリケーションとの完全な互換性を保つ一方で、マルチキャストを含む TCP と UDP のアプリケーションに必要な基本的な IPv6 機能へのアクセスを提供するよう設計されている。拡張された IPv6 機能 (生のソケットと IPv6 拡張ヘッダへのアクセス) のための追加拡張は、別のドキュメント [4] で定義されている。

Table of Contents

1. 導入
2. 設計時の考慮事項
  2.1 変更する際の必要事項
  2.2 データ型
  2.3 ヘッダ
  2.4 構造体
3. ソケットインタフェース
  3.1 IPv6 アドレスファミリとプロトコルファミリ
  3.2 IPv6 アドレス構造体
  3.3 4.3 BSD ベースシステムのソケットアドレス構造体
  3.4 4.4 BSD ベースシステムのソケットアドレス構造体
  3.5 ソケット関数
  3.6 IPv4 アプリケーションとの互換性
  3.7 IPv4 ノードとの互換性
  3.8 IPv6 ワイルドカードアドレス
  3.9 IPv6 ループバックアドレス
  3.10 移植性の追加
4. インタフェース識別子
  4.1 名前からインデックス
  4.2 インデックスから名前
  4.3 全てのインタフェース名とインデックスの返却
  4.4 メモリ解放
5. ソケットオプション
  5.1 ユニキャストホップ制限
  5.2 マルチキャストパケットの送受信
6. ライブラリ関数
  6.1 ノード名からアドレスへの変換
  6.2 アドレスからノード名への変換
  6.3 getipnodebyname と getipnodebyaddr におけるメモリ解放
  6.4 プロトコル無依存のノード名とサービス名の変換
  6.5 Socket Address Structure to Nodename and Service Name
  6.6 Address Conversion Functions
  6.7 Address Testing Macros
7. Summary of New Definitions
8. Security Considerations
9. Year 2000 Considerations
Changes From RFC 2133
Acknowledgments
References
Authors' Addresses
Full Copyright Statement


1. 導入

IPv4 アドレスは 32ビット長であるが、IPv6 インタフェースは 128 ビットアドレスで識別される。ソケットインタフェースは、IP アドレスのサイズをアプリケーションに極めて可視的にし、実質的には、BSD ベースのシステムにおける全ての TCP/IP アプリケーションは、IP アドレスのサイズに関する知識を持っている。アドレスを明らかにする API のこうした部分は、より大きな IPv6 アドレスの長さに適合するよう変更しなければならない。IPv6 はさらに新しい機能を導入しており (トラフィッククラスやフローラベル等)、そのうち幾つかは、API を通じてアプリケーションに見えなければならない。このメモは、より大きいアドレス長や IPv6 の新機能をサポートするための、ソケットインタフェースへの一セットの拡張を定義している。

2. 設計時の考慮事項

この使い古された API への変更を設計するために、数多くの重要な考慮点がある。

IPv4 互換の API を提供することは重要なため、これらの拡張は、IPv4 と IPv6 の両方の完全なサポートを提供するマシン上で動作するように、明確に設計されている。この API のサブセットは、IPv6 だけをサポートするシステム上で動作するために設計できるかもしれない。しかし、これはこのメモでは扱わない。

2.1 変更する際の必要事項

ソケットインタフェース API は、幾つかの別々のコンポーネントから構成される。

核のソケット関数 -- この関数は、TCP コネクションの確立や解放、UDP パケットの送受信などの事象を取り扱い、トランスポートには依存せず設計された。プロトコルアドレスを関数のパラメタとして渡す場合、それらは不透明なポインタによって渡される。プロトコル固有のアドレスデータ構造体は、ソケット関数がサポートする各プロトコルに対して定義される。ソケット関数を使用する時、アプリケーションは、これらのプロトコル固有のアドレス構造体へのポインタを、汎用的な "sockaddr" アドレス構造体へのポインタにキャストしなければならない。これらの関数は IPv6 で変更する必要はないが、IPv6 固有の新しいアドレスデータ構造体が必要である。

"sockaddr_in" 構造体は、IPv4 のためのプロトコル固有のデータ構造体である。このデータ構造体は実際は 8 オクテットの未使用域を含んでおり、sockaddr_in 構造体を IPv6 に適応させるために、この空間を使用したいところである。不幸にも sockaddr_in 構造体は、他の必要な情報 (アドレスファミリやポート番号) だけでなく、16 オクテットの IPv6 アドレスを保持するのに十分な大きさではない。よって、IPv6 用の新しいアドレスデータ構造体を定義しなければならない。

IPv6 アドレスは [2] の範囲であり、リンクローカル、サイト、組織、グローバル、あるいは現時点では定義されていない他の範囲があり得る。ある特定の範囲の一セットのインタフェースを識別できることを望むアプリケーションをサポートするために、IPv6 sockaddr_in 構造体は、IPv6 アドレスの範囲を識別する一セットのインタフェースを識別するために実装体が使用できるフィールドをサポートしなければならない。

ソケットインタフェースにおける名前−アドレス変換関数は、gethostbyname() と gethostbyaddr() である。これらは現状のまま残され、IPv4 と IPv6 をサポートするために新しい関数が定義されている。さらに、POSIX 1003.g ドラフト [3] は、プロトコルに依存しないノード名〜アドレス変換関数を規定している。この関数もまた、IPv4 と IPv6 で使用することができる。

アドレス変換関数 -- inet_ntoa() や inet_addr() -- は、IPv4 アドレスをバイナリと表示可能形式の間で変換する。これらの関数は、32 ビットの IPv4 アドレスに極めて固有である。我々は IPv4 と IPv6 アドレスの両方を変換する、類似した 2 つの関数を設計した。これはアドレスタイプのパラメタを渡すので、他のプロトコルファミリにも拡張できる。

最後に、IPv6 をサポートするために、幾つかの様々な機能が必要である。IPv6 トラフィッククラス、フローラベル、ホップ制限ヘッダフィールドをサポートするために、新しいインタフェースが必要とされる。IPv6 マルチキャストパケットの送受信を制御するには、新しいソケットオプションが必要である。

ソケットインタフェースは、他の IPv6 機能へのアクセスを提供するために将来拡張されるかもしれない。これらの拡張は [4] で説明されている。

2.2 データ型

このメモに記述された構造体要素のデータ型は、絶対的な要件ではなく、一例であることを意図している。可能な場合は常に、POSIX 1003.1g のドラフト 6.6 (1997 3 月) のデータ型が使用される。uintN_t は正確に N ビット (例えば uint16_t) の符号無し整数を意味する。さらに可能な場合は、1003.1g のアーギュメントデータ型を想定する (例えば setsockopt() の最後のパラメタは size_t 値である等)。バッファサイズが指定される場合は常に、POSIX 1003.1 の size_t データ型が使用される (例えば getnameinfo() の 2 バイト長のアーギュメント等)。

2.3 ヘッダ

関数のプロトタイプと構造体を表す時、アイテムを定義させるためにインクルードしなければならないヘッダを示す。

2.4 構造体

構造体が示される場合、記述されたメンバは実装体に表れなければならないものである。加えて、非標準のメンバもまた実装体によっては定義してもよい。付加的な予防措置として、IEEE Std 1003.1 で説明されている機能テストマクロで非標準メンバーをチェックしてもよい。(こうした機能テストマクロについては、この RFC では定義しない)。

構造体のメンバが表れる順序は、複数バイトメンバのアライメントを考慮した推奨された順序である。しかし、実装体はメンバを異なる順序にしてもよい。

3. ソケットインタフェース

このセクションは、IPv6 でのソケットインタフェースの変更点を説明する。

3.1 IPv6 アドレスファミリとプロトコルファミリ

新しいアドレスファミリ名 AF_INET6 が、<sys/socket.h> で定義される。AF_INET6 の定義は、元の sockaddr_in アドレスデータ構造体と新規の sockaddr_in6 データ構造体とを区別している。

新しいプロトコルファミリ名 PF_INET6 が、<sys/socket.h> で定義される。他の大半のプロトコルファミリ名と同様、以下の様に対応するアドレスファミリ名と同じ値を持つよう定義される。

    #define PF_INET6        AF_INET6

IPv6 ソケットの生成を指示するために、socket() 関数の第一パラメタで PF_INET6 を使用する。

3.2 IPv6 アドレス構造体

新たな in6_addr 構造体が一つの IPv6 アドレスを保持し、<netinet/in.h> をインクルードすることによって定義される。

  struct in6_addr {
      uint8_t  s6_addr[16];   /* IPv6 アドレス */
  };

このデータ構造体は 16 個の 8 ビット要素の配列を持ち、1 つの 128 ビット IPv6 アドレスを作成する。IPv6 アドレスは、ネットワークバイトオーダーで格納される。

上記の in6_addr 構造体は通常、"in_addr 構造体" の BSD 実装に似た方法で、望ましい境界調整レベルを強制させる余分なフィールドを持つ埋め込み union で実装される。これらの付加的な実装詳細は、単純さのためにここでは省略する。

以下に例を示す。

struct in6_addr {
     union {
         uint8_t  _S6_u8[16];
         uint32_t _S6_u32[4];
         uint64_t _S6_u64[2];
     } _S6_un;
};
#define s6_addr _S6_un._S6_u8

3.3 4.3 BSD ベースシステムのソケットアドレス構造体

ソケットインタフェースでは、各々のプロトコルスイートのアドレスを送信するために、異なるプロトコル固有のデータ構造体を定義する。各プロトコル固有のデータ構造体は、プロトコルに依存しないデータ構造体である "sockaddr" 構造体にキャストできるよう設計されている。各々は、sockaddr データ構造体の "sa_family" を上書きする "family" フィールドを持つ。このフィールドはデータ構造体の型を識別する。

sockaddr_in 構造体は、IPv4 用のプロトコル固有アドレスデータ構造体である。これは、アプリケーションとシステム間で、ソケット関数でアドレスを渡すために使用される。以下の sockaddr_in6 構造体は IPv6 アドレスを保持し、<netinet/in.h> ベッダをインクルードすることによって定義される。

struct sockaddr_in6 {
    sa_family_t     sin6_family;    /* AF_INET6 */
    in_port_t       sin6_port;      /* トランスポート層のポート # */
    uint32_t        sin6_flowinfo;  /* IPv6 トラフィッククラスとフロー情報 */
    struct in6_addr sin6_addr;      /* IPv6 アドレス */
    uint32_t        sin6_scope_id;  /* スコープのためのインタフェースのセット */
};

この構造体は、4.3BSD 版で使用される sockaddr データ構造体と互換性を保つよう設計されている。

sin6_family フィールドは、sockaddr_in6 構造体としてこれを識別する。このフィールドは、バッファが sockaddr データ構造体にキャストされる時に sa_family フィールドを上書きする。このフィールドの値は AF_INET6 でなければならない。

sin6_port フィールドは、16 ビットの UDP/TCP ポート番号を含む。このフィールドは、sockaddr_in 構造体の sin_port フィールドと同じように使われる。ポート番号は、ネットワークバイトオーダーで格納される。

sin6_flowinfo フィールドは、二つの情報を含む 32 ビットフィールドである。二つの情報とは、トラフィッククラスとフローラベルである。このメンバの内容と解釈については、[1] で規定されている。受信動作でアプリケーションが sockaddr_in6 構造体を使用する前に、sin6_flowinfo フィールドに 0 を設定すべきである (SHOULD)。

sin6_addrフィールドは、一つの in6_addr 構造体 (前のセクションで定義されている) である。このフィールドは 128 ビットの IPv6 アドレスを保持する。このアドレスはネットワークバイトオーダーで格納される。

この構造体の要素の順番は、特に sin6_addr フィールドが 64 ビット境界に調整されるよう設計されている。これは、64 ビットアーキテクチャ上で最高のパフォーマンスを得るためである。

sin6_scope_id フィールドは、一セットのインタフェースを、sin6_addr フィールドで送信されるアドレスのスコープを満たすように識別する 32 ビットの整数である。リンクスコープの sin6_addr の場合、sin6_scope_id はサイト識別子になる。sin6_scope_id からインタフェースやインタフェースのセットへのマッピングは、実装やサイト識別子に関する将来の規定に任される。

sockaddr_in6 構造体は通常、一般的な sockaddr 構造体よりも大きくなることに注意されたい。多くの既存の実装では、sizeof(struct sockaddr_in) と sizeof(struct sockaddr) は等しく、両方とも 16 バイトになっている。これを想定して作成している既存のコードは、IPv6 に移行する際、注意深く検証する必要がある。

3.4 4.4 BSD ベースシステムのソケットアドレス構造体

4.4BSD 版は、ソケットインタフェースに対し、小さいが非互換となる変更を含んでいる。sockaddr データ構造体の "sa_family" フィールドは、16 ビット値から 8 ビット値に変更され、節約された空間は "sa_len" という名前の長さフィールドを保持するために使用された。前のセクションで示された sockaddr_in6 データ構造体は、新しい sockaddr データ構造体に正しくキャストすることができない。このため、以下の代わりの IPv6 アドレスデータ構造体が、4.4BSD ベースのシステムで使用するために提供される。これは、<netinet/in.h> ベッダをインクルードすることによって定義される。

struct sockaddr_in6 {
    uint8_t         sin6_len;       /* この構造体の長さ */
    sa_family_t     sin6_family;    /* AF_INET6 */
    in_port_t       sin6_port;      /* トランスポート層のポート */
    uint32_t        sin6_flowinfo;  /* IPv6 フロー情報 */
    struct in6_addr sin6_addr;      /* IPv6 アドレス */
    uint32_t        sin6_scope_id;  /* スコープのインタフェースのセット */
};

この構造体と 4.3BSD の構造体との唯一の違いは、長さフィールドを含むことと、ファミリフィールドを 8 ビットのデータ型に変更したことである。その他のフィールドは全て、前のセクションで定義された構造体と同じである。

sockaddr_in6 データ構造体のこの版を提供するシステムは、<netinet/in.h> ヘッダをインクルードした結果として、SIN6_LEN も宣言しなければならない。このマクロは、アプリケーションが 4.3BSD か 4.4BSD のどちらのデータ構造体をサポートするシステム上で作成されているかを決定することを可能にする。

3.5 ソケット関数

アプリケーションは、通信の端点を示すソケット記述子を生成するために socket() 関数を呼び出す。socket() 関数への引数は、どのプロトコルを使用するか、その後の関数で使用されるアドレス構造体の形式が何かをシステムに通知する。例えば、IPv4/TCP ソケットを作成するには、アプリケーションは以下の様に呼び出す。

  s = socket(PF_INET, SOCK_STREAM, 0);

IPv4/UDP ソケットを作成するには、アプリケーションは以下の様に呼び出す。

  s = socket(PF_INET, SOCK_DGRAM, 0);

アプリケーションは、第一引数として PF_INET の代わりに PF_INET6 の定数を単に使うだけで、IPv6/TCP と IPv6/UDP ソケット生成することができる。例えば、IPv6/TCP ソケットを作成するには、アプリケーションは以下の様に呼び出す。

  s = socket(PF_INET6, SOCK_STREAM, 0);

IPv6/UDP ソケットを作成するには、アプリケーションは以下の様に呼び出す。

  s = socket(PF_INET6, SOCK_DGRAM, 0);

ひとたびアプリケーションが PF_INET6 ソケットを作成したら、システムにアドレスを渡す際に sockaddr_in6 アドレス構造体を使用しなければならない。アプリケーションがシステムにアドレスを渡すために使用する関数は、以下のものである。

  bind()
  connect()
  sendmsg()
  sendto()

システムは、sockaddr_in6 アドレス構造体を使用して、PF_INET6 ソケットを使用しているアプリケーションにアドレスを返却する。システムからアプリケーションにアドレスを返却する関数は、以下のものである。

  accept()
  recvfrom()
  recvmsg()
  getpeername()
  getsockname()

"アドレスを運ぶ" 全ての関数は不透明なアドレスポインタを使用し、関数の引数でアドレス長を渡すので、IPv6 をサポートするためにソケット関数のシンタックスを変更する必要はない。

3.6 IPv4 アプリケーションとの互換性

元の API を使用しているアプリケーションの大半の基盤をサポートするために、システム実装は、元の API とソースやバイナリの完全な互換性を提供しなければならない。これは、システムが PF_INET ソケットと sockaddr_in アドレス構造体を継続してサポートしなければならないことを意味する。アプリケーションは、前のセクションで説明されているように、socket() 関数で PF_INET 定数を使用して、IPv4/TCP と IPv4/UDP ソケットを生成できなければならない。アプリケーションは、IPv4/TCP, IPv4/UDP, IPv6/TCP, IPv6/UDP ソケットの組み合わせを、同時に同じプロセスで保持できるべきである。

元の API を使用しているアプリケーションは、IPv4 のみをサポートしているシステム上で行っていたように、継続して運用できるべきである。すなわち、IPv4 ノードと継続して相互接続できるべきである。

3.7 IPv4 ノードとの互換性

API はさらに、異なるタイプの互換性、IPv6 アプリケーションが IPv4 アプリケーションと相互接続する能力を提供する。この機能は、IPv6 アドレス体系規約 [2] で定義された IPv4 マッピングされた IPv6 アドレス形式を使用する。このアドレス形式は、IPv4 ノードの IPv4 アドレスを IPv6 アドレスとして表すことを可能にする。IPv4 アドレスは IPv6 アドレスの下位 32 ビットに符号化され、上位 96 ビットは、固定のプレフィクス 0:0:0:0:0:FFFF を持つ。IPv4 マッピングされたアドレスは、以下の様に記述される。

  ::FFFF:<IPv4-address>

指定されたホストが IPv4 アドレスだけを持つ場合 (セクション6.1で説明されている様に)、getipnodebyname() 関数によってこれらのアドレスを自動的に生成することができる。

アプリケーションは、IPv4 ノードへの TCP コネクションをオープンしたり、IPv4 ノードに UDP パケットを送信するために PF_INET6 ソケットを使用してもよい。その場合、単に宛先の IPv4 アドレスを IPv4 マッピングされた IPv6 アドレスに符号化し、そのアドレスを sockaddr_in6 構造体で connect() か sendto() 関数に渡せばよい。アプリケーションが PF_INET6 ソケットを使用して IPv4 ノードからの TCP コネクションを受諾するか、あるいは IPv4 ノードから UDP パケットを受信する時、システムはこの方法で符号化された sockaddr_in6 構造体を使用して、accept(), recvfrom(), getpeername() 関数で相手のアドレスをアプリケーションに返却する。

ほとんどのアプリケーションは、相互接続しているノードがどのタイプであるか知る必要はない。しかし、実際に知る必要があるアプリケーションのために、IN6_IS_ADDR_V4MAPPED() マクロが提供される。これは、セクション 6.7 で定義されている。

3.8 IPv6 ワイルドカードアドレス

アプリケーションは、bind() 関数を使用して UDP パケットや TCP コネクションの送信元 IP アドレスを選択できるが、アプリケーションは送信元アドレスをシステムに選択させたいことが多い。IPv4 では、bind() 関数にアドレスとしてシンボリック定数 INADDR_ANY ("ワイルドカード" アドレスと呼ばれる) を指定するか、単に bind() 自体を省略すればよい。

IPv6 アドレスタイプは構造体 (struct in6_addr) なので、IPv6 アドレス変数を初期化するためにシンボリック定数を使用することができる。しかし、代入で使用することはできない。従って、システムは IPv6 ワイルドカードアドレスを 2 つの形式で提供する。

一つ目のバージョンは、in6_addr 構造体の "in6addr_any" という名前のグローバル変数である。この変数の extern 宣言は、<netinet/in.h> で定義される。

  extern const struct in6_addr in6addr_any;

アプリケーションは、IPv4 で INADDR_ANY を使用するのと同様な方法で in6addr_any を使用する。例えば、ソケットをポート番号 23 にバインドするが、送信元アドレスはシステムに選択させる場合、アプリケーションは以下のコードを使用できる。

  struct sockaddr_in6 sin6;
   . . .
  sin6.sin6_family = AF_INET6;
  sin6.sin6_flowinfo = 0;
  sin6.sin6_port = htons(23);
  sin6.sin6_addr = in6addr_any;  /* 構造体の代入 */
   . . .
  if (bind(s, (struct sockaddr *) &sin6, sizeof(sin6)) == -1)
              . . .

もう一つのバージョンは IN6ADDR_ANY_INIT という名前のシンボリック定数であり、<netinet/in.h> に定義される。この定数は in6_addr 構造体を初期化するのに使用できる。

  struct in6_addr anyaddr = IN6ADDR_ANY_INIT;

この定数は、宣言時にしか使用できないことに注意されたい。これは、前に宣言した in6_addr 構造体に代入するために使用することはできない。例えば、以下のコードは動作しない。

  /* これは無指定アドレスを代入する誤った方法である。*/
  struct sockaddr_in6 sin6;
   . . .
  sin6.sin6_addr = IN6ADDR_ANY_INIT; /* コンパイル不可 */

IPv4 INADDR_xxx 定数は全てホストバイトオーダで定義されるが、IPv6 IN6ADDR_xxx 定数と in6addr_xxx 外部宣言はネットワークバイトオーダで定義されることを注意されたい。

3.9 IPv6 ループバックアドレス

アプリケーションは、自側ノード上にあるサービスに対して UDP パケットを送信したり、TCP コネクションを起動する必要があるかもしれない。IPv4 では、connect(), sendto(), sendmsg() 関数で、IPv4 アドレス定数 INADDR_LOOPBACK を使用することによって、これを行うことができる。

IPv6 もまた、自側の TCP/UDP サービスと通信するためのループバックアドレスを提供する。無指定アドレスと同様に、IPv6 ループバックアドレスは 2 つの形式 - グローバル変数とシンボリック定数で提供される。

グローバル変数は、"in6addr_loopback" という名前の in6_addr 構造体である。この変数の extern 宣言は、<netinet/in.h> で定義される。

  extern const struct in6_addr in6addr_loopback;

アプリケーションは、IPv4 で INADDR_LOOPBACK を使用するのと同様な方法で in6addr_loopback を使用する (しかし、前のセクションの最後に示したバイトオーダの違いはあることに注意されたい)。例えば、自側の telnet サーバに TCP コネクションを開設する場合、アプリケーションは以下のコードを使用する。

  struct sockaddr_in6 sin6;
   . . .
  sin6.sin6_family = AF_INET6;
  sin6.sin6_flowinfo = 0;
  sin6.sin6_port = htons(23);
  sin6.sin6_addr = in6addr_loopback;  /* 構造体の代入 */
   . . .
  if (connect(s, (struct sockaddr *) &sin6, sizeof(sin6)) == -1)
          . . .

シンボリック定数は IN6ADDR_LOOPBACK_INIT という名前であり、<netinet/in.h> に定義される。この定数は宣言時のみ使用できる。例えば、

  struct in6_addr loopbackaddr = IN6ADDR_LOOPBACK_INIT;

IN6ADDR_ANY_INIT と同様に、この定数は、前に宣言した IPv6 アドレス変数に代入するために使用することはできない。

3.10 移植性の追加

アプリケーション作成者の助けになるソケット API への一つの簡単な追加は、"struct sockaddr_storage" である。このデータ構造体は、複数のアドレスファミリやプラットホーム間で移植可能なコードを書くことを容易にする。このデータ構造体は、以下を目標として設計されている。

そのようなデータ構造体の設計の実装例は、以下の通り。

/*
 * 最大長と調整の望ましい設計
 */
#define _SS_MAXSIZE    128  /* 実装特定の最大長 */
#define _SS_ALIGNSIZE  (sizeof (int64_t))
                         /* 実装固有の望ましい調整 */
/*
 * sockaddr_storage 構造体のパディング設計で使用される定義
 */
#define _SS_PAD1SIZE   (_SS_ALIGNSIZE - sizeof (sa_family_t))
#define _SS_PAD2SIZE   (_SS_MAXSIZE - (sizeof (sa_family_t)+
                              _SS_PAD1SIZE + _SS_ALIGNSIZE))
struct sockaddr_storage {
    sa_family_t  __ss_family;     /* アドレスファミリ */
    /* 以下のフィールドは実装固有 */
    char      __ss_pad1[_SS_PAD1SIZE];
              /* 6 バイトのパディング。これは実装固有の   */
              /* パディングを、データ構造体の中で後続する */
              /* 調整フィールドまで明確にさせる           */
    int64_t   __ss_align;     /* 望ましい構造にするためのフィールド */
               /* 領域調整 */
    char      __ss_pad2[_SS_PAD2SIZE];
              /* 望ましいサイズにするための 112 バイトのパディング。 */
              /* _SS_MAXSIZE 値から ss_family, __ss_pad1, __ss_align */
              /* を引いた値は 112 である。                           */
};

sockaddr データ構造体が "sa_len" フィールドを含む実装では、このデータ構造体は以下のようになる。

/*
 * sockaddr_storage 構造体のパディング設計で使用される定義
 */
#define _SS_PAD1SIZE (_SS_ALIGNSIZE -
                            (sizeof (uint8_t) + sizeof (sa_family_t))
#define _SS_PAD2SIZE (_SS_MAXSIZE - (sizeof (sa_family_t)+
                              _SS_PAD1SIZE + _SS_ALIGNSIZE))
struct sockaddr_storage {
    uint8_t      __ss_len;        /* アドレス長       */
    sa_family_t  __ss_family;     /* アドレスファミリ */
    /* 以下のフィールドは実装固有 */
    char         __ss_pad1[_SS_PAD1SIZE];
                  /* 6 バイトのパディング。これは実装固有の   */
                  /* パディングを、データ構造体の中で後続する */
                  /* 調整フィールドまで明確にさせる           */
    int64_t      __ss_align;  /* 望ましい構造にするためのフィールド */
                  /* 領域調整 */
    char         __ss_pad2[_SS_PAD2SIZE];
                  /* 望ましいサイズにするための 112 バイトの         */
                  /* パディング。_SS_MAXSIZE 値から ss_len,          */
                  /* __ss_family, __ss_pad1, __ss_align フィールドを */
                  /* 引いた値は 112 である。                         */
};

上記の実装例は、64 ビット境界で調整するデータ構造体を示している。実装固有のフィールド "__ss_align" に続く "__ss_pad1" は、sockaddr_in6 (IPv6), sockaddr_in (IPv4) アドレスデータ構造体の要件に十分適切な調整をまかなう 64 ビット調整にさせるために使用する。パディングフィールド __ss_pad1 のサイズは、選択された調整境界に依存する。パディングフィールド __ss_pad2 のサイズは、構造体の全体長として選択された全体長の値に依存する。このサイズと調整は、上記の例では実装固有 (必須ではない) の定数 _SS_MAXSIZE (選択された値は 128) と _SS_ALIGNMENT (選択された値は 8) によって示されている。定数 _SS_PAD1SIZE (導き出される値は 6) と _SS_PAD2SIZE (導き出される値は 112) もまた例であり、必須ではない。上記の実装固有の定義と構造体のフィールド名は、実装の内部的な名前空間を示すためにアンダースコアで始まっている。移植性のあるコードは、これらのフィールドや定数にアクセスすることは求められていない。

sockaddr_storage 構造体は、全てのファミリのソケットアドレスデータ構造体を格納するのに十分な大きさを持ち、調整可能な自動変数の領域を宣言するための問題を解決する。例えば、ファイルディスクリプタを持ち、アドレスファミリのコンテキストを持たないコードは、この型の変数へのポインタを渡すことができる。getpeername() のような関数では、ソケットアドレス構造体へのポインタが用いられ、関数呼び出し後に受け取った内容によってアドレスファミリを決定する。

sockaddr_storage 構造体は、複数のアドレスファミリで使用するのに十分な大きさを持ち調整可能な一般的なソケットアドレスで必要とされる、ある別のインタフェースにも有効であり、適用できる。このようなインタフェースの議論については、このドキュメントの適用外である。

さらに、多くの既存のコードは、全てのソケットアドレス構造体が一般的な sockaddr 構造体に合うものと仮定している。これは IPv4 ソケットアドレス構造体では真であったが、Unix ドメインソケットアドレス構造体では常に偽であり (しかし実際のところ、これは問題ではなかった)、IPv6 ソケットアドレス構造体でも偽である (これは問題になり得る)。

従って、今アプリケーションは以下のように書くことができる。

  struct sockaddr_storage __ss;
  struct sockaddr_in6 *sin6;
  sin6 = (struct sockaddr_in6 *) &__ss;

4. インタフェース識別子

この API は、マルチキャストグループを結び付ける (セクション 5.3) ローカルインタフェースを識別するために、インタフェースインデックス (小さい正の整数) を使用する。加えて、拡張 API [4] は、データグラムを受信するインタフェースを識別するため、あるいはデータグラムを送信するインタフェースを指定するために、これらの同じインタフェースインデックスを使用する。

インタフェースは通常、例えば "le0", "sl1", "ppp2" 等のような名前によって識別されている。バークレイ由来の実装では、インタフェースがシステムに認識される時、カーネルがユニークな正の整数値を (インタフェースインデックスと呼ばれる) インタフェースに割り当てる。これらは、1 から始まる小さい正の整数である。(0 はインタフェースインデックスとして使用されないことに注意)。ある特定の正のインタフェースインデックスのためのインタフェースが現在存在しないために、空きがあるかもしれない。

この API は、インタフェース名とインデックスをマッピングする二つの関数と、全てのインタフェース名とインデックスを返却する関数と、前の関数によって割り当てられた動的メモリを返却する関数を定義する。これらの関数の実装方法は、実装体に任される。4.4BSD 実装では、これらの関数を NET_RT_IFLIST コマンド付きの既存の sysctl() 関数を使用して実装することができる。他の実装は、この目的のために ioctl() を使用したいかもしれない。

4.1 名前からインデックス

一番目の関数は、インタフェース名を対応するインデックスにマッピングする。

  #include <net/if.h>

  unsigned int  if_nametoindex(const char *ifname);

もし指定されたインタフェース名が存在しなければ、その戻り値は 0 であり、errno に ENXIO が設定される。もしシステムエラー (例えばメモリ不足) が発生したら、その戻り値は 0 であり、errno に適切な値 (例えば ENOMEM 等) が設定される。

4.2 インデックスから名前

二番目の関数は、インタフェースインデックスを対応する名前にマッピングする。

  #include <net/if.h>

  char  *if_indextoname(unsigned int ifindex, char *ifname);

ifname アーギュメントは、指定されたインデックスに対応するインタフェース名を格納して返却するために、少なくとも IF_NAMESIZE バイトのバッファを指していなければならない。(IF_NAMESIZE もまた <net/if.h> で定義されており、その値はインタフェース名の最後に付く終端のヌルバイトを含む)。このポインタは、関数の戻り値でもある。もし指定されたインデックスに対応するインタフェースが存在しなければ、NULL が返却され、errno に ENXIO が設定される。もしシステムエラー (例えばメモリ不足) が発生したら、if_indextoname は NULL を返却し、errno に適切な値 (例えば ENOMEM 等) が設定される。

4.3 全てのインタフェース名とインデックスの返却

if_nameindex 構造体は一つのインタフェースに関する情報を保持し、<net/if.h> ヘッダをインクルードすることによって定義される。

  struct if_nameindex {
    unsigned int   if_index;  /* 1, 2, ... */
    char          *if_name;   /* ヌルで終わる名前: "le0", ... */
  };

最後の関数は if_nameindex 構造体の配列を返却する。一つのインタフェースにつき 1 つの構造体である。

  struct if_nameindex  *if_nameindex(void);

構造体の配列の最後は、0 の if_index と NULL の if_name を持つ構造体によって示される。関数はエラー時に NULL ポインタを返却し、errno に適切な値を設定する。

この構造体の配列のために使用されるメモリは、if_name メンバが指すインタフェース名と一緒に動的に獲得される。このメモリは、次の関数によって解放される。

4.4 メモリ解放

以下の関数は、if_nameindex() によって割り当てられた動的メモリを解放する。

  #include <net/if.h>

  void  if_freenameindex(struct if_nameindex *ptr);

この関数のアーギュメントは、if_nameindex() が返却したポインタでなければならない。

現在、net/if.h には関数のプロトタイプ定義が無い。これらの定義や struct if_nameindex{} は、net/if.h に定義されることが推奨される。

5. ソケットオプション

IPv6 では、数多くの新しいソケットオプションが定義されている。これらの新しいオプションは全て、IPPROTO_IPV6 レベルである。すなわち、これらのオプションを使用する時は、getsockopt() と setsockopt() 関数の "level" パラメタを IPPROTO_IPV6 とする。新しいソケットオプションの全てに、プレフィクス IPV6_ の定数名が使用される。これによって、IPv6 に適用されるこれらのオプションを識別しやすくなる。

IPPROTO_IPV6 の宣言や新しい IPv6 ソケットオプション、このセクションで定義される関連定数は、<netinet/in.h> ヘッダをインクルードすることによって使用できる。

5.1 ユニキャストホップ制限

新しい setsockopt() オプションは、ユニキャストの IPv6 出力パケットで使用するホップ制限を制御する。このオプションの名前は IPV6_UNICAST_HOPS であり、IPPROTO_IPV6 層で使用される。以下の例は、その使用方法を示している。

  int  hoplimit = 10;

  if (setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
                 (char *) &hoplimit, sizeof(hoplimit)) == -1)
      perror("setsockopt IPV6_UNICAST_HOPS");

setsockopt() で IPV6_UNICAST_HOPS を設定した場合、以降そのソケットによって送信される全てのユニキャストパケットのホップ制限として、指定されたオプション値が使用される。もしこのオプションが設定されなければ、システムはデフォルト値を選択する。整数のホップ制限値 (x と呼ぶ) は、以下のように解釈される。

  x < -1:        EINVAL のエラーを返却
  x == -1:       カーネルデフォルトを使用
  0 <= x <= 255: x を使用
  x >= 256:      EINVAL のエラーを返却

そのソケットで送信するユニキャストパケットにシステムが使用するホップ制限値を知るために、getsockopt() で IPV6_UNICAST_HOPS オプションを使用してもよい。例えば、

  int  hoplimit;
  size_t  len = sizeof(hoplimit);

  if (getsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
                 (char *) &hoplimit, &len) == -1)
      perror("getsockopt IPV6_UNICAST_HOPS");
  else
      printf("Using %d for hop limit.\n", hoplimit);

5.2 マルチキャストパケットの送受信

IPv6 アプリケーションは、IPv6 マルチキャストアドレスを sendto() 関数のアドレスアーギュメントに単に指定することによって、UDP マルチキャストパケットを送信してもよい。

IPPROTO_IPV6 層における三つのソケットオプションが、マルチキャストパケットを送信するための幾つかのパラメタを制御する。これらのオプションょ設定する必要はない。アプリケーションは、これらのオプションを使用せずにマルチキャストパケットを送信してもよい。マルチキャストパケットの送信を制御する setsockopt() のオプションは、以下のように要約される。これらの三つのオプションは、getsockopt() でも使用することができる。

IPV6_MULTICAST_IF

マルチキャストパケットを出力するために使用するインタフェースを設定する。このアーギュメントは、使用するインタフェースのインデックスである。

アーギュメントの型: unsigned int

IPV6_MULTICAST_HOPS

マルチキャストパケットの出力で使用するホップ制限を設定する。(注記: 異なるオプション IPV6_UNICAST_HOPS は、ユニキャストパケットの出力で使用するホップ制限を設定するために提供される)。

このアーギュメントの解釈は、IPV6_UNICAST_HOPS オプションと同じである。
  x < -1:        EINVAL のエラーを返却
  x == -1:       カーネルデフォルトを使用
  0 <= x <= 255: x を使用
  x >= 256:      EINVAL のエラーを返却
もし、IPV6_MULTICAST_HOPS が設定されなければ、デフォルト値は 1 である (今日の IPv4 と同じ)。

アーギュメントの型: int

IPV6_MULTICAST_LOOP

もしマルチキャストデータグラムを送信側ホスト自身が属するグループに送信したら (その出力インタフェース上で)、もしこのオプションが 1 に設定されていれば、IP 層はローカル配送でデータグラムの複製をループバックする。もしこのオプションが 0 に設定されていれば、複製をループバックしない。その他のオプションの値は EINVAL のエラーを返却する。

もし IPV6_MULTICAST_LOOP が設定されなければ、デフォルト値は 1 (ループバックする: 今日の IPv4 と同じ) である。

アーギュメントの型: unsigned int

マルチキャストパケットの受信は、以下で要約する二つの setsockopt() オプションによって制御される。もしこれら二つのオプションを getsockopt() で使用したら、EOPNOTSUPP のエラーが返却される。

IPV6_JOIN_GROUP

指定されたローカルインタフェースをマルチキャストグループに加える。もしインタフェースインデックスに 0 が指定されたら、カーネルがローカルインタフェースを選択する。例えば、あるカーネルは、通常の IPv6 ルーティングテーブル内にマルチキャストグループを検索し、見つかったインタフェースを使用する。

アーギュメントの型: struct ipv6_mreq

IPV6_LEAVE_GROUP

指定されたインタフェースをマルチキャストグループから除く。

アーギュメントの型: struct ipv6_mreq

これらの両方のオプションのアーギュメントの型は ipv6_mreq 構造体であり、<netinet/in.h> ヘッダをインクルードすることによって定義される。

  struct ipv6_mreq {
      struct in6_addr ipv6mr_multiaddr; /* IPv6 マルチキャストアドレス */
      unsigned int    ipv6mr_interface; /* インタフェースインデックス  */
  };

マルチキャストデータグラムを受信するには、プロセスがマルチキャストグループに加わり、データグラムを送信する UDP ポートをバインドしなければならないことに注意されたい。同じポート宛ての別のデータグラムが、そのソケットに配送されることを防ぐために、幾つかのプロセスはポートに加えて、マルチキャストグループアドレスもソケットにバインドする。

6. ライブラリ関数

IPv6 アドレスに対する様々な操作を実現するために、新しい関数が必要である。ドメイン名システム (DNS) で IPv6 アドレスを検索するための関数が必要である。正順検索 (ノード名からアドレスへの変換)と逆順検索 (アドレスからノード名への変換) の両方をサポートする必要がある。また、IPv6 アドレスをバイナリ形式とテキスト形式との間で変換するための関数も必要である。

二つの既存の関数 gethostbyname() と gethostbyaddr() は、そのまま残ることに注意されたい。新しい関数は IPv4 アドレスと IPv6 アドレスの両方を扱うために定義される。

6.1 ノード名からアドレスへの変換

一般に使用される関数 gethostbyname() は、多くのアプリケーションで不十分である。まず第一に、この関数は呼出し側が望む (IPv4 のみ、IPv6 のみ、IPv4 マッピングされた IPv6 は OK 等) アドレスのタイプを指定する方法を提供しない。さらにこの関数の多くの実装はスレッドセーフでない。RFC2133 は gethostbyname2() という名前の関数を定義しているが、この関数も不十分である。まず第一に、IPv6 アドレスを必要とする場合、この関数を使用するにはグローバルオプション (RES_USE_INET6) を設定する必要がある。さらに、必要なアドレスのタイプにおいて呼出し側に付加制御を提供するためには、フラグアーギュメントが必要である。

以下の関数は新しく、スレッドセーフのはずである。

   #include <sys/socket.h>
   #include <netdb.h>

   struct hostent *getipnodebyname(const char *name, int af, int flags int *error_num);

name アーギュメントは、ノード名でも数字で表したアドレス文字列 (すなわちドット付き10進表記の IPv4 アドレスや IPv6 16進アドレス) でも、どちらでもよい。af アーギュメントはアドレスファミリ、AF_INET または AF_INET6 のいずれかを指定する。error_num の値は、スレッドセーフなエラーコードの返却をサポートするために、error_num に適切なエラーコードが設定されてポインタ経由で呼出し側に返却される。error_num は、以下の値のいずれか一つが設定される。

HOST_NOT_FOUND
未知のホストである。

NO_ADDRESS
サーバは要求と名前を認識したが、アドレスが利用できない。ドメインに対するネームサーバへの別の要求タイプが応答を返すかもしれない。

NO_RECOVERY
回復不能な予期せぬサーバ障害が発生した。

TRY_AGAIN
サーバの応答失敗等のような、一時的な恐らく瞬間的なエラーが発生した。

falgs アーギュメントは、検索するアドレスのタイプや返却するアドレスのタイプを指定する。AI_DEFAULT という特殊なフラグ値 (以下で定義) は、大半のアプリケーションで扱うべきであることを注記する。

つまり、単純なアプリケーションを IPv6 を使うために書き直すには、以下の関数

  hptr = gethostbyname(name);

を、以下の関数に置きかえればよい。

  hptr = getipnodebyname(name, AF_INET6, AI_DEFAULT, &error_num);

そして、その後のエラー診断コードを、h_errno のような外部宣言された変数ではなく、error_num を使うよう変更する。

検索したり返却するアドレスのタイプに対して、より優れた制御を望むアプリケーションは、flags アーギュメントで他の組み合わせを指定することができる。

0 の flags は、af アーギュメントを厳密に解釈することを意味する。

この関数の振る舞いを変更するために、flags アーギュメントに他の定数を論理和で組み合わせることができる。

特別な flags の値として AI_DEFAULT が以下のように定義される。

  #define  AI_DEFAULT  (AI_V4MAPPED | AI_ADDRCONFIG)

getipnodebyname() 関数は、name アーギュメントにノード名かリテラルアドレス文字列 (すなわちドット付き10進表記の IPv4 アドレスや IPv6 16進アドレス) のいずれかを許さなければならないことを注記する。これにより、アプリケーションはリテラルアドレス文字列を扱うために inet_pton() を呼ぶ必要がない。

リテラルアドレス文字列のタイプとアーギュメントの値に従って、4 つのシナリオがある。

二つの簡単なケースは以下の通り。

name がドット付き10進表記の IPv4 アドレスで、af が AF_INET と等しい、あるいは name が IPv6 16進アドレスで、af が AF_INET6 と等しい場合。返却される hostent 構造体のメンバは、h_name は name アーギュメントの複製を指し、h_aliases は NULL ポインタ、h_addrtype は af アーギュメントの複製、h_length は 4 (AF_INET) か 16 (AF_INET6) のいずれか、h_addr_list[0] は 4 バイトか 16 バイトのバイナリアドレスへのポインタで、h_addr_list[1] は NULL ポインタである。

name がドット付き10進表記の IPv4 アドレスで、af が AF_INET6 と等しく、flags が AI_V4MAPPED と等しい場合、IPv4 マッピングされた IPv6 アドレスが返却される。h_name は IPv4 マッピングされた IPv6 アドレスを含む IPv6 16進アドレスを指し、h_aliases は NULL ポインタで、h_addrtype は AF_INET6 で、h_length は 16 で、h_addr_list[0] は 16 バイトのバイナリアドレスへのポインタで、h_addr_list[1] は NULL ポインタである。もし AI_V4MAPPED が設定されれば (AI_ALL が付いても付かなくても)、IPv4 マッピンクが返却され、設定されなければ NULL が返却される。

name が IPv6 16進アドレスで、af が AF_INET と等しければエラーである。この関数の戻り値は NULL ポインタであり、error_num は HOST_NOT_FOUND である。

6.2 アドレスからノード名への変換

以下の関数は、既存の gethostbyaddr() 関数と同じアーギュメントを持つが、エラー番号が追加される。

  #include <sys/socket.h>
  #include <netdb.h>

  struct hostent *getipnodebyaddr(const void *src, size_t len,
                                  int af, int *error_num);

getipnodebyname() と同じように、getipnodebyaddr() はスレッドセーフでなければならない。error_num の値は、スレッドセーフなエラーコードの返却をサポートするために、適切なエラーコードを呼出し側に返却する。以下のエラー状態を error_num で返却してもよい。

HOST_NOT_FOUND
未知のホストである。

NO_ADDRESS
サーバは要求と名前を認識したが、アドレスが利用できない。ドメインに対するネームサーバへの別の要求タイプが応答を返すかもしれない。

NO_RECOVERY
回復不能な予期せぬサーバ障害が発生した。

TRY_AGAIN
サーバの応答失敗等のような、一時的な恐らく瞬間的なエラーが発生した。

一つの起こり得る混乱の元は、IPv4 マッピングされた IPv6 アドレスと IPv4 互換の IPv6 アドレスの扱いである。しかし、以下のロジックを適用すべきである。

  1. もし af が AF_INET6 で、len が 16 と等しく、IPv6 アドレスが IPv4 マッピングされた IPv6 アドレスか IPv4 互換の IPv6 アドレスならば、IPv6 アドレスの最初の 12 バイトをスキップし、af に AF_INET を設定し、len に 4 を設定する。

  2. もし af が AF_INET ならば、指定された IPv4 アドレスに対する名前を検索する (例えば、in-addr.arpa ドメインの PTR レコードに対してキュエリを発行する)。

  3. もし af が AF_INET6 ならば、指定された IPv6 アドレスに対する名前を検索する (例えば、ip6.int ドメインの PTR レコードに対してキュエリを発行する)。

  4. もしこの関数が正常を返却したら、hostent 構造体で返却される一つのアドレスは、この関数にアーギュメントとして渡されたのと同じアドレスファミリを持つ、関数への第一アーギュメントのコピーである。

一覧化されている 4 つのステップは、全てこの順序で実行される。また、IPv6 16進アドレス "::" と "::1" も IPv4 互換のアドレスとして扱ってはならない (MUST NOT) ことに注意されたい。そして、もしアドレスが "::" ならば、HOST_NOT_FOUND が返却されなければならず (MUST)、アドレス検索は実行しない。

さらに、セクション 6,7 に記述されているマクロ IN6_IS_ADDR_V4COMPAT は、"::" と "::1" に対して false を返却しなければならない (MUST)。

6.3 getipnodebyname と getipnodebyaddr におけるメモリ解放

hostent 構造体は既存の定義から変更しない。この構造体と構造体によって示される情報は、getipnodebyname と getipnodebyaddr によって動的に割り当てられる。以下の関数は、このメモリを解放する。

  #include <netdb.h>

  void freehostent(struct hostent *ptr);

6.4 プロトコル無依存のノード名とサービス名の変換

ノード名からアドレスへの変換は、アメリカ電子電気学会 (IEEE: Institute of Electrical and Electronic Engineers) の POSIX 1003.1g (プロトコル無依存インタフェース) トラフト規約 [3] から抜粋した getaddrinfo() 関数を使用して、プロトコルに依存しない方法で行われる。

この関数の正式な規約は、以下の追加要件が付いて最終的なPOSIX 標準になるだろう。

POSIX 標準は (IETF ドキュメントと同様に) 無料で利用することができないので、この関数の規定を独自に提供している。

  #include <sys/socket.h>
  #include <netdb.h>

  int getaddrinfo(const char *nodename, const char *servname,
                  const struct addrinfo *hints,
                  struct addrinfo **res);