1# wmediumd validity checks 2# Copyright (c) 2015, Intel Deutschland GmbH 3# 4# This software may be distributed under the terms of the BSD license. 5# See README for more details. 6 7import tempfile, os, subprocess, errno, hwsim_utils, time 8from utils import HwsimSkip 9from wpasupplicant import WpaSupplicant 10from tshark import run_tshark 11from test_ap_open import _test_ap_open 12from test_scan import test_scan_only_one as _test_scan_only_one 13from test_wpas_mesh import check_mesh_support, check_mesh_group_added 14from test_wpas_mesh import check_mesh_peer_connected, add_open_mesh_network 15from test_wpas_mesh import check_mesh_group_removed 16 17class LocalVariables: 18 revs = [] 19 20CFG = """ 21ifaces : 22{ 23 ids = ["%s", "%s"] 24 links = ( 25 (0, 1, 30) 26 ) 27} 28""" 29 30CFG2 = """ 31ifaces : 32{ 33 ids = ["%s", "%s", "%s"] 34} 35 36model: 37{ 38 type = "prob" 39 40 links = ( 41 (0, 1, 0.000000), 42 (0, 2, 0.000000), 43 (1, 2, 1.000000) 44 ) 45} 46""" 47 48CFG3 = """ 49ifaces : 50{ 51 ids = ["%s", "%s", "%s", "%s", "%s"] 52} 53 54model: 55{ 56 type = "prob" 57 58 default_prob = 1.0 59 links = ( 60 (0, 1, 0.000000), 61 (1, 2, 0.000000), 62 (2, 3, 0.000000), 63 (3, 4, 0.000000) 64 ) 65} 66""" 67 68def get_wmediumd_version(): 69 if len(LocalVariables.revs) > 0: 70 return LocalVariables.revs 71 72 try: 73 verstr = subprocess.check_output(['wmediumd', '-V']).decode() 74 except OSError as e: 75 if e.errno == errno.ENOENT: 76 raise HwsimSkip('wmediumd not available') 77 raise 78 79 vernum = verstr.split(' ')[1][1:] 80 LocalVariables.revs = vernum.split('.') 81 for i in range(0, len(LocalVariables.revs)): 82 LocalVariables.revs[i] = int(LocalVariables.revs[i]) 83 while len(LocalVariables.revs) < 3: 84 LocalVariables.revs += [0] 85 86 return LocalVariables.revs 87 88def require_wmediumd_version(major, minor, patch): 89 revs = get_wmediumd_version() 90 if revs[0] < major or revs[1] < minor or revs[2] < patch: 91 raise HwsimSkip('wmediumd v%s.%s.%s is too old for this test' % 92 (revs[0], revs[1], revs[2])) 93 94def output_wmediumd_log(p, params, data): 95 log_file = open(os.path.abspath(os.path.join(params['logdir'], 96 'wmediumd.log')), 'a') 97 log_file.write(data) 98 log_file.close() 99 100def start_wmediumd(fn, params): 101 try: 102 p = subprocess.Popen(['wmediumd', '-c', fn], 103 stdout=subprocess.PIPE, 104 stderr=subprocess.STDOUT) 105 except OSError as e: 106 if e.errno == errno.ENOENT: 107 raise HwsimSkip('wmediumd not available') 108 raise 109 110 logs = '' 111 while True: 112 line = p.stdout.readline().decode() 113 if not line: 114 output_wmediumd_log(p, params, logs) 115 raise Exception('wmediumd was terminated unexpectedly') 116 if line.find('REGISTER SENT!') > -1: 117 break 118 logs += line 119 return p 120 121def stop_wmediumd(p, params): 122 p.terminate() 123 p.wait() 124 stdoutdata, stderrdata = p.communicate() 125 output_wmediumd_log(p, params, stdoutdata.decode()) 126 127def test_wmediumd_simple(dev, apdev, params): 128 """test a simple wmediumd configuration""" 129 fd, fn = tempfile.mkstemp() 130 try: 131 f = os.fdopen(fd, 'w') 132 f.write(CFG % (apdev[0]['bssid'], dev[0].own_addr())) 133 f.close() 134 p = start_wmediumd(fn, params) 135 try: 136 _test_ap_open(dev, apdev) 137 finally: 138 stop_wmediumd(p, params) 139 # test that releasing hwsim works correctly 140 _test_ap_open(dev, apdev) 141 finally: 142 os.unlink(fn) 143 144def test_wmediumd_path_simple(dev, apdev, params): 145 """test a mesh path""" 146 # 0 and 1 is connected 147 # 0 and 2 is connected 148 # 1 and 2 is not connected 149 # 1 --- 0 --- 2 150 # | | 151 # +-----X-----+ 152 # This tests if 1 and 2 can communicate each other via 0. 153 require_wmediumd_version(0, 3, 1) 154 fd, fn = tempfile.mkstemp() 155 try: 156 f = os.fdopen(fd, 'w') 157 f.write(CFG2 % (dev[0].own_addr(), dev[1].own_addr(), 158 dev[2].own_addr())) 159 f.close() 160 p = start_wmediumd(fn, params) 161 try: 162 _test_wmediumd_path_simple(dev, apdev) 163 finally: 164 stop_wmediumd(p, params) 165 finally: 166 os.unlink(fn) 167 168def _test_wmediumd_path_simple(dev, apdev): 169 for i in range(0, 3): 170 check_mesh_support(dev[i]) 171 add_open_mesh_network(dev[i], freq="2462", basic_rates="60 120 240") 172 173 # Check for mesh joined 174 for i in range(0, 3): 175 check_mesh_group_added(dev[i]) 176 177 state = dev[i].get_status_field("wpa_state") 178 if state != "COMPLETED": 179 raise Exception("Unexpected wpa_state on dev" + str(i) + ": " + state) 180 181 mode = dev[i].get_status_field("mode") 182 if mode != "mesh": 183 raise Exception("Unexpected mode: " + mode) 184 185 # Check for peer connected 186 check_mesh_peer_connected(dev[0]) 187 check_mesh_peer_connected(dev[0]) 188 check_mesh_peer_connected(dev[1]) 189 check_mesh_peer_connected(dev[2]) 190 191 # Test connectivity 1->2 and 2->1 192 hwsim_utils.test_connectivity(dev[1], dev[2]) 193 194 # Check mpath table on 0 195 res, data = dev[0].cmd_execute(['iw', dev[0].ifname, 'mpath', 'dump']) 196 if res != 0: 197 raise Exception("iw command failed on dev0") 198 if data.find(dev[1].own_addr() + ' ' + dev[1].own_addr()) == -1 or \ 199 data.find(dev[2].own_addr() + ' ' + dev[2].own_addr()) == -1: 200 raise Exception("mpath not found on dev0:\n" + data) 201 if data.find(dev[0].own_addr()) > -1: 202 raise Exception("invalid mpath found on dev0:\n" + data) 203 204 # Check mpath table on 1 205 res, data = dev[1].cmd_execute(['iw', dev[1].ifname, 'mpath', 'dump']) 206 if res != 0: 207 raise Exception("iw command failed on dev1") 208 if data.find(dev[0].own_addr() + ' ' + dev[0].own_addr()) == -1 or \ 209 data.find(dev[2].own_addr() + ' ' + dev[0].own_addr()) == -1: 210 raise Exception("mpath not found on dev1:\n" + data) 211 if data.find(dev[2].own_addr() + ' ' + dev[2].own_addr()) > -1 or \ 212 data.find(dev[1].own_addr()) > -1: 213 raise Exception("invalid mpath found on dev1:\n" + data) 214 215 # Check mpath table on 2 216 res, data = dev[2].cmd_execute(['iw', dev[2].ifname, 'mpath', 'dump']) 217 if res != 0: 218 raise Exception("iw command failed on dev2") 219 if data.find(dev[0].own_addr() + ' ' + dev[0].own_addr()) == -1 or \ 220 data.find(dev[1].own_addr() + ' ' + dev[0].own_addr()) == -1: 221 raise Exception("mpath not found on dev2:\n" + data) 222 if data.find(dev[1].own_addr() + ' ' + dev[1].own_addr()) > -1 or \ 223 data.find(dev[2].own_addr()) > -1: 224 raise Exception("invalid mpath found on dev2:\n" + data) 225 226 # remove mesh groups 227 for i in range(0, 3): 228 dev[i].mesh_group_remove() 229 check_mesh_group_removed(dev[i]) 230 dev[i].dump_monitor() 231 232def test_wmediumd_path_ttl(dev, apdev, params): 233 """Mesh path request TTL""" 234 # 0 --- 1 --- 2 --- 3 --- 4 235 # Test the TTL of mesh path request. 236 # If the TTL is shorter than path, the mesh path request should be dropped. 237 require_wmediumd_version(0, 3, 1) 238 239 local_dev = [] 240 for i in range(0, 3): 241 local_dev.append(dev[i]) 242 243 for i in range(5, 7): 244 wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5') 245 wpas.interface_add("wlan" + str(i)) 246 check_mesh_support(wpas) 247 temp_dev = wpas.request("MESH_INTERFACE_ADD ifname=mesh" + str(i)) 248 if "FAIL" in temp_dev: 249 raise Exception("MESH_INTERFACE_ADD failed") 250 local_dev.append(WpaSupplicant(ifname=temp_dev)) 251 252 fd, fn = tempfile.mkstemp() 253 try: 254 f = os.fdopen(fd, 'w') 255 f.write(CFG3 % (local_dev[0].own_addr(), local_dev[1].own_addr(), 256 local_dev[2].own_addr(), local_dev[3].own_addr(), 257 local_dev[4].own_addr())) 258 f.close() 259 p = start_wmediumd(fn, params) 260 try: 261 _test_wmediumd_path_ttl(local_dev, True) 262 _test_wmediumd_path_ttl(local_dev, False) 263 finally: 264 stop_wmediumd(p, params) 265 finally: 266 os.unlink(fn) 267 for i in range(5, 7): 268 wpas.interface_remove("wlan" + str(i)) 269 270def _test_wmediumd_path_ttl(dev, ok): 271 for i in range(0, 5): 272 check_mesh_support(dev[i]) 273 add_open_mesh_network(dev[i], freq="2462", basic_rates="60 120 240") 274 275 # Check for mesh joined 276 for i in range(0, 5): 277 check_mesh_group_added(dev[i]) 278 279 state = dev[i].get_status_field("wpa_state") 280 if state != "COMPLETED": 281 raise Exception("Unexpected wpa_state on dev" + str(i) + ": " + state) 282 283 mode = dev[i].get_status_field("mode") 284 if mode != "mesh": 285 raise Exception("Unexpected mode: " + mode) 286 287 # set mesh path request ttl 288 subprocess.check_call(["iw", "dev", dev[0].ifname, "set", "mesh_param", 289 "mesh_element_ttl=" + ("4" if ok else "3")]) 290 291 # Check for peer connected 292 for i in range(0, 5): 293 check_mesh_peer_connected(dev[i]) 294 for i in range(1, 4): 295 check_mesh_peer_connected(dev[i]) 296 297 # Test connectivity 0->4 and 0->4 298 hwsim_utils.test_connectivity(dev[0], dev[4], success_expected=ok) 299 300 # Check mpath table on 0 301 res, data = dev[0].cmd_execute(['iw', dev[0].ifname, 'mpath', 'dump']) 302 if res != 0: 303 raise Exception("iw command failed on dev0") 304 if ok: 305 if data.find(dev[1].own_addr() + ' ' + dev[1].own_addr()) == -1 or \ 306 data.find(dev[4].own_addr() + ' ' + dev[1].own_addr()) == -1: 307 raise Exception("mpath not found on dev0:\n" + data) 308 else: 309 if data.find(dev[1].own_addr() + ' ' + dev[1].own_addr()) == -1 or \ 310 data.find(dev[4].own_addr() + ' 00:00:00:00:00:00') == -1: 311 raise Exception("mpath not found on dev0:\n" + data) 312 if data.find(dev[0].own_addr()) > -1 or \ 313 data.find(dev[2].own_addr()) > -1 or \ 314 data.find(dev[3].own_addr()) > -1: 315 raise Exception("invalid mpath found on dev0:\n" + data) 316 317 # remove mesh groups 318 for i in range(0, 3): 319 dev[i].mesh_group_remove() 320 check_mesh_group_removed(dev[i]) 321 dev[i].dump_monitor() 322 323def test_wmediumd_path_rann(dev, apdev, params): 324 """Mesh path with RANN""" 325 # 0 and 1 is connected 326 # 0 and 2 is connected 327 # 1 and 2 is not connected 328 # 2 is mesh root and RANN enabled 329 # 1 --- 0 --- 2 330 # | | 331 # +-----X-----+ 332 # This tests if 1 and 2 can communicate each other via 0. 333 require_wmediumd_version(0, 3, 1) 334 fd, fn = tempfile.mkstemp() 335 try: 336 f = os.fdopen(fd, 'w') 337 f.write(CFG2 % (dev[0].own_addr(), dev[1].own_addr(), 338 dev[2].own_addr())) 339 f.close() 340 p = start_wmediumd(fn, params) 341 try: 342 _test_wmediumd_path_rann(dev, apdev) 343 finally: 344 stop_wmediumd(p, params) 345 finally: 346 os.unlink(fn) 347 348 capfile = os.path.join(params['logdir'], "hwsim0.pcapng") 349 350 # check Root STA address in root announcement element 351 filt = "wlan.fc.type_subtype == 0x000d && " + \ 352 "wlan_mgt.fixed.mesh_action == 0x01 && " + \ 353 "wlan_mgt.tag.number == 126" 354 out = run_tshark(capfile, filt, ["wlan.rann.root_sta"]) 355 if out is None: 356 raise Exception("No captured data found\n") 357 if out.find(dev[2].own_addr()) == -1 or \ 358 out.find(dev[0].own_addr()) > -1 or \ 359 out.find(dev[1].own_addr()) > -1: 360 raise Exception("RANN should be sent by dev2 only:\n" + out) 361 362 # check RANN interval is in range 363 filt = "wlan.sa == 02:00:00:00:02:00 && " + \ 364 "wlan.fc.type_subtype == 0x000d && " + \ 365 "wlan_mgt.fixed.mesh_action == 0x01 && " + \ 366 "wlan_mgt.tag.number == 126" 367 out = run_tshark(capfile, filt, ["frame.time_relative"]) 368 if out is None: 369 raise Exception("No captured data found\n") 370 lines = out.splitlines() 371 prev = float(lines[len(lines) - 1]) 372 for i in reversed(list(range(1, len(lines) - 1))): 373 now = float(lines[i]) 374 if prev - now < 1.0 or 3.0 < prev - now: 375 raise Exception("RANN interval " + str(prev - now) + 376 "(sec) should be close to 2.0(sec)\n") 377 prev = now 378 379 # check no one uses broadcast path request 380 filt = "wlan.da == ff:ff:ff:ff:ff:ff && " + \ 381 "wlan.fc.type_subtype == 0x000d && " + \ 382 "wlan_mgt.fixed.mesh_action == 0x01 && " + \ 383 "wlan_mgt.tag.number == 130" 384 out = run_tshark(capfile, filt, ["wlan.sa", "wlan.da"]) 385 if out is None: 386 raise Exception("No captured data found\n") 387 if len(out) > 0: 388 raise Exception("invalid broadcast path requests\n" + out) 389 390def _test_wmediumd_path_rann(dev, apdev): 391 for i in range(0, 3): 392 check_mesh_support(dev[i]) 393 add_open_mesh_network(dev[i], freq="2462", basic_rates="60 120 240") 394 395 # Check for mesh joined 396 for i in range(0, 3): 397 check_mesh_group_added(dev[i]) 398 399 state = dev[i].get_status_field("wpa_state") 400 if state != "COMPLETED": 401 raise Exception("Unexpected wpa_state on dev" + str(i) + ": " + state) 402 403 mode = dev[i].get_status_field("mode") 404 if mode != "mesh": 405 raise Exception("Unexpected mode: " + mode) 406 407 # set node 2 as RANN supported root 408 subprocess.check_call(["iw", "dev", dev[0].ifname, "set", "mesh_param", 409 "mesh_hwmp_rootmode=0"]) 410 subprocess.check_call(["iw", "dev", dev[1].ifname, "set", "mesh_param", 411 "mesh_hwmp_rootmode=0"]) 412 subprocess.check_call(["iw", "dev", dev[2].ifname, "set", "mesh_param", 413 "mesh_hwmp_rootmode=4"]) 414 subprocess.check_call(["iw", "dev", dev[2].ifname, "set", "mesh_param", 415 "mesh_hwmp_rann_interval=2000"]) 416 417 # Check for peer connected 418 check_mesh_peer_connected(dev[0]) 419 check_mesh_peer_connected(dev[0]) 420 check_mesh_peer_connected(dev[1]) 421 check_mesh_peer_connected(dev[2]) 422 423 # Wait for RANN frame 424 time.sleep(10) 425 426 # Test connectivity 1->2 and 2->1 427 hwsim_utils.test_connectivity(dev[1], dev[2]) 428 429 # Check mpath table on 0 430 res, data = dev[0].cmd_execute(['iw', dev[0].ifname, 'mpath', 'dump']) 431 if res != 0: 432 raise Exception("iw command failed on dev0") 433 if data.find(dev[1].own_addr() + ' ' + dev[1].own_addr()) == -1 or \ 434 data.find(dev[2].own_addr() + ' ' + dev[2].own_addr()) == -1: 435 raise Exception("mpath not found on dev0:\n" + data) 436 if data.find(dev[0].own_addr()) > -1: 437 raise Exception("invalid mpath found on dev0:\n" + data) 438 439 # Check mpath table on 1 440 res, data = dev[1].cmd_execute(['iw', dev[1].ifname, 'mpath', 'dump']) 441 if res != 0: 442 raise Exception("iw command failed on dev1") 443 if data.find(dev[0].own_addr() + ' ' + dev[0].own_addr()) == -1 or \ 444 data.find(dev[2].own_addr() + ' ' + dev[0].own_addr()) == -1: 445 raise Exception("mpath not found on dev1:\n" + data) 446 if data.find(dev[2].own_addr() + ' ' + dev[2].own_addr()) > -1 or \ 447 data.find(dev[1].own_addr()) > -1: 448 raise Exception("invalid mpath found on dev1:\n" + data) 449 450 # Check mpath table on 2 451 res, data = dev[2].cmd_execute(['iw', dev[2].ifname, 'mpath', 'dump']) 452 if res != 0: 453 raise Exception("iw command failed on dev2") 454 if data.find(dev[0].own_addr() + ' ' + dev[0].own_addr()) == -1 or \ 455 data.find(dev[1].own_addr() + ' ' + dev[0].own_addr()) == -1: 456 raise Exception("mpath not found on dev2:\n" + data) 457 if data.find(dev[1].own_addr() + ' ' + dev[1].own_addr()) > -1 or \ 458 data.find(dev[2].own_addr()) > -1: 459 raise Exception("invalid mpath found on dev2:\n" + data) 460 461 # remove mesh groups 462 for i in range(0, 3): 463 dev[i].mesh_group_remove() 464 check_mesh_group_removed(dev[i]) 465 dev[i].dump_monitor() 466 467def test_wmediumd_scan_only_one(dev, apdev, params): 468 """Test that scanning with a single active AP only returns that one (wmediund)""" 469 fd, fn = tempfile.mkstemp() 470 try: 471 f = os.fdopen(fd, 'w') 472 f.write(CFG % (apdev[0]['bssid'], dev[0].own_addr())) 473 f.close() 474 p = start_wmediumd(fn, params) 475 try: 476 _test_scan_only_one(dev, apdev) 477 finally: 478 stop_wmediumd(p, params) 479 finally: 480 os.unlink(fn) 481