1# FST tests related classes 2# Copyright (c) 2015, Qualcomm Atheros, Inc. 3# 4# This software may be distributed under the terms of the BSD license. 5# See README for more details. 6 7import logging 8import os 9import signal 10import time 11import re 12 13import hostapd 14import wpaspy 15import utils 16from wpasupplicant import WpaSupplicant 17 18import fst_test_common 19 20logger = logging.getLogger() 21 22def parse_fst_iface_event(ev): 23 """Parses FST iface event that comes as a string, e.g. 24 "<3>FST-EVENT-IFACE attached ifname=wlan9 group=fstg0" 25 Returns a dictionary with parsed "event_type", "ifname", and "group"; or 26 None if not an FST event or can't be parsed.""" 27 event = {} 28 if ev.find("FST-EVENT-IFACE") == -1: 29 return None 30 if ev.find("attached") != -1: 31 event['event_type'] = 'attached' 32 elif ev.find("detached") != -1: 33 event['event_type'] = 'detached' 34 else: 35 return None 36 f = re.search("ifname=(\S+)", ev) 37 if f is not None: 38 event['ifname'] = f.group(1) 39 f = re.search("group=(\S+)", ev) 40 if f is not None: 41 event['group'] = f.group(1) 42 return event 43 44def parse_fst_session_event(ev): 45 """Parses FST session event that comes as a string, e.g. 46 "<3>FST-EVENT-SESSION event_type=EVENT_FST_SESSION_STATE session_id=0 reason=REASON_STT" 47 Returns a dictionary with parsed "type", "id", and "reason"; or None if not 48 a FST event or can't be parsed""" 49 event = {} 50 if ev.find("FST-EVENT-SESSION") == -1: 51 return None 52 event['new_state'] = '' # The field always exists in the dictionary 53 f = re.search("event_type=(\S+)", ev) 54 if f is None: 55 return None 56 event['type'] = f.group(1) 57 f = re.search("session_id=(\d+)", ev) 58 if f is not None: 59 event['id'] = f.group(1) 60 f = re.search("old_state=(\S+)", ev) 61 if f is not None: 62 event['old_state'] = f.group(1) 63 f = re.search("new_state=(\S+)", ev) 64 if f is not None: 65 event['new_state'] = f.group(1) 66 f = re.search("reason=(\S+)", ev) 67 if f is not None: 68 event['reason'] = f.group(1) 69 return event 70 71def start_two_ap_sta_pairs(apdev, rsn=False): 72 """auxiliary function that creates two pairs of APs and STAs""" 73 ap1 = FstAP(apdev[0]['ifname'], 'fst_11a', 'a', 74 fst_test_common.fst_test_def_chan_a, 75 fst_test_common.fst_test_def_group, 76 fst_test_common.fst_test_def_prio_low, 77 fst_test_common.fst_test_def_llt, rsn=rsn) 78 ap1.start() 79 ap2 = FstAP(apdev[1]['ifname'], 'fst_11g', 'g', 80 fst_test_common.fst_test_def_chan_g, 81 fst_test_common.fst_test_def_group, 82 fst_test_common.fst_test_def_prio_high, 83 fst_test_common.fst_test_def_llt, rsn=rsn) 84 ap2.start() 85 86 sta1 = FstSTA('wlan5', 87 fst_test_common.fst_test_def_group, 88 fst_test_common.fst_test_def_prio_low, 89 fst_test_common.fst_test_def_llt, rsn=rsn) 90 sta1.start() 91 sta2 = FstSTA('wlan6', 92 fst_test_common.fst_test_def_group, 93 fst_test_common.fst_test_def_prio_high, 94 fst_test_common.fst_test_def_llt, rsn=rsn) 95 sta2.start() 96 97 return ap1, ap2, sta1, sta2 98 99def stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2): 100 sta1.stop() 101 sta2.stop() 102 ap1.stop() 103 ap2.stop() 104 fst_test_common.fst_clear_regdom() 105 106def connect_two_ap_sta_pairs(ap1, ap2, dev1, dev2, rsn=False): 107 """Connects a pair of stations, each one to a separate AP""" 108 dev1.scan(freq=fst_test_common.fst_test_def_freq_a) 109 dev2.scan(freq=fst_test_common.fst_test_def_freq_g) 110 111 if rsn: 112 dev1.connect(ap1, psk="12345678", 113 scan_freq=fst_test_common.fst_test_def_freq_a) 114 dev2.connect(ap2, psk="12345678", 115 scan_freq=fst_test_common.fst_test_def_freq_g) 116 else: 117 dev1.connect(ap1, key_mgmt="NONE", 118 scan_freq=fst_test_common.fst_test_def_freq_a) 119 dev2.connect(ap2, key_mgmt="NONE", 120 scan_freq=fst_test_common.fst_test_def_freq_g) 121 122def disconnect_two_ap_sta_pairs(ap1, ap2, dev1, dev2): 123 dev1.disconnect() 124 dev2.disconnect() 125 126def external_sta_connect(sta, ap, **kwargs): 127 """Connects the external station to the given AP""" 128 if not isinstance(sta, WpaSupplicant): 129 raise Exception("Bad STA object") 130 if not isinstance(ap, FstAP): 131 raise Exception("Bad AP object to connect to") 132 hap = ap.get_instance() 133 sta.connect(ap.get_ssid(), **kwargs) 134 135def disconnect_external_sta(sta, ap, check_disconnect=True): 136 """Disconnects the external station from the AP""" 137 if not isinstance(sta, WpaSupplicant): 138 raise Exception("Bad STA object") 139 if not isinstance(ap, FstAP): 140 raise Exception("Bad AP object to connect to") 141 sta.request("DISCONNECT") 142 if check_disconnect: 143 hap = ap.get_instance() 144 ev = hap.wait_event(["AP-STA-DISCONNECTED"], timeout=10) 145 if ev is None: 146 raise Exception("No disconnection event received from %s" % ap.get_ssid()) 147 148# 149# FstDevice class 150# This is the parent class for the AP (FstAP) and STA (FstSTA) that implements 151# FST functionality. 152# 153class FstDevice: 154 def __init__(self, iface, fst_group, fst_pri, fst_llt=None, rsn=False): 155 self.iface = iface 156 self.fst_group = fst_group 157 self.fst_pri = fst_pri 158 self.fst_llt = fst_llt # None llt means no llt parameter will be set 159 self.instance = None # Hostapd/WpaSupplicant instance 160 self.peer_obj = None # Peer object, must be a FstDevice child object 161 self.new_peer_addr = None # Peer MAC address for new session iface 162 self.old_peer_addr = None # Peer MAC address for old session iface 163 self.role = 'initiator' # Role: initiator/responder 164 s = self.grequest("FST-MANAGER TEST_REQUEST IS_SUPPORTED") 165 if not s.startswith('OK'): 166 raise utils.HwsimSkip("FST not supported") 167 self.rsn = rsn 168 169 def ifname(self): 170 return self.iface 171 172 def get_instance(self): 173 """Gets the Hostapd/WpaSupplicant instance""" 174 raise Exception("Virtual get_instance() called!") 175 176 def get_own_mac_address(self): 177 """Gets the device's own MAC address""" 178 raise Exception("Virtual get_own_mac_address() called!") 179 180 def get_new_peer_addr(self): 181 return self.new_peer_addr 182 183 def get_old_peer_addr(self): 184 return self.old_peer_addr 185 186 def get_actual_peer_addr(self): 187 """Gets the peer address. A connected AP/station address is returned.""" 188 raise Exception("Virtual get_actual_peer_addr() called!") 189 190 def grequest(self, req): 191 """Send request on the global control interface""" 192 raise Exception("Virtual grequest() called!") 193 194 def wait_gevent(self, events, timeout=None): 195 """Wait for a list of events on the global interface""" 196 raise Exception("Virtual wait_gevent() called!") 197 198 def request(self, req): 199 """Issue a request to the control interface""" 200 h = self.get_instance() 201 return h.request(req) 202 203 def wait_event(self, events, timeout=None): 204 """Wait for an event from the control interface""" 205 h = self.get_instance() 206 if timeout is not None: 207 return h.wait_event(events, timeout=timeout) 208 else: 209 return h.wait_event(events) 210 211 def set_old_peer_addr(self, peer_addr=None): 212 """Sets the peer address""" 213 if peer_addr is not None: 214 self.old_peer_addr = peer_addr 215 else: 216 self.old_peer_addr = self.get_actual_peer_addr() 217 218 def set_new_peer_addr(self, peer_addr=None): 219 """Sets the peer address""" 220 if peer_addr is not None: 221 self.new_peer_addr = peer_addr 222 else: 223 self.new_peer_addr = self.get_actual_peer_addr() 224 225 def add_peer(self, obj, old_peer_addr=None, new_peer_addr=None): 226 """Add peer for FST session(s). 'obj' is a FstDevice subclass object. 227 The method must be called before add_session(). 228 If peer_addr is not specified, the address of the currently connected 229 station is used.""" 230 if not isinstance(obj, FstDevice): 231 raise Exception("Peer must be a FstDevice object") 232 self.peer_obj = obj 233 self.set_old_peer_addr(old_peer_addr) 234 self.set_new_peer_addr(new_peer_addr) 235 236 def get_peer(self): 237 """Returns peer object""" 238 return self.peer_obj 239 240 def set_fst_parameters(self, group_id=None, pri=None, llt=None): 241 """Change/set new FST parameters. Can be used to start FST sessions with 242 different FST parameters than defined in the configuration file.""" 243 if group_id is not None: 244 self.fst_group = group_id 245 if pri is not None: 246 self.fst_pri = pri 247 if llt is not None: 248 self.fst_llt = llt 249 250 def get_local_mbies(self, ifname=None): 251 if_name = ifname if ifname is not None else self.iface 252 return self.grequest("FST-MANAGER TEST_REQUEST GET_LOCAL_MBIES " + if_name) 253 254 def add_session(self): 255 """Adds an FST session. add_peer() must be called calling this 256 function""" 257 if self.peer_obj is None: 258 raise Exception("Peer wasn't added before starting session") 259 self.dump_monitor() 260 grp = ' ' + self.fst_group if self.fst_group != '' else '' 261 sid = self.grequest("FST-MANAGER SESSION_ADD" + grp) 262 sid = sid.strip() 263 if sid.startswith("FAIL"): 264 raise Exception("Cannot add FST session with groupid ==" + grp) 265 self.dump_monitor() 266 return sid 267 268 def set_session_param(self, params): 269 request = "FST-MANAGER SESSION_SET" 270 if params is not None and params != '': 271 request = request + ' ' + params 272 return self.grequest(request) 273 274 def get_session_params(self, sid): 275 request = "FST-MANAGER SESSION_GET " + sid 276 res = self.grequest(request) 277 if res.startswith("FAIL"): 278 return None 279 params = {} 280 for i in res.splitlines(): 281 p = i.split('=') 282 params[p[0]] = p[1] 283 return params 284 285 def iface_peers(self, ifname): 286 grp = self.fst_group if self.fst_group != '' else '' 287 res = self.grequest("FST-MANAGER IFACE_PEERS " + grp + ' ' + ifname) 288 if res.startswith("FAIL"): 289 return None 290 return res.splitlines() 291 292 def get_peer_mbies(self, ifname, peer_addr): 293 return self.grequest("FST-MANAGER GET_PEER_MBIES %s %s" % (ifname, peer_addr)) 294 295 def list_ifaces(self): 296 grp = self.fst_group if self.fst_group != '' else '' 297 res = self.grequest("FST-MANAGER LIST_IFACES " + grp) 298 if res.startswith("FAIL"): 299 return None 300 ifaces = [] 301 for i in res.splitlines(): 302 p = i.split(':') 303 iface = {} 304 iface['name'] = p[0] 305 iface['priority'] = p[1] 306 iface['llt'] = p[2] 307 ifaces.append(iface) 308 return ifaces 309 310 def list_groups(self): 311 res = self.grequest("FST-MANAGER LIST_GROUPS") 312 if res.startswith("FAIL"): 313 return None 314 return res.splitlines() 315 316 def configure_session(self, sid, new_iface, old_iface=None): 317 """Calls session_set for a number of parameters some of which are stored 318 in "self" while others are passed to this function explicitly. If 319 old_iface is None, current iface is used; if old_iface is an empty 320 string.""" 321 self.dump_monitor() 322 oldiface = old_iface if old_iface is not None else self.iface 323 s = self.set_session_param(sid + ' old_ifname=' + oldiface) 324 if not s.startswith("OK"): 325 raise Exception("Cannot set FST session old_ifname: " + s) 326 if new_iface is not None: 327 s = self.set_session_param(sid + " new_ifname=" + new_iface) 328 if not s.startswith("OK"): 329 raise Exception("Cannot set FST session new_ifname:" + s) 330 if self.new_peer_addr is not None and self.new_peer_addr != '': 331 s = self.set_session_param(sid + " new_peer_addr=" + self.new_peer_addr) 332 if not s.startswith("OK"): 333 raise Exception("Cannot set FST session peer address:" + s + " (new)") 334 if self.old_peer_addr is not None and self.old_peer_addr != '': 335 s = self.set_session_param(sid + " old_peer_addr=" + self.old_peer_addr) 336 if not s.startswith("OK"): 337 raise Exception("Cannot set FST session peer address:" + s + " (old)") 338 if self.fst_llt is not None and self.fst_llt != '': 339 s = self.set_session_param(sid + " llt=" + self.fst_llt) 340 if not s.startswith("OK"): 341 raise Exception("Cannot set FST session llt:" + s) 342 self.dump_monitor() 343 344 def send_iface_attach_request(self, ifname, group, llt, priority): 345 request = "FST-ATTACH " + ifname + ' ' + group 346 if llt is not None: 347 request += " llt=" + llt 348 if priority is not None: 349 request += " priority=" + priority 350 res = self.grequest(request) 351 if not res.startswith("OK"): 352 raise Exception("Cannot attach FST iface: " + res) 353 354 def send_iface_detach_request(self, ifname): 355 res = self.grequest("FST-DETACH " + ifname) 356 if not res.startswith("OK"): 357 raise Exception("Cannot detach FST iface: " + res) 358 359 def send_session_setup_request(self, sid): 360 s = self.grequest("FST-MANAGER SESSION_INITIATE " + sid) 361 if not s.startswith('OK'): 362 raise Exception("Cannot send setup request: %s" % s) 363 return s 364 365 def send_session_setup_response(self, sid, response): 366 request = "FST-MANAGER SESSION_RESPOND " + sid + " " + response 367 s = self.grequest(request) 368 if not s.startswith('OK'): 369 raise Exception("Cannot send setup response: %s" % s) 370 return s 371 372 def send_test_session_setup_request(self, fsts_id, 373 additional_parameter=None): 374 request = "FST-MANAGER TEST_REQUEST SEND_SETUP_REQUEST " + fsts_id 375 if additional_parameter is not None: 376 request += " " + additional_parameter 377 s = self.grequest(request) 378 if not s.startswith('OK'): 379 raise Exception("Cannot send FST setup request: %s" % s) 380 return s 381 382 def send_test_session_setup_response(self, fsts_id, 383 response, additional_parameter=None): 384 request = "FST-MANAGER TEST_REQUEST SEND_SETUP_RESPONSE " + fsts_id + " " + response 385 if additional_parameter is not None: 386 request += " " + additional_parameter 387 s = self.grequest(request) 388 if not s.startswith('OK'): 389 raise Exception("Cannot send FST setup response: %s" % s) 390 return s 391 392 def send_test_ack_request(self, fsts_id): 393 s = self.grequest("FST-MANAGER TEST_REQUEST SEND_ACK_REQUEST " + fsts_id) 394 if not s.startswith('OK'): 395 raise Exception("Cannot send FST ack request: %s" % s) 396 return s 397 398 def send_test_ack_response(self, fsts_id): 399 s = self.grequest("FST-MANAGER TEST_REQUEST SEND_ACK_RESPONSE " + fsts_id) 400 if not s.startswith('OK'): 401 raise Exception("Cannot send FST ack response: %s" % s) 402 return s 403 404 def send_test_tear_down(self, fsts_id): 405 s = self.grequest("FST-MANAGER TEST_REQUEST SEND_TEAR_DOWN " + fsts_id) 406 if not s.startswith('OK'): 407 raise Exception("Cannot send FST tear down: %s" % s) 408 return s 409 410 def get_fsts_id_by_sid(self, sid): 411 s = self.grequest("FST-MANAGER TEST_REQUEST GET_FSTS_ID " + sid) 412 if s == ' ' or s.startswith('FAIL'): 413 raise Exception("Cannot get fsts_id for sid == %s" % sid) 414 return int(s) 415 416 def wait_for_iface_event(self, timeout): 417 while True: 418 ev = self.wait_gevent(["FST-EVENT-IFACE"], timeout) 419 if ev is None: 420 raise Exception("No FST-EVENT-IFACE received") 421 event = parse_fst_iface_event(ev) 422 if event is None: 423 # We can't parse so it's not our event, wait for next one 424 continue 425 return event 426 427 def wait_for_session_event(self, timeout, events_to_ignore=[], 428 events_to_count=[]): 429 while True: 430 ev = self.wait_gevent(["FST-EVENT-SESSION"], timeout) 431 if ev is None: 432 raise Exception("No FST-EVENT-SESSION received") 433 event = parse_fst_session_event(ev) 434 if event is None: 435 # We can't parse so it's not our event, wait for next one 436 continue 437 if len(events_to_ignore) > 0: 438 if event['type'] in events_to_ignore: 439 continue 440 elif len(events_to_count) > 0: 441 if event['type'] not in events_to_count: 442 continue 443 return event 444 445 def initiate_session(self, sid, response="accept"): 446 """Initiates FST session with given session id 'sid'. 447 'response' is the session respond answer: "accept", "reject", or a 448 special "timeout" value to skip the response in order to test session 449 timeouts. 450 Returns: "OK" - session has been initiated, otherwise the reason for the 451 reset: REASON_REJECT, REASON_STT.""" 452 strsid = ' ' + sid if sid != '' else '' 453 s = self.grequest("FST-MANAGER SESSION_INITIATE"+ strsid) 454 if not s.startswith('OK'): 455 raise Exception("Cannot initiate fst session: %s" % s) 456 ev = self.peer_obj.wait_gevent(["FST-EVENT-SESSION"], timeout=5) 457 if ev is None: 458 raise Exception("No FST-EVENT-SESSION received") 459 # We got FST event 460 event = parse_fst_session_event(ev) 461 if event == None: 462 raise Exception("Unrecognized FST event: " % ev) 463 if event['type'] != 'EVENT_FST_SETUP': 464 raise Exception("Expected FST_SETUP event, got: " + event['type']) 465 ev = self.peer_obj.wait_gevent(["FST-EVENT-SESSION"], timeout=5) 466 if ev is None: 467 raise Exception("No FST-EVENT-SESSION received") 468 event = parse_fst_session_event(ev) 469 if event == None: 470 raise Exception("Unrecognized FST event: " % ev) 471 if event['type'] != 'EVENT_FST_SESSION_STATE': 472 raise Exception("Expected EVENT_FST_SESSION_STATE event, got: " + event['type']) 473 if event['new_state'] != "SETUP_COMPLETION": 474 raise Exception("Expected new state SETUP_COMPLETION, got: " + event['new_state']) 475 if response == '': 476 return 'OK' 477 if response != "timeout": 478 s = self.peer_obj.grequest("FST-MANAGER SESSION_RESPOND "+ event['id'] + " " + response) # Or reject 479 if not s.startswith('OK'): 480 raise Exception("Error session_respond: %s" % s) 481 # Wait for EVENT_FST_SESSION_STATE events. We should get at least 2 482 # events. The 1st event will be EVENT_FST_SESSION_STATE 483 # old_state=INITIAL new_state=SETUP_COMPLETED. The 2nd event will be 484 # either EVENT_FST_ESTABLISHED with the session id or 485 # EVENT_FST_SESSION_STATE with new_state=INITIAL if the session was 486 # reset, the reason field will tell why. 487 result = '' 488 while result == '': 489 ev = self.wait_gevent(["FST-EVENT-SESSION"], timeout=5) 490 if ev is None: 491 break # No session event received 492 event = parse_fst_session_event(ev) 493 if event == None: 494 # We can't parse so it's not our event, wait for next one 495 continue 496 if event['type'] == 'EVENT_FST_ESTABLISHED': 497 result = "OK" 498 break 499 elif event['type'] == "EVENT_FST_SESSION_STATE": 500 if event['new_state'] == "INITIAL": 501 # Session was reset, the only reason to get back to initial 502 # state. 503 result = event['reason'] 504 break 505 if result == '': 506 raise Exception("No event for session respond") 507 return result 508 509 def transfer_session(self, sid): 510 """Transfers the session. 'sid' is the session id. 'hsta' is the 511 station-responder object. 512 Returns: REASON_SWITCH - the session has been transferred successfully 513 or a REASON_... reported by the reset event.""" 514 request = "FST-MANAGER SESSION_TRANSFER" 515 self.dump_monitor() 516 if sid != '': 517 request += ' ' + sid 518 s = self.grequest(request) 519 if not s.startswith('OK'): 520 raise Exception("Cannot transfer fst session: %s" % s) 521 result = '' 522 while result == '': 523 ev = self.peer_obj.wait_gevent(["FST-EVENT-SESSION"], timeout=5) 524 if ev is None: 525 raise Exception("Missing session transfer event") 526 # We got FST event. We expect TRANSITION_CONFIRMED state and then 527 # INITIAL (reset) with the reason (e.g. "REASON_SWITCH"). 528 # Right now we'll be waiting for the reset event and record the 529 # reason. 530 event = parse_fst_session_event(ev) 531 if event == None: 532 raise Exception("Unrecognized FST event: " % ev) 533 if event['new_state'] == 'INITIAL': 534 result = event['reason'] 535 self.dump_monitor() 536 return result 537 538 def wait_for_tear_down(self): 539 ev = self.wait_gevent(["FST-EVENT-SESSION"], timeout=5) 540 if ev is None: 541 raise Exception("No FST-EVENT-SESSION received") 542 # We got FST event 543 event = parse_fst_session_event(ev) 544 if event == None: 545 raise Exception("Unrecognized FST event: " % ev) 546 if event['type'] != 'EVENT_FST_SESSION_STATE': 547 raise Exception("Expected EVENT_FST_SESSION_STATE event, got: " + event['type']) 548 if event['new_state'] != "INITIAL": 549 raise Exception("Expected new state INITIAL, got: " + event['new_state']) 550 if event['reason'] != 'REASON_TEARDOWN': 551 raise Exception("Expected reason REASON_TEARDOWN, got: " + event['reason']) 552 553 def teardown_session(self, sid): 554 """Tears down FST session with a given session id ('sid')""" 555 strsid = ' ' + sid if sid != '' else '' 556 s = self.grequest("FST-MANAGER SESSION_TEARDOWN" + strsid) 557 if not s.startswith('OK'): 558 raise Exception("Cannot tear down fst session: %s" % s) 559 self.peer_obj.wait_for_tear_down() 560 561 562 def remove_session(self, sid, wait_for_tear_down=True): 563 """Removes FST session with a given session id ('sid')""" 564 strsid = ' ' + sid if sid != '' else '' 565 s = self.grequest("FST-MANAGER SESSION_REMOVE" + strsid) 566 if not s.startswith('OK'): 567 raise Exception("Cannot remove fst session: %s" % s) 568 if wait_for_tear_down == True: 569 self.peer_obj.wait_for_tear_down() 570 571 def remove_all_sessions(self): 572 """Removes FST session with a given session id ('sid')""" 573 grp = ' ' + self.fst_group if self.fst_group != '' else '' 574 s = self.grequest("FST-MANAGER LIST_SESSIONS" + grp) 575 if not s.startswith('FAIL'): 576 for sid in s.splitlines(): 577 sid = sid.strip() 578 if len(sid) != 0: 579 self.remove_session(sid, wait_for_tear_down=False) 580 581 582# 583# FstAP class 584# 585class FstAP(FstDevice): 586 def __init__(self, iface, ssid, mode, chan, fst_group, fst_pri, 587 fst_llt=None, rsn=False): 588 """If fst_group is empty, then FST parameters will not be set 589 If fst_llt is empty, the parameter will not be set and the default value 590 is expected to be configured.""" 591 self.ssid = ssid 592 self.mode = mode 593 self.chan = chan 594 self.reg_ctrl = fst_test_common.HapdRegCtrl() 595 self.reg_ctrl.add_ap(iface, self.chan) 596 self.global_instance = hostapd.HostapdGlobal() 597 FstDevice.__init__(self, iface, fst_group, fst_pri, fst_llt, rsn) 598 599 def start(self, return_early=False): 600 """Starts AP the "standard" way as it was intended by hostapd tests. 601 This will work only when FST supports fully dynamically loading 602 parameters in hostapd.""" 603 params = {} 604 params['ssid'] = self.ssid 605 params['hw_mode'] = self.mode 606 params['channel'] = self.chan 607 params['country_code'] = 'US' 608 if self.rsn: 609 params['wpa'] = '2' 610 params['wpa_key_mgmt'] = 'WPA-PSK' 611 params['rsn_pairwise'] = 'CCMP' 612 params['wpa_passphrase'] = '12345678' 613 self.hapd = hostapd.add_ap(self.iface, params) 614 if not self.hapd.ping(): 615 raise Exception("Could not ping FST hostapd") 616 self.reg_ctrl.start() 617 self.get_global_instance() 618 if return_early: 619 return self.hapd 620 if len(self.fst_group) != 0: 621 self.send_iface_attach_request(self.iface, self.fst_group, 622 self.fst_llt, self.fst_pri) 623 return self.hapd 624 625 def stop(self): 626 """Removes the AP, To be used when dynamic fst APs are implemented in 627 hostapd.""" 628 if len(self.fst_group) != 0: 629 self.remove_all_sessions() 630 try: 631 self.send_iface_detach_request(self.iface) 632 except Exception as e: 633 logger.info(str(e)) 634 self.reg_ctrl.stop() 635 del self.global_instance 636 self.global_instance = None 637 638 def get_instance(self): 639 """Return the Hostapd/WpaSupplicant instance""" 640 if self.instance is None: 641 self.instance = hostapd.Hostapd(self.iface) 642 return self.instance 643 644 def get_global_instance(self): 645 return self.global_instance 646 647 def get_own_mac_address(self): 648 """Gets the device's own MAC address""" 649 h = self.get_instance() 650 status = h.get_status() 651 return status['bssid[0]'] 652 653 def get_actual_peer_addr(self): 654 """Gets the peer address. A connected station address is returned.""" 655 # Use the device instance, the global control interface doesn't have 656 # station address 657 h = self.get_instance() 658 sta = h.get_sta(None) 659 if sta is None or 'addr' not in sta: 660 # Maybe station is not connected? 661 addr = None 662 else: 663 addr = sta['addr'] 664 return addr 665 666 def grequest(self, req): 667 """Send request on the global control interface""" 668 logger.debug("FstAP::grequest: " + req) 669 h = self.get_global_instance() 670 return h.request(req) 671 672 def wait_gevent(self, events, timeout=None): 673 """Wait for a list of events on the global interface""" 674 h = self.get_global_instance() 675 if timeout is not None: 676 return h.wait_event(events, timeout=timeout) 677 else: 678 return h.wait_event(events) 679 680 def get_ssid(self): 681 return self.ssid 682 683 def dump_monitor(self): 684 """Dump control interface monitor events""" 685 if self.instance: 686 self.instance.dump_monitor() 687 688# 689# FstSTA class 690# 691class FstSTA(FstDevice): 692 def __init__(self, iface, fst_group, fst_pri, fst_llt=None, rsn=False): 693 """If fst_group is empty, then FST parameters will not be set 694 If fst_llt is empty, the parameter will not be set and the default value 695 is expected to be configured.""" 696 FstDevice.__init__(self, iface, fst_group, fst_pri, fst_llt, rsn) 697 self.connected = None # FstAP object the station is connected to 698 699 def start(self): 700 """Current implementation involves running another instance of 701 wpa_supplicant with fixed FST STAs configurations. When any type of 702 dynamic STA loading is implemented, rewrite the function similarly to 703 FstAP.""" 704 h = self.get_instance() 705 h.interface_add(self.iface, drv_params="force_connect_cmd=1") 706 if not h.global_ping(): 707 raise Exception("Could not ping FST wpa_supplicant") 708 if len(self.fst_group) != 0: 709 self.send_iface_attach_request(self.iface, self.fst_group, 710 self.fst_llt, self.fst_pri) 711 return None 712 713 def stop(self): 714 """Removes the STA. In a static (temporary) implementation does nothing, 715 the STA will be removed when the fst wpa_supplicant process is killed by 716 fstap.cleanup().""" 717 h = self.get_instance() 718 h.dump_monitor() 719 if len(self.fst_group) != 0: 720 self.remove_all_sessions() 721 self.send_iface_detach_request(self.iface) 722 h.dump_monitor() 723 h.interface_remove(self.iface) 724 h.close_ctrl() 725 del h 726 self.instance = None 727 728 def get_instance(self): 729 """Return the Hostapd/WpaSupplicant instance""" 730 if self.instance is None: 731 self.instance = WpaSupplicant(global_iface='/tmp/wpas-wlan5') 732 return self.instance 733 734 def get_own_mac_address(self): 735 """Gets the device's own MAC address""" 736 h = self.get_instance() 737 status = h.get_status() 738 return status['address'] 739 740 def get_actual_peer_addr(self): 741 """Gets the peer address. A connected station address is returned""" 742 h = self.get_instance() 743 status = h.get_status() 744 return status['bssid'] 745 746 def grequest(self, req): 747 """Send request on the global control interface""" 748 logger.debug("FstSTA::grequest: " + req) 749 h = self.get_instance() 750 return h.global_request(req) 751 752 def wait_gevent(self, events, timeout=None): 753 """Wait for a list of events on the global interface""" 754 h = self.get_instance() 755 if timeout is not None: 756 return h.wait_global_event(events, timeout=timeout) 757 else: 758 return h.wait_global_event(events) 759 760 def scan(self, freq=None, no_wait=False, only_new=False): 761 """Issue Scan with given parameters. Returns the BSS dictionary for the 762 AP found (the 1st BSS found. TODO: What if the AP required is not the 763 1st in list?) or None if no BSS found. None call be also a result of 764 no_wait=True. Note, request("SCAN_RESULTS") can be used to get all the 765 results at once.""" 766 h = self.get_instance() 767 h.dump_monitor() 768 h.scan(None, freq, no_wait, only_new) 769 r = h.get_bss('0') 770 h.dump_monitor() 771 return r 772 773 def connect(self, ap, **kwargs): 774 """Connects to the given AP""" 775 if not isinstance(ap, FstAP): 776 raise Exception("Bad AP object to connect to") 777 h = self.get_instance() 778 hap = ap.get_instance() 779 h.dump_monitor() 780 h.connect(ap.get_ssid(), **kwargs) 781 h.dump_monitor() 782 self.connected = ap 783 784 def connect_to_external_ap(self, ap, ssid, check_connection=True, **kwargs): 785 """Connects to the given external AP""" 786 if not isinstance(ap, hostapd.Hostapd): 787 raise Exception("Bad AP object to connect to") 788 h = self.get_instance() 789 h.dump_monitor() 790 h.connect(ssid, **kwargs) 791 self.connected = ap 792 if check_connection: 793 ev = ap.wait_event(["AP-STA-CONNECTED"], timeout=10) 794 if ev is None: 795 self.connected = None 796 raise Exception("No connection event received from %s" % ssid) 797 h.dump_monitor() 798 799 def disconnect(self, check_disconnect=True): 800 """Disconnects from the AP the station is currently connected to""" 801 if self.connected is not None: 802 h = self.get_instance() 803 h.dump_monitor() 804 h.request("DISCONNECT") 805 if check_disconnect: 806 hap = self.connected.get_instance() 807 ev = hap.wait_event(["AP-STA-DISCONNECTED"], timeout=10) 808 if ev is None: 809 raise Exception("No disconnection event received from %s" % self.connected.get_ssid()) 810 h.dump_monitor() 811 self.connected = None 812 813 814 def disconnect_from_external_ap(self, check_disconnect=True): 815 """Disconnects from the external AP the station is currently connected 816 to""" 817 if self.connected is not None: 818 h = self.get_instance() 819 h.dump_monitor() 820 h.request("DISCONNECT") 821 if check_disconnect: 822 hap = self.connected 823 ev = hap.wait_event(["AP-STA-DISCONNECTED"], timeout=10) 824 if ev is None: 825 raise Exception("No disconnection event received from AP") 826 h.dump_monitor() 827 self.connected = None 828 829 def dump_monitor(self): 830 """Dump control interface monitor events""" 831 if self.instance: 832 self.instance.dump_monitor() 833