名前
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.
パケットを送る場合は、
sll_family, sll_addr, sll_halen, sll_ifindex,
sll_protocol
を指定すれば十分である。
その他のフィールドは
0
にしておくべきである。
sll_hatype と sll_pkttype
には受信したパケットの情報が設定される。
ソケットオプション
パケットソケットのオプションは、レベル
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
レイヤーでのパケットバッファーが行われなくなるという副作用がある。
これにより、
ネットワークデバイスの送信キューの使用量が高い場合にパケット廃棄が起きやすくなる。
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/
に書かれている。