xref: /linux/tools/testing/selftests/drivers/net/hw/ipsec_vxlan.py (revision fcee7d82f27d6a8b1ddc5bbefda59b4e441e9bc0)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3"""Traffic test for VXLAN + IPsec crypto-offload."""
4
5import os
6
7from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_ge
8from lib.py import ksft_variants, KsftNamedVariant, KsftSkipEx
9from lib.py import CmdExitFailure, NetDrvEpEnv, cmd, defer, ethtool, ip
10from lib.py import Iperf3Runner
11
12# Inner tunnel addresses - TEST-NET-2 (RFC 5737) / doc prefix (RFC 3849)
13INNER_V4_LOCAL = "198.51.100.1"
14INNER_V4_REMOTE = "198.51.100.2"
15INNER_V6_LOCAL = "2001:db8:100::1"
16INNER_V6_REMOTE = "2001:db8:100::2"
17
18# ESP parameters
19SPI_OUT = "0x1000"
20SPI_IN = "0x1001"
21# 128-bit key + 32-bit salt = 20 bytes hex, 128-bit ICV
22ESP_AEAD = "aead 'rfc4106(gcm(aes))' 0x" + "01" * 20 + " 128"
23
24
25def xfrm(args, host=None):
26    """Runs 'ip xfrm' via shell to preserve parentheses in algo names."""
27    cmd(f"ip xfrm {args}", shell=True, host=host)
28
29
30def check_xfrm_offload_support():
31    """Skips if iproute2 lacks xfrm offload support."""
32    out = cmd("ip xfrm state help", fail=False)
33    if "offload" not in out.stdout + out.stderr:
34        raise KsftSkipEx("iproute2 too old, missing xfrm offload")
35
36
37def check_esp_hw_offload(cfg):
38    """Skips if device lacks esp-hw-offload support."""
39    check_xfrm_offload_support()
40    try:
41        feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
42    except (CmdExitFailure, IndexError) as e:
43        raise KsftSkipEx(f"can't query features: {e}") from e
44    if not feat.get("esp-hw-offload", {}).get("active"):
45        raise KsftSkipEx("Device does not support esp-hw-offload")
46
47
48def get_tx_drops(cfg):
49    """Returns TX dropped counter from the physical device."""
50    stats = ip("-s -s link show dev " + cfg.ifname, json=True)[0]
51    return stats["stats64"]["tx"]["dropped"]
52
53
54def setup_vxlan_ipsec(cfg, outer_ipver, inner_ipver):
55    """Sets up VXLAN tunnel with IPsec transport-mode crypto-offload."""
56    vxlan_name = f"vx{os.getpid()}"
57    local_addr = cfg.addr_v[outer_ipver]
58    remote_addr = cfg.remote_addr_v[outer_ipver]
59
60    if inner_ipver == "4":
61        inner_local = f"{INNER_V4_LOCAL}/24"
62        inner_remote = f"{INNER_V4_REMOTE}/24"
63        addr_extra = ""
64    else:
65        inner_local = f"{INNER_V6_LOCAL}/64"
66        inner_remote = f"{INNER_V6_REMOTE}/64"
67        addr_extra = " nodad"
68
69    if outer_ipver == "6":
70        vxlan_opts = "udp6zerocsumtx udp6zerocsumrx"
71    else:
72        vxlan_opts = "noudpcsum"
73
74    # VXLAN tunnel - local side
75    ip(f"link add {vxlan_name} type vxlan id 100 dstport 4789 {vxlan_opts} "
76       f"local {local_addr} remote {remote_addr} dev {cfg.ifname}")
77    defer(ip, f"link del {vxlan_name}")
78    ip(f"addr add {inner_local} dev {vxlan_name}{addr_extra}")
79    ip(f"link set {vxlan_name} up")
80
81    # VXLAN tunnel - remote side
82    ip(f"link add {vxlan_name} type vxlan id 100 dstport 4789 {vxlan_opts} "
83       f"local {remote_addr} remote {local_addr} dev {cfg.remote_ifname}",
84       host=cfg.remote)
85    defer(ip, f"link del {vxlan_name}", host=cfg.remote)
86    ip(f"addr add {inner_remote} dev {vxlan_name}{addr_extra}",
87       host=cfg.remote)
88    ip(f"link set {vxlan_name} up", host=cfg.remote)
89
90    # xfrm state - local outbound SA
91    xfrm(f"state add src {local_addr} dst {remote_addr} "
92         f"proto esp spi {SPI_OUT} "
93         f"{ESP_AEAD} "
94         f"mode transport offload crypto dev {cfg.ifname} dir out")
95    defer(xfrm, f"state del src {local_addr} dst {remote_addr} "
96                f"proto esp spi {SPI_OUT}")
97
98    # xfrm state - local inbound SA
99    xfrm(f"state add src {remote_addr} dst {local_addr} "
100         f"proto esp spi {SPI_IN} "
101         f"{ESP_AEAD} "
102         f"mode transport offload crypto dev {cfg.ifname} dir in")
103    defer(xfrm, f"state del src {remote_addr} dst {local_addr} "
104                f"proto esp spi {SPI_IN}")
105
106    # xfrm state - remote outbound SA (mirror, software crypto)
107    xfrm(f"state add src {remote_addr} dst {local_addr} "
108         f"proto esp spi {SPI_IN} "
109         f"{ESP_AEAD} "
110         f"mode transport",
111         host=cfg.remote)
112    defer(xfrm, f"state del src {remote_addr} dst {local_addr} "
113                f"proto esp spi {SPI_IN}", host=cfg.remote)
114
115    # xfrm state - remote inbound SA (mirror, software crypto)
116    xfrm(f"state add src {local_addr} dst {remote_addr} "
117         f"proto esp spi {SPI_OUT} "
118         f"{ESP_AEAD} "
119         f"mode transport",
120         host=cfg.remote)
121    defer(xfrm, f"state del src {local_addr} dst {remote_addr} "
122                f"proto esp spi {SPI_OUT}", host=cfg.remote)
123
124    # xfrm policy - local out
125    xfrm(f"policy add src {local_addr} dst {remote_addr} "
126         f"proto udp dport 4789 dir out "
127         f"tmpl src {local_addr} dst {remote_addr} proto esp mode transport")
128    defer(xfrm, f"policy del src {local_addr} dst {remote_addr} "
129                f"proto udp dport 4789 dir out")
130
131    # xfrm policy - local in
132    xfrm(f"policy add src {remote_addr} dst {local_addr} "
133         f"proto udp dport 4789 dir in "
134         f"tmpl src {remote_addr} dst {local_addr} proto esp mode transport")
135    defer(xfrm, f"policy del src {remote_addr} dst {local_addr} "
136                f"proto udp dport 4789 dir in")
137
138    # xfrm policy - remote out
139    xfrm(f"policy add src {remote_addr} dst {local_addr} "
140         f"proto udp dport 4789 dir out "
141         f"tmpl src {remote_addr} dst {local_addr} proto esp mode transport",
142         host=cfg.remote)
143    defer(xfrm, f"policy del src {remote_addr} dst {local_addr} "
144                f"proto udp dport 4789 dir out", host=cfg.remote)
145
146    # xfrm policy - remote in
147    xfrm(f"policy add src {local_addr} dst {remote_addr} "
148         f"proto udp dport 4789 dir in "
149         f"tmpl src {local_addr} dst {remote_addr} proto esp mode transport",
150         host=cfg.remote)
151    defer(xfrm, f"policy del src {local_addr} dst {remote_addr} "
152                f"proto udp dport 4789 dir in", host=cfg.remote)
153
154
155def _vxlan_ipsec_variants():
156    """Generates outer/inner IP version variants."""
157    for outer in ["4", "6"]:
158        for inner in ["4", "6"]:
159            yield KsftNamedVariant(f"outer_v{outer}_inner_v{inner}", outer, inner)
160
161
162@ksft_variants(_vxlan_ipsec_variants())
163def test_vxlan_ipsec_crypto_offload(cfg, outer_ipver, inner_ipver):
164    """Tests VXLAN+IPsec crypto-offload has no TX drops."""
165    cfg.require_ipver(outer_ipver)
166    check_esp_hw_offload(cfg)
167
168    setup_vxlan_ipsec(cfg, outer_ipver, inner_ipver)
169
170    if inner_ipver == "4":
171        inner_local = INNER_V4_LOCAL
172        inner_remote = INNER_V4_REMOTE
173        ping = "ping"
174    else:
175        inner_local = INNER_V6_LOCAL
176        inner_remote = INNER_V6_REMOTE
177        ping = "ping -6"
178
179    cmd(f"{ping} -c 1 -W 2 {inner_remote}")
180
181    drops_before = get_tx_drops(cfg)
182
183    runner = Iperf3Runner(cfg, server_ip=inner_local,
184                          client_ip=inner_remote)
185    bw_gbps = runner.measure_bandwidth(reverse=True)
186
187    cfg.wait_hw_stats_settle()
188    drops_after = get_tx_drops(cfg)
189
190    ksft_eq(drops_after - drops_before, 0,
191            comment="TX drops during VXLAN+IPsec")
192    ksft_ge(bw_gbps, 0.1,
193            comment="Minimum 100Mbps over VXLAN+IPsec")
194
195
196def main():
197    """Runs VXLAN+IPsec crypto-offload GSO selftest."""
198    with NetDrvEpEnv(__file__, nsim_test=False) as cfg:
199        ksft_run([test_vxlan_ipsec_crypto_offload], args=(cfg,))
200    ksft_exit()
201
202
203if __name__ == "__main__":
204    main()
205