Network Working Group
Request for Comments: 2133
Category: Informational
April 1997

Basic Socket Interface Extensions for IPv6

Status of this Memo

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

Abstract

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

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 ループバックアドレス

4. インタフェース識別子
 4.1. 名前-to-インデックス
 4.2. インデックス-to-名前
 4.3. 全てのインタフェース名とインデックスの返却
 4.4. メモリ解放

5. ソケットオプション
 5.1. ソケットタイプの変更
 5.2. ユニキャストホップ制限
 5.3. マルチキャストパケットの送受信

6. ライブラリ関数
 6.1. ホスト名-to-アドレス変換
 6.2. アドレス-to-ホスト名変換
 6.3. プロトコル無依存のホスト名とサービス名の変換
 6.4. ソケットアドレス構造体-to-ホスト名/サービス名
 6.5. アドレス変換関数
 6.6. アドレステストマクロ

7. 新しい定義の要約

8. セキュリティの考慮

9. 謝辞

10. 参照

11. 作者のアドレス


1. 導入

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

2. 設計時の考慮事項

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

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

2.1. 変更する際の必要事項

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

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

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

ソケットインタフェースにおける名前−アドレス変換関数は、gethostbyname() と gethostbyaddr() である。これらは IPv6 をサポートする為に修正しなければならず、定義されたセマンティクスは、新しいアプリケーションに対して IPv6 をサポートするのと一緒に、既存の IPv4 アプリケーション全てとの下位互換を 100% 提供しなければならない。

加えて、POSIX 1003.g は新しいプロトコル無依存のホスト名−アドレス変換関数を規定する [4] を作成中である。この関数は、IPv6 でも又使用できる。

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

最後に、IPv6 をサポートする為に、幾つかの雑多な特徴が必要である。IPv6 のフローラベル、優先度、ホップ制限ヘッダフィールドをサポートするには、新しいインタフェースが必要である。IPv6 のマルチキャストパケットを送受信するには、新しいソケットオプションが必要である。

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

2.2. データ型

このメモで与えられた構造体要素のデータ型は、絶対的な要件ではなく、一例であることを意図している。可能であればいつでも、POSIX 1003.1g のデータ型が使用される。u_intN_t は正確に N ビット (例えば u_int16_t) の符号無し整数を意味し、u_intNm_t は少なくとも N ビット (例えば u_int32m_t) の符号無し整数を意味する。さらにパラメタのデータ型も、可能なときは 1003.1g から仮定する (例えば setsockopt() への最後のパラメタは size_t 型の値である等)。バッファサイズが指定されるときは常に、POSIX 1003.1 の size_t データ型が使用される (例えば getnameinfo() への 2 バイト長のパラメタ等)。

2.3. ヘッダ

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

2.4. 構造体

構造体を記述する時、そのメンバは実装に表れるはずのものでなければならない。加えて、非標準のメンバもまた実装によっては定義してもよい。

構造体のメンバの順番は、複数バイトメンバのアライメントを考慮して記述されることが推奨される。しかし、実装によってメンバが別々の順序であってもよい。

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 ソケットを生成することを指示するために、PF_INET6 は socket() 関数の第一パラメタで使用される。

3.2. IPv6 アドレス構造体

単一の IPv6 アドレスを保持する新しいデータ構造体は、以下の様に定義される。

  #include <netinet/in.h>

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

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

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

ソケットインタフェースでは、各々のプロトコルスイートのアドレスを運ぶ為に、異なるプロトコル固有のデータ構造体が定義される。各プロトコル固有のデータ構造体は、プロトコル無依存のデータ構造体「sockaddr」構造体にキャストできるよう設計される。各々は、sockaddr データ構造体の「sa_family」を上書きする「ファミリ」フィールドを持つ。このフィールドはデータ構造体の型を識別する。

sockaddr_in 構造体は、IPv4 のプロトコル固有アドレスデータ構造体である。これは、アプリケーションとソケット関数のあるシステム間で、アドレスを渡すのに使用される。以下の構造体が IPv6 アドレスを運ぶのために定義される。

#include <netinet/in.h>

struct sockaddr_in6 {
    u_int16m_t      sin6_family;    /* AF_INET6 */
    u_int16m_t      sin6_port;      /* トランスポート層のポート */
    u_int32m_t      sin6_flowinfo;  /* IPv6 フロー情報 */
    struct in6_addr sin6_addr;      /* IPv6 アドレス */
};

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

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

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

sin6_flowinfo フィールドは、24 ビットのフローラベルと 4 ビットの優先度フィールドの二個の情報を含む 32 ビットフィールドである。このメンバの内容と解釈は、ここでは規定しない。

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

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

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 ベースのシステムで使用するために提供される。

#include <netinet/in.h>

#define SIN6_LEN

struct sockaddr_in6 {
    u_char          sin6_len;       /* この構造体の長さ */
    u_char          sin6_family;    /* AF_INET6 */
    u_int16m_t      sin6_port;      /* トランスポート層のポート */
    u_int32m_t      sin6_flowinfo;  /* IPv6 フロー情報 */
    struct in6_addr sin6_addr;      /* IPv6 アドレス */
};

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

