packet -
デバイスレベルのパケットインターフェース
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/ethernet.h> /* the L2 protocols */
packet_socket = socket(AF_PACKET, int socket_type, int protocol);
packet
ソケットは、デバイスドライバ
(OSI レイヤ 2) レベルで
生のパケット (raw packet)
を送受信するために用いられる。
packet
ソケットを使うと、ユーザー空間で物理層の上に
プロトコルモジュールを実装することができる。
socket_type には
SOCK_RAW と
SOCK_DGRAM
のいずれかを指定する。
SOCK_RAW
はリンクレベルヘッダを含む
raw パケットを、
SOCK_DGRAM
はリンクレベルヘッダが削除された加工済みパケットを示す。
リンクレベルヘッダ情報は
sockaddr_ll
で共通のフォーマットで入手できる。
protocol には IEEE 802.3
プロトコル番号を
ネットワークバイトオーダーで指定する。
指定できるプロトコルのリストは、インクルードファイル
<linux/if_ether.h>
を参照。プロトコルを
htons(ETH_P_ALL)
にすると、全てのプロトコルが受信される。
外部から来たパケットのうち指定したプロトコルのものは、
カーネルに実装されているプロトコルに渡される前の段階で、
packet
ソケットに渡される。
packet
ソケットをオープンできるのは、
実効ユーザーID が 0
のプロセスか、
CAP_NET_RAW
ケーパビリティを持つプロセスだけである。
SOCK_RAW
パケットでは、パケットをデバイスドライバと受け渡しする際、
パケットデータに変更が行われることはない。
パケットの受信時には、アドレスの解析だけは行われ、
標準的な
sockaddr_ll
アドレス構造体に渡される。パケットの送信時には、ユーザが指定する
バッファに物理層のヘッダが含まれている必要がある。
パケットはそのまま修正を受けずに、行き先アドレスから決定される
インターフェースのネットワークドライバにキューイングされる。
デバイスドライバによっては、他のヘッダを常に追加するものもある。
SOCK_RAW は Linux 2.0 の obosolete な
AF_INET/SOCK_PACKET
と似ているが、互換性があるわけではない。
SOCK_DGRAM
はやや高位のレベルで動作する。物理ヘッダは、パケットがユーザーに
渡される前に削除される。
SOCK_DGRAM の packet
ソケットを通して送られるパケットは、
sockaddr_ll
の行き先アドレスの情報に基づき、適切な物理層のヘッダが付加されてから、
キューに送られる。
デフォルトでは、指定したプロトコル型のパケットはすべて
packet
ソケットに送られる。特定のインターフェースからのパケットだけを
取得したい場合には、
struct sockaddr_ll
にアドレスを指定して
bind(2) を呼び、 packet
ソケットをそのインターフェースに結び付ける
(バインドする)。
バインドの際には、アドレスフィールドのうち
sll_protocol と
sll_ifindex
だけが用いられる。
connect(2) 操作は packet
ソケットではサポートされていない。
MSG_TRUNC フラグが
recvmsg(2),
recv(2),
recvfrom(2)
に渡されると、
(バッファサイズより大きかったとしても)
常に実際に通信された
パケットの長さが返される。
アドレスのタイプ¶
sockaddr_ll
はデバイスに依存しない物理層のアドレスである。
struct sockaddr_ll {
unsigned short sll_family; /* 常に AF_PACKET */
unsigned short sll_protocol; /* 物理層のプロトコル */
int sll_ifindex; /* インターフェース番号 */
unsigned short sll_hatype; /* ARP ハードウェア種別 */
unsigned char sll_pkttype; /* パケット種別 */
unsigned char sll_halen; /* アドレスの長さ */
unsigned char sll_addr[8]; /* 物理層のアドレス */
};
sll_protocol
は標準的なイーサネットプロトコルのタイプで、
ネットワーク
バイトオーダーで記述する。
インクルードファイル
<linux/if_ether.h>
で定義されている。
これがこのソケットのプロト
コルのデフォルトとなる。
sll_ifindex
はそのインターフェースの
interface index である (
netdevice(7)
を参照)。 0 は
(バインドが許可されている)
任
意のインターフェースにマッチする。
sll_hatype
は、インクルードファイル
<linux/if_arp.h>
で定義されている ARP
種別である。
sll_pkttype
はパケット種別である。指定できる種別は以下のいずれかである:
PACKET_HOST
(ローカルホスト向けのパケット)、
PACKET_BORADCAST (物理層
のブロードキャストパケット)、
PACKET_MULTICAST
(物理層のマルチキャストア
ドレスに送るパケット)、
PACKET_OTHERHOST
(他のホストに向けられたパケット
のうち、
無差別モード (promiscuous mode:
後述)
のデバイスドライバにより補足
されたもの)、
PACKET_OUTGOING
(ローカルホストから発信され、
packet ソケッ
トにループバックしてきたパケット)。
これらの種別が意味を持つのは受信時のみ
である。
sll_addr と
sll_halen
は、物理層の (つまり
IEEE 802.3 の)
アドレスとその長さである。
厳密な解釈はデバイスに依存する。
パケットを送る場合は、
sll_family,
sll_addr,
sll_halen,
sll_ifindex
を指定すれば十分である。
その他のフィールドは
0
にしておくべきである。
sll_hatype と
sll_pkttype
には受信したパケットの情報が設定される。
バインドの際には、
sll_protocol と
sll_ifindex
だけが使用される。
ソケットオプション¶
packet
ソケットは、物理層のマルチキャストや
無差別モード (promiscuous mode)
を設定して使うことができる。
これには
SOL_PACKET
と以下のオプションのいずれかを指定して
setsockopt(2) を呼べばよい。
バインドを追加する場合は
PACKET_ADD_MEMBERSHIP
であり、取り去る場合は
PACKET_DROP_MEMBERSHIP
である。これらはいずれも
packet_mreq
構造体を引き数に取る。
struct packet_mreq {
int mr_ifindex; /* インターフェース番号 */
unsigned short mr_type; /* 動作 */
unsigned short mr_alen; /* アドレスの長さ */
unsigned char mr_address[8]; /* 物理層のアドレス */
};
mr_ifindex
は、ステータスを変更したいインターフェースの
インターフェース番号である。
mr_type
パラメータは実行する動作を指定する:
PACKET_MR_PROMISC
は、共有している媒体からの全てのパケットを受信できるようにする
(しばしば
"無差別モード (promiscuous
mode)" と呼ばれる)。
PACKET_MR_MULTICAST
は、そのソケットを、
mr_address と
mr_alen
で指定される物理層のマルチキャストブループにバインドする。
PACKET_MR_ALLMULTI は socket を up
にして、そのインターフェースに到達したすべての
マルチキャストパケットを受信できるようにする。
昔からある ioctl
だけでなく、
SIOCSIFFLAGS,
SIOCADDMULTI,
SIOCDELMULTI
を同じ目的に用いることができる。
ioctl¶
SIOCGSTAMP
を用いると、最後に受信したパケットのタイムスタンプを得ることができる。
引き数は
struct timeval
である。
さらに、
netdevice(7) および
socket(7)
で定義されている標準の
ioctl はいずれも packet
ソケットに指定可能である。
エラー処理¶
packet
ソケットは、パケットをデバイスドライバに渡すときに
起きたエラーしか処理しない。遅延エラー
(pending error)
に関する概念は持っていない。
エラー¶
- EADDRNOTAVAIL
- 不明なマルチキャストグループアドレスが渡された。
- EFAULT
- ユーザが渡したメモリアドレスが不正。
- EINVAL
- 引き数が不正。
- EMSGSIZE
- パケットがインターフェースの
MTU より大きい。
- ENETDOWN
- インターフェースが
up でない。
- ENOBUFS
- パケットに割り当てるメモリが足りない。
- ENODEV
- デバイス名が不明。あるいはインターフェースアドレスで指定された
インターフェースインデックスが不明。
- ENOENT
- パケットを一つも受信していない。
- ENOTCONN
- インターフェースアドレスが渡されなかった。
- ENXIO
- インターフェースアドレスに不正なインターフェースインデックスが含まれている。
- EPERM
- この操作を行うのに必要な権限をユーザが持っていない。
上記以外のエラーが、低レベルのドライバで生成されることがある。
バージョン¶
AF_PACKET は Linux 2.2
の新機能である。これより古いバージョンの
Linux では
SOCK_PACKET
のみをサポートしていた。
インクルードファイル
<netpacket/packet.h>
が存在するのは glibc 2.1
以降である。
それ以前のシステムでは以下のようにする必要がある:
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h> /* The L2 protocols */
移植性の必要なプログラムでは、
pcap(3) 経由で
AF_PACKET
を用いることをお薦めする。ただし、この方法では
AF_PACKET
の機能すべてを利用することはできない。
SOCK_DGRAM packet ソケットは、IEEE
802.3 フレームの IEEE 802.2 LLC
ヘッダの
生成や解析を行おうとしない。
ETH_P_802_3
が送信プロトコルに指定されると、カーネルは
802.3 フレームを
生成して length
フィールドに書き込む。
完全に準拠したパケットを得るためにはユーザーが
LLC ヘッダを
与える必要がある。到着した
802.3 パケットでは、 DSAP/SSAP
protocol
の各フィールドは多重化
(multiplex) されていない。
代わりにこれらは LLC
ヘッダが前置された
ETH_P_802_2
プロトコルとして与えられる。したがって、
ETH_P_802_3
にバインドすることはできない。かわりに
ETH_P_802_2
にバインドし、自分自身でプロトコルの多重化を行うこと。
送信のデフォルトは、プロトコルフィールドを持つ
標準の Ethernet DIX encapsulation
である。
packet ソケットは入出力の
firewall chain
に影響をうけない。
移植性¶
Linux 2.0 では、 packet
ソケットを得る方法は
socket(AF_INET, SOCK_PACKET, protocol)
を呼ぶやり方しかなかった。この方法はまだサポートされているが、
用いないことを強く推奨する。現在の方法との主な違いは、
SOCK_PACKET
ではインターフェースの指定に古い
struct sockaddr_pkt
を用いる点である。これには物理層からの独立性がない。
struct sockaddr_pkt {
unsigned short spkt_family;
unsigned char spkt_device[14];
unsigned short spkt_protocol;
};
spkt_family
はデバイスのタイプ、
spkt_protocol は
<sys/if_ether.h>
で定義されている IEEE 802.3
プロトコルタイプ、
spkt_device
はデバイスの名前を NULL
終端された文字列で与えたもの
(例: eth0) である。
この構造体は obsolete
であり、
新しくコードを書く時には用いるべきでない。
glibc 2.1 には
SOL_PACKET
の定義がない。回避策としては、以下のようにするとよい。
#ifndef SOL_PACKET
#define SOL_PACKET 263
#endif
この問題は新しいバージョンの
glibc
では修正されている。
libc5
のシステムにはこの問題はない。
IEEE 802.2/803.3 の LLC
の扱い方は、バグと考えても良いだろう。
ソケットフィルターについて記載されていない。
MSG_TRUNC recvmsg(2)
拡張は非常にまずい対処であり、制御メッセージで置き換えるべきである。
今のところ
SOCK_DGRAM
経由でパケットについていた宛先アドレスを得る方法がない。
関連項目¶
socket(2),
pcap(3),
capabilities(7),
ip(7),
raw(7),
socket(7)
標準 IP Ethernet encapsulation
に関する情報は RFC 894
にある。
IEEE 802.3 IP encapsulation
に関する情報は RFC 1700
にある。
物理層のプロトコルに関する記述は
<linux/if_ether.h>
インクルードファイルにある。
この文書について¶
この man ページは Linux
man-pages
プロジェクトのリリース
3.41 の一部
である。プロジェクトの説明とバグ報告に関する情報は
http://www.kernel.org/doc/man-pages/
に書かれている。