Updated to hostap_2_6
[mech_eap.git] / libeap / src / ap / gas_serv.c
index 9d19f98..6ce178d 100644 (file)
@@ -101,6 +101,7 @@ gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
                if (sta->gas_dialog[i].dialog_token != dialog_token ||
                    !sta->gas_dialog[i].valid)
                        continue;
+               ap_sta_replenish_timeout(hapd, sta, 5);
                return &sta->gas_dialog[i];
        }
        wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
@@ -167,27 +168,107 @@ static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
 #endif /* CONFIG_HS20 */
 
 
+static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
+                                          u16 infoid)
+{
+       struct anqp_element *elem;
+
+       dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element,
+                        list) {
+               if (elem->infoid == infoid)
+                       return elem;
+       }
+
+       return NULL;
+}
+
+
+static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
+                         u16 infoid)
+{
+       struct anqp_element *elem;
+
+       elem = get_anqp_elem(hapd, infoid);
+       if (!elem)
+               return;
+       if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
+               wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
+                          infoid);
+               return;
+       }
+
+       wpabuf_put_le16(buf, infoid);
+       wpabuf_put_le16(buf, wpabuf_len(elem->payload));
+       wpabuf_put_buf(buf, elem->payload);
+}
+
+
+static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
+                            u16 infoid)
+{
+       if (get_anqp_elem(hapd, infoid)) {
+               anqp_add_elem(hapd, buf, infoid);
+               return 1;
+       }
+
+       return 0;
+}
+
+
 static void anqp_add_capab_list(struct hostapd_data *hapd,
                                struct wpabuf *buf)
 {
        u8 *len;
+       u16 id;
+
+       if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
+               return;
 
        len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
        wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
-       if (hapd->conf->venue_name)
+       if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
                wpabuf_put_le16(buf, ANQP_VENUE_NAME);
-       if (hapd->conf->network_auth_type)
+       if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER))
+               wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER);
+       if (hapd->conf->network_auth_type ||
+           get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE))
                wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
-       if (hapd->conf->roaming_consortium)
+       if (hapd->conf->roaming_consortium ||
+           get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM))
                wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
-       if (hapd->conf->ipaddr_type_configured)
+       if (hapd->conf->ipaddr_type_configured ||
+           get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY))
                wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
-       if (hapd->conf->nai_realm_data)
+       if (hapd->conf->nai_realm_data ||
+           get_anqp_elem(hapd, ANQP_NAI_REALM))
                wpabuf_put_le16(buf, ANQP_NAI_REALM);
-       if (hapd->conf->anqp_3gpp_cell_net)
+       if (hapd->conf->anqp_3gpp_cell_net ||
+           get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK))
                wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
-       if (hapd->conf->domain_name)
+       if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION))
+               wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION);
+       if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION))
+               wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION);
+       if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI))
+               wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI);
+       if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME))
                wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+       if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
+               wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
+       if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
+               wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
+       if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
+               wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
+       for (id = 273; id < 277; id++) {
+               if (get_anqp_elem(hapd, id))
+                       wpabuf_put_le16(buf, id);
+       }
+       if (get_anqp_elem(hapd, ANQP_VENUE_URL))
+               wpabuf_put_le16(buf, ANQP_VENUE_URL);
+       if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
+               wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
+       if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
+               wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
 #ifdef CONFIG_HS20
        anqp_add_hs_capab_list(hapd, buf);
 #endif /* CONFIG_HS20 */