sockaddr_in6 データ構造体のこの版を提供するシステムは、SIN6_LEN を <netinet/in.h> ヘッダをインクルードした結果として宣言しなければならない。このマクロにより、アプリケーションが 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);

アプリケーションは、IPv6/TCP と IPv6/UDP ソケットを、第一パラメタとして PF_INET の代わりに PF_INET6 の定数を単に使うだけで生成してよい。例えば、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()

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

  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で説明されている様に)、これらのアドレスはしばしば、gethostbyname() 関数によって自動的に生成される。

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

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

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

bind() 関数は、アプリケーションが UDP パケットと TCP コネクションの送信元の IP アドレスを選択できるようにする一方、アプリケーションは、しばしばシステムにそれらに対する送信元アドレスを選択させたいと思う。IPv4 では、シンボリックな定数 INADDR_ANY (「ワイルドカード」アドレスと呼ばれる)を bind() 関数にパラメタ渡しするか、単に 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 つの形式 - グローバル変数とシンボリック定数で提供される。

グローバル変数は、in6_addr 構造体の「in6addr_loopback」という名前である。この変数の 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 アドレス変数が宣言される前に割当てに使用することはできない。

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

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

インタフェースは通常、「le0」「sl1」「ppp2」といった名前によって認識され、リンクされる。バークレイ版の実装では、インタフェースがシステムに認識される時、カーネルがユニークな正の整数値を (インタフェースインデックスと呼ばれる) インタフェースに割り当てる。これらは、1 から始まる (0 はインタフェースインデックスには決して使用されない) 小さい正の整数である。特定の正のインタフェースインデックスに対する現インタフェースが存在しないために、ギャップはあってもよい。

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

4.1. 名前-to-インデックス

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

  #include <net/if.h>

  unsigned int  if_nametoindex(const char *ifname);

もし指定されたインタフェースが存在しない場合、その戻り値は 0 である。

4.2. インデックス-to-名前

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

  #include <net/if.h>

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

ifname パラメタは、指定されたインデックスに対応するインタフェース名が返却される少なくとも IFNAMSIZ バイトのバッファをポイントしなければならない。(IFNAMSIZ も <net/if.h> で定義され、その値はインタフェース名の最後に付く終端 NULL のバイトを含む)。このポインタは、関数の戻り値でもある。指定されたインデックスに対応するインタフェースが存在しない場合は、NULL が返却される。

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

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

  #include <net/if.h>

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

  struct if_nameindex  *if_nameindex(void);

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

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

4.4. メモリ解放

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

  #include <net/if.h>

  void  if_freenameindex(struct if_nameindex *ptr);

この関数のパラメタは、if_nameindex() によって返却されたポインタでなければならない。

5. ソケットオプション

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

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

5.1. ソケットタイプの変更

Unix では、exec() 関数や他の手段によってオープンしたソケットをプロセス間に渡すことができる。オープンしたソケットを exec() 関数を通じて渡すことは、比較的共通のアプリケーション処理である。従って、元の API を使用しているアプリケーションが、オープンした PF_INET ソケットを、PF_INET6 ソケットを受け取ることを期待しているアプリケーションに渡すことは可能である。同様に、拡張 API を使用しているアプリケーションが、オープンした PF_INET6 ソケットを元の API を使用していて、PF_INET ソケットを扱うことのみ装備しているアプリケーションにに渡すことも可能である。オープンしたソケットを渡されるアプリケーションは、後続するソケット関数で返却されるアドレス構造体の符号化方法を知らないかもしれないため、これらのケースのどちらかは、問題を起こし得る。

この問題を改善するために、アプリケーションが PF_INET6 ソケットを PF_INET ソケットに、またはその逆に「変換」することを可能にする新しい setsockopt() オプションが定義される。

未知のプロセスからオープンしたソケットを渡される IPv6 アプリケーションは、IPV6_ADDRFORM の setsockopt() オプションをを使用して、ソケットを PF_INET6 に「変換」してもよい。一旦これが実行されたら、システムは後続するソケット関数で sockaddr_in6 アドレス構造体を返却するだろう。

オープンした PF_INET6 ソケットを IPv6 能力のないプログラムに渡そうとしている IPv6 アプリケーションは、exec() を呼ぶ前にソケットを PF_INET に「下げる」ことができる。その後システムは、exec() されたアプリケーションに sockaddr_in アドレス構造体を返却するだろう。もし既に IPv6 ソケットに割り当てられた全ての非ワイルドカードアドレスが IPv4 マッピングされた IPv6 アドレスでないならば、IPv6 ソケットを IPv4 ソケットに下げることはできないことは把握されたい。

IPV6_ADDRFORM オプションは、IPPROTO_IP と IPPROTO_IPV6 レベルの両方で有効である。有効なオプション値は PF_INET6 と PF_INET だけである。例えば、PF_INET6 ソケットを PF_INET に変換するには、プログラムは以下の様に呼ぶ。

    int    addrform = PF_INET;

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

