]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
HS 2.0R2: Add support for Policy/RequiredProtoPortTuple
authorJouni Malinen <jouni@qca.qualcomm.com>
Thu, 8 Aug 2013 17:31:41 +0000 (20:31 +0300)
committerJouni Malinen <j@w1.fi>
Tue, 25 Feb 2014 23:24:23 +0000 (01:24 +0200)
The new credential parameter req_conn_capab can be used to specify
restrictions on roaming networks providing connectivity for a set of
protocols/ports.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>

wpa_supplicant/README-HS20
wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/interworking.c
wpa_supplicant/wpa_supplicant.conf

index c79b05ef26eefbc3802cc3c0a0747a8f5445fe53..e65efeea430506191f2ee4787370ce4a93925a50 100644 (file)
@@ -244,6 +244,21 @@ Credentials can be pre-configured for automatic network selection:
 #      BSS Load or if the limit would prevent any connection, this constraint
 #      will be ignored.
 #
+# req_conn_capab: Required connection capability
+#      (PPS/<X+>/Policy/RequiredProtoPortTuple)
+#      This value is used to configure set of required protocol/port pairs that
+#      a roaming network shall support (include explicitly in Connection
+#      Capability ANQP element). This constraint is ignored if the AP does not
+#      advertise Connection Capability or if this constraint would prevent any
+#      network connection. This policy is not used in home networks.
+#      Format: <protocol>[:<comma-separated list of ports]
+#      Multiple entries can be used to list multiple requirements.
+#      For example, number of common TCP protocols:
+#      req_conn_capab=6,22,80,443
+#      For example, IPSec/IKE:
+#      req_conn_capab=17:500
+#      req_conn_capab=50
+#
 # for example:
 #
 #cred={
index f4a81aa05b5438603c81ccd8f99cd3efba4f1849..3132a2c2bda582aa1e34fde2c35d31ec26113b36 100644 (file)
@@ -1931,6 +1931,10 @@ void wpa_config_free_cred(struct wpa_cred *cred)
        os_free(cred->excluded_ssid);
        os_free(cred->roaming_partner);
        os_free(cred->provisioning_sp);
+       for (i = 0; i < cred->num_req_conn_capab; i++)
+               os_free(cred->req_conn_capab_port[i]);
+       os_free(cred->req_conn_capab_port);
+       os_free(cred->req_conn_capab_proto);
        os_free(cred);
 }
 
@@ -2402,6 +2406,69 @@ void wpa_config_update_psk(struct wpa_ssid *ssid)
 }
 
 
+static int wpa_config_set_cred_req_conn_capab(struct wpa_cred *cred,
+                                             const char *value)
+{
+       u8 *proto;
+       int **port;
+       int *ports, *nports;
+       const char *pos;
+       unsigned int num_ports;
+
+       proto = os_realloc_array(cred->req_conn_capab_proto,
+                                cred->num_req_conn_capab + 1, sizeof(u8));
+       if (proto == NULL)
+               return -1;
+       cred->req_conn_capab_proto = proto;
+
+       port = os_realloc_array(cred->req_conn_capab_port,
+                               cred->num_req_conn_capab + 1, sizeof(int *));
+       if (port == NULL)
+               return -1;
+       cred->req_conn_capab_port = port;
+
+       proto[cred->num_req_conn_capab] = atoi(value);
+
+       pos = os_strchr(value, ':');
+       if (pos == NULL) {
+               port[cred->num_req_conn_capab] = NULL;
+               cred->num_req_conn_capab++;
+               return 0;
+       }
+       pos++;
+
+       ports = NULL;
+       num_ports = 0;
+
+       while (*pos) {
+               nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
+               if (nports == NULL) {
+                       os_free(ports);
+                       return -1;
+               }
+               ports = nports;
+               ports[num_ports++] = atoi(pos);
+
+               pos = os_strchr(pos, ',');
+               if (pos == NULL)
+                       break;
+               pos++;
+       }
+
+       nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
+       if (nports == NULL) {
+               os_free(ports);
+               return -1;
+       }
+       ports = nports;
+       ports[num_ports] = -1;
+
+       port[cred->num_req_conn_capab] = ports;
+       cred->num_req_conn_capab++;
+       return 0;
+}
+
+
 int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
                        const char *value, int line)
 {
@@ -2478,6 +2545,9 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
                return 0;
        }
 
+       if (os_strcmp(var, "req_conn_capab") == 0)
+               return wpa_config_set_cred_req_conn_capab(cred, value);
+
        val = wpa_config_parse_string(value, &len);
        if (val == NULL) {
                wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
index f2da234d45e813c5701b95fbe9a16f4f54bd77d5..55c5dc68f56c6005795236a5e8b349ad1933112d 100644 (file)
@@ -265,6 +265,10 @@ struct wpa_cred {
         * constraint will be ignored.
         */
        unsigned int max_bss_load;
+
+       unsigned int num_req_conn_capab;
+       u8 *req_conn_capab_proto;
+       int **req_conn_capab_port;
 };
 
 
index 860cddca525f9814bd44c19fa7d1a8cf4f2f1836..89dd0abe4eb5951c888da3cd7d2a9557a7316747 100644 (file)
@@ -167,6 +167,7 @@ static int cred_with_domain(struct wpa_supplicant *wpa_s)
 
 
 #ifdef CONFIG_HS20
+
 static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
 {
        struct wpa_cred *cred;
@@ -180,6 +181,19 @@ static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
        }
        return 0;
 }
+
+
+static int cred_with_conn_capab(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_cred *cred;
+
+       for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+               if (cred->num_req_conn_capab)
+                       return 1;
+       }
+       return 0;
+}
+
 #endif /* CONFIG_HS20 */
 
 
