]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
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 dc68fc8baa1692f467f505bdc0ad517a63b3b225..3a3ae0ba3e5447e8bcedd9baf730ba1a451bc5d4 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 be941c44f6ff2bfe7fe1defae530bc9cf1f24919..a504aac796ddd2c556dd924128916bec93660c47 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 de1bf75f36a3fdf08167960bb8065b03660c9018..a2e4cd4603d52fd84e0ba90855444763e0084a30 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 5f48fc89c079b20ecb0c88399c4c2ff723f16c99..932ae0eae332e1e3263505c3ccc4c7333895af3a 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 90e59665ff37225bf6f677d3e4aefd43522453f5..6a673479542bbc39b96729f010d0b18928d6a667 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 c5531faa785a231b04b0b78ff9a73478c3b7fd24..803998a48aeab1d637d1eaeb93b8c2b46df6c06a 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 062182aff8c92ce516593e198d6a0abffa5f73b7..9023eaba893ecdecf3d616942584a9b4feaef15d 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 8cbdde663bbfd5dc0ddadc5a8916b89c16a1841c..8a2b4dc7dd4ef3c15701fdeb2740c1b3717864bc 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 774d2418102e3cb3192bde0ca084880ff49e98c8..b30da145ed81a1aac1f5d03e716eec878bc7ca4b 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 781f826946017d70fa252850f99ea9173c26f5ca..c7db7f44c62f14d94298f94bcd7b8944c9efcde0 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)
 {
@@ -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);
index 2aab56de4682581e38d1fd347d939c2ca5e62fc5..61f13167e3eac06619303ae74844f0a91178471f 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 c36bbe39916a78ff0494df0b08fe84a7b6d459b4..41722189d5f040d5ad978413ed44071bc10959b3 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 e5b506938ed7ef3c857402901a2dd13c3831306c..197e46bcba10820ab2390e30a91c5ddeac74448e 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 137c30909e5233274a0f3c4960d334ddaa488a23..9c9a6c3825d95a6823cfe8168da38631a7d113de 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 03c8b75c2428c2349718fd36bc0440a1032f7676..39db01cc240f04dff79eb6b96aff6c0fafbc04b1 100644 (file)
@@ -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