From 6088c065078fde3ed522c8ae7fb9c3e4a1608adc Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 24 Jul 2019 11:36:56 -0700 Subject: [PATCH] ie: add parser/builder for hotspot indication element The parser fully parses the IE and returns the version, Domain ID, and PPS MO ID. This is meant to be used with an IE in scan results. The builder only takes the version number, and assumes DGAF disabled, and no Domain ID or PPS MO ID. --- src/ie.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/ie.h | 7 +++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/ie.c b/src/ie.c index 5e607df4..4d3ffa9b 100644 --- a/src/ie.c +++ b/src/ie.c @@ -1287,7 +1287,7 @@ bool is_ie_wfa_ie(const uint8_t *data, uint8_t len, uint8_t oi_type) if (oi_type == IE_WFA_OI_OSEN && len < 22) return false; - else if (oi_type == IE_WFA_OI_HS20_INDICATION && len < 5) + else if (oi_type == IE_WFA_OI_HS20_INDICATION && len != 5 && len != 7) return false; else if (len < 4) /* OI not handled, but at least check length */ return false; @@ -2483,3 +2483,94 @@ int ie_build_roaming_consortium(const uint8_t *rc, size_t rc_len, uint8_t *to) return 0; } + +int ie_parse_hs20_indication(struct ie_tlv_iter *iter, uint8_t *version_out, + uint16_t *pps_mo_id_out, uint8_t *domain_id_out) +{ + unsigned int len = ie_tlv_iter_get_length(iter); + const uint8_t *data = ie_tlv_iter_get_data(iter); + uint8_t hs20_config; + bool pps_mo_present, domain_id_present; + + if (!is_ie_wfa_ie(data, iter->len, IE_WFA_OI_HS20_INDICATION)) + return -EPROTOTYPE; + + hs20_config = l_get_u8(data + 4); + + pps_mo_present = util_is_bit_set(hs20_config, 1); + domain_id_present = util_is_bit_set(hs20_config, 2); + + /* + * Hotspot 2.0 Spec - Section 3.1.1 + * + * "Either the PPS MO ID field or the ANQP Domain ID field (these + * are mutually exclusive fields) is included in the HS2.0 Indication + * element" + */ + if (pps_mo_present && domain_id_present) + return -EPROTOTYPE; + + if (version_out) + *version_out = util_bit_field(hs20_config, 4, 4); + + if (pps_mo_id_out) + *pps_mo_id_out = 0; + + if (domain_id_out) + *domain_id_out = 0; + + /* No PPS MO ID or Domain ID */ + if (len == 5) + return 0; + + /* we know from is_ie_wfa_ie that the length must be 7 */ + if (pps_mo_present) { + if (pps_mo_id_out) + *pps_mo_id_out = l_get_u16(data + 5); + } else if (domain_id_present) { + if (domain_id_out) + *domain_id_out = l_get_u16(data + 5); + } + + return 0; +} + +int ie_parse_hs20_indication_from_data(const uint8_t *data, size_t len, + uint8_t *version, uint16_t *pps_mo_id, + uint8_t *domain_id) +{ + struct ie_tlv_iter iter; + + ie_tlv_iter_init(&iter, data, len); + + if (!ie_tlv_iter_next(&iter)) + return -EMSGSIZE; + + if (ie_tlv_iter_get_tag(&iter) != IE_TYPE_VENDOR_SPECIFIC) + return -EPROTOTYPE; + + return ie_parse_hs20_indication(&iter, version, pps_mo_id, domain_id); +} + +/* + * Only use version for building as this is meant for the (Re)Association IE. + * In this case DGAF is always disabled, Domain ID should not be present, and + * this device was not configured with PerProviderSubscription MO. + */ +int ie_build_hs20_indication(uint8_t version, uint8_t *to) +{ + if (version > 2) + return -EINVAL; + + *to++ = IE_TYPE_VENDOR_SPECIFIC; + *to++ = 5; + + memcpy(to, wifi_alliance_oui, 3); + to += 3; + + *to++ = IE_WFA_OI_HS20_INDICATION; + + *to++ = (version << 4) & 0xf0; + + return 0; +} diff --git a/src/ie.h b/src/ie.h index 61190326..37fb95a7 100644 --- a/src/ie.h +++ b/src/ie.h @@ -517,3 +517,10 @@ int ie_parse_roaming_consortium_from_data(const uint8_t *data, size_t len, size_t *oi3_len_out); int ie_build_roaming_consortium(const uint8_t *rc, size_t rc_len, uint8_t *to); + +int ie_parse_hs20_indication(struct ie_tlv_iter *iter, uint8_t *version, + uint16_t *pps_mo_id, uint8_t *domain_id); +int ie_parse_hs20_indication_from_data(const uint8_t *data, size_t len, + uint8_t *version, uint16_t *pps_mo_id, + uint8_t *domain_id); +int ie_build_hs20_indication(uint8_t version, uint8_t *to);