@@ -197,6 +278,9 @@ static void anqp_add_capab_list(struct hostapd_data *hapd,
 
 static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
 {
+       if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
+               return;
+
        if (hapd->conf->venue_name) {
                u8 *len;
                unsigned int i;
@@ -218,6 +302,9 @@ static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
 static void anqp_add_network_auth_type(struct hostapd_data *hapd,
                                       struct wpabuf *buf)
 {
+       if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
+               return;
+
        if (hapd->conf->network_auth_type) {
                wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
                wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
@@ -233,6 +320,9 @@ static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
        unsigned int i;
        u8 *len;
 
+       if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
+               return;
+
        len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
        for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
                struct hostapd_roaming_consortium *rc;
@@ -247,6 +337,9 @@ static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
 static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
                                               struct wpabuf *buf)
 {
+       if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
+               return;
+
        if (hapd->conf->ipaddr_type_configured) {
                wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
                wpabuf_put_le16(buf, 1);
@@ -309,7 +402,7 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
 
        pos = home_realm;
        end = pos + home_realm_len;
-       if (pos + 1 > end) {
+       if (end - pos < 1) {
                wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
                            home_realm, home_realm_len);
                return -1;
@@ -317,7 +410,7 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
        num_realms = *pos++;
 
        for (i = 0; i < num_realms && num_matching < 10; i++) {
-               if (pos + 2 > end) {
+               if (end - pos < 2) {
                        wpa_hexdump(MSG_DEBUG,
                                    "Truncated NAI Home Realm Query",
                                    home_realm, home_realm_len);
@@ -325,7 +418,7 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
                }
                encoding = *pos++;
                realm_len = *pos++;
-               if (pos + realm_len > end) {
+               if (realm_len > end - pos) {
                        wpa_hexdump(MSG_DEBUG,
                                    "Truncated NAI Home Realm Query",
                                    home_realm, home_realm_len);
@@ -391,6 +484,10 @@ static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
                               const u8 *home_realm, size_t home_realm_len,
                               int nai_realm, int nai_home_realm)
 {
+       if (nai_realm && !nai_home_realm &&
+           anqp_add_override(hapd, buf, ANQP_NAI_REALM))
+               return;
+
        if (nai_realm && hapd->conf->nai_realm_data) {
                u8 *len;
                unsigned int i, j;
@@ -424,6 +521,9 @@ static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
 static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
                                           struct wpabuf *buf)
 {
+       if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
+               return;
+
        if (hapd->conf->anqp_3gpp_cell_net) {
                wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
                wpabuf_put_le16(buf,
@@ -436,6 +536,9 @@ static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
 
 static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
 {
+       if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
+               return;
+
        if (hapd->conf->domain_name) {
                wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
                wpabuf_put_le16(buf, hapd->conf->domain_name_len);
@@ -683,20 +786,42 @@ static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
 #endif /* CONFIG_HS20 */
 
 
+static size_t anqp_get_required_len(struct hostapd_data *hapd,
+                                   const u16 *infoid,
+                                   unsigned int num_infoid)
+{
+       size_t len = 0;
+       unsigned int i;
+
+       for (i = 0; i < num_infoid; i++) {
+               struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]);
+
+               if (elem)
+                       len += 2 + 2 + wpabuf_len(elem->payload);
+       }
+
+       return len;
+}
+
+
 static struct wpabuf *
 gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
                                unsigned int request,
                                const u8 *home_realm, size_t home_realm_len,
-                               const u8 *icon_name, size_t icon_name_len)
+                               const u8 *icon_name, size_t icon_name_len,
+                               const u16 *extra_req,
+                               unsigned int num_extra_req)
 {
        struct wpabuf *buf;
        size_t len;
+       unsigned int i;
 
        len = 1400;
        if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
                len += 1000;
        if (request & ANQP_REQ_ICON_REQUEST)
                len += 65536;
+       len += anqp_get_required_len(hapd, extra_req, num_extra_req);
 
        buf = wpabuf_alloc(len);
        if (buf == NULL)
@@ -706,6 +831,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
                anqp_add_capab_list(hapd, buf);
        if (request & ANQP_REQ_VENUE_NAME)
                anqp_add_venue_name(hapd, buf);
+       if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
+               anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
        if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
                anqp_add_network_auth_type(hapd, buf);
        if (request & ANQP_REQ_ROAMING_CONSORTIUM)
@@ -718,8 +845,23 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
                                   request & ANQP_REQ_NAI_HOME_REALM);
        if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
                anqp_add_3gpp_cellular_network(hapd, buf);
+       if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
+               anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
+       if (request & ANQP_REQ_AP_CIVIC_LOCATION)
+               anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
+       if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
+               anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
        if (request & ANQP_REQ_DOMAIN_NAME)
                anqp_add_domain_name(hapd, buf);
+       if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
+               anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
+       if (request & ANQP_REQ_TDLS_CAPABILITY)
+               anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
+       if (request & ANQP_REQ_EMERGENCY_NAI)
+               anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
+
+       for (i = 0; i < num_extra_req; i++)
+               anqp_add_elem(hapd, buf, extra_req[i]);
 
 #ifdef CONFIG_HS20
        if (request & ANQP_REQ_HS_CAPABILITY_LIST)
@@ -742,6 +884,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
 }
 
 
+#define ANQP_MAX_EXTRA_REQ 20
+
 struct anqp_query_info {
        unsigned int request;
        const u8 *home_realm_query;
@@ -749,6 +893,8 @@ struct anqp_query_info {
        const u8 *icon_name;
        size_t icon_name_len;
        int p2p_sd;
+       u16 extra_req[ANQP_MAX_EXTRA_REQ];
+       unsigned int num_extra_req;
 };
 
 
@@ -776,6 +922,11 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
                set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
                             hapd->conf->venue_name != NULL, qi);
                break;
+       case ANQP_EMERGENCY_CALL_NUMBER:
+               set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER,
+                            "Emergency Call Number",
+                            get_anqp_elem(hapd, info_id) != NULL, qi);
+               break;
        case ANQP_NETWORK_AUTH_TYPE:
                set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
                             hapd->conf->network_auth_type != NULL, qi);
@@ -798,13 +949,55 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
                             "3GPP Cellular Network",
                             hapd->conf->anqp_3gpp_cell_net != NULL, qi);
                break;
+       case ANQP_AP_GEOSPATIAL_LOCATION:
+               set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION,
+                            "AP Geospatial Location",
+                            get_anqp_elem(hapd, info_id) != NULL, qi);
+               break;
+       case ANQP_AP_CIVIC_LOCATION:
+               set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION,
+                            "AP Civic Location",
+                            get_anqp_elem(hapd, info_id) != NULL, qi);
+               break;
+       case ANQP_AP_LOCATION_PUBLIC_URI:
+               set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI,
+                            "AP Location Public URI",
+                            get_anqp_elem(hapd, info_id) != NULL, qi);
+               break;
        case ANQP_DOMAIN_NAME:
                set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
                             hapd->conf->domain_name != NULL, qi);
                break;
+       case ANQP_EMERGENCY_ALERT_URI:
+               set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI,
+                            "Emergency Alert URI",
+                            get_anqp_elem(hapd, info_id) != NULL, qi);
+               break;
+       case ANQP_TDLS_CAPABILITY:
+               set_anqp_req(ANQP_REQ_TDLS_CAPABILITY,
+                            "TDLS Capability",
+                            get_anqp_elem(hapd, info_id) != NULL, qi);
+               break;
+       case ANQP_EMERGENCY_NAI:
+               set_anqp_req(ANQP_REQ_EMERGENCY_NAI,
+                            "Emergency NAI",
+                            get_anqp_elem(hapd, info_id) != NULL, qi);
+               break;
        default:
-               wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
-                          info_id);
+               if (!get_anqp_elem(hapd, info_id)) {
+                       wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
+                                  info_id);
+                       break;
+               }
+               if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) {
+                       wpa_printf(MSG_DEBUG,
+                                  "ANQP: No more room for extra requests - ignore Info Id %u",
+                                  info_id);
+                       break;
+               }
+               wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id);
+               qi->extra_req[qi->num_extra_req] = info_id;
+               qi->num_extra_req++;
                break;
        }
 }
