WPS: Add initial part of External Registrar functionality
authorJouni Malinen <j@w1.fi>
Sat, 7 Nov 2009 10:41:01 +0000 (12:41 +0200)
committerJouni Malinen <j@w1.fi>
Sat, 7 Nov 2009 10:41:01 +0000 (12:41 +0200)
This is the first step in adding support for using wpa_supplicant as a
WPS External Registrar to manage APs over UPnP. Only the device
discovery part is implemented in this commit.

src/wps/wps.h
src/wps/wps_er.c [new file with mode: 0644]
src/wps/wps_upnp.c
src/wps/wps_upnp_i.h
src/wps/wps_upnp_ssdp.c
wpa_supplicant/Makefile
wpa_supplicant/ctrl_iface.c
wpa_supplicant/wpa_cli.c
wpa_supplicant/wpa_supplicant_i.h
wpa_supplicant/wps_supplicant.c
wpa_supplicant/wps_supplicant.h

index 2661e94..76b818e 100644 (file)
@@ -32,6 +32,7 @@ enum wsc_op_code {
 
 struct wps_registrar;
 struct upnp_wps_device_sm;
+struct wps_er;
 
 /**
  * struct wps_credential - WPS Credential
@@ -606,4 +607,7 @@ int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
                    int registrar);
 int wps_attr_text(struct wpabuf *data, char *buf, char *end);
 
+struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname);
+void wps_er_deinit(struct wps_er *er);
+
 #endif /* WPS_H */
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
new file mode 100644 (file)
index 0000000..2908b38
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * Wi-Fi Protected Setup - External Registrar
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "uuid.h"
+#include "eloop.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+
+/* TODO:
+ * SSDP M-SEARCH multicast TX for WFA
+ * create AP entry
+ * fetch wps_device info based on LOCATION: from SSDP NOTIFY
+ * parse wps_device info into AP entry (name, SCPD/control/eventSub URLs, etc.
+ * subscribe to events
+ * send notification of new AP device with wpa_msg
+ * re-send notifications with wpa_msg if ER re-started (to update wpa_gui-qt4)
+ * (also re-send SSDP M-SEARCH in this case to find new APs)
+ * parse UPnP event messages
+ */
+
+static void wps_er_ap_timeout(void *eloop_data, void *user_ctx);
+
+
+struct wps_er_ap {
+       struct wps_er_ap *next;
+       struct in_addr addr;
+       char *location;
+
+};
+
+struct wps_er {
+       struct wps_registrar *reg;
+       char ifname[17];
+       char *mac_addr_text; /* mac addr of network i.f. we use */
+       u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
+       char *ip_addr_text; /* IP address of network i.f. we use */
+       unsigned ip_addr; /* IP address of network i.f. we use (host order) */
+       int multicast_sd;
+       int ssdp_sd;
+       struct wps_er_ap *ap;
+};
+
+
+static void wps_er_pin_needed_cb(void *ctx, const u8 *uuid_e,
+                                const struct wps_device_data *dev)
+{
+       wpa_printf(MSG_DEBUG, "WPS ER: PIN needed");
+}
+
+
+static struct wps_er_ap * wps_er_ap_get(struct wps_er *er,
+                                       struct in_addr *addr)
+{
+       struct wps_er_ap *ap;
+       for (ap = er->ap; ap; ap = ap->next) {
+               if (ap->addr.s_addr == addr->s_addr)
+                       break;
+       }
+       return ap;
+}
+
+
+static void wps_er_ap_free(struct wps_er *er, struct wps_er_ap *ap)
+{
+       wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)",
+                  inet_ntoa(ap->addr), ap->location);
+       eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
+       os_free(ap->location);
+       os_free(ap);
+}
+
+
+static void wps_er_ap_timeout(void *eloop_data, void *user_ctx)
+{
+       struct wps_er *er = eloop_data;
+       struct wps_er_ap *ap = user_ctx;
+       wpa_printf(MSG_DEBUG, "WPS ER: AP advertisement timed out");
+       wps_er_ap_free(er, ap);
+}
+
+
+static void wps_er_ap_add(struct wps_er *er, struct in_addr *addr,
+                         const char *location, int max_age)
+{
+       struct wps_er_ap *ap;
+
+       ap = wps_er_ap_get(er, addr);
+       if (ap) {
+               /* Update advertisement timeout */
+               eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
+               eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
+               return;
+       }
+
+       ap = os_zalloc(sizeof(*ap));
+       if (ap == NULL)
+               return;
+       ap->location = os_strdup(location);
+       if (ap->location == NULL) {
+               os_free(ap);
+               return;
+       }
+       ap->next = er->ap;
+       er->ap = ap;
+
+       ap->addr.s_addr = addr->s_addr;
+       eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
+
+       wpa_printf(MSG_DEBUG, "WPS ER: Added AP entry for %s (%s)",
+                  inet_ntoa(ap->addr), ap->location);
+
+       /* TODO: get device data and subscribe for events */
+}
+
+
+static void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr)
+{
+       struct wps_er_ap *prev = NULL, *ap = er->ap;
+
+       while (ap) {
+               if (ap->addr.s_addr == addr->s_addr) {
+                       if (prev)
+                               prev->next = ap->next;
+                       else
+                               er->ap = ap->next;
+                       wps_er_ap_free(er, ap);
+                       return;
+               }
+               prev = ap;
+               ap = ap->next;
+       }
+}
+
+
+static void wps_er_ap_remove_all(struct wps_er *er)
+{
+       struct wps_er_ap *prev, *ap;
+
+       ap = er->ap;
+       er->ap = NULL;
+
+       while (ap) {
+               prev = ap;
+               ap = ap->next;
+               wps_er_ap_free(er, prev);
+       }
+}
+
+
+static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx)
+{
+       struct wps_er *er = eloop_ctx;
+       struct sockaddr_in addr; /* client address */
+       socklen_t addr_len;
+       int nread;
+       char buf[MULTICAST_MAX_READ], *pos, *pos2, *start;
+       int wfa = 0, byebye = 0;
+       int max_age = -1;
+       char *location = NULL;
+
+       addr_len = sizeof(addr);
+       nread = recvfrom(sd, buf, sizeof(buf) - 1, 0,
+                        (struct sockaddr *) &addr, &addr_len);
+       if (nread <= 0)
+               return;
+       buf[nread] = '\0';
+
+       wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s",
+                  inet_ntoa(addr.sin_addr));
+       wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Received SSDP contents",
+                         (u8 *) buf, nread);
+
+       if (sd == er->multicast_sd) {
+               /* Reply to M-SEARCH */
+               if (os_strncmp(buf, "HTTP/1.1 200 OK", 15) != 0)
+                       return; /* unexpected response header */
+       } else {
+               /* Unsolicited message (likely NOTIFY or M-SEARCH) */
+               if (os_strncmp(buf, "NOTIFY ", 7) != 0)
+                       return; /* only process notifications */
+       }
+
+       for (start = buf; start && *start; start = pos) {
+               pos = os_strchr(start, '\n');
+               if (pos) {
+                       if (pos[-1] == '\r')
+                               pos[-1] = '\0';
+                       *pos++ = '\0';
+               }
+               if (os_strstr(start, "schemas-wifialliance-org:device:"
+                             "WFADevice:1"))
+                       wfa = 1;
+               if (os_strstr(start, "schemas-wifialliance-org:service:"
+                             "WFAWLANConfig:1"))
+                       wfa = 1;
+               if (os_strncasecmp(start, "LOCATION:", 9) == 0) {
+                       start += 9;
+                       while (*start == ' ')
+                               start++;
+                       location = start;
+               } else if (os_strncasecmp(start, "NTS:", 4) == 0) {
+                       if (os_strstr(start, "ssdp:byebye"))
+                               byebye = 1;
+               } else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) {
+                       start += 9;
+                       while (*start == ' ')
+                               start++;
+                       pos2 = os_strstr(start, "max-age=");
+                       if (pos2 == NULL)
+                               continue;
+                       pos2 += 8;
+                       max_age = atoi(pos2);
+               }
+       }
+
+       if (!wfa)
+               return; /* Not WPS advertisement/reply */
+
+       if (byebye) {
+               wps_er_ap_remove(er, &addr.sin_addr);
+               return;
+       }
+
+       if (!location)
+               return; /* Unknown location */
+
+       if (max_age < 1)
+               return; /* No max-age reported */
+
+       wpa_printf(MSG_DEBUG, "WPS ER: AP discovered: %s "
+                  "(packet source: %s  max-age: %d)",
+                  location, inet_ntoa(addr.sin_addr), max_age);
+
+       wps_er_ap_add(er, &addr.sin_addr, location, max_age);
+}
+
+
+static void wps_er_send_ssdp_msearch(struct wps_er *er)
+{
+       struct wpabuf *msg;
+       struct sockaddr_in dest;
+
+       msg = wpabuf_alloc(500);
+       if (msg == NULL)
+               return;
+
+       wpabuf_put_str(msg,
+                      "M-SEARCH * HTTP/1.1\r\n"
+                      "HOST: 239.255.255.250:1900\r\n"
+                      "MAN: \"ssdp:discover\"\r\n"
+                      "MX: 3\r\n"
+                      "ST: urn:schemas-wifialliance-org:device:WFADevice:1"
+                      "\r\n"
+                      "\r\n");
+
+       os_memset(&dest, 0, sizeof(dest));
+       dest.sin_family = AF_INET;
+       dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+       dest.sin_port = htons(UPNP_MULTICAST_PORT);
+
+       if (sendto(er->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+                  (struct sockaddr *) &dest, sizeof(dest)) < 0)
+               wpa_printf(MSG_DEBUG, "WPS ER: M-SEARCH sendto failed: "
+                          "%d (%s)", errno, strerror(errno));
+
+       wpabuf_free(msg);
+}
+
+
+struct wps_er *
+wps_er_init(struct wps_context *wps, const char *ifname)
+{
+       struct wps_er *er;
+       struct wps_registrar_config rcfg;
+
+       er = os_zalloc(sizeof(*er));
+       if (er == NULL)
+               return NULL;
+
+       er->multicast_sd = -1;
+       er->ssdp_sd = -1;
+
+       os_strlcpy(er->ifname, ifname, sizeof(er->ifname));
+       os_memset(&rcfg, 0, sizeof(rcfg));
+       rcfg.pin_needed_cb = wps_er_pin_needed_cb;
+       rcfg.cb_ctx = er;
+
+       er->reg = wps_registrar_init(wps, &rcfg);
+       if (er->reg == NULL) {
+               wps_er_deinit(er);
+               return NULL;
+       }
+
+       if (get_netif_info(ifname,
+                          &er->ip_addr, &er->ip_addr_text,
+                          er->mac_addr, &er->mac_addr_text)) {
+               wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
+                          "for %s. Does it have IP address?", ifname);
+               wps_er_deinit(er);
+               return NULL;
+       }
+
+       if (add_ssdp_network(ifname)) {
+               wps_er_deinit(er);
+               return NULL;
+       }
+
+       er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr);
+       if (er->multicast_sd < 0) {
+               wps_er_deinit(er);
+               return NULL;
+       }
+
+       er->ssdp_sd = ssdp_listener_open();
+       if (er->ssdp_sd < 0) {
+               wps_er_deinit(er);
+               return NULL;
+       }
+       if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ,
+                               wps_er_ssdp_rx, er, NULL) ||
+           eloop_register_sock(er->ssdp_sd, EVENT_TYPE_READ,
+                               wps_er_ssdp_rx, er, NULL)) {
+               wps_er_deinit(er);
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS ER: Start (ifname=%s ip_addr=%s "
+                  "mac_addr=%s)",
+                  er->ifname, er->ip_addr_text, er->mac_addr_text);
+
+       wps_er_send_ssdp_msearch(er);
+
+       return er;
+}
+
+
+void wps_er_deinit(struct wps_er *er)
+{
+       if (er == NULL)
+               return;
+       wps_er_ap_remove_all(er);
+       if (er->multicast_sd >= 0) {
+               eloop_unregister_sock(er->multicast_sd, EVENT_TYPE_READ);
+               close(er->multicast_sd);
+       }
+       if (er->ssdp_sd >= 0) {
+               eloop_unregister_sock(er->ssdp_sd, EVENT_TYPE_READ);
+               close(er->ssdp_sd);
+       }
+       wps_registrar_deinit(er->reg);
+       os_free(er->ip_addr_text);
+       os_free(er->mac_addr_text);
+       os_free(er);
+}
index da488bc..25c6f44 100644 (file)
@@ -885,9 +885,8 @@ static int eth_get(const char *device, u8 ea[ETH_ALEN])
  * @mac_addr_text: Buffer for returning allocated MAC address text
  * Returns: 0 on success, -1 on failure
  */
