+
+def test_ap_wps_check_pin(dev, apdev):
+ """Verify PIN checking through control interface"""
+ hostapd.add_ap(apdev[0]['ifname'],
+ { "ssid": "wps", "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP" })
+ hapd = hostapd.Hostapd(apdev[0]['ifname'])
+ for t in [ ("12345670", "12345670"),
+ ("12345678", "FAIL-CHECKSUM"),
+ ("12345", "FAIL"),
+ ("123456789", "FAIL"),
+ ("1234-5670", "12345670"),
+ ("1234 5670", "12345670"),
+ ("1-2.3:4 5670", "12345670") ]:
+ res = hapd.request("WPS_CHECK_PIN " + t[0]).rstrip('\n')
+ res2 = dev[0].request("WPS_CHECK_PIN " + t[0]).rstrip('\n')
+ if res != res2:
+ raise Exception("Unexpected difference in WPS_CHECK_PIN responses")
+ if res != t[1]:
+ raise Exception("Incorrect WPS_CHECK_PIN response {} (expected {})".format(res, t[1]))
+
+ if "FAIL" not in hapd.request("WPS_CHECK_PIN 12345"):
+ raise Exception("Unexpected WPS_CHECK_PIN success")
+ if "FAIL" not in hapd.request("WPS_CHECK_PIN 123456789"):
+ raise Exception("Unexpected WPS_CHECK_PIN success")
+
+ for i in range(0, 10):
+ pin = dev[0].request("WPS_PIN get")
+ rpin = dev[0].request("WPS_CHECK_PIN " + pin).rstrip('\n')
+ if pin != rpin:
+ raise Exception("Random PIN validation failed for " + pin)
+
+def test_ap_wps_wep_config(dev, apdev):
+ """WPS 2.0 AP rejecting WEP configuration"""
+ ssid = "test-wps-config"
+ appin = "12345670"
+ hostapd.add_ap(apdev[0]['ifname'],
+ { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "ap_pin": appin})
+ hapd = hostapd.Hostapd(apdev[0]['ifname'])
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], appin, "wps-new-ssid-wep", "OPEN", "WEP",
+ "hello", no_wait=True)
+ ev = hapd.wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-FAIL timed out")
+ if "reason=2" not in ev:
+ raise Exception("Unexpected reason code in WPS-FAIL")
+ status = hapd.request("WPS_GET_STATUS")
+ if "Last WPS result: Failed" not in status:
+ raise Exception("WPS failure result not shown correctly")
+ if "Failure Reason: WEP Prohibited" not in status:
+ raise Exception("Failure reason not reported correctly")
+ if "Peer Address: " + dev[0].p2p_interface_addr() not in status:
+ raise Exception("Peer address not shown correctly")
+
+def test_ap_wps_wep_enroll(dev, apdev):
+ """WPS 2.0 STA rejecting WEP configuration"""
+ ssid = "test-wps-wep"
+ hostapd.add_ap(apdev[0]['ifname'],
+ { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "skip_cred_build": "1", "extra_cred": "wps-wep-cred" })
+ hapd = hostapd.Hostapd(apdev[0]['ifname'])
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-FAIL event timed out")
+ if "msg=12" not in ev or "reason=2 (WEP Prohibited)" not in ev:
+ raise Exception("Unexpected WPS-FAIL event: " + ev)
+
+def test_ap_wps_ie_fragmentation(dev, apdev):
+ """WPS AP using fragmented WPS IE"""
+ ssid = "test-wps-ie-fragmentation"
+ params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "1234567890abcdef1234567890abcdef",
+ "manufacturer": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
+ "model_name": "1234567890abcdef1234567890abcdef",
+ "model_number": "1234567890abcdef1234567890abcdef",
+ "serial_number": "1234567890abcdef1234567890abcdef" }
+ hostapd.add_ap(apdev[0]['ifname'], params)
+ hapd = hostapd.Hostapd(apdev[0]['ifname'])
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if "wps_device_name" not in bss or bss['wps_device_name'] != "1234567890abcdef1234567890abcdef":
+ logger.info("Device Name not received correctly")
+ logger.info(bss)
+ # This can fail if Probe Response frame is missed and Beacon frame was
+ # used to fill in the BSS entry. This can happen, e.g., during heavy
+ # load every now and then and is not really an error, so try to
+ # workaround by runnign another scan.
+ dev[0].scan(freq="2412", only_new=True)
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if not bss or "wps_device_name" not in bss or bss['wps_device_name'] != "1234567890abcdef1234567890abcdef":
+ logger.info(bss)
+ raise Exception("Device Name not received correctly")
+ if len(re.findall("dd..0050f204", bss['ie'])) != 2:
+ raise Exception("Unexpected number of WPS IEs")
+
+def get_psk(pskfile):
+ psks = {}
+ with open(pskfile, "r") as f:
+ lines = f.read().splitlines()
+ for l in lines:
+ if l == "# WPA PSKs":
+ continue
+ (addr,psk) = l.split(' ')
+ psks[addr] = psk
+ return psks
+
+def test_ap_wps_per_station_psk(dev, apdev):
+ """WPS PBC provisioning with per-station PSK"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+ ssid = "wps"
+ appin = "12345670"
+ pskfile = "/tmp/ap_wps_per_enrollee_psk.psk_file"
+ try:
+ os.remove(pskfile)
+ except:
+ pass
+
+ try:
+ with open(pskfile, "w") as f:
+ f.write("# WPA PSKs\n")
+
+ params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa": "2", "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": "CCMP", "ap_pin": appin,
+ "wpa_psk_file": pskfile }
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ logger.info("First enrollee")
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+
+ logger.info("Second enrollee")
+ hapd.request("WPS_PBC")
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[1].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[1].wait_connected(timeout=30)
+
+ logger.info("External registrar")
+ dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[2].wps_reg(apdev[0]['bssid'], appin)
+
+ logger.info("Verifying PSK results")
+ psks = get_psk(pskfile)
+ if addr0 not in psks:
+ raise Exception("No PSK recorded for sta0")
+ if addr1 not in psks:
+ raise Exception("No PSK recorded for sta1")
+ if addr2 not in psks:
+ raise Exception("No PSK recorded for sta2")
+ if psks[addr0] == psks[addr1]:
+ raise Exception("Same PSK recorded for sta0 and sta1")
+ if psks[addr0] == psks[addr2]:
+ raise Exception("Same PSK recorded for sta0 and sta2")
+ if psks[addr1] == psks[addr2]:
+ raise Exception("Same PSK recorded for sta1 and sta2")
+
+ dev[0].request("REMOVE_NETWORK all")
+ logger.info("Second external registrar")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], appin)
+ psks2 = get_psk(pskfile)
+ if addr0 not in psks2:
+ raise Exception("No PSK recorded for sta0(reg)")
+ if psks[addr0] == psks2[addr0]:
+ raise Exception("Same PSK recorded for sta0(enrollee) and sta0(reg)")
+ finally:
+ os.remove(pskfile)
+
+def test_ap_wps_per_station_psk_failure(dev, apdev):
+ """WPS PBC provisioning with per-station PSK (file not writable)"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+ ssid = "wps"
+ appin = "12345670"
+ pskfile = "/tmp/ap_wps_per_enrollee_psk.psk_file"
+ try:
+ os.remove(pskfile)
+ except:
+ pass
+
+ try:
+ with open(pskfile, "w") as f:
+ f.write("# WPA PSKs\n")
+
+ params = { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa": "2", "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": "CCMP", "ap_pin": appin,
+ "wpa_psk_file": pskfile }
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ if "FAIL" in hapd.request("SET wpa_psk_file /tmp/does/not/exists/ap_wps_per_enrollee_psk_failure.psk_file"):
+ raise Exception("Failed to set wpa_psk_file")
+
+ logger.info("First enrollee")
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+
+ logger.info("Second enrollee")
+ hapd.request("WPS_PBC")
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[1].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[1].wait_connected(timeout=30)
+
+ logger.info("External registrar")
+ dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[2].wps_reg(apdev[0]['bssid'], appin)
+
+ logger.info("Verifying PSK results")
+ psks = get_psk(pskfile)
+ if len(psks) > 0:
+ raise Exception("PSK recorded unexpectedly")
+ finally:
+ os.remove(pskfile)
+
+def test_ap_wps_pin_request_file(dev, apdev):
+ """WPS PIN provisioning with configured AP"""
+ ssid = "wps"
+ pinfile = "/tmp/ap_wps_pin_request_file.log"
+ if os.path.exists(pinfile):
+ subprocess.call(['sudo', 'rm', pinfile])
+ hostapd.add_ap(apdev[0]['ifname'],
+ { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wps_pin_requests": pinfile,
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ hapd = hostapd.Hostapd(apdev[0]['ifname'])
+ uuid = dev[0].get_status_field("uuid")
+ pin = dev[0].wps_read_pin()
+ try:
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = hapd.wait_event(["WPS-PIN-NEEDED"], timeout=15)
+ if ev is None:
+ raise Exception("PIN needed event not shown")
+ if uuid not in ev:
+ raise Exception("UUID mismatch")
+ dev[0].request("WPS_CANCEL")
+ success = False
+ with open(pinfile, "r") as f:
+ lines = f.readlines()
+ for l in lines:
+ if uuid in l:
+ success = True
+ break
+ if not success:
+ raise Exception("PIN request entry not in the log file")
+ finally:
+ subprocess.call(['sudo', 'rm', pinfile])
+
+def test_ap_wps_auto_setup_with_config_file(dev, apdev):
+ """WPS auto-setup with configuration file"""
+ conffile = "/tmp/ap_wps_auto_setup_with_config_file.conf"
+ ifname = apdev[0]['ifname']
+ try:
+ with open(conffile, "w") as f:
+ f.write("driver=nl80211\n")
+ f.write("hw_mode=g\n")
+ f.write("channel=1\n")
+ f.write("ieee80211n=1\n")
+ f.write("interface=%s\n" % ifname)
+ f.write("ctrl_interface=/var/run/hostapd\n")
+ f.write("ssid=wps\n")
+ f.write("eap_server=1\n")
+ f.write("wps_state=1\n")
+ hostapd.add_bss('phy3', ifname, conffile)
+ hapd = hostapd.Hostapd(ifname)
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ with open(conffile, "r") as f:
+ lines = f.read().splitlines()
+ vals = dict()
+ for l in lines:
+ try:
+ [name,value] = l.split('=', 1)
+ vals[name] = value
+ except ValueError, e:
+ if "# WPS configuration" in l:
+ pass
+ else:
+ raise Exception("Unexpected configuration line: " + l)
+ if vals['ieee80211n'] != '1' or vals['wps_state'] != '2' or "WPA-PSK" not in vals['wpa_key_mgmt']:
+ raise Exception("Incorrect configuration: " + str(vals))
+ finally:
+ subprocess.call(['sudo', 'rm', conffile])
+
+def test_ap_wps_pbc_timeout(dev, apdev, params):
+ """wpa_supplicant PBC walk time [long]"""
+ if not params['long']:
+ logger.info("Skip test case with long duration due to --long not specified")
+ return "skip"
+ ssid = "test-wps"
+ hostapd.add_ap(apdev[0]['ifname'],
+ { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+ hapd = hostapd.Hostapd(apdev[0]['ifname'])
+ logger.info("Start WPS_PBC and wait for PBC walk time expiration")
+ if "OK" not in dev[0].request("WPS_PBC"):
+ raise Exception("WPS_PBC failed")
+ ev = dev[0].wait_event(["WPS-TIMEOUT"], timeout=150)
+ if ev is None:
+ raise Exception("WPS-TIMEOUT not reported")
+
+def add_ssdp_ap(ifname, ap_uuid):
+ ssid = "wps-ssdp"
+ ap_pin = "12345670"
+ hostapd.add_ap(ifname,
+ { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo",
+ "friendly_name": "WPS Access Point",
+ "manufacturer_url": "http://www.example.com/",
+ "model_description": "Wireless Access Point",
+ "model_url": "http://www.example.com/model/",
+ "upc": "123456789012" })
+
+def ssdp_send(msg, no_recv=False):
+ socket.setdefaulttimeout(1)
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
+ sock.bind(("127.0.0.1", 0))
+ sock.sendto(msg, ("239.255.255.250", 1900))
+ if no_recv:
+ return None
+ return sock.recv(1000)
+
+def ssdp_send_msearch(st):
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MX: 1',
+ 'MAN: "ssdp:discover"',
+ 'ST: ' + st,
+ '', ''])
+ return ssdp_send(msg)
+
+def test_ap_wps_ssdp_msearch(dev, apdev):
+ """WPS AP and SSDP M-SEARCH messages"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'Host: 239.255.255.250:1900',
+ 'Mx: 1',
+ 'Man: "ssdp:discover"',
+ 'St: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ ssdp_send(msg)
+
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'host:\t239.255.255.250:1900\t\t\t\t \t\t',
+ 'mx: \t1\t\t ',
+ 'man: \t \t "ssdp:discover" ',
+ 'st: urn:schemas-wifialliance-org:device:WFADevice:1\t\t',
+ '', ''])
+ ssdp_send(msg)
+
+ ssdp_send_msearch("ssdp:all")
+ ssdp_send_msearch("upnp:rootdevice")
+ ssdp_send_msearch("uuid:" + ap_uuid)
+ ssdp_send_msearch("urn:schemas-wifialliance-org:service:WFAWLANConfig:1")
+ ssdp_send_msearch("urn:schemas-wifialliance-org:device:WFADevice:1");
+
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST:\t239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 130',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ ssdp_send(msg, no_recv=True)
+
+def test_ap_wps_ssdp_invalid_msearch(dev, apdev):
+ """WPS AP and invalid SSDP M-SEARCH messages"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+ socket.setdefaulttimeout(1)
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
+ sock.bind(("127.0.0.1", 0))
+
+ logger.debug("Missing MX")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+
+ logger.debug("Negative MX")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MX: -1',
+ 'MAN: "ssdp:discover"',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+
+ logger.debug("Invalid MX")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MX; 1',
+ 'MAN: "ssdp:discover"',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+
+ logger.debug("Missing MAN")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+
+ logger.debug("Invalid MAN")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MX: 1',
+ 'MAN: foo',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MX: 1',
+ 'MAN; "ssdp:discover"',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+
+ logger.debug("Missing HOST")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+
+ logger.debug("Missing ST")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+
+ logger.debug("Mismatching ST")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: uuid:16d5f8a9-4ee4-4f5e-81f9-cc6e2f47f42d',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: foo:bar',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: foobar',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+
+ logger.debug("Invalid ST")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST; urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+
+ logger.debug("Invalid M-SEARCH")
+ msg = '\r\n'.join([
+ 'M+SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+ msg = '\r\n'.join([
+ 'M-SEARCH-* HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+
+ logger.debug("Invalid message format")
+ sock.sendto("NOTIFY * HTTP/1.1", ("239.255.255.250", 1900))
+ msg = '\r'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+
+ try:
+ r = sock.recv(1000)
+ raise Exception("Unexpected M-SEARCH response: " + r)
+ except socket.timeout:
+ pass
+
+ logger.debug("Valid M-SEARCH")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg, ("239.255.255.250", 1900))
+
+ try:
+ r = sock.recv(1000)
+ pass
+ except socket.timeout:
+ raise Exception("No SSDP response")
+
+def test_ap_wps_ssdp_burst(dev, apdev):
+ """WPS AP and SSDP burst"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ socket.setdefaulttimeout(1)
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
+ sock.bind(("127.0.0.1", 0))
+ for i in range(0, 25):
+ sock.sendto(msg, ("239.255.255.250", 1900))
+ resp = 0
+ while True:
+ try:
+ r = sock.recv(1000)
+ if not r.startswith("HTTP/1.1 200 OK\r\n"):
+ raise Exception("Unexpected message: " + r)
+ resp += 1
+ except socket.timeout:
+ break
+ if resp < 20:
+ raise Exception("Too few SSDP responses")
+
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
+ sock.bind(("127.0.0.1", 0))
+ for i in range(0, 25):
+ sock.sendto(msg, ("239.255.255.250", 1900))
+ while True:
+ try:
+ r = sock.recv(1000)
+ if ap_uuid in r:
+ break
+ except socket.timeout:
+ raise Exception("No SSDP response")
+
+def ssdp_get_location(uuid):
+ res = ssdp_send_msearch("uuid:" + uuid)
+ location = None
+ for l in res.splitlines():
+ if l.lower().startswith("location:"):
+ location = l.split(':', 1)[1].strip()
+ break
+ if location is None:
+ raise Exception("No UPnP location found")
+ return location
+
+def upnp_get_urls(location):
+ conn = urllib.urlopen(location)
+ tree = ET.parse(conn)
+ root = tree.getroot()
+ urn = '{urn:schemas-upnp-org:device-1-0}'
+ service = root.find("./" + urn + "device/" + urn + "serviceList/" + urn + "service")
+ res = {}
+ res['scpd_url'] = urlparse.urljoin(location, service.find(urn + 'SCPDURL').text)
+ res['control_url'] = urlparse.urljoin(location, service.find(urn + 'controlURL').text)
+ res['event_sub_url'] = urlparse.urljoin(location, service.find(urn + 'eventSubURL').text)
+ return res
+
+def upnp_soap_action(conn, path, action, include_soap_action=True, soap_action_override=None):
+ soapns = 'http://schemas.xmlsoap.org/soap/envelope/'
+ wpsns = 'urn:schemas-wifialliance-org:service:WFAWLANConfig:1'
+ ET.register_namespace('soapenv', soapns)
+ ET.register_namespace('wfa', wpsns)
+ attrib = {}
+ attrib['{%s}encodingStyle' % soapns] = 'http://schemas.xmlsoap.org/soap/encoding/'
+ root = ET.Element("{%s}Envelope" % soapns, attrib=attrib)
+ body = ET.SubElement(root, "{%s}Body" % soapns)
+ act = ET.SubElement(body, "{%s}%s" % (wpsns, action))
+ tree = ET.ElementTree(root)
+ soap = StringIO.StringIO()
+ tree.write(soap, xml_declaration=True, encoding='utf-8')
+
+ headers = { "Content-type": 'text/xml; charset="utf-8"' }
+ if include_soap_action:
+ headers["SOAPAction"] = '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#%s"' % action
+ elif soap_action_override:
+ headers["SOAPAction"] = soap_action_override
+ conn.request("POST", path, soap.getvalue(), headers)
+ return conn.getresponse()
+
+def test_ap_wps_upnp(dev, apdev):
+ """WPS AP and UPnP operations"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+ location = ssdp_get_location(ap_uuid)
+ urls = upnp_get_urls(location)
+
+ conn = urllib.urlopen(urls['scpd_url'])
+ scpd = conn.read()
+
+ conn = urllib.urlopen(urlparse.urljoin(location, "unknown.html"))
+ if conn.getcode() != 404:
+ raise Exception("Unexpected HTTP response to GET unknown URL")
+
+ url = urlparse.urlparse(location)
+ conn = httplib.HTTPConnection(url.netloc)
+ #conn.set_debuglevel(1)
+ headers = { "Content-type": 'text/xml; charset="utf-8"',
+ "SOAPAction": '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#GetDeviceInfo"' }
+ conn.request("POST", "hello", "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 404:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ conn.request("UNKNOWN", "hello", "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 501:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ headers = { "Content-type": 'text/xml; charset="utf-8"',
+ "SOAPAction": '"urn:some-unknown-action#GetDeviceInfo"' }
+ ctrlurl = urlparse.urlparse(urls['control_url'])
+ conn.request("POST", ctrlurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 401:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("GetDeviceInfo without SOAPAction header")
+ resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo",
+ include_soap_action=False)
+ if resp.status != 401:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("GetDeviceInfo with invalid SOAPAction header")
+ for act in [ "foo",
+ "urn:schemas-wifialliance-org:service:WFAWLANConfig:1#GetDeviceInfo",
+ '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1"',
+ '"urn:schemas-wifialliance-org:service:WFAWLANConfig:123#GetDevice']:
+ resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo",
+ include_soap_action=False,
+ soap_action_override=act)
+ if resp.status != 401:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+ dev = resp.read()
+ if "NewDeviceInfo" not in dev:
+ raise Exception("Unexpected GetDeviceInfo response")
+
+ logger.debug("PutMessage without required parameters")
+ resp = upnp_soap_action(conn, ctrlurl.path, "PutMessage")
+ if resp.status != 600:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("PutWLANResponse without required parameters")
+ resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse")
+ if resp.status != 600:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("SetSelectedRegistrar from unregistered ER")
+ resp = upnp_soap_action(conn, ctrlurl.path, "SetSelectedRegistrar")
+ if resp.status != 501:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("Unknown action")
+ resp = upnp_soap_action(conn, ctrlurl.path, "Unknown")
+ if resp.status != 401:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+def test_ap_wps_upnp_subscribe(dev, apdev):
+ """WPS AP and UPnP event subscription"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+ location = ssdp_get_location(ap_uuid)
+ urls = upnp_get_urls(location)
+ eventurl = urlparse.urlparse(urls['event_sub_url'])
+
+ url = urlparse.urlparse(location)
+ conn = httplib.HTTPConnection(url.netloc)
+ #conn.set_debuglevel(1)
+ headers = { "callback": '<http://127.0.0.1:12345/event>',
+ "timeout": "Second-1234" }
+ conn.request("SUBSCRIBE", "hello", "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ headers = { "NT": "upnp:event",
+ "timeout": "Second-1234" }
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ headers = { "callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:foobar",
+ "timeout": "Second-1234" }
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("Valid subscription")
+ headers = { "callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "timeout": "Second-1234" }
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+ sid = resp.getheader("sid")
+ logger.debug("Subscription SID " + sid)
+
+ logger.debug("Invalid re-subscription")
+ headers = { "NT": "upnp:event",
+ "sid": "123456734567854",
+ "timeout": "Second-1234" }
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("Invalid re-subscription")
+ headers = { "NT": "upnp:event",
+ "sid": "uuid:123456734567854",
+ "timeout": "Second-1234" }
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("Invalid re-subscription")
+ headers = { "callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "sid": sid,
+ "timeout": "Second-1234" }
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("SID mismatch in re-subscription")
+ headers = { "NT": "upnp:event",
+ "sid": "uuid:4c2bca79-1ff4-4e43-85d4-952a2b8a51fb",
+ "timeout": "Second-1234" }
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("Valid re-subscription")
+ headers = { "NT": "upnp:event",
+ "sid": sid,
+ "timeout": "Second-1234" }
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+ sid2 = resp.getheader("sid")
+ logger.debug("Subscription SID " + sid2)
+
+ if sid != sid2:
+ raise Exception("Unexpected SID change")
+
+ logger.debug("Valid re-subscription")
+ headers = { "NT": "upnp:event",
+ "sid": "uuid: \t \t" + sid.split(':')[1],
+ "timeout": "Second-1234" }
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("Invalid unsubscription")
+ headers = { "sid": sid }
+ conn.request("UNSUBSCRIBE", "/hello", "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+ headers = { "foo": "bar" }
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("Valid unsubscription")
+ headers = { "sid": sid }
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("Unsubscription for not existing SID")
+ headers = { "sid": sid }
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("Invalid unsubscription")
+ headers = { "sid": " \t \tfoo" }
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("Invalid unsubscription")
+ headers = { "sid": "uuid:\t \tfoo" }
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("Invalid unsubscription")
+ headers = { "NT": "upnp:event",
+ "sid": sid }
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+ headers = { "callback": '<http://127.0.0.1:12345/event>',
+ "sid": sid }
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+ logger.debug("Valid subscription with multiple callbacks")
+ headers = { "callback": '<http://127.0.0.1:12345/event> <http://127.0.0.1:12345/event>\t<http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "timeout": "Second-1234" }
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %s" % resp.status)
+ sid = resp.getheader("sid")
+ logger.debug("Subscription SID " + sid)
+
+def test_ap_wps_disabled(dev, apdev):
+ """WPS operations while WPS is disabled"""
+ ssid = "test-wps-disabled"
+ hostapd.add_ap(apdev[0]['ifname'], { "ssid": ssid })
+ hapd = hostapd.Hostapd(apdev[0]['ifname'])
+ if "FAIL" not in hapd.request("WPS_PBC"):
+ raise Exception("WPS_PBC succeeded unexpectedly")
+ if "FAIL" not in hapd.request("WPS_CANCEL"):
+ raise Exception("WPS_CANCEL succeeded unexpectedly")
+
+def test_ap_wps_mixed_cred(dev, apdev):
+ """WPS 2.0 STA merging mixed mode WPA/WPA2 credentials"""
+ ssid = "test-wps-wep"
+ hostapd.add_ap(apdev[0]['ifname'],
+ { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "skip_cred_build": "1", "extra_cred": "wps-mixed-cred" })
+ hapd = hostapd.Hostapd(apdev[0]['ifname'])
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=30)
+ if ev is None:
+ raise Exception("WPS-SUCCESS event timed out")
+ nets = dev[0].list_networks()
+ if len(nets) != 1:
+ raise Exception("Unexpected number of network blocks")
+ id = nets[0]['id']
+ proto = dev[0].get_network(id, "proto")
+ if proto != "WPA RSN":
+ raise Exception("Unexpected merged proto field value: " + proto)
+ pairwise = dev[0].get_network(id, "pairwise")
+ if pairwise != "CCMP TKIP" and pairwise != "CCMP GCMP TKIP":
+ raise Exception("Unexpected merged pairwise field value: " + pairwise)
+
+def test_ap_wps_while_connected(dev, apdev):
+ """WPS PBC provisioning while connected to another AP"""
+ ssid = "test-wps-conf"
+ hostapd.add_ap(apdev[0]['ifname'],
+ { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+ hostapd.add_ap(apdev[1]['ifname'], { "ssid": "open" })
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+ logger.info("WPS provisioning step")
+ hapd.request("WPS_PBC")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ if status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Unexpected BSSID")
+
+def test_ap_wps_while_connected_no_autoconnect(dev, apdev):
+ """WPS PBC provisioning while connected to another AP and STA_AUTOCONNECT disabled"""
+ ssid = "test-wps-conf"
+ hostapd.add_ap(apdev[0]['ifname'],
+ { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+ hostapd.add_ap(apdev[1]['ifname'], { "ssid": "open" })
+
+ try:
+ dev[0].request("STA_AUTOCONNECT 0")
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+ logger.info("WPS provisioning step")
+ hapd.request("WPS_PBC")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ if status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Unexpected BSSID")
+ finally:
+ dev[0].request("STA_AUTOCONNECT 1")
+
+def test_ap_wps_from_event(dev, apdev):
+ """WPS PBC event on AP to enable PBC"""
+ ssid = "test-wps-conf"
+ hapd = hostapd.add_ap(apdev[0]['ifname'],
+ { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+
+ ev = hapd.wait_event(['WPS-ENROLLEE-SEEN'], timeout=15)
+ if ev is None:
+ raise Exception("No WPS-ENROLLEE-SEEN event on AP")
+ vals = ev.split(' ')
+ if vals[1] != dev[0].p2p_interface_addr():
+ raise Exception("Unexpected enrollee address: " + vals[1])
+ if vals[5] != '4':
+ raise Exception("Unexpected Device Password Id: " + vals[5])
+ hapd.request("WPS_PBC")
+ dev[0].wait_connected(timeout=30)
+
+def test_ap_wps_ap_scan_2(dev, apdev):
+ """AP_SCAN 2 for WPS"""
+ ssid = "test-wps-conf"
+ hapd = hostapd.add_ap(apdev[0]['ifname'],
+ { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ hapd.request("WPS_PBC")
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+
+ if "OK" not in wpas.request("AP_SCAN 2"):
+ raise Exception("Failed to set AP_SCAN 2")
+
+ wpas.scan_for_bss(apdev[0]['bssid'], freq="2412")
+ wpas.request("WPS_PBC " + apdev[0]['bssid'])
+ ev = wpas.wait_event(["WPS-SUCCESS"], timeout=15)