Linux においてパケットミラーを実現する方法はいくつかあるが、 XDP単体ではパケットミラーリングは実装できないのではないかと思っていた。 つまりTCなど他の機能と組み合わせる必要があると考えていた。
いくつか実験の結果、devmap 全エントリへの同時送信の機能を使うことによってパケットミラーリングできることがわかったので記録を残しておく。 コードは xdp_mirror.py として公開している。
全体像はこのような感じ。3台のホスト h1-3 があり、ゲートウェイ gw がそれらを接続している。 ホストとゲートウェイは netns で仮想的に作ったもので、veth ペアで接続されている。 h1からh2へ Packet1 が転送されている状況において、 gwが事前に設定した5tupleにマッチした場合には、 h2へ転送するのに加えてパケットを加工しつつh3へも転送する。 繰り返しになるが TC などの外部の機能を使わずXDP単体で実装する。 灰色のブロックがXDPプログラムを示す。全部で4種類ある。 host_pass.c は veth のために作成したが本質ではないので無視して良い。
filter.c では5tupleによるマッチングを行い、マッチした場合には devmap の全エントリに対してパケットを送信する。
// filter.c
SEC("xdp")
int xdp_filter(struct xdp_md *ctx)
{
ip = ... ;
udp = ... ;
struct five_tuple key = {
.saddr = ip->saddr,
.daddr = ip->daddr,
.sport = udp->source,
.dport = udp->dest,
.proto = ip->protocol,
};
// Mirror Enabled: the packet is sent to
// all entries (for h2 & h3) in the devmap.
if (bpf_map_lookup_elem(&mirror_targets, &key))
return bpf_redirect_map(&tx_port, 0, BPF_F_BROADCAST);
// Mirror Disabled: the packet is sent to
// the first entry (for h2) in the devmap.
return bpf_redirect_map(&tx_port, 0, 0);
}
ここで filter.c から bpf_redirect_map を経由して実行される mirror.c は BPF_XDP_DEVMAP attach type としてコンパイルされる必要がある。bpf_xdp_adjust_head などを使ってパケットを加工でき、この例ではVXLANヘッダを付与している。
おしまい。