tests: GAS comeback protocol testing
Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
		
							parent
							
								
									c61e5a822c
								
							
						
					
					
						commit
						75f6134dd4
					
				
					 1 changed files with 170 additions and 18 deletions
				
			
		|  | @ -221,12 +221,14 @@ def test_gas_comeback_delay(dev, apdev): | ||||||
|         if ev is None: |         if ev is None: | ||||||
|             raise Exception("Operation timed out") |             raise Exception("Operation timed out") | ||||||
| 
 | 
 | ||||||
| def expect_gas_result(dev, result): | def expect_gas_result(dev, result, status=None): | ||||||
|     ev = dev.wait_event(["GAS-QUERY-DONE"], timeout=10) |     ev = dev.wait_event(["GAS-QUERY-DONE"], timeout=10) | ||||||
|     if ev is None: |     if ev is None: | ||||||
|         raise Exception("GAS query timed out") |         raise Exception("GAS query timed out") | ||||||
|     if "result=" + result not in ev: |     if "result=" + result not in ev: | ||||||
|         raise Exception("Unexpected GAS query result") |         raise Exception("Unexpected GAS query result") | ||||||
|  |     if status and "status_code=" + str(status) + ' ' not in ev: | ||||||
|  |         raise Exception("Unexpected GAS status code") | ||||||
| 
 | 
 | ||||||
| def anqp_get(dev, bssid, id): | def anqp_get(dev, bssid, id): | ||||||
|     dev.request("ANQP_GET " + bssid + " " + str(id)) |     dev.request("ANQP_GET " + bssid + " " + str(id)) | ||||||
|  | @ -263,13 +265,19 @@ GAS_ACTIONS = [ GAS_INITIAL_REQUEST, GAS_INITIAL_RESPONSE, | ||||||
| def anqp_adv_proto(): | def anqp_adv_proto(): | ||||||
|     return struct.pack('BBBB', 108, 2, 127, 0) |     return struct.pack('BBBB', 108, 2, 127, 0) | ||||||
| 
 | 
 | ||||||
| def anqp_initial_resp(dialog_token, status_code): | def anqp_initial_resp(dialog_token, status_code, comeback_delay=0): | ||||||
|     return struct.pack('<BBBHH', ACTION_CATEG_PUBLIC, GAS_INITIAL_RESPONSE, |     return struct.pack('<BBBHH', ACTION_CATEG_PUBLIC, GAS_INITIAL_RESPONSE, | ||||||
|                        dialog_token, status_code, 0) + anqp_adv_proto() |                        dialog_token, status_code, comeback_delay) + anqp_adv_proto() | ||||||
| 
 | 
 | ||||||
| def anqp_comeback_resp(dialog_token): | def anqp_comeback_resp(dialog_token, status_code=0, id=0, more=False, comeback_delay=0, bogus_adv_proto=False): | ||||||
|  |     if more: | ||||||
|  |         id |= 0x80 | ||||||
|  |     if bogus_adv_proto: | ||||||
|  |         adv = struct.pack('BBBB', 108, 2, 127, 1) | ||||||
|  |     else: | ||||||
|  |         adv = anqp_adv_proto() | ||||||
|     return struct.pack('<BBBHBH', ACTION_CATEG_PUBLIC, GAS_COMEBACK_RESPONSE, |     return struct.pack('<BBBHBH', ACTION_CATEG_PUBLIC, GAS_COMEBACK_RESPONSE, | ||||||
|                        dialog_token, 0, 0, 0) + anqp_adv_proto() |                        dialog_token, status_code, id, comeback_delay) + adv | ||||||
| 
 | 
 | ||||||
| def gas_rx(hapd): | def gas_rx(hapd): | ||||||
|     count = 0 |     count = 0 | ||||||
|  | @ -300,7 +308,7 @@ def parse_gas(payload): | ||||||
|     gas['action'] = action |     gas['action'] = action | ||||||
|     pos = pos[3:] |     pos = pos[3:] | ||||||
| 
 | 
 | ||||||
|     if len(pos) < 1: |     if len(pos) < 1 and action != GAS_COMEBACK_REQUEST: | ||||||
|         return None |         return None | ||||||
| 
 | 
 | ||||||
|     gas['dialog_token'] = dialog_token |     gas['dialog_token'] = dialog_token | ||||||
|  | @ -314,6 +322,14 @@ def action_response(req): | ||||||
|     resp['bssid'] = req['bssid'] |     resp['bssid'] = req['bssid'] | ||||||
|     return resp |     return resp | ||||||
| 
 | 
 | ||||||
|  | def send_gas_resp(hapd, resp): | ||||||
|  |     hapd.mgmt_tx(resp) | ||||||
|  |     ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5) | ||||||
|  |     if ev is None: | ||||||
|  |         raise Exception("Missing TX status for GAS response") | ||||||
|  |     if "ok=1" not in ev: | ||||||
|  |         raise Exception("GAS response not acknowledged") | ||||||
|  | 
 | ||||||