オープンしたソケットが PF_INET6 ソケットの PF_INETであるか否かを知るために、アプリケーションは getsockopt() で IPV6_ADDRFORMを使用してもよい。例えば、

    int     addrform;
    size_t  len = sizeof(addrform);

    if (getsockopt(s, IPPROTO_IPV6, IPV6_ADDRFORM,
                      (char *) &addrform, &len) == -1)
        perror("getsockopt IPV6_ADDRFORM");
    else if (addrform == PF_INET)
        printf("This is an IPv4 socket.\n");
    else if (addrform == PF_INET6)
        printf("This is an IPv6 socket.\n");
    else
        printf("This system is broken.\n");

5.2. ユニキャストホップ制限

新しい 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");

IPV6_UNICAST_HOPS が setsockopt() で設定される時、与えられたオプションの値は、そのソケットを経由して送信される全ての後続するユニキャストパケットのホップ制限として使用される。もしこのオプションが設定されないんらば、システムはデフォルト値を選択する。整数のホップ制限値 (x と呼ぶ) は、以下の様に解釈される。

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

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

    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.3. マルチキャストパケットの送受信

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

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

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 のエラーを返却

パラメタの型: int

IPV6_MULTICAST_LOOP
送信された出力マルチキャストパケットが、自側アプリケーションに返送させるか否かを制御する。トグル。もしこのオプションに 1 が設定されたら、マルチキャストパケットはループバックする。もしこのオプションに 0 が設定されたら、ループバックしない。

パラメタの型: unsigned int

マルチキャストパケットの受信は、二つの setsockopt() オプションによって制御される。以下のように要約される。

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

パラメタの型: struct ipv6_mreq

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

パラメタの型: struct ipv6_mreq

これらのオプションの両方のパラメタの型は ipv6_mreq 構造体であり、以下のように定義される。

#include <netinet/in.h>

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

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

6. ライブラリ関数

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

6.1. ホスト名-to-アドレス変換

ポインタで返却される hostent 構造体は変更されるが、共通で使用される関数 gethostbyname() は変更されない。この関数を呼ぶ既存のアプリケーションは、A レコードのための DNS へのキュエリの結果として、IPv4 アドレスだけを受信する。(ここでは、DNS が使用されていると仮定する。ある環境では、ホストファイルか他の名前解決システムを使用しているかもしれず、そのうちどちらかは再番号付けを妨げるかもしれない。RES_USE_INET6 リゾルバオプションが設定されていないと仮定する。これは、より詳細に手短に説明する)。

IPv6 アドレスをサポートするために、新たに二つの変更がなされる。第一に、以下の関数が新しい。

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

    struct hostent *gethostbyname2(const char *name, int af);

af パラメタは、アドレスファミリを指定する。この関数のデフォルトの処理は簡単である。

追加機能を提供する二番目の変更は、新しいリゾルバオプション RES_USE_INET6 である。これは、<resolv.h> ヘッダをインクルードした結果として定義される。(このオプションは、BIND 4.9.4版で初めて提供される)。このオプションを設定する方法は三つある。

1) 一番目の方法

    res_init();
    _res.options |= RES_USE_INET6;

その後、gethostbyname() か gethostbyname2() を呼ぶ。この方法では、オプションはリゾルバを呼ぶプロセスにのみ効果がある。

2) 二番目の方法

RES_OPTIONS=inet6 の様に、環境変数 RES_OPTIONS を設定する (これは、Bourne, Korn シェルでの例である)。この方法では、この環境変数を参照する全てのプロセスに効果がある。

3) 三番目の方法

リゾルバ環境定義ファイル (通常 /etc/resolv.conf) に設定する。このホスト上の全てのアプリケーションに効果がある。そのホスト上の全てのアプリケーションが IPv6 アドレスを扱うことができるようになるまでは、この方法を適用してはならない。

これらの三つの方法には、優先度はない。RES_USE_INET6 オプションが設定される時、二つの変更が発生する。

アプリケーションは、返却された hostent 構造体中の 16 バイトアドレスを扱う準備がなされるまでは、RES_USE_INET6 オプションを可用にしてはならない。

以下のテーブルは、新しいリゾルバオプション RES_USE_INET6 と共に、既存の gethostbyname() 関数と新しい gethostbyname2() 関数の処理を要約する。

RES_USE_INET6 オプション
OFF
ON


gethostbyname
(host)

A レコードを検索する。

もし見つかったら、IPv4 アドレス (h_length=4) を返却する。
見つからなかったらエラー。

既存の全ての IPv4 アプリケーションとの互換性を提供する。

AAAA レコードを検索する。

もし見つかったら、IPv6 アドレス (h_length=16) を返却する。
見つからなかったら、A レコードを検索する。
もし見つかったら IPv4 マッピングされたIpv6 アドレス (h_length=16) を返却する。
見つからなかったらエラー。


gethostbyname2
(host, AF_INET)

A レコードを検索する。

もし見つかったら、IPv4 アドレス (h_length=4) を返却する。
見つからなかったらエラー。


A レコードを検索する。

もし見つかったら、IPv4 マッピングされた IPv6 アドレス (h_length=16) を返却する。
見つからなかったらエラー。


gethostbyname2
(host, AF_INET6)

AAAA レコードを検索する。

もし見つかったら、IPv6 アドレス (h_length=16) を返却する。 見つからなかったらエラー。

AAAA レコードを検索する。

もし見つかったら、IPv6 アドレス (h_length=16) を返却する。 見つからなかったらエラー。

