]>
Commit | Line | Data |
---|---|---|
ead573d8 JM |
1 | # Test cases for MACsec/MKA |
2 | # Copyright (c) 2018, Jouni Malinen <j@w1.fi> | |
3 | # | |
4 | # This software may be distributed under the terms of the BSD license. | |
5 | # See README for more details. | |
6 | ||
7 | import logging | |
8 | logger = logging.getLogger() | |
e1810300 | 9 | import binascii |
ead573d8 JM |
10 | import os |
11 | import signal | |
12 | import subprocess | |
13 | import time | |
14 | ||
15 | from wpasupplicant import WpaSupplicant | |
16 | import hwsim_utils | |
344929a9 | 17 | from utils import HwsimSkip, alloc_fail, fail_test, wait_fail_trigger |
ead573d8 JM |
18 | |
19 | def cleanup_macsec(): | |
20 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
21 | wpas.interface_remove("veth0") | |
22 | wpas.interface_remove("veth1") | |
23 | subprocess.call(["ip", "link", "del", "veth0"], | |
24 | stderr=open('/dev/null', 'w')) | |
25 | ||
26 | def test_macsec_psk(dev, apdev, params): | |
27 | """MACsec PSK""" | |
28 | try: | |
29 | run_macsec_psk(dev, apdev, params, "macsec_psk") | |
30 | finally: | |
31 | cleanup_macsec() | |
32 | ||
33 | def test_macsec_psk_mka_life_time(dev, apdev, params): | |
34 | """MACsec PSK - MKA life time""" | |
35 | try: | |
36 | run_macsec_psk(dev, apdev, params, "macsec_psk_mka_life_time") | |
37 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
38 | wpas.interface_remove("veth1") | |
39 | # Wait for live peer to be removed on veth0 | |
40 | time.sleep(6.1) | |
41 | finally: | |
42 | cleanup_macsec() | |
43 | ||
44 | def test_macsec_psk_integ_only(dev, apdev, params): | |
45 | """MACsec PSK (integrity only)""" | |
46 | try: | |
47 | run_macsec_psk(dev, apdev, params, "macsec_psk_integ_only", | |
48 | integ_only=True) | |
49 | finally: | |
50 | cleanup_macsec() | |
51 | ||
52 | def test_macsec_psk_port(dev, apdev, params): | |
53 | """MACsec PSK (port)""" | |
54 | try: | |
55 | run_macsec_psk(dev, apdev, params, "macsec_psk_port", | |
56 | port0=65534, port1=65534) | |
57 | finally: | |
58 | cleanup_macsec() | |
59 | ||
60 | def test_macsec_psk_different_ports(dev, apdev, params): | |
61 | """MACsec PSK (different ports)""" | |
62 | try: | |
63 | run_macsec_psk(dev, apdev, params, "macsec_psk_different_ports", | |
64 | port0=2, port1=3) | |
65 | finally: | |
66 | cleanup_macsec() | |
67 | ||
68 | def test_macsec_psk_shorter_ckn(dev, apdev, params): | |
69 | """MACsec PSK (shorter CKN)""" | |
70 | try: | |
71 | ckn = "11223344" | |
72 | run_macsec_psk(dev, apdev, params, "macsec_psk_shorter_ckn", | |
73 | ckn0=ckn, ckn1=ckn) | |
74 | finally: | |
75 | cleanup_macsec() | |
76 | ||
77 | def test_macsec_psk_shorter_ckn2(dev, apdev, params): | |
78 | """MACsec PSK (shorter CKN, unaligned)""" | |
79 | try: | |
80 | ckn = "112233" | |
81 | run_macsec_psk(dev, apdev, params, "macsec_psk_shorter_ckn2", | |
82 | ckn0=ckn, ckn1=ckn) | |
83 | finally: | |
84 | cleanup_macsec() | |
85 | ||
86 | def test_macsec_psk_ckn_mismatch(dev, apdev, params): | |
87 | """MACsec PSK (CKN mismatch)""" | |
88 | try: | |
89 | ckn0 = "11223344" | |
90 | ckn1 = "1122334455667788" | |
91 | run_macsec_psk(dev, apdev, params, "macsec_psk_ckn_mismatch", | |
92 | ckn0=ckn0, ckn1=ckn1, expect_failure=True) | |
93 | finally: | |
94 | cleanup_macsec() | |
95 | ||
96 | def test_macsec_psk_cak_mismatch(dev, apdev, params): | |
97 | """MACsec PSK (CAK mismatch)""" | |
98 | try: | |
99 | cak0 = 16*"11" | |
100 | cak1 = 16*"22" | |
101 | run_macsec_psk(dev, apdev, params, "macsec_psk_cak_mismatch", | |
102 | cak0=cak0, cak1=cak1, expect_failure=True) | |
103 | finally: | |
104 | cleanup_macsec() | |
105 | ||
106 | def test_macsec_psk_256(dev, apdev, params): | |
107 | """MACsec PSK with 256-bit keys""" | |
108 | try: | |
109 | cak = "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" | |
110 | run_macsec_psk(dev, apdev, params, "macsec_psk_256", cak0=cak, cak1=cak) | |
111 | finally: | |
112 | cleanup_macsec() | |
113 | ||
114 | def set_mka_psk_config(dev, mka_priority=None, integ_only=False, port=None, | |
115 | ckn=None, cak=None): | |
116 | dev.set("eapol_version", "3") | |
117 | dev.set("ap_scan", "0") | |
118 | dev.set("fast_reauth", "1") | |
119 | ||
120 | id = dev.add_network() | |
121 | dev.set_network(id, "key_mgmt", "NONE") | |
122 | if cak is None: | |
123 | cak = "000102030405060708090a0b0c0d0e0f" | |
124 | dev.set_network(id, "mka_cak", cak) | |
125 | if ckn is None: | |
126 | ckn = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" | |
127 | dev.set_network(id, "mka_ckn", ckn) | |
128 | dev.set_network(id, "eapol_flags", "0") | |
129 | dev.set_network(id, "macsec_policy", "1") | |
130 | if integ_only: | |
131 | dev.set_network(id, "macsec_integ_only", "1") | |
132 | if mka_priority is not None: | |
133 | dev.set_network(id, "mka_priority", str(mka_priority)) | |
134 | if port is not None: | |
135 | dev.set_network(id, "macsec_port", str(port)) | |
136 | ||
137 | dev.select_network(id) | |
138 | ||
139 | def log_ip_macsec(): | |
fab49f61 | 140 | cmd = subprocess.Popen(["ip", "macsec", "show"], |
ead573d8 JM |
141 | stdout=subprocess.PIPE, |
142 | stderr=open('/dev/null', 'w')) | |
1c48c9bc | 143 | res = cmd.stdout.read().decode() |
ead573d8 JM |
144 | cmd.stdout.close() |
145 | logger.info("ip macsec:\n" + res) | |
146 | ||
147 | def log_ip_link(): | |
fab49f61 | 148 | cmd = subprocess.Popen(["ip", "link", "show"], |
ead573d8 | 149 | stdout=subprocess.PIPE) |
1c48c9bc | 150 | res = cmd.stdout.read().decode() |
ead573d8 JM |
151 | cmd.stdout.close() |
152 | logger.info("ip link:\n" + res) | |
153 | ||
344929a9 | 154 | def add_veth(): |
ead573d8 | 155 | try: |
fab49f61 JM |
156 | subprocess.check_call(["ip", "link", "add", "veth0", "type", "veth", |
157 | "peer", "name", "veth1"]) | |
ead573d8 JM |
158 | except subprocess.CalledProcessError: |
159 | raise HwsimSkip("veth not supported (kernel CONFIG_VETH)") | |
160 | ||
344929a9 JM |
161 | def add_wpas_interfaces(count=2): |
162 | wpa = [] | |
163 | try: | |
164 | for i in range(count): | |
165 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
166 | wpas.interface_add("veth%d" % i, driver="macsec_linux") | |
167 | wpa.append(wpas) | |
bab493b9 | 168 | except Exception as e: |
344929a9 | 169 | if "Failed to add a dynamic wpa_supplicant interface" in str(e): |
3281c159 | 170 | raise HwsimSkip("macsec supported (wpa_supplicant CONFIG_MACSEC, CONFIG_DRIVER_MACSEC_LINUX; kernel CONFIG_MACSEC)") |
344929a9 JM |
171 | raise |
172 | ||
173 | return wpa | |
174 | ||
0d09bd08 JM |
175 | def lower_addr(addr1, addr2): |
176 | a1 = addr1.split(':') | |
177 | a2 = addr2.split(':') | |
178 | for i in range(6): | |
e1810300 | 179 | if binascii.unhexlify(a1[i]) < binascii.unhexlify(a2[i]): |
0d09bd08 | 180 | return True |
e1810300 | 181 | if binascii.unhexlify(a1[i]) > binascii.unhexlify(a2[i]): |
0d09bd08 JM |
182 | return False |
183 | return False | |
184 | ||
185 | def wait_mka_done(wpa, expect_failure=False): | |
344929a9 JM |
186 | max_iter = 14 if expect_failure else 40 |
187 | for i in range(max_iter): | |
0d09bd08 JM |
188 | done = True |
189 | for w in wpa: | |
190 | secured = w.get_status_field("Secured") | |
191 | peers = int(w.get_status_field("live_peers")) | |
192 | if expect_failure and (secured == "Yes" or peers > 0): | |
193 | raise Exception("MKA completed unexpectedly") | |
194 | if peers != len(wpa) - 1 or secured != "Yes": | |
195 | done = False | |
196 | break | |
197 | w.dump_monitor() | |
198 | if done: | |
199 | break | |
344929a9 JM |
200 | time.sleep(0.5) |
201 | ||
202 | if expect_failure: | |
344929a9 JM |
203 | return |
204 | ||
0d09bd08 JM |
205 | if not done: |
206 | raise Exception("MKA not completed successfully") | |
207 | ||
208 | key_server = None | |
209 | ks_prio = 999 | |
210 | for w in wpa: | |
211 | logger.info("%s STATUS:\n%s" % (w.ifname, w.request("STATUS"))) | |
212 | addr = w.get_status_field("address") | |
213 | prio = int(w.get_status_field("Actor Priority")) | |
214 | if key_server is None or prio < ks_prio or \ | |
215 | (prio == ks_prio and lower_addr(addr, ks_addr)): | |
216 | key_server = w | |
217 | ks_addr = addr | |
218 | ks_prio = prio | |
219 | ||
220 | logger.info("Expected key server: " + key_server.ifname) | |
221 | if key_server.get_status_field("is_key_server") != "Yes": | |
222 | raise Exception("Expected key server was not elected") | |
223 | for w in wpa: | |
224 | if w != key_server and w.get_status_field("is_key_server") == "Yes": | |
225 | raise Exception("Unexpected key server") | |
344929a9 JM |
226 | |
227 | def run_macsec_psk(dev, apdev, params, prefix, integ_only=False, port0=None, | |
228 | port1=None, ckn0=None, ckn1=None, cak0=None, cak1=None, | |
229 | expect_failure=False): | |
230 | add_veth() | |
231 | ||
ead573d8 JM |
232 | cap_veth0 = os.path.join(params['logdir'], prefix + ".veth0.pcap") |
233 | cap_veth1 = os.path.join(params['logdir'], prefix + ".veth1.pcap") | |
234 | cap_macsec0 = os.path.join(params['logdir'], prefix + ".macsec0.pcap") | |
235 | cap_macsec1 = os.path.join(params['logdir'], prefix + ".macsec1.pcap") | |
236 | ||
237 | for i in range(2): | |
fab49f61 | 238 | subprocess.check_call(["ip", "link", "set", "dev", "veth%d" % i, "up"]) |
ead573d8 JM |
239 | |
240 | cmd = {} | |
241 | cmd[0] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', 'veth0', | |
242 | '-w', cap_veth0, '-s', '2000', | |
243 | '--immediate-mode'], | |
244 | stderr=open('/dev/null', 'w')) | |
245 | cmd[1] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', 'veth1', | |
246 | '-w', cap_veth1, '-s', '2000', | |
247 | '--immediate-mode'], | |
248 | stderr=open('/dev/null', 'w')) | |
249 | ||
344929a9 JM |
250 | wpa = add_wpas_interfaces() |
251 | wpas0 = wpa[0] | |
252 | wpas1 = wpa[1] | |
ead573d8 JM |
253 | |
254 | set_mka_psk_config(wpas0, integ_only=integ_only, port=port0, ckn=ckn0, | |
255 | cak=cak0) | |
256 | set_mka_psk_config(wpas1, mka_priority=100, integ_only=integ_only, | |
257 | port=port1, ckn=ckn1, cak=cak1) | |
258 | ||
259 | log_ip_macsec() | |
260 | log_ip_link() | |
261 | ||
262 | logger.info("wpas0 STATUS:\n" + wpas0.request("STATUS")) | |
263 | logger.info("wpas1 STATUS:\n" + wpas1.request("STATUS")) | |
264 | logger.info("wpas0 STATUS-DRIVER:\n" + wpas0.request("STATUS-DRIVER")) | |
265 | logger.info("wpas1 STATUS-DRIVER:\n" + wpas1.request("STATUS-DRIVER")) | |
266 | macsec_ifname0 = wpas0.get_driver_status_field("parent_ifname") | |
267 | macsec_ifname1 = wpas1.get_driver_status_field("parent_ifname") | |
268 | ||
0d09bd08 | 269 | wait_mka_done(wpa, expect_failure=expect_failure) |
ead573d8 JM |
270 | |
271 | if expect_failure: | |
ead573d8 JM |
272 | for i in range(len(cmd)): |
273 | cmd[i].terminate() | |
274 | return | |
275 | ||
276 | cmd[2] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', macsec_ifname0, | |
277 | '-w', cap_macsec0, '-s', '2000', | |
278 | '--immediate-mode'], | |
279 | stderr=open('/dev/null', 'w')) | |
280 | cmd[3] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', macsec_ifname1, | |
281 | '-w', cap_macsec1, '-s', '2000', | |
282 | '--immediate-mode'], | |
283 | stderr=open('/dev/null', 'w')) | |
284 | time.sleep(0.5) | |
285 | ||
90eb910e JM |
286 | mi0 = wpas0.get_status_field("mi") |
287 | mi1 = wpas1.get_status_field("mi") | |
288 | sci0 = wpas0.get_status_field("actor_sci") | |
289 | sci1 = wpas1.get_status_field("actor_sci") | |
290 | logger.info("wpas0 MIB:\n" + wpas0.request("MIB")) | |
291 | logger.info("wpas1 MIB:\n" + wpas1.request("MIB")) | |
292 | mib0 = wpas0.get_mib() | |
293 | mib1 = wpas1.get_mib() | |
294 | ||
295 | if mib0['ieee8021XKayMkaPeerListMI'] != mi1: | |
296 | raise Exception("Unexpected ieee8021XKayMkaPeerListMI value (0)") | |
297 | if mib0['ieee8021XKayMkaPeerListType'] != "1": | |
298 | raise Exception("Unexpected ieee8021XKayMkaPeerListType value (0)") | |
299 | if mib0['ieee8021XKayMkaPeerListSCI'] != sci1: | |
300 | raise Exception("Unexpected ieee8021XKayMkaPeerListSCI value (0)") | |
301 | if mib1['ieee8021XKayMkaPeerListMI'] != mi0: | |
302 | raise Exception("Unexpected ieee8021XKayMkaPeerListMI value (1)") | |
303 | if mib1['ieee8021XKayMkaPeerListType'] != "1": | |
304 | raise Exception("Unexpected ieee8021XKayMkaPeerListType value (1)") | |
305 | if mib1['ieee8021XKayMkaPeerListSCI'] != sci0: | |
306 | raise Exception("Unexpected ieee8021XKayMkaPeerListSCI value (1)") | |
307 | ||
ead573d8 JM |
308 | logger.info("wpas0 STATUS:\n" + wpas0.request("STATUS")) |
309 | logger.info("wpas1 STATUS:\n" + wpas1.request("STATUS")) | |
310 | log_ip_macsec() | |
311 | hwsim_utils.test_connectivity(wpas0, wpas1, | |
312 | ifname1=macsec_ifname0, | |
313 | ifname2=macsec_ifname1, | |
314 | send_len=1400) | |
315 | log_ip_macsec() | |
316 | ||
317 | time.sleep(1) | |
318 | for i in range(len(cmd)): | |
319 | cmd[i].terminate() | |
320 | ||
a2acadf6 JM |
321 | def cleanup_macsec_br(count): |
322 | wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') | |
323 | for i in range(count): | |
324 | wpas.interface_remove("veth%d" % i) | |
325 | subprocess.call(["ip", "link", "del", "veth%d" % i], | |
326 | stderr=open('/dev/null', 'w')) | |
fe5400dd | 327 | del wpas |
a2acadf6 JM |
328 | subprocess.call(["ip", "link", "set", "brveth", "down"]) |
329 | subprocess.call(["brctl", "delbr", "brveth"]) | |
330 | ||
331 | def test_macsec_psk_br2(dev, apdev): | |
332 | """MACsec PSK (bridge; 2 devices)""" | |
333 | try: | |
334 | run_macsec_psk_br(dev, apdev, 2, [10, 20]) | |
335 | finally: | |
336 | cleanup_macsec_br(count=2) | |
337 | ||
338 | def test_macsec_psk_br2_same_prio(dev, apdev): | |
339 | """MACsec PSK (bridge; 2 devices, same mka_priority)""" | |
340 | try: | |
341 | run_macsec_psk_br(dev, apdev, 2, [None, None]) | |
342 | finally: | |
343 | cleanup_macsec_br(count=2) | |
344 | ||
345 | def test_macsec_psk_br3(dev, apdev): | |
346 | """MACsec PSK (bridge; 3 devices)""" | |
347 | try: | |
348 | run_macsec_psk_br(dev, apdev, 3, [10, 20, 30]) | |
349 | finally: | |
350 | cleanup_macsec_br(count=3) | |
351 | ||
352 | def test_macsec_psk_br3_same_prio(dev, apdev): | |
353 | """MACsec PSK (bridge; 3 devices, same mka_priority)""" | |
354 | try: | |
355 | run_macsec_psk_br(dev, apdev, 3, [None, None, None]) | |
356 | finally: | |
357 | cleanup_macsec_br(count=3) | |
358 | ||
359 | def run_macsec_psk_br(dev, apdev, count, mka_priority): | |
360 | subprocess.check_call(["brctl", "addbr", "brveth"]) | |
361 | subprocess.call(["echo 8 > /sys/devices/virtual/net/brveth/bridge/group_fwd_mask"], | |
362 | shell=True) | |
363 | ||
364 | try: | |
365 | for i in range(count): | |
fab49f61 JM |
366 | subprocess.check_call(["ip", "link", "add", "veth%d" % i, |
367 | "type", "veth", | |
368 | "peer", "name", "vethbr%d" % i]) | |
a2acadf6 | 369 | subprocess.check_call(["ip", "link", "set", "vethbr%d" % i, "up"]) |
fab49f61 JM |
370 | subprocess.check_call(["brctl", "addif", "brveth", |
371 | "vethbr%d" % i]) | |
a2acadf6 JM |
372 | except subprocess.CalledProcessError: |
373 | raise HwsimSkip("veth not supported (kernel CONFIG_VETH)") | |
374 | ||
375 | subprocess.check_call(["ip", "link", "set", "brveth", "up"]) | |
376 | ||
377 | log_ip_link() | |
378 | ||
379 | wpa = add_wpas_interfaces(count=count) | |
380 | for i in range(count): | |
381 | set_mka_psk_config(wpa[i], mka_priority=mka_priority[i]) | |
382 | wpa[i].dump_monitor() | |
383 | wait_mka_done(wpa) | |
384 | ||
385 | macsec_ifname = [] | |
386 | for i in range(count): | |
387 | macsec_ifname.append(wpa[i].get_driver_status_field("parent_ifname")) | |
388 | ||
389 | timeout = 2 | |
390 | max_tries = 2 if count > 2 else 1 | |
391 | success_seen = False | |
392 | failure_seen = False | |
393 | for i in range(1, count): | |
394 | try: | |
395 | hwsim_utils.test_connectivity(wpa[0], wpa[i], | |
396 | ifname1=macsec_ifname[0], | |
397 | ifname2=macsec_ifname[i], | |
398 | send_len=1400, | |
399 | timeout=timeout, max_tries=max_tries) | |
400 | success_seen = True | |
401 | logger.info("Traffic test %d<->%d success" % (0, i)) | |
402 | except: | |
403 | failure_seen = True | |
404 | logger.info("Traffic test %d<->%d failure" % (0, i)) | |
405 | for i in range(2, count): | |
406 | try: | |
407 | hwsim_utils.test_connectivity(wpa[1], wpa[i], | |
408 | ifname1=macsec_ifname[1], | |
409 | ifname2=macsec_ifname[i], | |
410 | send_len=1400, | |
411 | timeout=timeout, max_tries=max_tries) | |
412 | success_seen = True | |
413 | logger.info("Traffic test %d<->%d success" % (1, i)) | |
414 | except: | |
415 | failure_seen = True | |
416 | logger.info("Traffic test %d<->%d failure" % (1, i)) | |
417 | ||
418 | if not success_seen: | |
419 | raise Exception("None of the data traffic tests succeeded") | |
420 | ||
421 | # Something seems to be failing with three device tests semi-regularly, so | |
422 | # do not report this as a failed test case until the real reason behind | |
423 | # those failures have been determined. | |
424 | if failure_seen: | |
425 | if count < 3: | |
426 | raise Exception("Data traffic test failed") | |
427 | else: | |
428 | logger.info("Data traffic test failed - ignore for now for >= 3 device cases") | |
429 | ||
fe5400dd JM |
430 | for i in range(count): |
431 | wpa[i].dump_monitor() | |
432 | for i in range(count): | |
433 | del wpa[0] | |
434 | ||
ead573d8 JM |
435 | def test_macsec_psk_ns(dev, apdev, params): |
436 | """MACsec PSK (netns)""" | |
437 | try: | |
438 | run_macsec_psk_ns(dev, apdev, params) | |
439 | finally: | |
440 | prefix = "macsec_psk_ns" | |
441 | pidfile = os.path.join(params['logdir'], prefix + ".pid") | |
442 | for i in range(2): | |
443 | was_running = False | |
444 | if os.path.exists(pidfile + str(i)): | |
445 | with open(pidfile + str(i), 'r') as f: | |
446 | pid = int(f.read().strip()) | |
447 | logger.info("wpa_supplicant for wpas%d still running with pid %d - kill it" % (i, pid)) | |
448 | was_running = True | |
449 | os.kill(pid, signal.SIGTERM) | |
450 | if was_running: | |
451 | time.sleep(1) | |
452 | ||
453 | subprocess.call(["ip", "netns", "exec", "ns0", | |
454 | "ip", "link", "del", "veth0"], | |
455 | stderr=open('/dev/null', 'w')) | |
456 | subprocess.call(["ip", "link", "del", "veth0"], | |
457 | stderr=open('/dev/null', 'w')) | |
458 | log_ip_link_ns() | |
459 | subprocess.call(["ip", "netns", "delete", "ns0"], | |
460 | stderr=open('/dev/null', 'w')) | |
461 | subprocess.call(["ip", "netns", "delete", "ns1"], | |
462 | stderr=open('/dev/null', 'w')) | |
463 | ||
464 | def log_ip_macsec_ns(): | |
fab49f61 | 465 | cmd = subprocess.Popen(["ip", "macsec", "show"], |
ead573d8 JM |
466 | stdout=subprocess.PIPE, |
467 | stderr=open('/dev/null', 'w')) | |
1c48c9bc | 468 | res = cmd.stdout.read().decode() |
ead573d8 JM |
469 | cmd.stdout.close() |
470 | logger.info("ip macsec show:\n" + res) | |
471 | ||
fab49f61 JM |
472 | cmd = subprocess.Popen(["ip", "netns", "exec", "ns0", |
473 | "ip", "macsec", "show"], | |
ead573d8 JM |
474 | stdout=subprocess.PIPE, |
475 | stderr=open('/dev/null', 'w')) | |
1c48c9bc | 476 | res = cmd.stdout.read().decode() |
ead573d8 JM |
477 | cmd.stdout.close() |
478 | logger.info("ip macsec show (ns0):\n" + res) | |
479 | ||
fab49f61 JM |
480 | cmd = subprocess.Popen(["ip", "netns", "exec", "ns1", |
481 | "ip", "macsec", "show"], | |
ead573d8 JM |
482 | stdout=subprocess.PIPE, |
483 | stderr=open('/dev/null', 'w')) | |
1c48c9bc | 484 | res = cmd.stdout.read().decode() |
ead573d8 JM |
485 | cmd.stdout.close() |
486 | logger.info("ip macsec show (ns1):\n" + res) | |
487 | ||
488 | def log_ip_link_ns(): | |
fab49f61 | 489 | cmd = subprocess.Popen(["ip", "link", "show"], |
ead573d8 | 490 | stdout=subprocess.PIPE) |
1c48c9bc | 491 | res = cmd.stdout.read().decode() |
ead573d8 JM |
492 | cmd.stdout.close() |
493 | logger.info("ip link:\n" + res) | |
494 | ||
fab49f61 JM |
495 | cmd = subprocess.Popen(["ip", "netns", "exec", "ns0", |
496 | "ip", "link", "show"], | |
ead573d8 JM |
497 | stdout=subprocess.PIPE, |
498 | stderr=open('/dev/null', 'w')) | |
1c48c9bc | 499 | res = cmd.stdout.read().decode() |
ead573d8 JM |
500 | cmd.stdout.close() |
501 | logger.info("ip link show (ns0):\n" + res) | |
502 | ||
fab49f61 JM |
503 | cmd = subprocess.Popen(["ip", "netns", "exec", "ns1", |
504 | "ip", "link", "show"], | |
ead573d8 JM |
505 | stdout=subprocess.PIPE, |
506 | stderr=open('/dev/null', 'w')) | |
1c48c9bc | 507 | res = cmd.stdout.read().decode() |
ead573d8 JM |
508 | cmd.stdout.close() |
509 | logger.info("ip link show (ns1):\n" + res) | |
510 | ||
511 | def write_conf(conffile, mka_priority=None): | |
512 | with open(conffile, 'w') as f: | |
513 | f.write("ctrl_interface=DIR=/var/run/wpa_supplicant\n") | |
514 | f.write("eapol_version=3\n") | |
515 | f.write("ap_scan=0\n") | |
516 | f.write("fast_reauth=1\n") | |
517 | f.write("network={\n") | |
26b0c290 MH |
518 | f.write(" key_mgmt=NONE\n") |
519 | f.write(" mka_cak=000102030405060708090a0b0c0d0e0f\n") | |
520 | f.write(" mka_ckn=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\n") | |
ead573d8 JM |
521 | if mka_priority is not None: |
522 | f.write(" mka_priority=%d\n" % mka_priority) | |
26b0c290 MH |
523 | f.write(" eapol_flags=0\n") |
524 | f.write(" macsec_policy=1\n") | |
ead573d8 JM |
525 | f.write("}\n") |
526 | ||
527 | def run_macsec_psk_ns(dev, apdev, params): | |
528 | try: | |
fab49f61 JM |
529 | subprocess.check_call(["ip", "link", "add", "veth0", "type", "veth", |
530 | "peer", "name", "veth1"]) | |
ead573d8 JM |
531 | except subprocess.CalledProcessError: |
532 | raise HwsimSkip("veth not supported (kernel CONFIG_VETH)") | |
533 | ||
534 | prefix = "macsec_psk_ns" | |
535 | conffile = os.path.join(params['logdir'], prefix + ".conf") | |
536 | pidfile = os.path.join(params['logdir'], prefix + ".pid") | |
537 | logfile0 = os.path.join(params['logdir'], prefix + ".veth0.log") | |
538 | logfile1 = os.path.join(params['logdir'], prefix + ".veth1.log") | |
539 | cap_veth0 = os.path.join(params['logdir'], prefix + ".veth0.pcap") | |
540 | cap_veth1 = os.path.join(params['logdir'], prefix + ".veth1.pcap") | |
541 | cap_macsec0 = os.path.join(params['logdir'], prefix + ".macsec0.pcap") | |
542 | cap_macsec1 = os.path.join(params['logdir'], prefix + ".macsec1.pcap") | |
543 | ||
544 | for i in range(2): | |
545 | try: | |
fab49f61 | 546 | subprocess.check_call(["ip", "netns", "add", "ns%d" % i]) |
ead573d8 JM |
547 | except subprocess.CalledProcessError: |
548 | raise HwsimSkip("network namespace not supported (kernel CONFIG_NAMESPACES, CONFIG_NET_NS)") | |
fab49f61 JM |
549 | subprocess.check_call(["ip", "link", "set", "veth%d" % i, |
550 | "netns", "ns%d" %i]) | |
551 | subprocess.check_call(["ip", "netns", "exec", "ns%d" % i, | |
552 | "ip", "link", "set", "dev", "veth%d" % i, | |
553 | "up"]) | |
ead573d8 JM |
554 | |
555 | cmd = {} | |
556 | cmd[0] = subprocess.Popen(['ip', 'netns', 'exec', 'ns0', | |
557 | 'tcpdump', '-p', '-U', '-i', 'veth0', | |
558 | '-w', cap_veth0, '-s', '2000', | |
559 | '--immediate-mode'], | |
560 | stderr=open('/dev/null', 'w')) | |
561 | cmd[1] = subprocess.Popen(['ip', 'netns', 'exec', 'ns1', | |
562 | 'tcpdump', '-p', '-U', '-i', 'veth1', | |
563 | '-w', cap_veth1, '-s', '2000', | |
564 | '--immediate-mode'], | |
565 | stderr=open('/dev/null', 'w')) | |
566 | ||
567 | write_conf(conffile + '0') | |
568 | write_conf(conffile + '1', mka_priority=100) | |
569 | ||
570 | prg = os.path.join(params['logdir'], | |
571 | 'alt-wpa_supplicant/wpa_supplicant/wpa_supplicant') | |
572 | if not os.path.exists(prg): | |
573 | prg = '../../wpa_supplicant/wpa_supplicant' | |
574 | ||
fab49f61 JM |
575 | arg = ["ip", "netns", "exec", "ns0", |
576 | prg, '-BdddtKW', '-P', pidfile + '0', '-f', logfile0, | |
577 | '-g', '/tmp/wpas-veth0', | |
578 | '-Dmacsec_linux', '-c', conffile + '0', '-i', "veth0"] | |
ead573d8 JM |
579 | logger.info("Start wpa_supplicant: " + str(arg)) |
580 | try: | |
581 | subprocess.check_call(arg) | |
582 | except subprocess.CalledProcessError: | |
3281c159 | 583 | raise HwsimSkip("macsec supported (wpa_supplicant CONFIG_MACSEC, CONFIG_DRIVER_MACSEC_LINUX; kernel CONFIG_MACSEC)") |
ead573d8 JM |
584 | |
585 | if os.path.exists("wpa_supplicant-macsec2"): | |
586 | logger.info("Use alternative wpa_supplicant binary for one of the macsec devices") | |
587 | prg = "wpa_supplicant-macsec2" | |
588 | ||
fab49f61 JM |
589 | arg = ["ip", "netns", "exec", "ns1", |
590 | prg, '-BdddtKW', '-P', pidfile + '1', '-f', logfile1, | |
591 | '-g', '/tmp/wpas-veth1', | |
592 | '-Dmacsec_linux', '-c', conffile + '1', '-i', "veth1"] | |
ead573d8 JM |
593 | logger.info("Start wpa_supplicant: " + str(arg)) |
594 | subprocess.check_call(arg) | |
595 | ||
596 | wpas0 = WpaSupplicant('veth0', '/tmp/wpas-veth0') | |
597 | wpas1 = WpaSupplicant('veth1', '/tmp/wpas-veth1') | |
598 | ||
599 | log_ip_macsec_ns() | |
600 | log_ip_link_ns() | |
601 | ||
602 | logger.info("wpas0 STATUS:\n" + wpas0.request("STATUS")) | |
603 | logger.info("wpas1 STATUS:\n" + wpas1.request("STATUS")) | |
604 | logger.info("wpas0 STATUS-DRIVER:\n" + wpas0.request("STATUS-DRIVER")) | |
605 | logger.info("wpas1 STATUS-DRIVER:\n" + wpas1.request("STATUS-DRIVER")) | |
606 | macsec_ifname0 = wpas0.get_driver_status_field("parent_ifname") | |
607 | macsec_ifname1 = wpas1.get_driver_status_field("parent_ifname") | |
608 | ||
609 | for i in range(10): | |
610 | key_tx0 = int(wpas0.get_status_field("Number of Keys Distributed")) | |
611 | key_rx0 = int(wpas0.get_status_field("Number of Keys Received")) | |
612 | key_tx1 = int(wpas1.get_status_field("Number of Keys Distributed")) | |
613 | key_rx1 = int(wpas1.get_status_field("Number of Keys Received")) | |
614 | if key_rx0 > 0 and key_tx1 > 0: | |
615 | break | |
616 | time.sleep(1) | |
617 | ||
618 | cmd[2] = subprocess.Popen(['ip', 'netns', 'exec', 'ns0', | |
619 | 'tcpdump', '-p', '-U', '-i', macsec_ifname0, | |
620 | '-w', cap_macsec0, '-s', '2000', | |
621 | '--immediate-mode'], | |
622 | stderr=open('/dev/null', 'w')) | |
623 | cmd[3] = subprocess.Popen(['ip', 'netns', 'exec', 'ns0', | |
624 | 'tcpdump', '-p', '-U', '-i', macsec_ifname1, | |
625 | '-w', cap_macsec1, '-s', '2000', | |
626 | '--immediate-mode'], | |
627 | stderr=open('/dev/null', 'w')) | |
628 | time.sleep(0.5) | |
629 | ||
630 | logger.info("wpas0 STATUS:\n" + wpas0.request("STATUS")) | |
631 | logger.info("wpas1 STATUS:\n" + wpas1.request("STATUS")) | |
632 | log_ip_macsec_ns() | |
633 | hwsim_utils.test_connectivity(wpas0, wpas1, | |
634 | ifname1=macsec_ifname0, | |
635 | ifname2=macsec_ifname1, | |
636 | send_len=1400) | |
637 | log_ip_macsec_ns() | |
638 | ||
639 | subprocess.check_call(['ip', 'netns', 'exec', 'ns0', | |
640 | 'ip', 'addr', 'add', '192.168.248.17/30', | |
641 | 'dev', macsec_ifname0]) | |
642 | subprocess.check_call(['ip', 'netns', 'exec', 'ns1', | |
643 | 'ip', 'addr', 'add', '192.168.248.18/30', | |
644 | 'dev', macsec_ifname1]) | |
645 | c = subprocess.Popen(['ip', 'netns', 'exec', 'ns0', | |
646 | 'ping', '-c', '2', '192.168.248.18'], | |
647 | stdout=subprocess.PIPE) | |
1c48c9bc | 648 | res = c.stdout.read().decode() |
ead573d8 JM |
649 | c.stdout.close() |
650 | logger.info("ping:\n" + res) | |
651 | if "2 packets transmitted, 2 received" not in res: | |
652 | raise Exception("ping did not work") | |
653 | ||
654 | wpas0.request("TERMINATE") | |
655 | del wpas0 | |
656 | wpas1.request("TERMINATE") | |
657 | del wpas1 | |
658 | ||
659 | time.sleep(1) | |
660 | for i in range(len(cmd)): | |
661 | cmd[i].terminate() | |
344929a9 JM |
662 | |
663 | def test_macsec_psk_fail_cp(dev, apdev): | |
664 | """MACsec PSK local failures in CP state machine""" | |
665 | try: | |
666 | add_veth() | |
667 | wpa = add_wpas_interfaces() | |
668 | set_mka_psk_config(wpa[0]) | |
669 | with alloc_fail(wpa[0], 1, "sm_CP_RECEIVE_Enter"): | |
670 | set_mka_psk_config(wpa[1]) | |
671 | wait_fail_trigger(wpa[0], "GET_ALLOC_FAIL", max_iter=100) | |
672 | ||
0d09bd08 | 673 | wait_mka_done(wpa) |
344929a9 JM |
674 | finally: |
675 | cleanup_macsec() | |
676 | ||
677 | def test_macsec_psk_fail_cp2(dev, apdev): | |
678 | """MACsec PSK local failures in CP state machine (2)""" | |
679 | try: | |
680 | add_veth() | |
681 | wpa = add_wpas_interfaces() | |
682 | set_mka_psk_config(wpa[0]) | |
683 | with alloc_fail(wpa[1], 1, "ieee802_1x_cp_sm_init"): | |
684 | set_mka_psk_config(wpa[1]) | |
685 | wait_fail_trigger(wpa[1], "GET_ALLOC_FAIL", max_iter=100) | |
686 | ||
0d09bd08 | 687 | wait_mka_done(wpa) |
344929a9 JM |
688 | finally: |
689 | cleanup_macsec() |