@@ -817,7 +1010,7 @@ static void rx_anqp_query_list(struct hostapd_data *hapd,
        wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
                   (unsigned int) (end - pos) / 2);
 
-       while (pos + 2 <= end) {
+       while (end - pos >= 2) {
                rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
                pos += 2;
        }
@@ -906,7 +1099,7 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
        u32 oui;
        u8 subtype;
 
-       if (pos + 4 > end) {
+       if (end - pos < 4) {
                wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
                           "Query element");
                return;
@@ -942,7 +1135,7 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
        }
        pos++;
 
-       if (pos + 1 >= end)
+       if (end - pos <= 1)
                return;
 
        subtype = *pos++;
@@ -973,14 +1166,16 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
 
 static void gas_serv_req_local_processing(struct hostapd_data *hapd,
                                          const u8 *sa, u8 dialog_token,
-                                         struct anqp_query_info *qi, int prot)
+                                         struct anqp_query_info *qi, int prot,
+                                         int std_addr3)
 {
        struct wpabuf *buf, *tx_buf;
 
        buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
                                              qi->home_realm_query,
                                              qi->home_realm_query_len,
-                                             qi->icon_name, qi->icon_name_len);
+                                             qi->icon_name, qi->icon_name_len,
+                                             qi->extra_req, qi->num_extra_req);
        wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
                        buf);
        if (!buf)
@@ -1033,15 +1228,22 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
                return;
        if (prot)
                convert_to_protected_dual(tx_buf);