-static int get_netif_info(const char *net_if, unsigned *ip_addr,
-                         char **ip_addr_text, u8 mac[ETH_ALEN],
-                         char **mac_addr_text)
+int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
+                  u8 mac[ETH_ALEN], char **mac_addr_text)
 {
        struct ifreq req;
        int sock = -1;
@@ -930,7 +929,7 @@ static int get_netif_info(const char *net_if, unsigned *ip_addr,
 #else
 #error MAC address fetch not implemented
 #endif
-       os_snprintf(*mac_addr_text, 18, MACSTR, MAC2STR(req.ifr_addr.sa_data));
+       os_snprintf(*mac_addr_text, 18, MACSTR, MAC2STR(mac));
 
        close(sock);
        return 0;
index d4b6569..dd4c44d 100644 (file)
@@ -25,6 +25,8 @@
 #define UPNP_WPS_DEVICE_CONTROL_FILE "wps_control"
 #define UPNP_WPS_DEVICE_EVENT_FILE "wps_event"
 
+#define MULTICAST_MAX_READ 1600 /* max bytes we'll read for UPD request */
+
 
 struct web_connection;
 struct subscription;
@@ -168,6 +170,8 @@ void subscription_destroy(struct subscription *s);
 struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
                                        const u8 uuid[UUID_LEN]);
 int send_wpabuf(int fd, struct wpabuf *buf);
+int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
+                  u8 mac[ETH_ALEN], char **mac_addr_text);
 
 /* wps_upnp_ssdp.c */
 void msearchreply_state_machine_stop(struct advertisement_state_machine *a);
