WPS ER: Add PIN configuration and SetSelectedRegistrar call

New PINs can now be added to WPS ER. This results in the ER code
using SetSelectedRegistrar to modify AP state so that Enrollees
will be able to notice the actice registrar more easily.
This commit is contained in:
Jouni Malinen 2009-11-13 22:07:11 +02:00
parent d64d9ddf6c
commit 72df2f5fc6
7 changed files with 304 additions and 36 deletions

View file

@ -252,6 +252,24 @@ struct wps_registrar_config {
void (*reg_success_cb)(void *ctx, const u8 *mac_addr, void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
const u8 *uuid_e); const u8 *uuid_e);
/**
* set_sel_reg_cb - Callback for reporting selected registrar changes
* @ctx: Higher layer context data (cb_ctx)
* @sel_reg: Whether the Registrar is selected
* @dev_passwd_id: Device Password ID to indicate with method or
* specific password the Registrar intends to use
* @sel_reg_config_methods: Bit field of active config methods
*
* This callback is called whenever the Selected Registrar state
* changes (e.g., a new PIN becomes available or PBC is invoked). This
* callback is only used by External Registrar implementation;
* set_ie_cb() is used by AP implementation in similar caes, but it
* provides the full WPS IE data instead of just the minimal Registrar
* state information.
*/
void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
u16 sel_reg_config_methods);
/** /**
* cb_ctx: Higher layer context data for Registrar callbacks * cb_ctx: Higher layer context data for Registrar callbacks
*/ */
@ -609,5 +627,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end);
struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname); struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname);
void wps_er_deinit(struct wps_er *er); void wps_er_deinit(struct wps_er *er);
void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
u16 sel_reg_config_methods);
#endif /* WPS_H */ #endif /* WPS_H */

View file

