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