| def test_gas_invalid_response_type(dev, apdev): | def test_gas_invalid_response_type(dev, apdev): | ||||||
|     """GAS invalid response type""" |     """GAS invalid response type""" | ||||||
|     hapd = start_ap(apdev[0]) |     hapd = start_ap(apdev[0]) | ||||||
|  | @ -330,12 +346,7 @@ def test_gas_invalid_response_type(dev, apdev): | ||||||
|     resp = action_response(query) |     resp = action_response(query) | ||||||
|     # GAS Comeback Response instead of GAS Initial Response |     # GAS Comeback Response instead of GAS Initial Response | ||||||
|     resp['payload'] = anqp_comeback_resp(gas['dialog_token']) + struct.pack('<H', 0) |     resp['payload'] = anqp_comeback_resp(gas['dialog_token']) + struct.pack('<H', 0) | ||||||
|     hapd.mgmt_tx(resp) |     send_gas_resp(hapd, resp) | ||||||
|     ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5) |  | ||||||
|     if ev is None: |  | ||||||
|         raise Exception("Missing TX status for GAS response") |  | ||||||
|     if "ok=1" not in ev: |  | ||||||
|         raise Exception("GAS response not acknowledged") |  | ||||||
| 
 | 
 | ||||||
|     # station drops the invalid frame, so this needs to result in GAS timeout |     # station drops the invalid frame, so this needs to result in GAS timeout | ||||||
|     expect_gas_result(dev[0], "TIMEOUT") |     expect_gas_result(dev[0], "TIMEOUT") | ||||||
|  | @ -355,12 +366,7 @@ def test_gas_failure_status_code(dev, apdev): | ||||||
| 
 | 
 | ||||||
|     resp = action_response(query) |     resp = action_response(query) | ||||||
|     resp['payload'] = anqp_initial_resp(gas['dialog_token'], 61) + struct.pack('<H', 0) |     resp['payload'] = anqp_initial_resp(gas['dialog_token'], 61) + struct.pack('<H', 0) | ||||||
|     hapd.mgmt_tx(resp) |     send_gas_resp(hapd, resp) | ||||||
|     ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5) |  | ||||||
|     if ev is None: |  | ||||||
|         raise Exception("Missing TX status for GAS response") |  | ||||||
|     if "ok=1" not in ev: |  | ||||||
|         raise Exception("GAS response not acknowledged") |  | ||||||
| 
 | 
 | ||||||
|     expect_gas_result(dev[0], "FAILURE") |     expect_gas_result(dev[0], "FAILURE") | ||||||
| 
 | 
 | ||||||
|  | @ -423,3 +429,149 @@ def test_gas_malformed(dev, apdev): | ||||||
|     # part of the response is not valid. This is reported as successfulyl |     # part of the response is not valid. This is reported as successfulyl | ||||||
|     # completed GAS exchange. |     # completed GAS exchange. | ||||||
|     expect_gas_result(dev[0], "SUCCESS") |     expect_gas_result(dev[0], "SUCCESS") | ||||||
|  | 
 | ||||||
|  | def init_gas(hapd, bssid, dev): | ||||||
|  |     anqp_get(dev, bssid, 263) | ||||||
|  |     query = gas_rx(hapd) | ||||||
|  |     gas = parse_gas(query['payload']) | ||||||
|  |     dialog_token = gas['dialog_token'] | ||||||
|  | 
 | ||||||
|  |     resp = action_response(query) | ||||||
|  |     resp['payload'] = anqp_initial_resp(dialog_token, 0, comeback_delay=1) + struct.pack('<H', 0) | ||||||
|  |     send_gas_resp(hapd, resp) | ||||||
|  | 
 | ||||||
|  |     query = gas_rx(hapd) | ||||||
|  |     gas = parse_gas(query['payload']) | ||||||
|  |     if gas['action'] != GAS_COMEBACK_REQUEST: | ||||||
|  |         raise Exception("Unexpected request action") | ||||||
|  |     if gas['dialog_token'] != dialog_token: | ||||||
|  |         raise Exception("Unexpected dialog token change") | ||||||
|  |     return query, dialog_token | ||||||
|  | 
 | ||||||