現在 gethostbyname() を呼んでいる通常の純粋なアプリケーションを、IPv6 を使用するよう修正する場合、IPv6 ソケットを使用して、gethostbyname() を呼ぶ前に RES_USE_INET6 のリゾルバオプションを可用にするだけの単純なプログラム変更とすることが期待される。そうすれば、このアプリケーションは、IPv4 か IPv6 のペアのいずれかで動作するだろう。

gethostbyname() と gethostbyname2() は、両方とも静的な hostent 構造体へのポインタを返却するので、スレッド-セーフではない。しかし、幾つかのベンダは、スレッド-セーフな 4 つの追加パラメタを必要とする gethostbyname_r() 関数を定義した。これらのベンダが、gethostbyname2_r() 関数もまた定義することが期待される。

6.2. アドレス-to-ホスト名変換

既存の gethostbyaddr() 関数は、既にアドレスファミリパラメタが必要であり、よって IPv6 アドレスで動作可能である。

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

    struct hostent *gethostbyaddr(const char *src, int len, int af);

混乱が有り得るソースの一つは、IPv4 マッピングされた IPv6 アドレスと IPv4 互換の IPv6 アドレスの取り扱いである。これについては [6] で説明されており、以下のロジックで解決される。

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

  2. もし af が AF_INET ならば、in-addr.arpa ドメインに PTR レコードのキュエリを発行する。

  3. もし af が AF_INET6 ならば、ip6.int ドメインに PTR レコードのキュエリを発行する。

  4. もし関数が成功し、af が AF_INET で、RES_USE_INET6 オプションが設定されていたら、hostent 構造体 (関数の第一パラメタのコピー) で返却される単一のアドレスが、IPv4 マッピングされた IPv6 アドレスとして返却され、h_length メッンバには 16 が設定される。

四つのステップが全て順番通りに実行される。前のセクションの最後で言及した gethostbyname() のスレッド-セーフ版に関する同じ注が、ここでも適用される。

6.3. プロトコル無依存のホスト名とサービス名の変換

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

この関数の公式的な規約は、POSIX 最終標準になるだろう。POSIC 標準は無料で利用可能ではないため (IETF ドキュメントと同様)、これを独立した規約として提供する。この規約と POSIC 規約との間に何らかの相違があった場合、POSIC 規約が優先される。

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

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

addrinfo 構造体は、以下の様に定義される。

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

    struct addrinfo {
        int    ai_flags;           /* AI_PASSIVE, AI_CANONNAME */
        int    ai_family;          /* PF_xxx */
        int    ai_socktype;        /* SOCK_xxx */
        int    ai_protocol;        /* 0 か IPv4 と IPv6 を示す IPPROTO_xxx */
        size_t ai_addrlen;         /* ai_addr の長さ */
        char   *ai_canonname;      /* ホスト名の表記上の名前 */
        struct sockaddr  *ai_addr; /* バイナリのアドレス */
        struct addrinfo  *ai_next; /* リンクされたリストの次の構造体 */
    };

この関数の戻り値は、成功した場合は 0 であり、さもなくば 0 でないエラーコードである。以下の名前は、getaddrinfo() から返却される 0 でないエラーコードであり、<netdb.h> で定義される。

EAI_ADDRFAMILY ホスト名のアドレスファミリがサポートされていない
EAI_AGAIN 名前解決時の一時的失敗
EAI_BADFLAGS ai_flags に不正な値が指定された
EAI_FAIL 名前解決時の回復不能な失敗
EAI_FAMILY ai_family がサポートされていない
EAI_MEMORY メモリ割当て失敗
EAI_NODATA ホスト名に割り当てられたアドレスが存在しない
EAI_NONAME hostname も servname も指定されていないか、あるいは未知である。
EAI_SERVICE ai_socktype に対して servname がサポートされていない
EAI_SOCKTYPE ai_socktype がサポートされていない
EAI_SYSTEM errno で返却されたシステムエラー

hostname と servname パラメタは、NULL 終端文字列へのポインタか NULL である。これらのパラメタの片方、あるいは両方は、NULL でないポインタでなければならない。通常のクライアントのシナリオでは、hostname と servname の両方が指定される。NULL でない hostname の文字列は、ホスト名かホストアドレスの数字の文字列 (すなわち、ドット付き 10進整数の IPv4 アドレスか、IPv6 16進アドレス)のどちらかを指定できる。NULL でない servname の文字列は、サービス名か10進整数のポート番号のどちらかを指定できる。

呼び出し元は任意に、呼び出し元がサポートしているソケットタイプに関するヒントを提供するために、addrinfo 構造体を第三パラメタでポイントして渡すことができる。この hints 構造体では、ai_flags, ai_family, ai_socktype, ai_protocol 以外の全てのメンバは、0 か NULL ポインタでなければならない。ai_family の PF_UNSPEC の値は、呼び出し元がいかなるプロトコルファミリも受け入れることを意味する。ai_socktype に 0 を指定することは、呼び出し元がいかなるソケットタイプも受け入れることを意味する。ai_protocol に 0 を指定することは、呼び出し元がいかなるプロトコルも受け入れることを意味する。例えば、もし呼び出し元が UDP ではなく TCP を扱う場合、getaddrinfo() を呼ぶ時、hints 構造体の ai_socktype メンバには SOCK_STREAM を設定しなければならない。もし呼び出し元が IPv6 ではなく IPv4 のみを扱う場合、、getaddrinfo() を呼ぶ時、hints 構造体の ai_family メンバには PF_INET を設定しなければならない。もし getaddrinfo() の第三パラメタが NULL ポインタならば、呼び出し元が、ai_family が PF_UNSPEC に設定され、0 に初期化された addrinfo 構造体を設定したことと同じである。

