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 "
#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 */
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;
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);
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;
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);
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;
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);
}
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);
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;
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,
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);
#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)
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)
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)
}
+#define ANQP_MAX_EXTRA_REQ 20
+
struct anqp_query_info {
unsigned int request;
const u8 *home_realm_query;
const u8 *icon_name;
size_t icon_name_len;
int p2p_sd;
+ u16 extra_req[ANQP_MAX_EXTRA_REQ];
+ unsigned int num_extra_req;
};
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);
"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;
}
}
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;
}
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;
}
pos++;
- if (pos + 1 >= end)
+ if (end - pos <= 1)
return;
subtype = *pos++;
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)
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;
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) {
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;
while (pos < end) {
u16 info_id, elen;
- if (pos + 4 > end)
+ if (end - pos < 4)
return;
info_id = WPA_GET_LE16(pos);
elen = WPA_GET_LE16(pos);
pos += 2;
- if (pos + elen > end) {
+ if (elen > end - pos) {
wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
return;
}
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;
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);
}
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)
*/
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;
}
}