@ -741,41 +741,24 @@ static const char *soap_postfix =
static const char *urn_wfawlanconfig = static const char *urn_wfawlanconfig =
"urn:schemas-wifialliance-org:service:WFAWLANConfig:1"; "urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg) static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg,
const char *name, const char *path,
const struct sockaddr_in *dst,
char **len_ptr, char **body_ptr)
{ {
unsigned char *encoded; unsigned char *encoded;
size_t encoded_len; size_t encoded_len;
struct wpabuf *buf; struct wpabuf *buf;
char *len_ptr, *body_ptr;
char len_buf[10];
struct sockaddr_in dst;
char *url, *path;
if (sta->http) {
wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - "
"ignore new request");
return;
}
url = http_client_url_parse(sta->ap->control_url, &dst, &path);
if (url == NULL) {
wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL");
return;
}
encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg), encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg),
&encoded_len); &encoded_len);
wpabuf_free(msg); if (encoded == NULL)
if (encoded == NULL) { return NULL;
os_free(url);
return;
}
buf = wpabuf_alloc(1000 + encoded_len); buf = wpabuf_alloc(1000 + encoded_len);
if (buf == NULL) { if (buf == NULL) {
os_free(encoded); os_free(encoded);
os_free(url); return NULL;
return;
} }
wpabuf_printf(buf, wpabuf_printf(buf,
@ -783,33 +766,79 @@ static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg)
"Host: %s:%d\r\n" "Host: %s:%d\r\n"
"Content-Type: text/xml; charset=\"utf-8\"\r\n" "Content-Type: text/xml; charset=\"utf-8\"\r\n"
"Content-Length: ", "Content-Length: ",
path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port)); path, inet_ntoa(dst->sin_addr), ntohs(dst->sin_port));
os_free(url);
len_ptr = wpabuf_put(buf, 0); *len_ptr = wpabuf_put(buf, 0);
wpabuf_printf(buf, wpabuf_printf(buf,
" \r\n" " \r\n"
"SOAPACTION: \"%s#PutWLANResponse\"\r\n" "SOAPACTION: \"%s#%s\"\r\n"
"\r\n", "\r\n",
urn_wfawlanconfig); urn_wfawlanconfig, name);
body_ptr = wpabuf_put(buf, 0); *body_ptr = wpabuf_put(buf, 0);
wpabuf_put_str(buf, soap_prefix); wpabuf_put_str(buf, soap_prefix);
wpabuf_put_str(buf, "<u:PutWLANResponse xmlns:u=\""); wpabuf_printf(buf, "<u:%s xmlns:u=\"", name);
wpabuf_put_str(buf, urn_wfawlanconfig); wpabuf_put_str(buf, urn_wfawlanconfig);
wpabuf_put_str(buf, "\">\n"); wpabuf_put_str(buf, "\">\n");
wpabuf_printf(buf, "<NewMessage>%s</NewMessage>\n", (char *) encoded); wpabuf_printf(buf, "<NewMessage>%s</NewMessage>\n", (char *) encoded);
os_free(encoded); os_free(encoded);
return buf;
}
static void wps_er_soap_end(struct wpabuf *buf, const char *name,
char *len_ptr, char *body_ptr)
{
char len_buf[10];
wpabuf_printf(buf, "</u:%s>\n", name);
wpabuf_put_str(buf, soap_postfix);
os_snprintf(len_buf, sizeof(len_buf), "%d",
(int) ((char *) wpabuf_put(buf, 0) - body_ptr));
os_memcpy(len_ptr, len_buf, os_strlen(len_buf));
}
static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg)
{
struct wpabuf *buf;
char *len_ptr, *body_ptr;
struct sockaddr_in dst;
char *url, *path;
if (sta->http) {
wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - "
"ignore new request");
wpabuf_free(msg);
return;
}
if (sta->ap->control_url == NULL) {
wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
wpabuf_free(msg);
return;
}
url = http_client_url_parse(sta->ap->control_url, &dst, &path);
if (url == NULL) {
wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
wpabuf_free(msg);
return;
}
buf = wps_er_soap_hdr(msg, "PutWLANResponse", path, &dst, &len_ptr,
&body_ptr);
wpabuf_free(msg);
os_free(url);
if (buf == NULL)
return;
wpabuf_printf(buf, "<NewWLANEventType>%d</NewWLANEventType>\n", wpabuf_printf(buf, "<NewWLANEventType>%d</NewWLANEventType>\n",
UPNP_WPS_WLANEVENT_TYPE_EAP); UPNP_WPS_WLANEVENT_TYPE_EAP);
wpabuf_printf(buf, "<NewWLANEventMAC>" MACSTR "</NewWLANEventMAC>\n", wpabuf_printf(buf, "<NewWLANEventMAC>" MACSTR "</NewWLANEventMAC>\n",
MAC2STR(sta->addr)); MAC2STR(sta->addr));
wpabuf_put_str(buf, "</u:PutWLANResponse>\n");
wpabuf_put_str(buf, soap_postfix);
os_snprintf(len_buf, sizeof(len_buf), "%d", wps_er_soap_end(buf, "PutWLANResponse", len_ptr, body_ptr);
(int) ((char *) wpabuf_put(buf, 0) - body_ptr));
os_memcpy(len_ptr, len_buf, os_strlen(len_buf));
sta->http = http_client_addr(&dst, buf, 1000, sta->http = http_client_addr(&dst, buf, 1000,
wps_er_http_put_wlan_response_cb, sta); wps_er_http_put_wlan_response_cb, sta);
@ -1088,3 +1117,115 @@ void wps_er_deinit(struct wps_er *er)
os_free(er->mac_addr_text); os_free(er->mac_addr_text);
os_free(er); os_free(er);
} }
static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c,
enum http_client_event event)
{
struct wps_er_ap *ap = ctx;
switch (event) {
case HTTP_CLIENT_OK:
wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK");
break;
case HTTP_CLIENT_FAILED:
case HTTP_CLIENT_INVALID_REPLY:
case HTTP_CLIENT_TIMEOUT:
wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed");
break;
}
http_client_free(ap->http);
ap->http = NULL;
}
static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg)
{
struct wpabuf *buf;
char *len_ptr, *body_ptr;
struct sockaddr_in dst;
char *url, *path;
if (ap->control_url == NULL) {
wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
return;
}
if (ap->http) {
wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for AP - "
"ignore new request");
return;
}
url = http_client_url_parse(ap->control_url, &dst, &path);
if (url == NULL) {
wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
return;
}
buf = wps_er_soap_hdr(msg, "SetSelectedRegistrar", path, &dst,
&len_ptr, &body_ptr);
os_free(url);
if (buf == NULL)
return;
wps_er_soap_end(buf, "SetSelectedRegistrar", len_ptr, body_ptr);
ap->http = http_client_addr(&dst, buf, 1000,
wps_er_http_set_sel_reg_cb, ap);
if (ap->http == NULL)
wpabuf_free(buf);
}
static int wps_er_build_selected_registrar(struct wpabuf *msg, int sel_reg)
{
wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, !!sel_reg);
return 0;
}
static int wps_er_build_dev_password_id(struct wpabuf *msg, u16 dev_passwd_id)
{
wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, dev_passwd_id);
return 0;
}
static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg,
u16 sel_reg_config_methods)
{
wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, sel_reg_config_methods);
return 0;
}
void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
u16 sel_reg_config_methods)
{
struct wpabuf *msg;
struct wps_er_ap *ap;
msg = wpabuf_alloc(500);
if (msg == NULL)
return;
if (wps_build_version(msg) ||
wps_er_build_selected_registrar(msg, sel_reg) ||
wps_er_build_dev_password_id(msg, dev_passwd_id) ||
wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods)) {
wpabuf_free(msg);
return;
}
for (ap = er->ap; ap; ap = ap->next)
wps_er_send_set_sel_reg(ap, msg);
wpabuf_free(msg);
}