成功時は、最終パラメタを通じて返却される一つ以上の addrinfo 構造体のリンクされたリストへのポインタを返却する。呼び出し元は、ai_next ポインタを追うことによって、NULL ポインタが現れるまで、このリスト中の各々の addrinfo 構造体を処理することができる。各々の返却された addrinfo 構造体において、ai_family, ai_socktype, ai_protocol の三つのメンバが、socket() 関数の呼び出しに対応したパラメタである。各々の addrinfo 構造体において、ai_addr メンバは ai_addrlen メンバによって長さが指定された、値の設定されたソケットアドレス構造体をポイントする。

hints 構造体の ai_flags メンバに AI_PASSIVE ビットが設定されたら、呼び出し元は返却されたソケットアドレス構造体を bind() の呼び出しで使用する予定がある。この場合、hostname パラメタが NULL ならば、ソケットアドレス構造体の IP アドレスの位置には、IPv4 アドレスの INADDR_ANY か IPv6 アドレスの IN6ADDR_ANY_INIT が設定されるだろう。

hints 構造体の ai_flags メンバに AI_PASSIVE ビットが設定されないならば、返却されたソケットアドレス構造体は connect() (コネクション型のプロトコルの場合) か、connect(), sendto(), sendmsg() (コネクションレス型のプロトコルの場合) の呼び出しに対して準備される。この場合、hostname パラメタが NULL ならば、ソケットアドレス構造体の IP アドレスの位置には、ループバックアドレスが設定されるだろう。

hints 構造体の ai_flags メンバに AI_CANONNAME ビットが設定されたら、関数の成功時に、リンクされたリストの第一番目の addrinfo 構造体の ai_canonname メンバは、指定された hostname の表記上の名前を含む NULL 終端文字列をポイントする。

getaddrinfo() によって返却される全ての情報、addrinfo 構造体やソケットアドレス構造体、addrinfo 構造体によってポイントされる表記上のホスト名の文字列は、動的に割り当てられる。この情報をシステムに返却するには、freeaddrinfo() 関数を呼ぶ。

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

    void freeaddrinfo(struct addrinfo *ai);

ai パラメタによってポイントされた addrinfo 構造体が、この構造体によってポイントされた動的獲得領域と共に解放される。この処理は、ai_next ポインタが NULL になるまで繰り返される。

getaddrinfo() によって返却される EAI_xxx コードに基づくエラーメッセージを表示する際にアプリケーションを助けるために、以下の関数が定義される。

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

    char *gai_strerror(int ecode);

このパラメタは前に定義された EAI_xxx 値の中の一つであり、その戻り値はエラーを説明する文字列をポイントする。もしこのパラメタが EAI_xxx 値の一つでないならば、この関数は内容が未知のエラーを示す文字列へのポインタを返却する。

6.4. ソケットアドレス構造体-to-ホスト名/サービス名

POSIX 1003.1g 規約は、ホスト名とサービス名を検索してバイナリのアドレスとポートを与えるような、getaddrinfo() からの逆変換を実現する関数を含んでいない。従って、以下の関数を定義する。

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

    int getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
             size_t hostlen, char *serv, size_t servlen, int flags);

この関数は、DNS やシステム固有のデータベースの中に、呼び出し元によって提供された IP アドレスとポート番号を検索し、呼び出し元によって提供されたバッファに両方の文字列を返却する。この関数は戻り値として 0 を返却することによって成功完了を示し、失敗を 0 でない値を返却することによって示す。

第一パラメタ sa は、IP アドレスとポート番号を保持する sockaddr_in 構造体 (IPv4) か sockaddr_in6 構造体 (IPv6)のどちらかをポイントする。salen パラメタは、sockaddr_in か sockaddr_in6 構造体の長さを指定する。

この関数は、host パラメタによってポイントされたバッファに、IP アドレスに割り当てられたホスト名を返却する。呼び出し元は、hostlen パラメタによってこのバッファのサイズを提供する。ポート番号に割り当てられたサービス名は、serv によってポイントされたバッファに返却され、servlen パラメタがこのバッファの長さを提供する。呼び出し元は、hostlen か servlen パラメタに 0 の値を指定することによって、どちらかの文字列を返却させないことを指定する。

不幸にも大半のシステムは、完全ドメイン名かサービス名の最大長を示す定数を提供していない。従って、これらの二つの返却文字列を格納するバッファを割り当てる際にアプリケーションを補助するために、以下の定数が <netdb.h> で定義される。

    #define  NI_MAXHOST    1025
    #define  NI_MAXSERV    32

