From c551700f1fdf073015531cdd3123e792f244c0a6 Mon Sep 17 00:00:00 2001 From: Kyeyoon Park Date: Wed, 24 Jul 2013 12:28:20 +0300 Subject: [PATCH] Interworking: Add support for QoS Mapping functionality for the AP This allows QoS Map Set element to be added to (Re)Association Response frames and in QoS Map Configure frame. The QoS Mapping parameters are also made available for the driver interface. Signed-hostap: Jouni Malinen --- hostapd/config_file.c | 44 ++++++++++++++ hostapd/ctrl_iface.c | 108 +++++++++++++++++++++++++++++++++++ hostapd/hostapd.conf | 17 ++++++ hostapd/hostapd_cli.c | 41 +++++++++++++ hostapd/main.c | 9 +++ src/ap/ap_config.h | 3 + src/ap/ap_drv_ops.c | 10 ++++ src/ap/ap_drv_ops.h | 3 + src/ap/drv_callbacks.c | 7 +++ src/ap/ieee802_11.c | 20 +++++++ src/ap/ieee802_11.h | 1 + src/ap/ieee802_11_shared.c | 19 ++++++ src/ap/sta_info.h | 1 + src/common/ieee802_11_defs.h | 10 ++++ src/drivers/driver.h | 9 +++ 15 files changed, 302 insertions(+) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index dc68fc8ba..3a3ae0ba3 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -1561,6 +1561,47 @@ fail: return -1; } + +static int parse_qos_map_set(struct hostapd_bss_config *bss, + char *buf, int line) +{ + u8 qos_map_set[16 + 2 * 21], count = 0; + char *pos = buf; + int val; + + for (;;) { + if (count == sizeof(qos_map_set)) { + wpa_printf(MSG_ERROR, "Line %d: Too many qos_map_set " + "parameters '%s'", line, buf); + return -1; + } + + val = atoi(pos); + if (val > 255 || val < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set " + "'%s'", line, buf); + return -1; + } + + qos_map_set[count++] = val; + pos = os_strchr(pos, ','); + if (!pos) + break; + pos++; + } + + if (count < 16 || count & 1) { + wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set '%s'", + line, buf); + return -1; + } + + os_memcpy(bss->qos_map_set, qos_map_set, count); + bss->qos_map_set_len = count; + + return 0; +} + #endif /* CONFIG_INTERWORKING */ @@ -2886,6 +2927,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->gas_frag_limit = atoi(pos); } else if (os_strcmp(buf, "gas_comeback_delay") == 0) { bss->gas_comeback_delay = atoi(pos); + } else if (os_strcmp(buf, "qos_map_set") == 0) { + if (parse_qos_map_set(bss, pos, line) < 0) + errors++; #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_RADIUS_TEST } else if (os_strcmp(buf, "dump_msk_file") == 0) { diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index be941c44f..a504aac79 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -559,6 +559,106 @@ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd, #endif /* CONFIG_WPS */ +#ifdef CONFIG_INTERWORKING + +static int hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data *hapd, + const char *cmd) +{ + u8 qos_map_set[16 + 2 * 21], count = 0; + const char *pos = cmd; + int val, ret; + + for (;;) { + if (count == sizeof(qos_map_set)) { + wpa_printf(MSG_ERROR, "Too many qos_map_set parameters"); + return -1; + } + + val = atoi(pos); + if (val < 0 || val > 255) { + wpa_printf(MSG_INFO, "Invalid QoS Map Set"); + return -1; + } + + qos_map_set[count++] = val; + pos = os_strchr(pos, ','); + if (!pos) + break; + pos++; + } + + if (count < 16 || count & 1) { + wpa_printf(MSG_INFO, "Invalid QoS Map Set"); + return -1; + } + + ret = hostapd_drv_set_qos_map(hapd, qos_map_set, count); + if (ret) { + wpa_printf(MSG_INFO, "Failed to set QoS Map Set"); + return -1; + } + + os_memcpy(hapd->conf->qos_map_set, qos_map_set, count); + hapd->conf->qos_map_set_len = count; + + return 0; +} + + +static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + struct wpabuf *buf; + u8 *qos_map_set = hapd->conf->qos_map_set; + u8 qos_map_set_len = hapd->conf->qos_map_set_len; + int ret; + + if (!qos_map_set_len) { + wpa_printf(MSG_INFO, "QoS Map Set is not set"); + return -1; + } + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Station " MACSTR " not found " + "for QoS Map Configuration message", + MAC2STR(addr)); + return -1; + } + + if (!sta->qos_map_enabled) { + wpa_printf(MSG_DEBUG, "Station " MACSTR " did not indicate " + "support for QoS Map", MAC2STR(addr)); + return -1; + } + + buf = wpabuf_alloc(2 + 2 + qos_map_set_len); + if (buf == NULL) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_QOS); + wpabuf_put_u8(buf, QOS_QOS_MAP_CONFIG); + + /* QoS Map Set Element */ + wpabuf_put_u8(buf, WLAN_EID_QOS_MAP_SET); + wpabuf_put_u8(buf, qos_map_set_len); + wpabuf_put_data(buf, qos_map_set, qos_map_set_len); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + + return ret; +} + +#endif /* CONFIG_INTERWORKING */ + + #ifdef CONFIG_WNM static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd, @@ -1093,6 +1193,14 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, reply_len = -1; #endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS */ +#ifdef CONFIG_INTERWORKING + } else if (os_strncmp(buf, "SET_QOS_MAP_SET ", 16) == 0) { + if (hostapd_ctrl_iface_set_qos_map_set(hapd, buf + 16)) + reply_len = -1; + } else if (os_strncmp(buf, "SEND_QOS_MAP_CONF ", 18) == 0) { + if (hostapd_ctrl_iface_send_qos_map_conf(hapd, buf + 18)) + reply_len = -1; +#endif /* CONFIG_INTERWORKING */ #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.conf b/hostapd/hostapd.conf index de1bf75f3..a2e4cd460 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1524,6 +1524,23 @@ own_ip_addr=127.0.0.1 # username/password #nai_realm=0,example.org,13[5:6],21[2:4][5:7] +# QoS Map Set configuration +# +# Comma delimited QoS Map Set in decimal values +# (see IEEE Std 802.11-2012, 8.4.2.97) +# +# format: +# [,],... +# +# There can be up to 21 optional DSCP Exceptions which are pairs of DSCP Value +# (0..63 or 255) and User Priority (0..7). This is followed by eight DSCP Range +# descriptions with DSCP Low Value and DSCP High Value pairs (0..63 or 255) for +# each UP starting from 0. If both low and high value are set to 255, the +# corresponding UP is not used. +# +# default: not set +#qos_map_set=53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,255,255 + ##### Hotspot 2.0 ############################################################# # Enable Hotspot 2.0 support diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 5f48fc89c..932ae0eae 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -683,6 +683,45 @@ static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char buf[200]; + int res; + + if (argc != 1) { + printf("Invalid 'set_qos_map_set' command - " + "one argument (comma delimited QoS map set) " + "is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]); + if (res < 0 || res >= (int) sizeof(buf)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char buf[50]; + int res; + + if (argc != 1) { + printf("Invalid 'send_qos_map_conf' command - " + "one argument (STA addr) is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]); + 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; @@ -838,6 +877,8 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = { { "quit", hostapd_cli_cmd_quit }, { "set", hostapd_cli_cmd_set }, { "get", hostapd_cli_cmd_get }, + { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set }, + { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf }, { NULL, NULL } }; diff --git a/hostapd/main.c b/hostapd/main.c index 90e59665f..6a6734795 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -282,6 +282,15 @@ static int hostapd_driver_init(struct hostapd_iface *iface) iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs; } +#ifdef CONFIG_INTERWORKING + if (hapd->driver->set_qos_map && conf->qos_map_set_len && + hapd->driver->set_qos_map(hapd->drv_priv, conf->qos_map_set, + conf->qos_map_set_len)) { + wpa_printf(MSG_ERROR, "Failed to initialize QoS Map."); + return -1; + } +#endif /* CONFIG_INTERWORKING */ + return 0; } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index c5531faa7..803998a48 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -442,6 +442,9 @@ struct hostapd_bss_config { u16 gas_comeback_delay; int gas_frag_limit; + u8 qos_map_set[16 + 2 * 21]; + unsigned int qos_map_set_len; + #ifdef CONFIG_HS20 int hs20; int disable_dgaf; diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index 062182aff..9023eaba8 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -742,3 +742,13 @@ int hostapd_start_dfs_cac(struct hostapd_data *hapd, int mode, int freq, return hapd->driver->start_dfs_cac(hapd->drv_priv, &data); } + + +int hostapd_drv_set_qos_map(struct hostapd_data *hapd, + const u8 *qos_map_set, u8 qos_map_set_len) +{ + if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL) + return 0; + return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set, + qos_map_set_len); +} diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 8cbdde663..8a2b4dc7d 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -113,6 +113,9 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper, const u8 *peer, u8 *buf, u16 *buf_len); +int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, + u8 qos_map_set_len); + static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, int enabled) { diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 774d24181..b30da145e 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -115,6 +115,13 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + if (elems.ext_capab && elems.ext_capab_len > 4) { + if (elems.ext_capab[4] & 0x01) + sta->qos_map_enabled = 1; + } +#endif /* CONFIG_INTERWORKING */ + #ifdef CONFIG_HS20 wpabuf_free(sta->hs20_ie); if (elems.hs20 && elems.hs20_len > 4) { diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 781f82694..c7db7f44c 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -859,6 +859,21 @@ static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, } +static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ext_capab_ie, size_t ext_capab_ie_len) +{ +#ifdef CONFIG_INTERWORKING + /* check for QoS Map support */ + if (ext_capab_ie_len >= 5) { + if (ext_capab_ie[4] & 0x01) + sta->qos_map_enabled = 1; + } +#endif /* CONFIG_INTERWORKING */ + + return WLAN_STATUS_SUCCESS; +} + + static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, int reassoc) { @@ -879,6 +894,9 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) return resp; resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = copy_supp_rates(hapd, sta, &elems); @@ -1169,6 +1187,8 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, p = hostapd_eid_ext_capab(hapd, p); p = hostapd_eid_bss_max_idle_period(hapd, p); + if (sta->qos_map_enabled) + p = hostapd_eid_qos_map_set(hapd, p); if (sta->flags & WLAN_STA_WMM) p = hostapd_eid_wmm(hapd, p); diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 2aab56de4..61f13167e 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -41,6 +41,7 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd, u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, int probe); u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index c36bbe399..41722189d 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -189,6 +189,8 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) *pos |= 0x80; /* Bit 31 - Interworking */ break; case 4: /* Bits 32-39 */ + if (hapd->conf->qos_map_set_len) + *pos |= 0x01; /* Bit 32 - QoS Map */ if (hapd->conf->tdls & TDLS_PROHIBIT) *pos |= 0x40; /* Bit 38 - TDLS Prohibited */ if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) { @@ -250,6 +252,23 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) } +u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + u8 len = hapd->conf->qos_map_set_len; + + if (!len) + return eid; + + *pos++ = WLAN_EID_QOS_MAP_SET; + *pos++ = len; + os_memcpy(pos, hapd->conf->qos_map_set, len); + pos += len; + + return pos; +} + + u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index e5b506938..197e46bcb 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -57,6 +57,7 @@ struct sta_info { unsigned int no_ht_set:1; unsigned int ht_20mhz_set:1; unsigned int no_p2p_set:1; + unsigned int qos_map_enabled:1; u16 auth_alg; u8 previous_ap[6]; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 137c30909..9c9a6c382 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -242,6 +242,7 @@ #define WLAN_EID_LINK_ID 101 #define WLAN_EID_INTERWORKING 107 #define WLAN_EID_ADV_PROTO 108 +#define WLAN_EID_QOS_MAP_SET 110 #define WLAN_EID_ROAMING_CONSORTIUM 111 #define WLAN_EID_EXT_CAPAB 127 #define WLAN_EID_CCKM 156 @@ -1079,6 +1080,15 @@ enum bss_trans_mgmt_status_code { #define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70 #define WNM_NEIGHBOR_MULTIPLE_BSSID 71 +/* QoS action */ +enum qos_action { + QOS_ADDTS_REQ = 0, + QOS_ADDTS_RESP = 1, + QOS_DELTS = 2, + QOS_SCHEDULE = 3, + QOS_QOS_MAP_CONFIG = 4, +}; + /* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */ #define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0) #define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1) diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 03c8b75c2..39db01cc2 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2571,6 +2571,15 @@ struct wpa_driver_ops { int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer, u8 *buf, u16 *buf_len); + /** + * set_qos_map - Set QoS Map + * @priv: Private driver interface data + * @qos_map_set: QoS Map + * @qos_map_set_len: Length of QoS Map + */ + int (*set_qos_map)(void *priv, const u8 *qos_map_set, + u8 qos_map_set_len); + /** * signal_poll - Get current connection information * @priv: Private driver interface data