#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/ethernet.h> /* the L2 protocols */ packet_socket = socket(PF_PACKET, int socket_type, int protocol);
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 な PF_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) に渡されると、 (バッファサイズより大きかったとしても) 常に実際に通信された パケットの長さが返される。
struct sockaddr_ll { unsigned short sll_family; /* 常に AF_PACKET */ unsigned short sll_protocol; /* 物理層のプロトコル */ int sll_ifindex; /* インターフェース番号 */ unsigned short sll_hatype; /* ヘッダ種別 */ 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 だけが使用される。
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 を同じ目的に用いることができる。
さらに、 netdevice(7) および socket(7) で定義されている標準の ioctl はいずれも packet ソケットに指定可能である。
上記以外のエラーが、低レベルのドライバで生成されることがある。
インクルードファイル <netpacket/packet.h> が存在するのは glibc 2.1 以降である。 それ以前のシステムでは以下のようにする必要がある:
#include <asm/types.h> #include <linux/if_packet.h> #include <linux/if_ether.h> /* The L2 protocols */
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 に影響をうけない。
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 であり、 新しくコードを書く時には用いるべきでない。
#ifndef SOL_PACKET #define SOL_PACKET 263 #endifこの問題は新しいバージョンの glibc では修正されている。 libc5 のシステムにはこの問題はない。
IEEE 802.2/803.3 の LLC の扱い方は、バグと考えても良いだろう。
ソケットフィルターについて記載されていない。
MSG_TRUNC recvmsg(2) 拡張は非常にまずい対処であり、制御メッセージで置き換えるべきである。 今のところ SOCK_DGRAM 経由でパケットについていた宛先アドレスを得る方法がない。
標準 IP Ethernet encapsulation に関する情報は RFC 894 にある。
IEEE 802.3 IP encapsulation に関する情報は RFC 1700 にある。
物理層のプロトコルに関する記述は <linux/if_ether.h> インクルードファイルにある。