Interworking: Add support for QoS Mapping functionality for the AP
authorKyeyoon Park <kyeyoonp@qca.qualcomm.com>
Wed, 24 Jul 2013 09:28:20 +0000 (12:28 +0300)
committerJouni Malinen <j@w1.fi>
Fri, 18 Oct 2013 11:13:45 +0000 (14:13 +0300)
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 <jouni@qca.qualcomm.com>

15 files changed:
hostapd/config_file.c
hostapd/ctrl_iface.c
hostapd/hostapd.conf
hostapd/hostapd_cli.c
hostapd/main.c
src/ap/ap_config.h
src/ap/ap_drv_ops.c
src/ap/ap_drv_ops.h
src/ap/drv_callbacks.c
src/ap/ieee802_11.c
src/ap/ieee802_11.h
src/ap/ieee802_11_shared.c
src/ap/sta_info.h
src/common/ieee802_11_defs.h
src/drivers/driver.h

index dc68fc8..3a3ae0b 100644 (file)
@@ -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) {
index be941c4..a504aac 100644 (file)
@@ -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))
index de1bf75..a2e4cd4 100644 (file)
@@ -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:
+# [<DSCP Exceptions[DSCP,UP]>,]<UP 0 range[low,high]>,...<UP 7 range[low,high]>
+#
+# 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
index 5f48fc8..932ae0e 100644 (file)
@@ -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 }
 };
 
index 90e5966..6a67347 100644 (file)
@@ -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;
 }
 
index c5531fa..803998a 100644 (file)
@@ -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;
index 062182a..9023eab 100644 (file)
@@ -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);
+}
index 8cbdde6..8a2b4dc 100644 (file)
@@ -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)
 {
index 774d241..b30da14 100644 (file)
@@ -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) {
index 781f826..c7db7f4 100644 (file)
@@ -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)
 {
@@ -881,6 +896,9 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
        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);
        if (resp != WLAN_STATUS_SUCCESS)
                return resp;
@@ -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);
index 2aab56d..61f1316 100644 (file)
@@ -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);
index c36bbe3..4172218 100644 (file)
@@ -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;
index e5b5069..197e46b 100644 (file)
@@ -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];
index 137c309..9c9a6c3 100644 (file)
 #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)
index 03c8b75..39db01c 100644 (file)
@@ -2572,6 +2572,15 @@ struct wpa_driver_ops {
                        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
         * @signal_info: Connection info structure