@@ -175,7 +179,9 @@ int advertisement_state_machine_start(struct upnp_wps_device_sm *sm);
 void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm);
 void ssdp_listener_stop(struct upnp_wps_device_sm *sm);
 int ssdp_listener_start(struct upnp_wps_device_sm *sm);
-int add_ssdp_network(char *net_if);
+int ssdp_listener_open(void);
+int add_ssdp_network(const char *net_if);
+int ssdp_open_multicast_sock(u32 ip_addr);
 int ssdp_open_multicast(struct upnp_wps_device_sm *sm);
 
 /* wps_upnp_web.c */
index 92b785c..3ffff09 100644 (file)
@@ -24,7 +24,6 @@
 #define UPNP_CACHE_SEC (UPNP_CACHE_SEC_MIN + 1) /* cache time we use */
 #define UPNP_CACHE_SEC_MIN 1800 /* min cachable time per UPnP standard */
 #define UPNP_ADVERTISE_REPEAT 2 /* no more than 3 */
-#define MULTICAST_MAX_READ 1600 /* max bytes we'll read for UPD request */
 #define MAX_MSEARCH 20          /* max simultaneous M-SEARCH replies ongoing */
 #define SSDP_TARGET  "239.0.0.0"
 #define SSDP_NETMASK "255.0.0.0"
