名前
packet - デバイスレベルのパケットインターフェース書式
#include <sys/socket.h> #include <linux/if_packet.h> #include <net/ethernet.h> /* L2 プロトコル */
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 ソケットに渡される。 In order to create a packet socket, a process must have the CAP_NET_RAW capability in the user namespace that governs its network namespace. SOCK_RAW パケットでは、パケットをデバイスドライバと受け渡しする際、 パケットデータに変更が行われることはない。 パケットの受信時には、アドレスの解析だけは行われ、 標準的な sockaddr_ll アドレス構造体に渡される。パケットの送信時には、ユーザーが指定する バッファーに物理層のヘッダーが含まれている必要がある。 パケットはそのまま修正を受けずに、行き先アドレスから決定される インターフェースのネットワークドライバにキューイングされる。 デバイスドライバによっては、他のヘッダーを常に追加するものもある。 SOCK_RAW は Linux 2.0 の obosolete な AF_INET/SOCK_PACKET と似ているが、互換性があるわけではない。 SOCK_DGRAM はやや高位のレベルで動作する。物理ヘッダーは、パケットがユーザーに 渡される前に削除される。 SOCK_DGRAM の packet ソケットを通して送られるパケットは、 sockaddr_ll の行き先アドレスの情報に基づき、適切な物理層のヘッダーが付加されてから、 キューに送られる。 By default, all packets of the specified protocol type are passed to a packet socket. To get packets only from a specific interface use bind(2) specifying an address in a struct sockaddr_ll to bind the packet socket to an interface. Fields used for binding are sll_family (should be AF_PACKET), sll_protocol, and 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 is the standard ethernet protocol type in network byte order as defined in the <linux/if_ether.h> include file. It defaults to the socket's protocol.
- *
- sll_ifindex is the interface index of the interface (see netdevice(7)); 0 matches any interface (only permitted for binding). sll_hatype is an ARP type as defined in the <linux/if_arp.h> include file.
- *
- sll_pkttype contains the packet type. Valid types are PACKET_HOST for a packet addressed to the local host, PACKET_BROADCAST for a physical-layer broadcast packet, PACKET_MULTICAST for a packet sent to a physical-layer multicast address, PACKET_OTHERHOST for a packet to some other host that has been caught by a device driver in promiscuous mode, and PACKET_OUTGOING for a packet originating from the local host that is looped back to a packet socket. These types make sense only for receiving.
- *
- sll_addr and sll_halen contain the physical-layer (e.g., IEEE 802.3) address and its length. The exact interpretation depends on the device.
ソケットオプション
パケットソケットのオプションは、レベル SOL_PACKET を指定して setsockopt(2) を呼び出すことで設定できる。- PACKET_ADD_MEMBERSHIP
- PACKET_DROP_MEMBERSHIP
- packet ソケットは、物理層のマルチキャストや 無差別モード (promiscuous mode) を設定して使うことができる。 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 を同じ目的に用いることができる。
- PACKET_AUXDATA (Linux 2.6.21 以降)
- ブール値のオプションを有効すると、 パケットソケットは、パケットと一緒にメタデータ構造体を recvmsg(2) コントロールフィールドで渡す。 この構造体は cmsg(3) を使って読むことができる。 定義は以下の通りである。
-
struct tpacket_auxdata { __u32 tp_status; __u32 tp_len; /* packet length */ __u32 tp_snaplen; /* captured length */ __u16 tp_mac; __u16 tp_net; __u16 tp_vlan_tci; __u16 tp_vlan_tpid; /* Since Linux 3.14; earlier, these were unused padding bytes */ };
- PACKET_FANOUT (Linux 3.1 以降)
- スレッドにまたがって処理をスケールさせるため、 パケットソケットはファンアウトグループを構成することができる。 このモードでは、 マッチしたそれぞれのパケットはグループ内のいずれか一つのソケットにだけキューイングされる。 ソケットをファンアウトグループに参加させるには、 レベル SOL_PACKET でオプション PACKET_FANOUT を指定して setsockopt(2) を呼び出す。 ネットワーク名前空間毎に最大 65536 個の独立したグループを持つことができる。 整数のオプション値の先頭 16 ビットに ID をエンコードすることで、 ソケットはグループを選択する。 あるグループへの最初のパケットソケットの参加があった時点で、 グループは暗黙のうちに作成される。 既存のグループへの参加が成功するためには、 それ以降にそのグループに参加しようとするパケットソケットは、 プロトコロ、 デバイス設定、ファンアウトモード、フラグが同じである必要がある (下記参照)。 パケットソケットがファンアウトグループから抜けるのは、 そのソケットをクローズした場合だけである。 ファンアウトグループは最後のソケットがクローズした場合に削除される。
- Fanout supports multiple algorithms to spread traffic between sockets, as follows:
- *
- The default mode, PACKET_FANOUT_HASH, sends packets from the same flow to the same socket to maintain per-flow ordering. For each packet, it chooses a socket by taking the packet flow hash modulo the number of sockets in the group, where a flow hash is a hash over network-layer address and optional transport-layer port fields.
- *
- The load-balance mode PACKET_FANOUT_LB implements a round-robin algorithm.
- *
- PACKET_FANOUT_CPU selects the socket based on the CPU that the packet arrived on.
- *
- PACKET_FANOUT_ROLLOVER processes all data on a single socket, moving to the next when one becomes backlogged.
- *
- PACKET_FANOUT_RND selects the socket using a pseudo-random number generator.
- *
- PACKET_FANOUT_QM (available since Linux 3.14) selects the socket using the recorded queue_mapping of the received skb.
- ファンアウトモードでは追加のオプションがある。 IP フラグメンテーションが起こると、 同じフローのパケットのフローハッシュが異なるハッシュを持つことになる。 フラグ PACKET_FANOUT_FLAG_DEFRAG をセットすると、 パケットはファンアウトを行う前にフラグメント再構築が行われるようになり、 フラグメントがあった場合でも順序が維持される。 ファンアウトモードとオプションは、 整数のオプション値の下位 16 ビットで指定される。 フラグ PACKET_FANOUT_FLAG_ROLLOVER を指定すると、 バックアップ戦略としてロールオーバー方式が有効になる。 元のファンアウトアルゴリズムが backlog ソケットを選択していれば、 パケットは次の利用可能なソケットにロールオーバーされる。
- PACKET_LOSS (PACKET_TX_RING で使用)
- 送信リングで不正な形式のパケットに遭遇した場合、 デフォルトではそのリングの tp_status を TP_STATUS_WRONG_FORMAT に戻し、その送信を直ちに中止する。 不正な形式のパケットにより、そのパケット自身とその以降にキューに入れられたパケットの送信がブロックされる。形式エラーを修正し、関連する tp_status を TP_STATUS_SEND_REQUEST に設定し直し、 send(2) を使って送信処理を再開しなければならない。 しかしながら、 PACKET_LOSS がセットされている場合、 不正な形式のパケットはすべてスキップされ、 その送信リングの tp_status は TP_STATUS_AVAILABLE に設定し直され、送信処理は継続される。
- PACKET_RESERVE (PACKET_RX_RING で使用)
- デフォルトでは、パケット受信リングはメタデータ構造体とアライメント用のパディングの直後にパケットを書き込む。 この整数オプションを設定すると、パケットの前に追加で領域が予約される。
- PACKET_RX_RING
- 非同期でのパケット受信用のメモリーマップされたリングバッファーを作成する。 パケットソケットはアプリケーションのアドレス空間に連続する領域を確保し、 そこにパケットスロットの配列を構成し、 (最大 tp_snaplen 個の) パケットを順にスロットにコピーする。 各パケットの前には tpacket_auxdata に似たメタデータ構造体が置かれる。 プロトコルフィールドには、データの、メタデータヘッダーの先頭からのオフセットが入る。 tp_net にはネットワーク層へのオフセットが格納される。 パケットソケットが SOCK_DGRAM 型の場合、 tp_mac も同じである。 SOCK_RAW 型の場合、 tp_net にはリンク層のフレームへのオフセットが入る。 パケットソケットとアプリケーションは tp_status フィールドを通してリングの先頭 (head) と末尾 (tail) の情報を受け渡す。 パケットソケットは tp_status が TP_STATUS_KERNEL のすべてのスロットを所有しており、 スロットにデータが入ると、 パケットソケットはそのスロットのステータスをアプリケーションに所有権を渡す状態に変更する。 通常の動作では、 新しい tp_status で少なくとも TP_STATUS_USER ビットがセットされていれば、 受信されたパケットが格納されたことを示している。 アプリケーションがパケットの処理を終えると、アプリケーションはそのスロットの tp_status を TP_STATUS_KERNEL に設定し、そのスロットの所有権をソケットに返す。
- パケットソケットは、複数バージョンのパケットリングを実装している。 実装の詳細は Linux カーネルソースツリーの Documentation/networking/packet_mmap.txt で説明されている。
- PACKET_STATISTICS
- パケットソケットの統計情報を次の構造体形式で取得する。
-
struct tpacket_stats { unsigned int tp_packets; /* 総パケット数 */ unsigned int tp_drops; /* ドロップパケット数 */ };
- 統計情報を取得すると、内部カウンターはリセットされる。 TPACKET_V3 のリングを使う場合には、統計情報構造体は違うものになる。
- PACKET_TIMESTAMP (PACKET_RX_RING で使用; Linux 2.6.36 以降)
- パケット受信リングでは常にタイムスタンプがメタデータヘッダーに格納される。 デフォルトでは、タイムスタンプはパケットがリングにコピーされた時点で生成されるソフトウェアによるタイムスタンプである。 この整数オプションによりタイムスタンプの種類を選択できる。 デフォルト以外では、 Linux カーネルソースツリーの Documentation/networking/timestamping.rst に説明がある 2 種類のハードウェアフォーマットがサポートされている。
- PACKET_TX_RING (Linux 2.6.31 以降)
- パケット送信用のメモリーマップされたリングバッファーを作成する。 このオプションは PACKET_RX_RING と同様で、同じ引数を取る。 アプリケーションは tp_status が TP_STATUS_AVAILABLE のスロットにパケットを書き込み、 tp_status を TP_STATUS_SEND_REQUEST に変更することでそのパケットの送信を予約する。 パケットの送信準備ができたら、アプリケーションは続けて send(2) 系のシステムコールを呼び出す。 システムコールの引数 buf と len は無視される。 sendto(2) や sendmsg(2) を使ってアドレスが渡された場合、 ソケットのデフォルト値ではなくそのアドレスが使用される。 送信に成功すると、ソケットはそのスロットの tp_status を TP_STATUS_AVAILABLE に戻す。 エラーの場合、 PACKET_LOSS がセットされていなければ、 直ちに送信を中断しエラーを上げる。
- PACKET_VERSION (PACKET_RX_RING で使用; Linux 2.6.27 以降)
- デフォルトでは、 PACKET_RX_RING は TPACKET_V1 のパケット受信リングを作成する。別のバージョンのリングを作成するには、そのリングを作成する前に希望するバージョンが使われるようにこの整数オプションを設定すること。
- PACKET_QDISC_BYPASS (Linux 3.14 以降)
- デフォルトでは、パケットはカーネルの qdisc (トラフィック制御) レイヤー経由で渡される。 これは大半のユースケースに合っている。 ネットワークに対して可能な限りパケットを送信する (例えば pkggen と同様の方法で負荷対象のデバイスを試験する) のにパケットソケットを使うトラフィック生成アプライアンスでは、この整数オプションを 1 に設定することで qdisc レイヤーを飛ばすことができる。 qdisc レイヤーでのパケットバッファーが行われなくなるという副作用がある。 これにより、 ネットワークデバイスの送信キューの使用量が高い場合にパケット廃棄が起きやすくなる。
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 のみをサポートしていた。注意
移植性の必要なプログラムでは、 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 に影響をうけない。移植性
In Linux 2.0, the only way to get a packet socket was with the call:socket(AF_INET, SOCK_PACKET, protocol) This is still supported, but deprecated and strongly discouraged. The main difference between the two methods is that SOCK_PACKET uses the old struct sockaddr_pkt to specify an interface, which doesn't provide physical-layer independence.
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 はデバイスの名前をヌル終端された文字列で与えたもの (例: eth0) である。 この構造体は obsolete であり、 新しくコードを書く時には用いるべきでない。
バグ
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> インクルードファイルにある。 Linux カーネルのソースツリー。 Documentation/networking/filter.rst には Berkeley Packet Filters をパケットソケットにどのように適用するかの説明がある。 /tools/testing/selftests/net/psock_tpacket.c には、 PACKET_RX_RING と PACKET_TX_RING の利用可能なすべてのバージョンのサンプルソースコードがある。この文書について
この man ページは Linux man-pages プロジェクトのリリース 5.10 の一部である。プロジェクトの説明とバグ報告に関する情報は https://www.kernel.org/doc/man-pages/ に書かれている。2020-12-21 | Linux |