From 3fb17a95307c19427c707813051e4043745df6ba Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 17 Mar 2013 16:03:42 +0200 Subject: [PATCH] HS 2.0R2 AP: Add WNM-Notification Request for Subscription Remediation Subscription remediation notification can now be sent from hostapd with: hostapd_cli hs20_wnm_notif 02:00:00:00:00:00 http://example.com/foo/ Signed-hostap: Jouni Malinen --- hostapd/ctrl_iface.c | 32 +++++++++++++++++++++++ hostapd/hostapd_cli.c | 21 +++++++++++++++ src/ap/hs20.c | 52 ++++++++++++++++++++++++++++++++++++++ src/ap/hs20.h | 2 ++ src/ap/ieee802_11_shared.c | 8 ++++++ 5 files changed, 115 insertions(+) diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index b073e8ce2..53d8d3dc9 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -30,6 +30,7 @@ #include "ap/wps_hostapd.h" #include "ap/ctrl_iface_ap.h" #include "ap/ap_drv_ops.h" +#include "ap/hs20.h" #include "ap/wnm_ap.h" #include "ap/wpa_auth.h" #include "wps/wps_defs.h" @@ -617,6 +618,32 @@ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd, #endif /* CONFIG_WPS */ +#ifdef CONFIG_HS20 + +static int hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + const char *url; + + if (hwaddr_aton(cmd, addr)) + return -1; + url = cmd + 17; + if (*url == '\0') { + url = NULL; + } else { + if (*url != ' ') + return -1; + url++; + if (*url == '\0') + url = NULL; + } + + return hs20_send_wnm_notification(hapd, addr, 1, url); +} + +#endif /* CONFIG_HS20 */ + #ifdef CONFIG_INTERWORKING @@ -1348,6 +1375,11 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, if (hostapd_ctrl_iface_send_qos_map_conf(hapd, buf + 18)) reply_len = -1; #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 + } else if (os_strncmp(buf, "HS20_WNM_NOTIF ", 15) == 0) { + if (hostapd_ctrl_iface_hs20_wnm_notif(hapd, buf + 15)) + reply_len = -1; +#endif /* CONFIG_HS20 */ #ifdef CONFIG_WNM } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) { if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18)) diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index eee85041b..4d5f269c4 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -743,6 +743,26 @@ static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl, } +static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[300]; + int res; + + if (argc < 2) { + printf("Invalid 'hs20_wnm_notif' command - two arguments (STA " + "addr and URL) are needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s", + argv[0], argv[1]); + if (res < 0 || res >= (int) sizeof(buf)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) { hostapd_cli_quit = 1; @@ -941,6 +961,7 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = { { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set }, { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf }, { "chan_switch", hostapd_cli_cmd_chan_switch }, + { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif }, { NULL, NULL } }; diff --git a/src/ap/hs20.c b/src/ap/hs20.c index 01c87e820..5875b4096 100644 --- a/src/ap/hs20.c +++ b/src/ap/hs20.c @@ -13,6 +13,7 @@ #include "common/ieee802_11_defs.h" #include "hostapd.h" #include "ap_config.h" +#include "ap_drv_ops.h" #include "hs20.h" @@ -36,3 +37,54 @@ u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid) return eid; } + + +int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr, + u8 osu_method, const char *url) +{ + struct wpabuf *buf; + size_t len = 0; + int ret; + + /* TODO: should refuse to send notification if the STA is not associated + * or if the STA did not indicate support for WNM-Notification */ + + if (url) { + len = 1 + os_strlen(url); + if (5 + len > 255) { + wpa_printf(MSG_INFO, "HS 2.0: Too long URL for " + "WNM-Notification: '%s'", url); + return -1; + } + } + + buf = wpabuf_alloc(4 + 7 + len); + if (buf == NULL) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); + wpabuf_put_u8(buf, 1); /* Dialog token */ + wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */ + + /* Subscription Remediation subelement */ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 5 + len); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED); + if (url) { + wpabuf_put_u8(buf, len - 1); + wpabuf_put_data(buf, url, len - 1); + wpabuf_put_u8(buf, osu_method); + } else { + /* Server URL and Server Method fields not included */ + wpabuf_put_u8(buf, 0); + } + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + + wpabuf_free(buf); + + return ret; +} diff --git a/src/ap/hs20.h b/src/ap/hs20.h index 98698ce2f..059764e47 100644 --- a/src/ap/hs20.h +++ b/src/ap/hs20.h @@ -12,5 +12,7 @@ struct hostapd_data; u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid); +int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr, + u8 osu_method, const char *url); #endif /* HS20_H */ diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index eadaa4d18..b78fd010e 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -199,6 +199,10 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) } break; case 5: /* Bits 40-47 */ +#ifdef CONFIG_HS20 + if (hapd->conf->hs20) + *pos |= 0x40; /* Bit 46 - WNM-Notification */ +#endif /* CONFIG_HS20 */ break; case 6: /* Bits 48-55 */ if (hapd->conf->ssid.utf8_ssid) @@ -225,6 +229,10 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) if (len < 4) len = 4; #endif /* CONFIG_WNM */ +#ifdef CONFIG_HS20 + if (hapd->conf->hs20 && len < 6) + len = 6; +#endif /* CONFIG_HS20 */ if (len < hapd->iface->extended_capa_len) len = hapd->iface->extended_capa_len; if (len == 0)