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