-       hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
-                               wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+       if (std_addr3)
+               hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+                                       wpabuf_head(tx_buf),
+                                       wpabuf_len(tx_buf));
+       else
+               hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
+                                                wpabuf_head(tx_buf),
+                                                wpabuf_len(tx_buf));
        wpabuf_free(tx_buf);
 }
 
 
 static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
                                        const u8 *sa,
-                                       const u8 *data, size_t len, int prot)
+                                       const u8 *data, size_t len, int prot,
+                                       int std_addr3)
 {
        const u8 *pos = data;
        const u8 *end = data + len;
@@ -1069,12 +1271,12 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
        adv_proto = pos++;
 
        slen = *pos++;
-       next = pos + slen;
-       if (next > end || slen < 2) {
+       if (slen > end - pos || slen < 2) {
                wpa_msg(hapd->msg_ctx, MSG_DEBUG,
                        "GAS: Invalid IE in GAS Initial Request");
                return;
        }
+       next = pos + slen;
        pos++; /* skip QueryRespLenLimit and PAME-BI */
 
        if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
@@ -1093,19 +1295,26 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
                wpabuf_put_le16(buf, 0); /* Query Response Length */
                if (prot)
                        convert_to_protected_dual(buf);
-               hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
-                                       wpabuf_head(buf), wpabuf_len(buf));
+               if (std_addr3)
+                       hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+                                               wpabuf_head(buf),
+                                               wpabuf_len(buf));
+               else
+                       hostapd_drv_send_action_addr3_ap(hapd,
+                                                        hapd->iface->freq, 0,
+                                                        sa, wpabuf_head(buf),
+                                                        wpabuf_len(buf));
                wpabuf_free(buf);
                return;
        }
 
        pos = next;
        /* Query Request */
-       if (pos + 2 > end)
+       if (end - pos < 2)
                return;
        slen = WPA_GET_LE16(pos);
        pos += 2;
-       if (pos + slen > end)
+       if (slen > end - pos)
                return;
        end = pos + slen;
 
@@ -1113,7 +1322,7 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
        while (pos < end) {
                u16 info_id, elen;
 
-               if (pos + 4 > end)
+               if (end - pos < 4)
                        return;
 
                info_id = WPA_GET_LE16(pos);
@@ -1121,7 +1330,7 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
                elen = WPA_GET_LE16(pos);
                pos += 2;
 
-               if (pos + elen > end) {
+               if (elen > end - pos) {
                        wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
                        return;
                }
@@ -1144,13 +1353,15 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
                pos += elen;
        }
 
-       gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
+       gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot,
+                                     std_addr3);
 }
 
 
 static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
                                         const u8 *sa,
-                                        const u8 *data, size_t len, int prot)
+                                        const u8 *data, size_t len, int prot,
+                                        int std_addr3)
 {
        struct gas_dialog_info *dialog;
        struct wpabuf *buf, *tx_buf;
@@ -1226,8 +1437,14 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
 send_resp:
        if (prot)
                convert_to_protected_dual(tx_buf);
-       hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
-                               wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+       if (std_addr3)
+               hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+                                       wpabuf_head(tx_buf),
+                                       wpabuf_len(tx_buf));
+       else
+               hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
+                                                wpabuf_head(tx_buf),
+                                                wpabuf_len(tx_buf));
        wpabuf_free(tx_buf);
 }
 
@@ -1238,7 +1455,7 @@ static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
        struct hostapd_data *hapd = ctx;
        const struct ieee80211_mgmt *mgmt;
        const u8 *sa, *data;
-       int prot;
+       int prot, std_addr3;
 
        mgmt = (const struct ieee80211_mgmt *) buf;
        if (len < IEEE80211_HDRLEN + 2)
@@ -1253,14 +1470,22 @@ static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
         */
        prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
        sa = mgmt->sa;
+       if (hapd->conf->gas_address3 == 1)
+               std_addr3 = 1;
+       else if (hapd->conf->gas_address3 == 2)
+               std_addr3 = 0;
+       else
+               std_addr3 = is_broadcast_ether_addr(mgmt->bssid);
        len -= IEEE80211_HDRLEN + 1;
        data = buf + IEEE80211_HDRLEN + 1;
        switch (data[0]) {
        case WLAN_PA_GAS_INITIAL_REQ:
-               gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
+               gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
+                                           std_addr3);
                break;
        case WLAN_PA_GAS_COMEBACK_REQ:
-               gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
+               gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
+                                            std_addr3);
                break;
        }
 }