View file

@ -101,6 +101,8 @@ struct wps_registrar {
const struct wps_device_data *dev); const struct wps_device_data *dev);
void (*reg_success_cb)(void *ctx, const u8 *mac_addr, void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
const u8 *uuid_e); const u8 *uuid_e);
void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
u16 sel_reg_config_methods);
void *cb_ctx; void *cb_ctx;
struct wps_uuid_pin *pins; struct wps_uuid_pin *pins;
@ -120,6 +122,7 @@ struct wps_registrar {
static int wps_set_ie(struct wps_registrar *reg); static int wps_set_ie(struct wps_registrar *reg);
static void wps_cb_set_sel_reg(struct wps_registrar *reg);
static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx); static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
static void wps_registrar_set_selected_timeout(void *eloop_ctx, static void wps_registrar_set_selected_timeout(void *eloop_ctx,
void *timeout_ctx); void *timeout_ctx);
@ -449,6 +452,7 @@ wps_registrar_init(struct wps_context *wps,
reg->set_ie_cb = cfg->set_ie_cb; reg->set_ie_cb = cfg->set_ie_cb;
reg->pin_needed_cb = cfg->pin_needed_cb; reg->pin_needed_cb = cfg->pin_needed_cb;
reg->reg_success_cb = cfg->reg_success_cb; reg->reg_success_cb = cfg->reg_success_cb;
reg->set_sel_reg_cb = cfg->set_sel_reg_cb;
reg->cb_ctx = cfg->cb_ctx; reg->cb_ctx = cfg->cb_ctx;
reg->skip_cred_build = cfg->skip_cred_build; reg->skip_cred_build = cfg->skip_cred_build;
if (cfg->extra_cred) { if (cfg->extra_cred) {
@ -536,6 +540,7 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
reg->selected_registrar = 1; reg->selected_registrar = 1;
reg->pbc = 0; reg->pbc = 0;
wps_set_ie(reg); wps_set_ie(reg);
wps_cb_set_sel_reg(reg);
eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
wps_registrar_set_selected_timeout, wps_registrar_set_selected_timeout,
@ -690,6 +695,7 @@ static void wps_registrar_stop_pbc(struct wps_registrar *reg)
reg->selected_registrar = 0; reg->selected_registrar = 0;
reg->pbc = 0; reg->pbc = 0;
wps_set_ie(reg); wps_set_ie(reg);
wps_cb_set_sel_reg(reg);
} }
@ -725,6 +731,7 @@ int wps_registrar_button_pushed(struct wps_registrar *reg)
reg->selected_registrar = 1; reg->selected_registrar = 1;
reg->pbc = 1; reg->pbc = 1;
wps_set_ie(reg); wps_set_ie(reg);
wps_cb_set_sel_reg(reg);
eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout, eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
@ -746,6 +753,7 @@ static void wps_registrar_pin_completed(struct wps_registrar *reg)
eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
reg->selected_registrar = 0; reg->selected_registrar = 0;
wps_set_ie(reg); wps_set_ie(reg);
wps_cb_set_sel_reg(reg);
} }
@ -843,6 +851,24 @@ static int wps_cb_set_ie(struct wps_registrar *reg,
} }
static void wps_cb_set_sel_reg(struct wps_registrar *reg)
{
u16 methods = 0;
if (reg->set_sel_reg_cb == NULL)
return;
if (reg->selected_registrar) {
methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
if (reg->pbc)
methods |= WPS_CONFIG_PUSHBUTTON;
}
reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar,
reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT,
methods);
}
/* Encapsulate WPS IE data with one (or more, if needed) IE headers */ /* Encapsulate WPS IE data with one (or more, if needed) IE headers */
static struct wpabuf * wps_ie_encapsulate(struct wpabuf *data) static struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
{ {
@ -2691,6 +2717,7 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx,
reg->sel_reg_dev_password_id_override = -1; reg->sel_reg_dev_password_id_override = -1;
reg->sel_reg_config_methods_override = -1; reg->sel_reg_config_methods_override = -1;
wps_set_ie(reg); wps_set_ie(reg);
wps_cb_set_sel_reg(reg);
} }

