Path: blob/master/tools/testing/selftests/drivers/net/hw/ntuple.py
171056 views
#!/usr/bin/env python31# SPDX-License-Identifier: GPL-2.02"""Test ethtool NFC (ntuple) flow steering rules."""34import random5from enum import Enum, auto6from lib.py import ksft_run, ksft_exit7from lib.py import ksft_eq, ksft_ge8from lib.py import ksft_variants, KsftNamedVariant9from lib.py import EthtoolFamily, NetDrvEpEnv, NetdevFamily10from lib.py import KsftSkipEx11from lib.py import cmd, ethtool, defer, rand_ports, bkg, wait_port_listen121314class NtupleField(Enum):15SRC_IP = auto()16DST_IP = auto()17SRC_PORT = auto()18DST_PORT = auto()192021def _require_ntuple(cfg):22features = ethtool(f"-k {cfg.ifname}", json=True)[0]23if not features["ntuple-filters"]["active"]:24raise KsftSkipEx("Ntuple filters not enabled on the device: " + str(features["ntuple-filters"]))252627def _get_rx_cnts(cfg, prev=None):28"""Get Rx packet counts for all queues, as a simple list of integers29if @prev is specified the prev counts will be subtracted"""30cfg.wait_hw_stats_settle()31data = cfg.netdevnl.qstats_get({"ifindex": cfg.ifindex, "scope": ["queue"]}, dump=True)32data = [x for x in data if x['queue-type'] == "rx"]33max_q = max([x["queue-id"] for x in data])34queue_stats = [0] * (max_q + 1)35for q in data:36queue_stats[q["queue-id"]] = q["rx-packets"]37if prev and q["queue-id"] < len(prev):38queue_stats[q["queue-id"]] -= prev[q["queue-id"]]39return queue_stats404142def _ntuple_rule_add(cfg, flow_spec):43"""Install an NFC rule via ethtool."""4445output = ethtool(f"-N {cfg.ifname} {flow_spec}").stdout46rule_id = int(output.split()[-1])47defer(ethtool, f"-N {cfg.ifname} delete {rule_id}")484950def _setup_isolated_queue(cfg):51"""Default all traffic to queue 0, and pick a random queue to52steer NFC traffic to."""5354channels = cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifindex}})55ch_max = channels['combined-max']56qcnt = channels['combined-count']5758if ch_max < 2:59raise KsftSkipEx(f"Need at least 2 combined channels, max is {ch_max}")6061desired_queues = min(ch_max, 4)62if qcnt >= desired_queues:63desired_queues = qcnt64else:65ethtool(f"-L {cfg.ifname} combined {desired_queues}")66defer(ethtool, f"-L {cfg.ifname} combined {qcnt}")6768ethtool(f"-X {cfg.ifname} equal 1")69defer(ethtool, f"-X {cfg.ifname} default")7071return random.randint(1, desired_queues - 1)727374def _send_traffic(cfg, ipver, proto, dst_port, src_port, pkt_cnt=40):75"""Generate traffic with the desired flow signature."""7677cfg.require_cmd("socat", remote=True)7879socat_proto = proto.upper()80dst_addr = f"[{cfg.addr_v['6']}]" if ipver == '6' else cfg.addr_v['4']8182extra_opts = ",nodelay" if proto == "tcp" else ",shut-null"8384listen_cmd = (f"socat -{ipver} -t 2 -u "85f"{socat_proto}-LISTEN:{dst_port},reuseport /dev/null")86with bkg(listen_cmd, exit_wait=True):87wait_port_listen(dst_port, proto=proto)88send_cmd = f"""89bash -c 'for i in $(seq {pkt_cnt}); do echo msg; sleep 0.02; done' |90socat -{ipver} -u - \91{socat_proto}:{dst_addr}:{dst_port},sourceport={src_port},reuseaddr{extra_opts}92"""93cmd(send_cmd, shell=True, host=cfg.remote)949596def _add_ntuple_rule_and_send_traffic(cfg, ipver, proto, fields, test_queue):97ports = rand_ports(2)98src_port = ports[0]99dst_port = ports[1]100flow_parts = [f"flow-type {proto}{ipver}"]101102for field in fields:103if field == NtupleField.SRC_IP:104flow_parts.append(f"src-ip {cfg.remote_addr_v[ipver]}")105elif field == NtupleField.DST_IP:106flow_parts.append(f"dst-ip {cfg.addr_v[ipver]}")107elif field == NtupleField.SRC_PORT:108flow_parts.append(f"src-port {src_port}")109elif field == NtupleField.DST_PORT:110flow_parts.append(f"dst-port {dst_port}")111112flow_parts.append(f"action {test_queue}")113_ntuple_rule_add(cfg, " ".join(flow_parts))114_send_traffic(cfg, ipver, proto, dst_port=dst_port, src_port=src_port)115116117def _ntuple_variants():118for ipver in ["4", "6"]:119for proto in ["tcp", "udp"]:120for fields in [[NtupleField.SRC_IP],121[NtupleField.DST_IP],122[NtupleField.SRC_PORT],123[NtupleField.DST_PORT],124[NtupleField.SRC_IP, NtupleField.DST_IP],125[NtupleField.SRC_IP, NtupleField.DST_IP,126NtupleField.SRC_PORT, NtupleField.DST_PORT]]:127name = ".".join(f.name.lower() for f in fields)128yield KsftNamedVariant(f"{proto}{ipver}.{name}",129ipver, proto, fields)130131132@ksft_variants(_ntuple_variants())133def queue(cfg, ipver, proto, fields):134"""Test that an NFC rule steers traffic to the correct queue."""135136cfg.require_ipver(ipver)137_require_ntuple(cfg)138139test_queue = _setup_isolated_queue(cfg)140141cnts = _get_rx_cnts(cfg)142_add_ntuple_rule_and_send_traffic(cfg, ipver, proto, fields, test_queue)143cnts = _get_rx_cnts(cfg, prev=cnts)144145ksft_ge(cnts[test_queue], 40, f"Traffic on test queue {test_queue}: {cnts}")146sum_idle = sum(cnts) - cnts[0] - cnts[test_queue]147ksft_eq(sum_idle, 0, f"Traffic on idle queues: {cnts}")148149150def main() -> None:151"""Ksft boilerplate main."""152153with NetDrvEpEnv(__file__, nsim_test=False) as cfg:154cfg.ethnl = EthtoolFamily()155cfg.netdevnl = NetdevFamily()156ksft_run([queue], args=(cfg,))157ksft_exit()158159160if __name__ == "__main__":161main()162163164