@@ -657,7 +656,7 @@ bad:
  * ssdp_listener_stop - Stop SSDP listered
  * @sm: WPS UPnP state machine from upnp_wps_device_init()
  *
- * This function stops the SSDP listerner that was started by calling
+ * This function stops the SSDP listener that was started by calling
  * ssdp_listener_start().
  */
 void ssdp_listener_stop(struct upnp_wps_device_sm *sm)
@@ -719,23 +718,16 @@ static void ssdp_listener_handler(int sd, void *eloop_ctx, void *sock_ctx)
 }
 
 
-/**
- * ssdp_listener_start - Set up for receiving discovery (UDP) packets
- * @sm: WPS UPnP state machine from upnp_wps_device_init()
- * Returns: 0 on success, -1 on failure
- *
- * The SSDP listerner is stopped by calling ssdp_listener_stop().
- */
-int ssdp_listener_start(struct upnp_wps_device_sm *sm)
+int ssdp_listener_open(void)
 {
-       int sd = -1;
        struct sockaddr_in addr;
        struct ip_mreq mcast_addr;
        int on = 1;
        /* per UPnP spec, keep IP packet time to live (TTL) small */
        unsigned char ttl = 4;
+       int sd;
 
-       sm->ssdp_sd = sd = socket(AF_INET, SOCK_DGRAM, 0);
+       sd = socket(AF_INET, SOCK_DGRAM, 0);
        if (sd < 0)
                goto fail;
        if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
@@ -757,8 +749,29 @@ int ssdp_listener_start(struct upnp_wps_device_sm *sm)
        if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
                       &ttl, sizeof(ttl)))
                goto fail;
