tc flowerオフロードを利用してNICハードウェア上でパケットフィルタを実施する

Linuxtcには、指定した条件にマッチしたパケットをドロップしたりヘッダを書き換えたりするためのflowerフィルタが存在する。

tc-flower(8) - Linux manual page

最近のNICの中にはこの処理をハードウェアにオフロードできるものがあり、通常のソフトウェア処理と比較して高スループットを実現しつつCPU使用率低減が期待できる。ここではtc flowerのNICハードウェアオフロード機能の動作を試してみる。

対応するNICドライバ

Linuxカーネル4.16.7のソースコード上で確認する限り、以下のNICドライバでtc flowerハードウェアオフロードに対応しているようである。ちなみに対応状況はTC_SETUP_CLSFLOWERないしTC_SETUP_BLOCK*1といったキーワードで検索するとおおよそ掴める。

  • Broadcom bnxt (BCM573xx; NetXtreme C-series/E-series)
  • Chelsio cxgb4 (T4/T5/T6)
  • Intel i40e (710 series)
  • Mellanox mlx5 (ConnectX-4/5)
  • Netronme nfp (Agilio)
  • (Mellanox mlxsw*2 )

ただし、オフロード可能なマッチング条件 (ヘッダのフィールド) およびアクションはNICドライバおよびハードウェアによって異なる。ドライバが対応していてもハードウェアの世代やファームウェアバージョンによっては動作しない場合もある。たとえばi40eはtc flowerハードウェアオフロード自体には対応しているがaction dropのオフロードに対応していない*3ため、これから述べるパケットフィルタの例は動作しない*4

設定方法

ethtoolコマンドでtc offloadを有効にしたのち、tcコマンドでフィルタを設定してやればよい。

$ uname -a
Linux server01 4.16.5-300.fc28.x86_64 #1 SMP Fri Apr 27 17:38:36 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
$ tc -V
tc utility, iproute2-ss180129
$ ethtool -i enp7s0f0
driver: mlx5_core
version: 5.0-0
firmware-version: 14.22.1002 (MT_2420110004)
(snip)

ethtool -K IFACE hw-tc-offload onでtc offloadを有効にしたのち、対象インタフェースのqdiscingressを追加する。qdiscingressではなくclsactを使用してもよい。clsactingressをegress方向への操作にも適用できるように一般化したもの (net, sched: add clsact qdisc)。ただしegress方向へのflower適用に対応しているNICは現状無さそう*5なので、特に差異は無いはず。また詳しく追えていないがFedora 28でNetworkManagerが動作していると、追加したingressがしばらく経過すると削除される事象がみられた。

$ sudo ethtool -K enp7s0f0 hw-tc-offload on
$ sudo tc qdisc add dev enp7s0f0 ingress
$ tc qdisc show dev enp7s0f0
qdisc mq 0: root 
qdisc fq_codel 0: parent :8 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn 
qdisc fq_codel 0: parent :7 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn 
qdisc fq_codel 0: parent :6 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn 
qdisc fq_codel 0: parent :5 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn 
qdisc fq_codel 0: parent :4 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn 
qdisc fq_codel 0: parent :3 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn 
qdisc fq_codel 0: parent :2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn 
qdisc fq_codel 0: parent :1 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn 
qdisc ingress ffff: parent ffff:fff1 ----------------

tc filter add ... flower ... action ...の形式でマッチ条件と動作を指定する。ここでは8080/tcp宛のパケットをドロップするよう設定している。skip_swを指定するとハードウェアオフロードのみを試行し、オフロードできない場合はエラーとなる。skip_hwを指定するとソフトウェア処理のみを試行する。何も指定しない場合はまずハードウェアオフロードを試行し、オフロードできない場合はソフトウェア処理にフォールバックする。また、実際にオフロードされていればin_hwが、そうでなければnot_in_hwが表示される。

$ sudo tc filter add dev enp7s0f0 ingress protocol ip \
    flower skip_sw ip_proto tcp dst_port 8080 action drop
$ tc filter show dev enp7s0f0 ingress
filter protocol ip pref 49152 flower chain 0 
filter protocol ip pref 49152 flower chain 0 handle 0x1 
  eth_type ipv4
  ip_proto tcp
  dst_port 8080
  skip_sw
  in_hw
        action order 1: gact action drop
         random type none pass val 0
         index 1 ref 1 bind 1

tcコマンドに-sオプションを付ければ統計情報も出力される。ここでは別ホストからhping -S -p 8080 --flood8080/tcp宛にパケットを送ってみたが、CPU使用率の上昇は特にみられなかった。

$ tc -s filter show dev enp7s0f0 ingress
filter protocol ip pref 49152 flower chain 0 
filter protocol ip pref 49152 flower chain 0 handle 0x1 
  eth_type ipv4
  ip_proto tcp
  dst_port 8080
  skip_sw
  in_hw
        action order 1: gact action drop
         random type none pass val 0
         index 1 ref 1 bind 1 installed 947 sec used 0 sec
        Action statistics:
        Sent 1002736860 bytes 16712281 pkt (dropped 16712281, overlimits 0 requeues 0) 
        backlog 0b 0p requeues 0

削除はtc filter delコマンドで。

$ sudo tc filter del dev enp7s0f0 ingress protocol ip pref 49152 handle 1 flower

tc-flower offloadのその他の利用例

ここまでtc flower offloadを使って特定のパケットをハードウェア上でドロップする例を見てきたが、実のところ同様のことはntupleフィルタをethtool経由で設定することで以前から実現可能である:

software.intel.com

fastnetmon.com

ethtoolを用いたntupleフィルタと比較したtc flower offloadの利点は、drop以外のアクションをオフロードすることでNICハードウェアに多彩な動作を行わせることが(仕組み上)可能な点にある。

たとえばpeditアクションとcsumアクションをオフロードすれば、簡易的なstatic NATを実装できる*6:

github.com

また、mirredアクションをオフロードすれば、NICをL2スイッチや簡易的なL3スイッチのように振る舞わせることができる。これをSR-IOV switchdev modeと組み合わせれば、SR-IOVでVM向けに高パフォーマンスなネットワーク環境を提供しつつ、ハイパーバイザ側からVM間通信の挙動を制御することができる。実際にOpen vSwitch 2.8以降はこれらを利用してVM間のL2通信をNICハードウェアにオフロードする機能が実装されている。

SR-IOV switchdev modeの概要:

Introduction to switchdev SR-IOV offloads - Netdev 1.2

OpenStackとの組み合わせ例:

OpenStack Docs: Open vSwitch hardware offloading

Getting started with Mellanox ASAP^2 | Mellanox Interconnect Community

参考資料

*1:https://github.com/torvalds/linux/commit/6529eaba33f0465fc6d228e1d05b1745f7d0e8c9

*2:NICドライバではなく、MellanoxのイーサネットスイッチASIC (spectrum) をネイティブなLinuxネットワークAPIで操作できるようにするドライバ

*3:https://github.com/torvalds/linux/commit/2f4b411a3d6766e6362ffbf00e0495a2dfe92507 https://patchwork.ozlabs.org/cover/824130/ あたりを参照

*4:正確に言うとtc filter add ... skip_sw ... action dropコマンドは通るが動作している気配が無い@Intel X710

*5:対応しているのは恐らくswitch ASICであるところのmlxswのみ https://github.com/Mellanox/mlxsw/wiki/ACLs

*6:ConnectX-4 Lxでは mlx5: parsed 0 pedit actions, can't do more と言われて設定できなかった; https://github.com/torvalds/linux/blob/v4.16/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c#L1653-L1656