最初の値は、定数 MAXDNAME として BIND の <arph/nameser.h> ヘッダの最近のバージョンで実際に定義されている (BIND の旧バージョンは、この定数を 256 で定義している)。二番目の値は、現在の番号割当て RFC でリスト化されたサービスに基づいた推量である。

最後のパラメタは、この関数のデフォルト動作を変更するフラグである。デフォルトでは、ホストの完全ドメイン名 (FQDN: fully-qualified domain name)が DNS から検索され、返却される。もし、このフラグのビット NI_NOFQDN が設定されたら、FQDN のホスト名の部分のみがローカルホストとして返却される。

もし、このフラグのビット NI_NUMERICHOST が設定されたら、あるいはもしホスト名が DNS 中に位置づけられないならば、ホストアドレスの名前の形式の代わりに数字の形式が返却される (gethostbyaddr() の代わりに inet_ntop) を呼ぶことによって等)。このフラグのビット NI_NAMEREQD が設定されたら、もしホスト名が DNS 中に位置づけられないならばエラーが返却される。

もし、このフラグのビット NI_NUMERICSERV が設定されたら、サービスアドレスの名前の代わりに数字の形式 (つまりポート番号)が返却される。二つの NI_NUMERICxxx フラグは、多くのコマンドが提供している "-n" フラグをサポートするために必要である。

5 番目のフラグビットである NI_DGRAM は、サービスがデータグラムサービスであることを指定し、getservbyport() の第二パラメタでデフォルトの "tcp" の代わりに "udp" が指定されて呼ばれる。これは、UDP と TCP で異なるサービスを持つ 2,3 個のポートで必要である。

これらの NI_xxx フラグは、getaddrinfo() で既に定義された AI_xxx フラグと共に<netdb.h> で定義される

6.5. アドレス変換関数

inet_addr() と inet_ntoa() の二つの関数は、IPv4 アドレスをバイナリとテキストの形式間を変換する。IPv6 アプリケーションは、同様な関数が必要である。以下の二つの関数は、IPv6 と IPv4 アドレスの両方を変換する。

    #include <sys/socket.h>
    #include <arpa/inet.h>

    int inet_pton(int af, const char *src, void *dst);
    const char *inet_ntop(int af, const void *src, char *dst, size_t size);

inet_pton() 関数は、標準テキスト表現形式のアドレスを、数字のバイナリ形式に変換する。af パラメタはアドレスのファミリを指定する。現在、 AF_INET と AF_INET6 アドレスファミリがサポートされている。src パラメタは渡される文字列をポイントする。dst パラメタは、この関数が数字のアドレスを格納するバッファをポイントする。そのアドレスは、ネットワークバイトオーダで返却される。inet_pton は、もし変換が成功したら 1 を返却し、もし入力が正しい IPv4 のドット付き整数文字列か正しい IPv6 アドレス文字列でないならば 0 を返却し、af パラメタが未知ならば errno に EAFNOSUPPORT が設定された -1 が返却される。呼び出し元のアプリケーションは、dst に参照されているバッファが数字のアドレスを保持するのに十分な大きさであることを保証しなければならない (AF_INET では 4 バイトで AF_INET6 では 16 バイト)。

もし af パラメタが AF_INET ならば、この関数は標準の IPv4 ドット付き整数の形式で文字列を受け入れる。

  ddd.ddd.ddd.ddd

ddd は、0 から 255 までの 1 個から 3 個の 10 進整数である。既存の inet_addr() と inet_aton() 関数の多くの実装は、非標準の入力: 8進数字や 16進数字、4つ以下の数字を受け入れる。inet_pton() は、これらの形式を受け入れない。

もし af パラメタが AF_INET6 ならば、この関数は、アドレス体系規定 [2]セクション 2.2 で定義されている標準の IPv6 テキスト形式の 1 つの文字列を受け入れる。

inet_ntop() 関数は、数字のアドレスを表現するのに適切なテキスト文字列に変換する。これは、AF_INET も AF_INET6 もとり得る。src パラメタは渡される文字列をポイントする。src パラメタは、もし af パラメタが AF_INET であるならば IPv4 アドレスを保持するバッファを、af パラメタが AF_INET6 であるならば IPv6 アドレスをポイントする。dst パラメタは、関数がテキスト文字列を結果として格納するバッファをポイントする。size パラメタは、このバッファのサイズを指定する。アプリケーションは、dst パラメタに NULL でない値を指定しなければならない。IPv6 アドレスでは、バッファは少なくとも 46 オクテットなければならない。アプリケーションが、IPv4 と IPv6 アドレスを文字列で格納するための適切なバッファのサイズを容易に宣言できるよう、以下の 2 つの定数が <netinet/in.h> で定義される。

    #define  INET_ADDRSTRLEN    16
    #define  INET6_ADDRSTRLEN   46

net_ntop() 関数は、もし変換が成功したらテキスト文字列を含んでいるバッファのポインタを返却し、もし失敗したら NULL を返却する。失敗時には、もし af パラメタが不正であれば errno に EAFNOSUPPORT が設定され、結果を格納するバッファのサイズが不十分であれば ENOSPC を設定する。

6.6. アドレステストマクロ