-       if (eloop_register_sock(sd, EVENT_TYPE_READ, ssdp_listener_handler,
-                               NULL, sm))
+
+       return sd;
+
+fail:
+       if (sd >= 0)
+               close(sd);
+       return -1;
+}
+
+
+/**
+ * ssdp_listener_start - Set up for receiving discovery (UDP) packets
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * The SSDP listener is stopped by calling ssdp_listener_stop().
+ */
+int ssdp_listener_start(struct upnp_wps_device_sm *sm)
+{
+       sm->ssdp_sd = ssdp_listener_open();
+
+       if (eloop_register_sock(sm->ssdp_sd, EVENT_TYPE_READ,
+                               ssdp_listener_handler, NULL, sm))
                goto fail;
        sm->ssdp_sd_registered = 1;
        return 0;
@@ -782,7 +795,7 @@ fail:
  * once after booting up, but it does not hurt to call this more frequently
  * "to be safe".
  */
-int add_ssdp_network(char *net_if)
+int add_ssdp_network(const char *net_if)
 {
 #ifdef __linux__
        int ret = -1;
@@ -798,7 +811,7 @@ int add_ssdp_network(char *net_if)
        if (sock < 0)
                goto fail;
 
-       rt.rt_dev = net_if;
+       rt.rt_dev = (char *) net_if;
        sin = aliasing_hide_typecast(&rt.rt_dst, struct sockaddr_in);
        sin->sin_family = AF_INET;
        sin->sin_port = 0;
@@ -833,19 +846,14 @@ fail:
 }
 
 
-/**
- * ssdp_open_multicast - Open socket for sending multicast SSDP messages
- * @sm: WPS UPnP state machine from upnp_wps_device_init()
- * Returns: 0 on success, -1 on failure
- */
-int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
+int ssdp_open_multicast_sock(u32 ip_addr)
 {
-       int sd = -1;
+       int sd;
         /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet
          * time to live (TTL) small */
        unsigned char ttl = 4;
 
-       sm->multicast_sd = sd = socket(AF_INET, SOCK_DGRAM, 0);
+       sd = socket(AF_INET, SOCK_DGRAM, 0);
        if (sd < 0)
                return -1;
 
@@ -855,7 +863,7 @@ int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
 #endif
 
        if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
-                      &sm->ip_addr, sizeof(sm->ip_addr)))
+                      &ip_addr, sizeof(ip_addr)))
                return -1;
        if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
                       &ttl, sizeof(ttl)))