|  | def test_gas_malformed_comeback_resp(dev, apdev): | ||||||
|  |     """GAS malformed comeback response frames""" | ||||||
|  |     hapd = start_ap(apdev[0]) | ||||||
|  |     bssid = apdev[0]['bssid'] | ||||||
|  | 
 | ||||||
|  |     dev[0].scan(freq="2412") | ||||||
|  |     hapd.set("ext_mgmt_frame_handling", "1") | ||||||
|  | 
 | ||||||
|  |     logger.debug("Non-zero status code in comeback response") | ||||||
|  |     query, dialog_token = init_gas(hapd, bssid, dev[0]) | ||||||
|  |     resp = action_response(query) | ||||||
|  |     resp['payload'] = anqp_comeback_resp(dialog_token, status_code=2) + struct.pack('<H', 0) | ||||||
|  |     send_gas_resp(hapd, resp) | ||||||
|  |     expect_gas_result(dev[0], "FAILURE", status=2) | ||||||
|  | 
 | ||||||
|  |     logger.debug("Different advertisement protocol in comeback response") | ||||||
|  |     query, dialog_token = init_gas(hapd, bssid, dev[0]) | ||||||
|  |     resp = action_response(query) | ||||||
|  |     resp['payload'] = anqp_comeback_resp(dialog_token, bogus_adv_proto=True) + struct.pack('<H', 0) | ||||||
|  |     send_gas_resp(hapd, resp) | ||||||
|  |     expect_gas_result(dev[0], "PEER_ERROR") | ||||||
|  | 
 | ||||||
|  |     logger.debug("Non-zero frag id and comeback delay in comeback response") | ||||||
|  |     query, dialog_token = init_gas(hapd, bssid, dev[0]) | ||||||
|  |     resp = action_response(query) | ||||||
|  |     resp['payload'] = anqp_comeback_resp(dialog_token, id=1, comeback_delay=1) + struct.pack('<H', 0) | ||||||
|  |     send_gas_resp(hapd, resp) | ||||||
|  |     expect_gas_result(dev[0], "PEER_ERROR") | ||||||
|  | 
 | ||||||
|  |     logger.debug("Unexpected frag id in comeback response") | ||||||
|  |     query, dialog_token = init_gas(hapd, bssid, dev[0]) | ||||||
|  |     resp = action_response(query) | ||||||
|  |     resp['payload'] = anqp_comeback_resp(dialog_token, id=1) + struct.pack('<H', 0) | ||||||
|  |     send_gas_resp(hapd, resp) | ||||||
|  |     expect_gas_result(dev[0], "PEER_ERROR") | ||||||
|  | 
 | ||||||
|  |     logger.debug("Empty fragment and replay in comeback response") | ||||||
|  |     query, dialog_token = init_gas(hapd, bssid, dev[0]) | ||||||
|  |     resp = action_response(query) | ||||||
|  |     resp['payload'] = anqp_comeback_resp(dialog_token, more=True) + struct.pack('<H', 0) | ||||||
|  |     send_gas_resp(hapd, resp) | ||||||
|  |     query = gas_rx(hapd) | ||||||
|  |     gas = parse_gas(query['payload']) | ||||||
|  |     if gas['action'] != GAS_COMEBACK_REQUEST: | ||||||
|  |         raise Exception("Unexpected request action") | ||||||
|  |     if gas['dialog_token'] != dialog_token: | ||||||
|  |         raise Exception("Unexpected dialog token change") | ||||||
|  |     resp = action_response(query) | ||||||
|  |     resp['payload'] = anqp_comeback_resp(dialog_token) + struct.pack('<H', 0) | ||||||
|  |     send_gas_resp(hapd, resp) | ||||||
|  |     resp['payload'] = anqp_comeback_resp(dialog_token, id=1) + struct.pack('<H', 0) | ||||||
|  |     send_gas_resp(hapd, resp) | ||||||
|  |     expect_gas_result(dev[0], "SUCCESS") | ||||||
|  | 
 | ||||||
|  |     logger.debug("Unexpected initial response when waiting for comeback response") | ||||||
|  |     query, dialog_token = init_gas(hapd, bssid, dev[0]) | ||||||
|  |     resp = action_response(query) | ||||||
|  |     resp['payload'] = anqp_initial_resp(dialog_token, 0) + struct.pack('<H', 0) | ||||||
|  |     send_gas_resp(hapd, resp) | ||||||
|  |     ev = hapd.wait_event(["MGMT-RX"], timeout=1) | ||||||
|  |     if ev is not None: | ||||||
|  |         raise Exception("Unexpected management frame") | ||||||
|  |     expect_gas_result(dev[0], "TIMEOUT") | ||||||
|  | 
 | ||||||