View file

@ -295,6 +295,21 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
ap.key_hex = new_key; ap.key_hex = new_key;
return wpas_wps_start_reg(wpa_s, _bssid, pin, &ap); return wpas_wps_start_reg(wpa_s, _bssid, pin, &ap);
} }
#ifdef CONFIG_WPS_ER
static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s,
char *cmd)
{
char *uuid = cmd, *pin;
pin = os_strchr(uuid, ' ');
if (pin == NULL)
return -1;
*pin++ = '\0';
return wpas_wps_er_add_pin(wpa_s, uuid, pin);
}
#endif /* CONFIG_WPS_ER */
#endif /* CONFIG_WPS */ #endif /* CONFIG_WPS */
@ -1632,12 +1647,17 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
reply_len = -1; reply_len = -1;
#ifdef CONFIG_WPS_ER
} else if (os_strcmp(buf, "WPS_ER_START") == 0) { } else if (os_strcmp(buf, "WPS_ER_START") == 0) {
if (wpas_wps_er_start(wpa_s)) if (wpas_wps_er_start(wpa_s))
reply_len = -1; reply_len = -1;
} else if (os_strcmp(buf, "WPS_ER_STOP") == 0) { } else if (os_strcmp(buf, "WPS_ER_STOP") == 0) {
if (wpas_wps_er_stop(wpa_s)) if (wpas_wps_er_stop(wpa_s))
reply_len = -1; reply_len = -1;
} else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) {
if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11))
reply_len = -1;
#endif /* CONFIG_WPS_ER */
#endif /* CONFIG_WPS */ #endif /* CONFIG_WPS */
#ifdef CONFIG_IBSS_RSN #ifdef CONFIG_IBSS_RSN
} else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) { } else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) {

View file

@ -549,6 +549,29 @@ static int wpa_cli_cmd_wps_er_stop(struct wpa_ctrl *ctrl, int argc,
} }
static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[256];
int res;
if (argc != 2) {
printf("Invalid WPS_ER_PIN command: need two arguments:\n"
"- UUID: use 'any' to select any\n"
"- PIN: Enrollee PIN\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
argv[0], argv[1]);
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
printf("Too long WPS_ER_PIN command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
{ {
char cmd[256]; char cmd[256];
@ -1419,6 +1442,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
{ "wps_er_stop", wpa_cli_cmd_wps_er_stop, { "wps_er_stop", wpa_cli_cmd_wps_er_stop,
cli_cmd_flag_none, cli_cmd_flag_none,
"= stop Wi-Fi Protected Setup External Registrar" }, "= stop Wi-Fi Protected Setup External Registrar" },
{ "wps_er_pin", wpa_cli_cmd_wps_er_pin,
cli_cmd_flag_sensitive,
"<UUID> <PIN> = add an Enrollee PIN to External Registrar" },
{ "ibss_rsn", wpa_cli_cmd_ibss_rsn, { "ibss_rsn", wpa_cli_cmd_ibss_rsn,
cli_cmd_flag_none, cli_cmd_flag_none,
"<addr> = request RSN authentication with <addr> in IBSS" }, "<addr> = request RSN authentication with <addr> in IBSS" },

View file

@ -723,6 +723,20 @@ static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
} }
static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id,
u16 sel_reg_config_methods)
{
#ifdef CONFIG_WPS_ER
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s->wps_er == NULL)
return;
wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id,
sel_reg_config_methods);
#endif /* CONFIG_WPS_ER */
}
int wpas_wps_init(struct wpa_supplicant *wpa_s) int wpas_wps_init(struct wpa_supplicant *wpa_s)
{ {
struct wps_context *wps; struct wps_context *wps;
@ -784,6 +798,7 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
os_memset(&rcfg, 0, sizeof(rcfg)); os_memset(&rcfg, 0, sizeof(rcfg));
rcfg.new_psk_cb = wpas_wps_new_psk_cb; rcfg.new_psk_cb = wpas_wps_new_psk_cb;
rcfg.pin_needed_cb = wpas_wps_pin_needed_cb; rcfg.pin_needed_cb = wpas_wps_pin_needed_cb;
rcfg.set_sel_reg_cb = wpas_wps_set_sel_reg_cb;
rcfg.cb_ctx = wpa_s; rcfg.cb_ctx = wpa_s;
wps->registrar = wps_registrar_init(wps, &rcfg); wps->registrar = wps_registrar_init(wps, &rcfg);
@ -1053,3 +1068,20 @@ int wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
#endif /* CONFIG_WPS_ER */ #endif /* CONFIG_WPS_ER */
return 0; return 0;
} }
#ifdef CONFIG_WPS_ER
int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
const char *pin)
{
u8 u[UUID_LEN];
int any = 0;
if (os_strcmp(uuid, "any") == 0)
any = 1;
else if (uuid_str2bin(uuid, u))
return -1;
return wps_registrar_add_pin(wpa_s->wps->registrar, any ? NULL : u,
(const u8 *) pin, os_strlen(pin), 300);
}
#endif /* CONFIG_WPS_ER */

View file

@ -51,6 +51,8 @@ int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos,
char *end); char *end);
int wpas_wps_er_start(struct wpa_supplicant *wpa_s); int wpas_wps_er_start(struct wpa_supplicant *wpa_s);
int wpas_wps_er_stop(struct wpa_supplicant *wpa_s); int wpas_wps_er_stop(struct wpa_supplicant *wpa_s);
int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
const char *pin);
#else /* CONFIG_WPS */ #else /* CONFIG_WPS */