@@ -865,7 +873,7 @@ int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
        {
                struct ip_mreq mreq;
                mreq.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
-               mreq.imr_interface.s_addr = sm->ip_addr;
+               mreq.imr_interface.s_addr = ip_addr;
                wpa_printf(MSG_DEBUG, "WPS UPnP: Multicast addr 0x%x if addr "
                           "0x%x",
                           mreq.imr_multiaddr.s_addr,
@@ -886,5 +894,19 @@ int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
         * which aids debugging I suppose but isn't really necessary?
         */
 
+       return sd;
+}
+
+
+/**
+ * ssdp_open_multicast - Open socket for sending multicast SSDP messages
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
+{
+       sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr);
+       if (sm->multicast_sd < 0)
+               return -1;
        return 0;
 }
index 6e15f24..e234bc8 100644 (file)
@@ -497,6 +497,12 @@ ifdef NEED_WPS_OOB
 CFLAGS += -DCONFIG_WPS_OOB
 endif
 
+ifdef CONFIG_WPS_ER
+CONFIG_WPS_UPNP=y
+CFLAGS += -DCONFIG_WPS_ER
+OBJS += ../src/wps/wps_er.o
+endif
+
 ifdef CONFIG_WPS_UPNP
 CFLAGS += -DCONFIG_WPS_UPNP
 OBJS += ../src/wps/wps_upnp.o
index bebb125..c00169c 100644 (file)
@@ -1632,6 +1632,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
                if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
                        reply_len = -1;
+       } else if (os_strcmp(buf, "WPS_ER_START") == 0) {
+               if (wpas_wps_er_start(wpa_s))
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "WPS_ER_STOP") == 0) {
+               if (wpas_wps_er_stop(wpa_s))
+                       reply_len = -1;
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_IBSS_RSN
        } else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) {
index ec103bf..f19a603 100644 (file)
@@ -533,6 +533,22 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc,
+                                   char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "WPS_ER_START");
+
+}
+
+
+static int wpa_cli_cmd_wps_er_stop(struct wpa_ctrl *ctrl, int argc,
+                                  char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "WPS_ER_STOP");
+
+}
+
+
 static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        char cmd[256];
@@ -1397,6 +1413,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "wps_reg", wpa_cli_cmd_wps_reg,
          cli_cmd_flag_sensitive,
          "<BSSID> <AP PIN> = start WPS Registrar to configure an AP" },
+       { "wps_er_start", wpa_cli_cmd_wps_er_start,
+         cli_cmd_flag_none,
+         "= start Wi-Fi Protected Setup External Registrar" },
+       { "wps_er_stop", wpa_cli_cmd_wps_er_stop,
+         cli_cmd_flag_none,
+         "= stop Wi-Fi Protected Setup External Registrar" },
        { "ibss_rsn", wpa_cli_cmd_ibss_rsn,
          cli_cmd_flag_none,
          "<addr> = request RSN authentication with <addr> in IBSS" },
index d426aaa..e47ac74 100644 (file)
@@ -380,6 +380,7 @@ struct wpa_supplicant {
 
        struct wps_context *wps;
        int wps_success; /* WPS success event received */
+       struct wps_er *wps_er;
        int blacklist_cleared;
 
        struct wpabuf *pending_eapol_rx;
index 95f3b88..5fd94e2 100644 (file)
@@ -814,6 +814,9 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
        os_free(wpa_s->wps->network_key);
        os_free(wpa_s->wps);
        wpa_s->wps = NULL;
+
+       wps_er_deinit(wpa_s->wps_er);
+       wpa_s->wps_er = NULL;
 }
 
 
@@ -1021,3 +1024,30 @@ int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
        wpabuf_free(wps_ie);
        return ret;
 }
+
+
+int wpas_wps_er_start(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WPS_ER
+       if (wpa_s->wps_er) {
+               /* TODO: re-send ctrl_iface events for current data? */
+               return 0;
+       }
+       wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname);
+       if (wpa_s->wps_er == NULL)
+               return -1;
+       return 0;
+#else /* CONFIG_WPS_ER */
+       return 0;
+#endif /* CONFIG_WPS_ER */
+}
+
+
+int wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WPS_ER
+       wps_er_deinit(wpa_s->wps_er);
+       wpa_s->wps_er = NULL;
+#endif /* CONFIG_WPS_ER */
+       return 0;
+}
index 0f23955..e75b3be 100644 (file)
@@ -49,6 +49,8 @@ void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s);
 int wpas_wps_searching(struct wpa_supplicant *wpa_s);
 int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos,
                              char *end);
+int wpas_wps_er_start(struct wpa_supplicant *wpa_s);
+int wpas_wps_er_stop(struct wpa_supplicant *wpa_s);
 
 #else /* CONFIG_WPS */