以下のマクロは、特別な IPv6 アドレスをテストするために使用される。

    #include <netinet/in.h>

    int  IN6_IS_ADDR_UNSPECIFIED (const struct in6_addr *);
    int  IN6_IS_ADDR_LOOPBACK    (const struct in6_addr *);
    int  IN6_IS_ADDR_MULTICAST   (const struct in6_addr *);
    int  IN6_IS_ADDR_LINKLOCAL   (const struct in6_addr *);
    int  IN6_IS_ADDR_SITELOCAL   (const struct in6_addr *);
    int  IN6_IS_ADDR_V4MAPPED    (const struct in6_addr *);
    int  IN6_IS_ADDR_V4COMPAT    (const struct in6_addr *);

    int  IN6_IS_ADDR_MC_NODELOCAL(const struct in6_addr *);
    int  IN6_IS_ADDR_MC_LINKLOCAL(const struct in6_addr *);
    int  IN6_IS_ADDR_MC_SITELOCAL(const struct in6_addr *);
    int  IN6_IS_ADDR_MC_ORGLOCAL (const struct in6_addr *);
    int  IN6_IS_ADDR_MC_GLOBAL   (const struct in6_addr *);

最初の 7 つのマクロは、もしアドレスが指定されたタイプのアドレスならば true を返却し、さもなくば false を返却する。最後の 5 つのマクロはマルチキャストアドレスのスコープをテストし、もしアドレスが指定されたスコープのマルチキャストアドレスであるならば true を、アドレスがマルチキャストアドレスでも指定されたスコープのマルチキャストアドレスでもないならば false を返却する。

7. 新しい定義の要約

以下のリストは、このメモで論じられ、ヘッダに格納されている定数、構造体、外部宣言を要約する。

  <net/if.h>   IFNAMSIZ
  <net/if.h>   struct if_nameindex{};

  <netdb.h>    AI_CANONNAME
  <netdb.h>    AI_PASSIVE
  <netdb.h>    EAI_ADDRFAMILY
  <netdb.h>    EAI_AGAIN
  <netdb.h>    EAI_BADFLAGS
  <netdb.h>    EAI_FAIL
  <netdb.h>    EAI_FAMILY
  <netdb.h>    EAI_MEMORY
  <netdb.h>    EAI_NODATA
  <netdb.h>    EAI_NONAME
  <netdb.h>    EAI_SERVICE
  <netdb.h>    EAI_SOCKTYPE
  <netdb.h>    EAI_SYSTEM
  <netdb.h>    NI_DGRAM
  <netdb.h>    NI_MAXHOST
  <netdb.h>    NI_MAXSERV
  <netdb.h>    NI_NAMEREQD
  <netdb.h>    NI_NOFQDN
  <netdb.h>    NI_NUMERICHOST
  <netdb.h>    NI_NUMERICSERV
  <netdb.h>    struct addrinfo{};

  <netinet/in.h>  IN6ADDR_ANY_INIT
  <netinet/in.h>  IN6ADDR_LOOPBACK_INIT
  <netinet/in.h>  INET6_ADDRSTRLEN
  <netinet/in.h>  INET_ADDRSTRLEN
  <netinet/in.h>  IPPROTO_IPV6
  <netinet/in.h>  IPV6_ADDRFORM
  <netinet/in.h>  IPV6_ADD_MEMBERSHIP
  <netinet/in.h>  IPV6_DROP_MEMBERSHIP
  <netinet/in.h>  IPV6_MULTICAST_HOPS
  <netinet/in.h>  IPV6_MULTICAST_IF
  <netinet/in.h>  IPV6_MULTICAST_LOOP
  <netinet/in.h>  IPV6_UNICAST_HOPS
  <netinet/in.h>  SIN6_LEN
  <netinet/in.h>  extern const struct in6_addr in6addr_any;
  <netinet/in.h>  extern const struct in6_addr in6addr_loopback;
  <netinet/in.h>  struct in6_addr{};
  <netinet/in.h>  struct ipv6_mreq{};
  <netinet/in.h>  struct sockaddr_in6{};

  <resolv.h>   RES_USE_INET6

  <sys/socket.h>  AF_INET6
  <sys/socket.h>  PF_INET6