|  |     logger.debug("Too short comeback response") | ||||||
|  |     query, dialog_token = init_gas(hapd, bssid, dev[0]) | ||||||
|  |     resp = action_response(query) | ||||||
|  |     resp['payload'] = struct.pack('<BBBH', ACTION_CATEG_PUBLIC, | ||||||
|  |                                   GAS_COMEBACK_RESPONSE, dialog_token, 0) | ||||||
|  |     send_gas_resp(hapd, resp) | ||||||
|  |     ev = hapd.wait_event(["MGMT-RX"], timeout=1) | ||||||
|  |     if ev is not None: | ||||||
|  |         raise Exception("Unexpected management frame") | ||||||
|  |     expect_gas_result(dev[0], "TIMEOUT") | ||||||
|  | 
 | ||||||
|  |     logger.debug("Too short comeback response(2)") | ||||||
|  |     query, dialog_token = init_gas(hapd, bssid, dev[0]) | ||||||
|  |     resp = action_response(query) | ||||||
|  |     resp['payload'] = struct.pack('<BBBHBB', ACTION_CATEG_PUBLIC, | ||||||
|  |                                   GAS_COMEBACK_RESPONSE, dialog_token, 0, 0x80, | ||||||
|  |                                   0) | ||||||
|  |     send_gas_resp(hapd, resp) | ||||||
|  |     ev = hapd.wait_event(["MGMT-RX"], timeout=1) | ||||||
|  |     if ev is not None: | ||||||
|  |         raise Exception("Unexpected management frame") | ||||||
|  |     expect_gas_result(dev[0], "TIMEOUT") | ||||||
|  | 
 | ||||||
|  |     logger.debug("Maximum comeback response fragment claiming more fragments") | ||||||
|  |     query, dialog_token = init_gas(hapd, bssid, dev[0]) | ||||||
|  |     resp = action_response(query) | ||||||
|  |     resp['payload'] = anqp_comeback_resp(dialog_token, more=True) + struct.pack('<H', 0) | ||||||
|  |     send_gas_resp(hapd, resp) | ||||||
|  |     for i in range(1, 129): | ||||||
|  |         query = gas_rx(hapd) | ||||||
|  |         gas = parse_gas(query['payload']) | ||||||
|  |         if gas['action'] != GAS_COMEBACK_REQUEST: | ||||||
|  |             raise Exception("Unexpected request action") | ||||||
|  |         if gas['dialog_token'] != dialog_token: | ||||||
|  |             raise Exception("Unexpected dialog token change") | ||||||
|  |         resp = action_response(query) | ||||||
|  |         resp['payload'] = anqp_comeback_resp(dialog_token, id=i, more=True) + struct.pack('<H', 0) | ||||||
|  |         send_gas_resp(hapd, resp) | ||||||
|  |     expect_gas_result(dev[0], "PEER_ERROR") | ||||||
|  | 
 | ||||||
|  | def test_gas_comeback_resp_additional_delay(dev, apdev): | ||||||
|  |     """GAS comeback response requesting additional delay""" | ||||||
|  |     hapd = start_ap(apdev[0]) | ||||||
|  |     bssid = apdev[0]['bssid'] | ||||||
|  | 
 | ||||||
|  |     dev[0].scan(freq="2412") | ||||||
|  |     hapd.set("ext_mgmt_frame_handling", "1") | ||||||
|  | 
 | ||||||
|  |     query, dialog_token = init_gas(hapd, bssid, dev[0]) | ||||||
|  |     for i in range(0, 2): | ||||||
|  |         resp = action_response(query) | ||||||
|  |         resp['payload'] = anqp_comeback_resp(dialog_token, status_code=95, comeback_delay=50) + struct.pack('<H', 0) | ||||||
|  |         send_gas_resp(hapd, resp) | ||||||
|  |         query = gas_rx(hapd) | ||||||
|  |         gas = parse_gas(query['payload']) | ||||||
|  |         if gas['action'] != GAS_COMEBACK_REQUEST: | ||||||
|  |             raise Exception("Unexpected request action") | ||||||
|  |         if gas['dialog_token'] != dialog_token: | ||||||
|  |             raise Exception("Unexpected dialog token change") | ||||||
|  |     resp = action_response(query) | ||||||
|  |     resp['payload'] = anqp_comeback_resp(dialog_token, status_code=0) + struct.pack('<H', 0) | ||||||
|  |     send_gas_resp(hapd, resp) | ||||||
|  |     expect_gas_result(dev[0], "SUCCESS") | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Jouni Malinen
						Jouni Malinen