@@ -253,7 +267,7 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
                                      HS20_STYPE_OPERATOR_FRIENDLY_NAME);
                if (all || cred_with_min_backhaul(wpa_s))
                        wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
-               if (all)
+               if (all || cred_with_conn_capab(wpa_s))
                        wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
                if (all)
                        wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
@@ -1153,6 +1167,76 @@ static int cred_over_max_bss_load(struct wpa_supplicant *wpa_s,
 }
 
 
+static int has_proto_match(const u8 *pos, const u8 *end, u8 proto)
+{
+       while (pos + 4 <= end) {
+               if (pos[0] == proto && pos[3] == 1 /* Open */)
+                       return 1;
+               pos += 4;
+       }
+
+       return 0;
+}
+
+
+static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto,
+                               u16 port)
+{
+       while (pos + 4 <= end) {
+               if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port &&
+                   pos[3] == 1 /* Open */)
+                       return 1;
+               pos += 4;
+       }
+
+       return 0;
+}
+
+
+static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s,
+                                  struct wpa_cred *cred, struct wpa_bss *bss)
+{
+       int res;
+       const u8 *capab, *end;
+       unsigned int i, j;
+       int *ports;
+
+       if (!cred->num_req_conn_capab)
+               return 0; /* No connection capability constraint specified */
+
+       if (bss->anqp == NULL || bss->anqp->hs20_connection_capability == NULL)
+               return 0; /* No Connection Capability known - ignore constraint
+                          */
+
+       res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+                                       bss->anqp->domain_name : NULL);
+       if (res > 0)
+               return 0; /* No constraint in home network */
+
+       capab = wpabuf_head(bss->anqp->hs20_connection_capability);
+       end = capab + wpabuf_len(bss->anqp->hs20_connection_capability);
+
+       for (i = 0; i < cred->num_req_conn_capab; i++) {
+               ports = cred->req_conn_capab_port[i];
+               if (!ports) {
+                       if (!has_proto_match(capab, end,
+                                            cred->req_conn_capab_proto[i]))
+                               return 1;
+               } else {
+                       for (j = 0; ports[j] > -1; j++) {
+                               if (!has_proto_port_match(
+                                           capab, end,
+                                           cred->req_conn_capab_proto[i],
+                                           ports[j]))
+                                       return 1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+
 static struct wpa_cred * interworking_credentials_available_roaming_consortium(
        struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw)
 {
@@ -1188,6 +1272,8 @@ static struct wpa_cred * interworking_credentials_available_roaming_consortium(
                        continue;
                if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
                        continue;
+               if (!ignore_bw && cred_conn_capab_missing(wpa_s, cred, bss))
+                       continue;
 
                if (selected == NULL ||
                    selected->priority < cred->priority)
@@ -1683,6 +1769,9 @@ static struct wpa_cred * interworking_credentials_available_3gpp(
                        if (!ignore_bw &&
                            cred_over_max_bss_load(wpa_s, cred, bss))
                                continue;
+                       if (!ignore_bw &&
+                           cred_conn_capab_missing(wpa_s, cred, bss))
+                               continue;
                        if (selected == NULL ||
                            selected->priority < cred->priority)
                                selected = cred;
@@ -1733,6 +1822,9 @@ static struct wpa_cred * interworking_credentials_available_realm(
                                if (!ignore_bw &&
                                    cred_over_max_bss_load(wpa_s, cred, bss))
                                        continue;
+                               if (!ignore_bw &&
+                                   cred_conn_capab_missing(wpa_s, cred, bss))
+                                       continue;
                                if (selected == NULL ||
                                    selected->priority < cred->priority)
                                        selected = cred;
@@ -2024,12 +2116,15 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s)
                        type = "roaming";
                else
                        type = "unknown";
-               wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s%s%s",
+               wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR
+                       " type=%s%s%s%s",
                        MAC2STR(bss->bssid), type,
                        cred_below_min_backhaul(wpa_s, cred, bss) ?
                        " below_min_backhaul=1" : "",
                        cred_over_max_bss_load(wpa_s, cred, bss) ?
-                       " over_max_bss_load=1" : "");
+                       " over_max_bss_load=1" : "",
+                       cred_conn_capab_missing(wpa_s, cred, bss) ?
+                       " conn_capab_missing=1" : "");
                if (wpa_s->auto_select ||
                    (wpa_s->conf->auto_interworking &&
                     wpa_s->auto_network_select)) {
index 3b0ea717f05fe53f636aa403ee38276e90661bc4..78dbd093942d789deacc1c7f4f0d60609165b67e 100644 (file)
@@ -463,6 +463,21 @@ fast_reauth=1
 #      BSS Load or if the limit would prevent any connection, this constraint
 #      will be ignored.
 #
+# req_conn_capab: Required connection capability
+#      (PPS/<X+>/Policy/RequiredProtoPortTuple)
+#      This value is used to configure set of required protocol/port pairs that
+#      a roaming network shall support (include explicitly in Connection
+#      Capability ANQP element). This constraint is ignored if the AP does not
+#      advertise Connection Capability or if this constraint would prevent any
+#      network connection. This policy is not used in home networks.
+#      Format: <protocol>[:<comma-separated list of ports]
+#      Multiple entries can be used to list multiple requirements.
+#      For example, number of common TCP protocols:
+#      req_conn_capab=6,22,80,443
+#      For example, IPSec/IKE:
+#      req_conn_capab=17:500
+#      req_conn_capab=50
+#
 # for example:
 #
 #cred={