以下のリストは、このメモで論じられ、ヘッダに格納されている関数やマクロのプロトタイプを要約する。

  <arpa/inet.h> int inet_pton(int, const char *, void *);
  <arpa/inet.h> const char *inet_ntop(int, const void *, 
                            char *, size_t);

  <net/if.h>  char  *if_indextoname(unsigned int, char *);
  <net/if.h>  unsigned int if_nametoindex(const char *);
  <net/if.h>  void if_freenameindex(struct if_nameindex *);
  <net/if.h>  struct if_nameindex *if_nameindex(void);

  <netdb.h>   int getaddrinfo(const char *, const char *,
                        const struct addrinfo *, struct addrinfo **);
  <netdb.h>   int getnameinfo(const struct sockaddr *, size_t,
                        char *, size_t, char *, size_t, int);
  <netdb.h>   void  freeaddrinfo(struct  addrinfo *);

  <netdb.h>   char *  gai_strerror(int);
  <netdb.h>   struct hostent *gethostbyname(const char *);
  <netdb.h>   struct hostent *gethostbyaddr(const char *, int, int);
  <netdb.h>   struct hostent *gethostbyname2(const char *, int);

  <netinet/in.h>  int IN6_IS_ADDR_LINKLOCAL(const struct in6_addr *);
  <netinet/in.h>  int IN6_IS_ADDR_LOOPBACK(const struct in6_addr *);
  <netinet/in.h>  int IN6_IS_ADDR_MC_GLOBAL(const struct in6_addr *);
  <netinet/in.h>  int IN6_IS_ADDR_MC_LINKLOCAL(const struct in6_addr *);
  <netinet/in.h>  int IN6_IS_ADDR_MC_NODELOCAL(const struct in6_addr *);
  <netinet/in.h>  int IN6_IS_ADDR_MC_ORGLOCAL(const struct in6_addr *);
  <netinet/in.h>  int IN6_IS_ADDR_MC_SITELOCAL(const struct in6_addr *);
  <netinet/in.h>  int IN6_IS_ADDR_MULTICAST(const struct in6_addr *);
  <netinet/in.h>  int IN6_IS_ADDR_SITELOCAL(const struct in6_addr *);
  <netinet/in.h>  int IN6_IS_ADDR_UNSPECIFIED(const struct in6_addr *);
  <netinet/in.h>  int IN6_IS_ADDR_V4COMPAT(const struct in6_addr *);
  <netinet/in.h>  int IN6_IS_ADDR_V4MAPPED(const struct in6_addr *);

8. セキュリティの考慮

IPv6 は数多くの新しいセキュリティメカニズムを提供し、その多くはアプリケーションにアクセス可能である必要がある。IPv6 セキュリティをサポートするソケットインタフェースの拡張を扱っている手引きのメモは、[3] にて記されている。

9. 謝辞

このドキュメントに対し、数多くの改訂へのフィードバックを提案/提供された多くの人に感謝する。次の方々を含む。Werner Almesberger, Ran Atkinson, Fred Baker, Dave Borman, Andrew Cherenson, Alex Conta, Alan Cox, Steve Deering, Richard Draves, Francis Dupont, Robert Elz, Marc Hasson, Tim Hartrick, Tom Herbert, Bob Hinden, Wan-Yen Hsu, Christian Huitema, Koji Imada, Markus Jork, Ron Lee, Alan Lloyd, Charles Lynn, Jack McCann, Dan McDonald, Dave Mitton, Thomas Narten, Erik Nordmark, Josh Osborne, Craig Partridge, Jean-Luc Richier, Erik Scoredos, Keith Sklower, Matt Thomas, Harvey Thompson, Dean D. Throop, Karen Tracey, Glenn Trewitt, Paul Vixie, David Waitzman, Carl Williams, Kazuhiko Yamamoto。

getaddrinfo() と getnameinfo() 関数は、Keith Sklower によって進められた以前の作業から取り込まれている。ドキュメントで注記しているように、William Durst, Steven Wise, Michael Karels, Eric Allman は、プロトコル無依存の名前-アドレス変換の課題について多くの役立つ議論を提供し、Keith Sklower の元の提案である前のバージョンをレビューしてくれた。Eric Allman は、getaddrinfo() の最初のプロトタイプを実装した。名前とサービスのペアを規定することはプロトコル無依存のサービスに結び付けることに対し十分であるという見解は、Marshall Rose により「一定ネットワークインタフェース (Uniform Network Interface)」に関する X/Open への提案でなされた。

Craig Metzは、このドキュメントに対し多くの寄書を提供してくれた。Ramesh Govindan は、このメモの前バージョンに対して多くの寄書を提供し、共同で編集してくれた。

10. 参照

[1] Deering, S., and R. Hinden, "Internet Protocol, Version 6 (IPv6) Specification", RFC 1883, December 1995.

[2] Hinden, R., and S. Deering, "IP Version 6 Addressing Architecture", RFC 1884, December 1995.

[3] McDonald, D., "A Simple IP Security API Extension to BSD Sockets", Work in Progress.

[4] IEEE, "Protocol Independent Interfaces", IEEE Std 1003.1g, DRAFT 6.3, November 1995.

[5] Stevens, W., and M. Thomas, "Advanced Sockets API for IPv6", Work in Progress.

[6] Vixie, P., "Reverse Name Lookups of Encapsulated IPv4 Addresses in IPv6", Work in Progress.

11. 作者のアドレス

  Robert E. Gilligan
  Freegate Corporation
  710 Lakeway Dr. STE 230
  Sunnyvale, CA 94086
  Phone: +1 408 524 4804
  EMail: gilligan@freegate.net

  Susan Thomson
  Bell Communications Research
  MRE 2P-343, 445 South Street
  Morristown, NJ 07960
  Phone: +1 201 829 4514
  EMail: set@thumper.bellcore.com

  Jim Bound
  Digital Equipment Corporation
  110 Spitbrook Road ZK3-3/U14
  Nashua, NH 03062-2698
  Phone: +1 603 881 0400
  Email: bound@zk3.dec.com

  W. Richard Stevens
  1202 E. Paseo del Zorro
  Tucson, AZ 85718-2826
  Phone: +1 520 297 9416
  EMail: rstevens@kohala.com