#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /*
上記のスーパーセット
*/
tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
raw_socket = socket(AF_INET, SOCK_RAW,
protocol);
Linux は RFC 791 と RFC 1122 で記述されている Internet Protocol, version 4 を実装している。 ip には RFC 1112 に準拠した level 2 マルチキャストの実装が含まれている。 またパケットフィルタ機能を含む IP ルーターも実装されている。
プログラミング・インターフェースは BSD ソケットと互換である。 ソケットに関するより詳細な情報は socket(7) を参照のこと。
IP ソケットは、 socket(2) 関数を socket(AF_INET, socket_type, protocol) のように呼び出すことで生成される。指定できるソケットタイプは 3 つあり、 tcp(7) ソケットをオープンする場合 SOCK_STREAM、 udp(7) ソケットをオープンする場合 SOCK_DGRAM、 IP プロトコルに直接アクセスする ために raw(7) ソケットをオープンする場合には SOCK_RAW である。 protocol は送受信される IP ヘッダに書かれる IP プロトコルである。 指定できる値は、 TCP ソケットには 0 か IPPROTO_TCP、 UDP ソケットには 0 か IPPROTO_UDP に限られる。 SOCK_RAW に対しては、 RFC 1700 で定義 されている有効な IANA IP プロトコルを、 割り当てられている番号で指定する ことができる。
あるプロセスで、やってくるパケットを受信したり 接続要求を受けたりしたい場合には、 そのプロセスはローカルなインターフェースアドレスに、 bind(2) を用いてソケットをバインドしなければならない。 この場合、 ローカルの「アドレスとポート」のペアに対してバインドできる IP ソケットは一つだけである。 bind(2) の呼び出しで INADDR_ANY が指定されていた場合は、 ソケットは すべて のローカルインターフェースにバインドされる。 listen(2) がバインドされていないソケットに対してコールされると、 そのソケットは、 ローカルポートはランダムに選択された空いているポートで、 ローカルアドレスは INADDR_ANY で自動的にバインドされる。 connect(2) がバインドされていないソケットに対してコールされると、 そのソケットは、 ローカルポートはランダムに選択された空いているポートか未使用の共有ポートで、 ローカルアドレスは INADDR_ANY で自動的にバインドされる。
SO_REUSEADDR フラグがセットされていない場合には、 バインドされていた TCP ローカルソケットアドレスは クローズされた後しばらくの間使えなくなる。 SO_REUSEADDR フラグを使うと TCP の信頼性を低下させるので、 使うときには注意が必要である。
IP ソケットアドレスは、 IP インターフェースアドレスと 16ビットのポート番号の組み合わせで定義される。 IP プロトコルそのものはポート番号を扱わない。 ポート番号は、 udp(7) や tcp(7) といった、上位のプロトコルで実装される。 raw ソケットでは、 sin_port が IP プロトコルにセットされる。
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
sin_familiy には常に AF_INET をセットする。これは必須である。 Linux 2.2 では、このセットを忘れると ほとんどのネットワーク関数は EINVAL を返すようになっている。 sin_port にはポート番号をネットワークバイトオーダーで指定する。 1024 未満のポート番号は 特権ポート (privileged ports) と呼ばれる (予約ポート (reserved ports) とも時々呼ばれる)。 特権プロセス (CAP_NET_BIND_SERVICE ケーパビリティを持つプロセス) 以外のプロセスは、これらのポートには bind(2) できない。 IPv4 プロトコルそのものにはポートに関する概念がない。ポートが実装されるのは、 tcp(7) や udp(7) といった、上位のプロトコルにおいてのみである。
sin_addr は IP ホストアドレスである。 struct in_addr の s_addr メンバには、ホストのインターフェースアドレスを ネットワークバイトオーダーで 指定する。 in_addr は、INADDR_* の一つ (例えば INADDR_ANY) を代入する、 ライブラリ関数 inet_aton(3), inet_addr(3), inet_makeaddr(3) を用いる、 あるいは名前解決機構 (name resolver) を直接用いる、のどれかで設定すべきである。 (gethostbyname(3) を見よ)。
IPv4 アドレスには、ユニキャストアドレス、 ブロードキャストアドレス、マルチキャストアドレスがある。 ユニキャストアドレスは、あるホストの一つのアドレスを指定する。 ブロードキャストアドレスは、あるネットワーク上の全てのホストを指定する。 マルチキャストアドレスは、マルチキャストグループに所属する 全てのホストを指定する。ブロードキャストアドレスへのデータグラムは、 SO_BROADCAST ソケットフラグがセットされていないと送信・受信できない。 現在の実装では、接続指向のソケットにはユニキャストアドレスしか使えない。
アドレスとポートは常にネットワークバイトオーダーで格納されることに注意せよ。 具体的には、ポートを指定する数値には htons(3) を呼び出す必要がある。 標準ライブラリにあるアドレス/ポート操作関数は すべてネットワークバイトオーダーで動作する。
特別なアドレスがいくつか存在する: INADDR_LOOPBACK(127.0.0.1) は loopback デバイスを通して常にローカルなホストを参照する。 INADDR_ANY(0.0.0.0) は任意のアドレスを意味し、バインド用である。 INADDR_BROADCAST(255.255.255.255) は任意のホストを意味し、歴史的理由から、バインドの際には INADDR_ANY と同じ効果になる。
IP にはプロトコル固有のソケットオプションがいくつか存在し、 setsockopt(2) で設定が、 getsockopt(2) で取得ができる。 IP のソケットオプションレベルは IPPROTO_IP である。 ブール整数値のフラグでは、 0 は偽、それ以外は真を意味する。
struct ip_mreqn {
struct in_addr imr_multiaddr; /* IP multicast group
address */
struct in_addr imr_address; /* IP address of local
interface */
int imr_ifindex; /* interface index */
};
imr_multiaddr には、アプリケーションが参加または脱退したい マルチキャストグループのアドレスが入る。 指定するアドレスは有効なマルチキャストアドレスでなければならない (さもなければ setsockopt(2) がエラー EINVAL で失敗する)。 imr_address はシステムがマルチキャストグループに参加する際に用いる ローカルなインターフェースのアドレスである。 これが INADDR_ANY であった場合には、適切なインターフェースがシステムによって選択される。 imr_ifindex は imr_multiaddr グループに参加/脱退するインターフェースの interface index である。 どのインターフェースでもよい場合は 0 にする。
struct ip_mreq_source {
struct in_addr imr_multiaddr; /* IP multicast group
address */
struct in_addr imr_interface; /* IP address of local
interface */
struct in_addr imr_sourceaddr; /* IP address of
multicast source */
};
ip_mreq_source 構造体は IP_ADD_MEMBERSIP の項で説明した ip_mreqn に似ている。 imr_multiaddr フィールドには、アプリケーションが参加または脱退したいマルチキャストグループのアドレスが入る。 imr_interface フィールドは、 マルチキャストグループに参加する際に システムが使用すべきローカルインターフェースのアドレスである。 imr_sourceaddr フィールドには、アプリケーションがデータを受信したい送信元のアドレスが入る。
struct ip_msfilter {
struct in_addr imsf_multiaddr; /* IP multicast group
address */
struct in_addr imsf_interface; /* IP address of local
interface */
uint32_t imsf_fmode; /* Filter-mode */
uint32_t imsf_numsrc; /* Number of sources in
the following array */
struct in_addr imsf_slist[1]; /* Array of source
addresses */
};
MCAST_INCLUDE と MCAST_EXCLUDE の 2 つのマクロがあり、 フィルタリングモードを指定するのに使用できる。 また、 IP_MSFILTER_SIZE(n) マクロがあり、 送信元リストに n 個の送信元が入った ip_msfilter 構造体を格納するのに必要なメモリ量を判定することができる。
システム全体のデフォルトは IP_PMTUDISC_WANT と IP_PMTUDISC_DONT の どちらかに設定することができる。設定の変更は、 /proc/sys/net/ipv4/ip_no_pmtu_disc ファイルに、0 (IP_PMTUDISC_WANT) か 0 以外 (IP_PMTUDISC_DONT) を書き込むことで行う。
| Path MTU discovery 値 | 意味 |
| IP_PMTUDISC_WANT | ルートごとの設定を用いる。 |
| IP_PMTUDISC_DONT | Path MTU Discovery を行わない。 |
| IP_PMTUDISC_DO | 常に Path MTU Discovery を行う。 |
| IP_PMTUDISC_PROBE | DFビットをセットするが、Path MTU を無視する。 |
path MTU discovery が有効になっていると、カーネルは宛先ホストごとに 自動的に path MTU を処理する。特定の相手に connect(2) で接続した場合には、 IP_MTU ソケットオプションを用いれば、既知の path MTU の取得に便利である (たとえば EMSGSIZE エラーが起きた後など)。 path MTU は時間とともに変化する かもしれない。 宛先がたくさんあるコネクションレスなソケットでは、 与えられた 宛先に対する新しい MTU にも、 エラーキューを用いてアクセスすることができる (IP_RECVERR を見よ)。 MTU 更新が到着するごとに、新たなエラーがキューイング される。
MTU discovery の進行中には、データグラムソケットからの初期パケットは 到着しないかもしれない。 UDP を用いるアプリケーションでは、 このことを気にかけておき、 パケットの再送アルゴリズムにこの分を除外させるべきである。
接続していないソケットに対して path MTU discovery プロセスを立ち上げるには、 大きなデータグラムサイズ (最大 64K ヘッダバイト長) からはじめて、 path MTU が更新されるまでサイズを縮めていくことも可能である。
path MTU の値をまず見積もってみるには、宛先アドレスに connect(2) を使ってデータグラムソケットを接続し、 getsockopt(2) を IP_MTU オプションとともに呼び、 MTU を取得することである。
IP_PMTUDISC_PROBE (Linux 2.6.22 以降で利用可能) を設定することで、 SOCK_DGRAM や SOCK_RAW のソケットで RFC 4821 の MTU 探索を実装することが可能である。 また、この機能は、 tracepath(8) のような診断ツールで特に有用である。これらのツールでは、 観測された Path MTU よりも大きな探索パケットを意図的に 送信しようとする。
struct in_pktinfo {
unsigned int ipi_ifindex; /* Interface index */
struct in_addr ipi_spec_dst; /* Local address */
struct in_addr ipi_addr; /* Header Destination
address */
};
#define SO_EE_ORIGIN_NONE 0
#define SO_EE_ORIGIN_LOCAL 1
#define SO_EE_ORIGIN_ICMP 2
#define SO_EE_ORIGIN_ICMP6 3
struct sock_extended_err {
uint32_t ee_errno; /* error number */
uint8_t ee_origin; /* where the error originated */
uint8_t ee_type; /* type */
uint8_t ee_code; /* code */
uint8_t ee_pad;
uint32_t ee_info; /* additional information */
uint32_t ee_data; /* other data */
/* More data may follow */
};
struct sockaddr *SO_EE_OFFENDER(struct sock_extended_err *);
IP プロトコルでは、いくつかのグローバルパラメータを設定するための /proc ファイル群が用意されている。 これらのパラメータには、 /proc/sys/net/ipv4/ ディレクトリ内のファイルの読み書きでアクセスできる。 Boolean と書かれたインタフェースは整数値をとり、 0 以外の値 ("true") は対応するオプションが有効、 0 値 ("false") は無効、であることを意味する。
このブール値のフラグが有効になっている (0 以外になっている) と、 到着したフラグメント (IP パケットの一部で、 発信元と発信先の間のどこかのホストで、そのパケットが 大きすぎると判断され、分割された場合に生じる) は、たとえフォワードされる場合であっても 処理前に再構築 (デフラグメント) される。
ファイアウォールがローカル側のネットワークに唯一のリンクを持っている 場合や、透過プロクシの場合に限って有効にすべきである。 通常のルーターやホストでは決して使用することのないように。 さもないとフラグメントが別のリンクを経由して伝わる場合に、 通信のフラグメント化ができなくなってしまう。 またフラグメント再構築処理はメモリと CPU 時間のコストが非常に大きい。
これはマスカレードや透過プロクシが設定されると、 不思議な仕組みによって自動的に有効になる。
socket(7) に記述されている ioctl は、すべて ip にも適用される。
ジェネリックデバイスのパラメータを設定する ioctl については netdevice(7) に記述されている。
他のエラーが上層のプロトコルによって生じるかもしれない。 tcp(7), raw(7), udp(7), socket(7) などを参照のこと。
IP_FREEBIND, IP_MSFILTER, IP_MTU, IP_MTU_DISCOVER, IP_RECVORIGDSTADDR, IP_PKTINFO, IP_RECVERR, IP_ROUTER_ALERT, and IP_TRANSPARENT は Linux 固有である。
SO_BROADCAST オプションの利用には、くれぐれも注意すること。 これは Linux では特権操作ではない。 不注意なブロードキャストを行うと、ネットワークは簡単に過負荷状態になる。 新しいアプリケーションプロトコルには、ブロードキャストではなく マルチキャストグループを用いるほうがよい。 ブロードキャストは推奨されない。
他の BSD のソケット実装では、 IP_RCVDSTADDR と IP_RECVIF といったソケットオプションがサポートされており、 宛先アドレスや受信データグラムのインターフェースが取得できるように なっていることもある。 Linux で同じことをやらせるには、より一般的な IP_PKTINFO が使える。
いくつかの BSD のソケット実装では IP_RECVTTL オプションも提供されているが、タイプ IP_RECVTTL の補助メッセージは受信パケットとともに渡される。 これは Linux で使われている IP_TTL オプションとは異なる動作である。
SOL_IP ソケットオプションレベルは移植性がない。 BSD ベースのプロトコルスタックでは IPPROTO_IP レベルが使用されている。
Linux 2.0 との互換性のために、 obsolete な socket(AF_INET, SOCK_PACKET, protocol) という書式でも packet(7) をオープンできるようになっているが、これはお勧めできない。今後は socket(AF_PACKET, SOCK_RAW, protocol) を代わりに用いるべきである。主な違いは、ジェネリックなリンク層用の sockaddr_ll アドレス構造体が、古い sockaddr_pkt に変わって用いられるようになったことである。
エラーの値がまったく首尾一貫していない。
IP 固有のインターフェースオプションを指定するための ioctl と ARP テーブルのことが記述されていない。
glibc のバージョンによっては in_pktinfo の定義を忘れているものがある。 現時点でのとりあえずの対策としては、この man ページにある定義をプログラム中に コピーすることである。
recvmsg(2) で msg_name に MSG_ERRQUEUE を指定して、受信パケットに入っていた宛先アドレスを取得する方法は 2.2 カーネルの一部でうまく動かない。
recvmsg(2), sendmsg(2), byteorder(3), ipfw(4), capabilities(7), icmp(7), ipv6(7), netlink(7), raw(7), socket(7), tcp(7), udp(7)
RFC 791: 元々の IP 仕様。 RFC 1122: IPv4 ホストの要件。 RFC 1812: IPv4 ルータの要件。
この man ページは Linux man-pages プロジェクトのリリース 3.54 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。