]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
SCS: Sending of SCS Request frames
authorVinita S. Maloo <vmaloo@codeaurora.org>
Tue, 19 Jan 2021 13:47:01 +0000 (19:17 +0530)
committerJouni Malinen <j@w1.fi>
Thu, 12 Aug 2021 15:28:07 +0000 (18:28 +0300)
Add support to parse SCS control interface command and form the SCS
Request frame to be sent to SCS enabled AP.

Signed-off-by: Vinita S. Maloo <vmaloo@codeaurora.org>
wpa_supplicant/ctrl_iface.c
wpa_supplicant/robust_av.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index 8a6a829b66659b45484bb0922d49847a449e5631..a0805e343ee92c0e41261977135830e26fa56e2d 100644 (file)
@@ -10887,6 +10887,456 @@ static int wpas_ctrl_iface_pasn_deauthenticate(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_PASN */
 
 
+static int set_type4_frame_classifier(const char *cmd,
+                                     struct type4_params *param)
+{
+       const char *pos, *end;
+       u8 classifier_mask = 0;
+       int ret;
+       char addr[INET6_ADDRSTRLEN];
+       size_t alen;
+
+       if (os_strstr(cmd, "ip_version=ipv4")) {
+               param->ip_version = IPV4;
+       } else if (os_strstr(cmd, "ip_version=ipv6")) {
+               param->ip_version = IPV6;
+       } else {
+               wpa_printf(MSG_ERROR, "IP version missing/invalid");
+               return -1;
+       }
+
+       classifier_mask |= BIT(0);
+
+       pos = os_strstr(cmd, "src_ip=");
+       if (pos) {
+               pos += 7;
+               end = os_strchr(pos, ' ');
+               if (!end)
+                       end = pos + os_strlen(pos);
+
+               alen = end - pos;
+               if (alen >= INET6_ADDRSTRLEN)
+                       return -1;
+               os_memcpy(addr, pos, alen);
+               addr[alen] = '\0';
+               if (param->ip_version == IPV4)
+                       ret = inet_pton(AF_INET, addr,
+                                       &param->ip_params.v4.src_ip);
+               else
+                       ret = inet_pton(AF_INET6, addr,
+                                       &param->ip_params.v6.src_ip);
+
+               if (ret != 1) {
+                       wpa_printf(MSG_ERROR,
+                                  "Error converting src IP address to binary ret=%d",
+                                  ret);
+                       return -1;
+               }
+
+               classifier_mask |= BIT(1);
+       }
+
+       pos = os_strstr(cmd, "dst_ip=");
+       if (pos) {
+               pos += 7;
+               end = os_strchr(pos, ' ');
+               if (!end)
+                       end = pos + os_strlen(pos);
+
+               alen = end - pos;
+               if (alen >= INET6_ADDRSTRLEN)
+                       return -1;
+               os_memcpy(addr, pos, alen);
+               addr[alen] = '\0';
+               if (param->ip_version == IPV4)
+                       ret = inet_pton(AF_INET, addr,
+                                       &param->ip_params.v4.dst_ip);
+               else
+                       ret = inet_pton(AF_INET6, addr,
+                                       &param->ip_params.v6.dst_ip);
+
+               if (ret != 1) {
+                       wpa_printf(MSG_ERROR,
+                                  "Error converting dst IP address to binary ret=%d",
+                                  ret);
+                       return -1;
+               }
+
+               classifier_mask |= BIT(2);
+       }
+
+       pos = os_strstr(cmd, "src_port=");
+       if (pos && atoi(pos + 9) > 0) {
+               if (param->ip_version == IPV4)
+                       param->ip_params.v4.src_port = atoi(pos + 9);
+               else
+                       param->ip_params.v6.src_port = atoi(pos + 9);
+               classifier_mask |= BIT(3);
+       }
+
+       pos = os_strstr(cmd, "dst_port=");
+       if (pos && atoi(pos + 9) > 0) {
+               if (param->ip_version == IPV4)
+                       param->ip_params.v4.dst_port = atoi(pos + 9);
+               else
+                       param->ip_params.v6.dst_port = atoi(pos + 9);
+               classifier_mask |= BIT(4);
+       }
+
+       pos = os_strstr(cmd, "dscp=");
+       if (pos && atoi(pos + 5) > 0) {
+               if (param->ip_version == IPV4)
+                       param->ip_params.v4.dscp = atoi(pos + 5);
+               else
+                       param->ip_params.v6.dscp = atoi(pos + 5);
+               classifier_mask |= BIT(5);
+       }
+
+       if (param->ip_version == IPV4) {
+               pos = os_strstr(cmd, "protocol=");
+               if (pos) {
+                       if (os_strstr(pos, "udp")) {
+                               param->ip_params.v4.protocol = 17;
+                       } else if (os_strstr(pos, "tcp")) {
+                               param->ip_params.v4.protocol = 6;
+                       } else if (os_strstr(pos, "esp")) {
+                               param->ip_params.v4.protocol = 50;
+                       } else {
+                               wpa_printf(MSG_ERROR, "Invalid protocol");
+                               return -1;
+                       }
+                       classifier_mask |= BIT(6);
+               }
+       } else {
+               pos = os_strstr(cmd, "next_header=");
+               if (pos) {
+                       if (os_strstr(pos, "udp")) {
+                               param->ip_params.v6.next_header = 17;
+                       } else if (os_strstr(pos, "tcp")) {
+                               param->ip_params.v6.next_header = 6;
+                       } else if (os_strstr(pos, "esp")) {
+                               param->ip_params.v6.next_header = 50;
+                       } else {
+                               wpa_printf(MSG_ERROR, "Invalid next header");
+                               return -1;
+                       }
+
+                       classifier_mask |= BIT(6);
+               }
+
+               pos = os_strstr(cmd, "flow_label=");
+               if (pos) {
+                       pos += 11;
+                       end = os_strchr(pos, ' ');
+                       if (!end)
+                               end = pos + os_strlen(pos);
+
+                       if (end - pos != 6 ||
+                           hexstr2bin(pos, param->ip_params.v6.flow_label,
+                                      3) ||
+                           param->ip_params.v6.flow_label[0] > 0x0F) {
+                               wpa_printf(MSG_ERROR, "Invalid flow label");
+                               return -1;
+                       }
+
+                       classifier_mask |= BIT(7);
+               }
+       }
+
+       param->classifier_mask = classifier_mask;
+       return 0;
+}
+
+
+static int set_type10_frame_classifier(const char *cmd,
+                                      struct type10_params *param)
+{
+       const char *pos, *end;
+       size_t filter_len;
+
+       pos = os_strstr(cmd, "prot_instance=");
+       if (!pos) {
+               wpa_printf(MSG_ERROR, "Protocol instance missing");
+               return -1;
+       }
+       param->prot_instance = atoi(pos + 14);
+
+       pos = os_strstr(cmd, "prot_number=");
+       if (!pos) {
+               wpa_printf(MSG_ERROR, "Protocol number missing");
+               return -1;
+       }
+       if (os_strstr(pos, "udp")) {
+               param->prot_number = 17;
+       } else if (os_strstr(pos, "tcp")) {
+               param->prot_number = 6;
+       } else if (os_strstr(pos, "esp")) {
+               param->prot_number = 50;
+       } else {
+               wpa_printf(MSG_ERROR, "Invalid protocol number");
+               return -1;
+       }
+
+       pos = os_strstr(cmd, "filter_value=");
+       if (!pos) {
+               wpa_printf(MSG_ERROR,
+                          "Classifier parameter filter_value missing");
+               return -1;
+       }
+
+       pos += 13;
+       end = os_strchr(pos, ' ');
+       if (!end)
+               end = pos + os_strlen(pos);
+
+       filter_len = (end - pos) / 2;
+       param->filter_value = os_malloc(filter_len);
+       if (!param->filter_value)
+               return -1;
+
+       if (hexstr2bin(pos, param->filter_value, filter_len)) {
+               wpa_printf(MSG_ERROR, "Invalid filter_value %s", pos);
+               goto free;
+       }
+
+       pos = os_strstr(cmd, "filter_mask=");
+       if (!pos) {
+               wpa_printf(MSG_ERROR,
+                          "Classifier parameter filter_mask missing");
+               goto free;
+       }
+
+       pos += 12;
+       end = os_strchr(pos, ' ');
+       if (!end)
+               end = pos + os_strlen(pos);
+
+       if (filter_len != (size_t) (end - pos) / 2) {
+               wpa_printf(MSG_ERROR,
+                          "Filter mask length mismatch expected=%zu received=%zu",
+                          filter_len, (size_t) (end - pos) / 2);
+               goto free;
+       }
+
+       param->filter_mask = os_malloc(filter_len);
+       if (!param->filter_mask)
+               goto free;
+
+       if (hexstr2bin(pos, param->filter_mask, filter_len)) {
+               wpa_printf(MSG_ERROR, "Invalid filter mask %s", pos);
+               os_free(param->filter_mask);
+               param->filter_mask = NULL;
+               goto free;
+       }
+
+       param->filter_len = filter_len;
+       return 0;
+free:
+       os_free(param->filter_value);
+       param->filter_value = NULL;
+       return -1;
+}
+
+
+static int scs_parse_type4(struct tclas_element *elem, const char *pos)
+{
+       struct type4_params type4_param = { 0 };
+
+       if (set_type4_frame_classifier(pos, &type4_param) == -1) {
+               wpa_printf(MSG_ERROR, "Failed to set frame_classifier 4");
+               return -1;
+       }
+
+       os_memcpy(&elem->frame_classifier.type4_param,
+                 &type4_param, sizeof(struct type4_params));
+       return 0;
+}
+
+
+static int scs_parse_type10(struct tclas_element *elem, const char *pos)
+{
+       struct type10_params type10_param = { 0 };
+
+       if (set_type10_frame_classifier(pos, &type10_param) == -1) {
+               wpa_printf(MSG_ERROR, "Failed to set frame_classifier 10");
+               return -1;
+       }
+
+       os_memcpy(&elem->frame_classifier.type10_param,
+                 &type10_param, sizeof(struct type10_params));
+       return 0;
+}
+
+
+static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s,
+                                        char *cmd)
+{
+       char *pos1, *pos;
+       struct scs_robust_av_data *scs_data = &wpa_s->scs_robust_av_req;
+       struct scs_desc_elem desc_elem = { 0 };
+       int val;
+       unsigned int num_scs_desc = 0;
+
+       /**
+        * format:
+        * [scs_id=<decimal number>] <add|remove|change> [scs_up=<0-7>]
+        * [classifier_type=<4|10>]
+        * [classifier params based on classifier type]
+        * [tclas_processing=<0|1>] [scs_id=<decimal number>] ...
+        */
+       pos1 = os_strstr(cmd, "scs_id=");
+       if (!pos1) {
+               wpa_printf(MSG_ERROR, "SCSID not present");
+               return -1;
+       }
+
+       free_up_scs_desc(scs_data);
+
+       while (pos1) {
+               struct scs_desc_elem *n1;
+               char *next_scs_desc;
+               unsigned int num_tclas_elem = 0;
+
+               desc_elem.scs_id = atoi(pos1 + 7);
+               pos1 += 7;
+
+               next_scs_desc = os_strstr(pos1, "scs_id=");
+               if (next_scs_desc) {
+                       char temp[20];
+
+                       os_snprintf(temp, sizeof(temp), "scs_id=%d ",
+                                   desc_elem.scs_id);
+                       if (os_strstr(next_scs_desc, temp)) {
+                               wpa_printf(MSG_ERROR,
+                                          "Multiple SCS descriptors configured with same SCSID(=%d)",
+                                          desc_elem.scs_id);
+                               goto free_scs_desc;
+                       }
+                       pos1[next_scs_desc - pos1 - 1] = '\0';
+               }
+
+               if (os_strstr(pos1, "add ")) {
+                       desc_elem.request_type = SCS_REQ_ADD;
+               } else if (os_strstr(pos1, "remove")) {
+                       desc_elem.request_type = SCS_REQ_REMOVE;
+                       goto scs_desc_end;
+               } else if (os_strstr(pos1, "change ")) {
+                       desc_elem.request_type = SCS_REQ_CHANGE;
+               } else {
+                       wpa_printf(MSG_ERROR, "SCS Request type invalid");
+                       goto free_scs_desc;
+               }
+
+               pos1 = os_strstr(pos1, "scs_up=");
+               if (!pos1) {
+                       wpa_printf(MSG_ERROR,
+                                  "Intra-Access user priority not present");
+                       goto free_scs_desc;
+               }
+
+               val = atoi(pos1 + 7);
+               if (val < 0 || val > 7) {
+                       wpa_printf(MSG_ERROR,
+                                  "Intra-Access user priority invalid %d",
+                                  val);
+                       goto free_scs_desc;
+               }
+
+               desc_elem.intra_access_priority = val;
+               desc_elem.scs_up_avail = true;
+
+               pos = os_strstr(pos1, "classifier_type=");
+               if (!pos) {
+                       wpa_printf(MSG_ERROR, "classifier type empty");
+                       goto free_scs_desc;
+               }
+
+               while (pos) {
+                       struct tclas_element elem = { 0 }, *n;
+                       char *next_tclas_elem;
+
+                       val = atoi(pos + 16);
+                       if (val != 4 && val != 10) {
+                               wpa_printf(MSG_ERROR,
+                                          "classifier type invalid %d", val);
+                               goto free_scs_desc;
+                       }
+
+                       elem.classifier_type = val;
+                       pos += 16;
+
+                       next_tclas_elem = os_strstr(pos, "classifier_type=");
+                       if (next_tclas_elem) {
+                               pos1 = next_tclas_elem;
+                               pos[next_tclas_elem - pos - 1] = '\0';
+                       }
+
+                       switch (val) {
+                       case 4:
+                               if (scs_parse_type4(&elem, pos) < 0)
+                                       goto free_scs_desc;
+                               break;
+                       case 10:
+                               if (scs_parse_type10(&elem, pos) < 0)
+                                       goto free_scs_desc;
+                               break;
+                       }
+
+                       n = os_realloc(desc_elem.tclas_elems,
+                                      (num_tclas_elem + 1) * sizeof(elem));
+                       if (!n)
+                               goto free_scs_desc;
+
+                       desc_elem.tclas_elems = n;
+                       os_memcpy((u8 *) desc_elem.tclas_elems +
+                                 num_tclas_elem * sizeof(elem),
+                                 &elem, sizeof(elem));
+                       num_tclas_elem++;
+                       desc_elem.num_tclas_elem = num_tclas_elem;
+                       pos = next_tclas_elem;
+               }
+
+               if (desc_elem.num_tclas_elem > 1) {
+                       pos1 = os_strstr(pos1, "tclas_processing=");
+                       if (!pos1) {
+                               wpa_printf(MSG_ERROR, "tclas_processing empty");
+                               goto free_scs_desc;
+                       }
+
+                       val = atoi(pos1 + 17);
+                       if (val != 0 && val != 1) {
+                               wpa_printf(MSG_ERROR,
+                                          "tclas_processing invalid");
+                               goto free_scs_desc;
+                       }
+
+                       desc_elem.tclas_processing = val;
+               }
+
+scs_desc_end:
+               n1 = os_realloc(scs_data->scs_desc_elems, (num_scs_desc + 1) *
+                               sizeof(struct scs_desc_elem));
+               if (!n1)
+                       goto free_scs_desc;
+
+               scs_data->scs_desc_elems = n1;
+               os_memcpy((u8 *) scs_data->scs_desc_elems + num_scs_desc *
+                         sizeof(desc_elem), &desc_elem, sizeof(desc_elem));
+               num_scs_desc++;
+               scs_data->num_scs_desc = num_scs_desc;
+               pos1 = next_scs_desc;
+               os_memset(&desc_elem, 0, sizeof(desc_elem));
+       }
+
+       return wpas_send_scs_req(wpa_s);
+
+free_scs_desc:
+       free_up_tclas_elem(&desc_elem);
+       free_up_scs_desc(scs_data);
+       return -1;
+}
+
+
 char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                                         char *buf, size_t *resp_len)
 {
@@ -11812,6 +12262,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                if (wpas_ctrl_iface_pasn_deauthenticate(wpa_s, buf + 12) < 0)
                        reply_len = -1;
 #endif /* CONFIG_PASN */
+       } else if (os_strncmp(buf, "SCS ", 4) == 0) {
+               if (wpas_ctrl_iface_configure_scs(wpa_s, buf + 4))
+                       reply_len = -1;
        } else {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
                reply_len = 16;
index 39d282f0870d37bdf9b096b1fa79267042da48f7..598bbdedf4dd3fb6b943961b29c3827236a651db 100644 (file)
@@ -45,6 +45,126 @@ void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av,
 }
 
 
+static int wpas_populate_type4_classifier(struct type4_params *type4_param,
+                                         struct wpabuf *buf)
+{
+       /* classifier parameters */
+       wpabuf_put_u8(buf, type4_param->classifier_mask);
+       if (type4_param->ip_version == IPV4) {
+               wpabuf_put_u8(buf, IPV4); /* IP version */
+               wpabuf_put_data(buf, &type4_param->ip_params.v4.src_ip.s_addr,
+                               4);
+               wpabuf_put_data(buf, &type4_param->ip_params.v4.dst_ip.s_addr,
+                               4);
+               wpabuf_put_be16(buf, type4_param->ip_params.v4.src_port);
+               wpabuf_put_be16(buf, type4_param->ip_params.v4.dst_port);
+               wpabuf_put_u8(buf, type4_param->ip_params.v4.dscp);
+               wpabuf_put_u8(buf, type4_param->ip_params.v4.protocol);
+               wpabuf_put_u8(buf, 0); /* Reserved octet */
+       } else {
+               wpabuf_put_u8(buf, IPV6);
+               wpabuf_put_data(buf, &type4_param->ip_params.v6.src_ip.s6_addr,
+                               16);
+               wpabuf_put_data(buf, &type4_param->ip_params.v6.dst_ip.s6_addr,
+                               16);
+               wpabuf_put_be16(buf, type4_param->ip_params.v6.src_port);
+               wpabuf_put_be16(buf, type4_param->ip_params.v6.dst_port);
+               wpabuf_put_u8(buf, type4_param->ip_params.v6.dscp);
+               wpabuf_put_u8(buf, type4_param->ip_params.v6.next_header);
+               wpabuf_put_data(buf, type4_param->ip_params.v6.flow_label, 3);
+       }
+
+       return 0;
+}
+
+
+static int wpas_populate_type10_classifier(struct type10_params *type10_param,
+                                          struct wpabuf *buf)
+{
+       /* classifier parameters */
+       wpabuf_put_u8(buf, type10_param->prot_instance);
+       wpabuf_put_u8(buf, type10_param->prot_number);
+       wpabuf_put_data(buf, type10_param->filter_value,
+                       type10_param->filter_len);
+       wpabuf_put_data(buf, type10_param->filter_mask,
+                       type10_param->filter_len);
+       return 0;
+}
+
+
+static int wpas_populate_scs_descriptor_ie(struct scs_desc_elem *desc_elem,
+                                          struct wpabuf *buf)
+{
+       u8 *len, *len1;
+       struct tclas_element *tclas_elem;
+       unsigned int i;
+
+       /* SCS Descriptor element */
+       wpabuf_put_u8(buf, WLAN_EID_SCS_DESCRIPTOR);
+       len = wpabuf_put(buf, 1);
+       wpabuf_put_u8(buf, desc_elem->scs_id);
+       wpabuf_put_u8(buf, desc_elem->request_type);
+       if (desc_elem->request_type == SCS_REQ_REMOVE)
+               goto end;
+
+       if (desc_elem->intra_access_priority || desc_elem->scs_up_avail) {
+               wpabuf_put_u8(buf, WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY);
+               wpabuf_put_u8(buf, 1);
+               wpabuf_put_u8(buf, desc_elem->intra_access_priority);
+       }
+
+       tclas_elem = desc_elem->tclas_elems;
+
+       if (!tclas_elem)
+               return -1;
+
+       for (i = 0; i < desc_elem->num_tclas_elem; i++, tclas_elem++) {
+               int ret;
+
+               /* TCLAS element */
+               wpabuf_put_u8(buf, WLAN_EID_TCLAS);
+               len1 = wpabuf_put(buf, 1);
+               wpabuf_put_u8(buf, 255); /* User Priority: not compared */
+               /* Frame Classifier */
+               wpabuf_put_u8(buf, tclas_elem->classifier_type);
+               /* Frame classifier parameters */
+               switch (tclas_elem->classifier_type) {
+               case 4:
+                       ret = wpas_populate_type4_classifier(
+                               &tclas_elem->frame_classifier.type4_param,
+                               buf);
+                       break;
+               case 10:
+                       ret = wpas_populate_type10_classifier(
+                               &tclas_elem->frame_classifier.type10_param,
+                               buf);
+                       break;
+               default:
+                       return -1;
+               }
+
+               if (ret == -1) {
+                       wpa_printf(MSG_ERROR,
+                                  "Failed to populate frame classifier");
+                       return -1;
+               }
+
+               *len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
+       }
+
+       if (desc_elem->num_tclas_elem > 1) {
+               /* TCLAS Processing element */
+               wpabuf_put_u8(buf, WLAN_EID_TCLAS_PROCESSING);
+               wpabuf_put_u8(buf, 1);
+               wpabuf_put_u8(buf, desc_elem->tclas_processing);
+       }
+
+end:
+       *len = (u8 *) wpabuf_put(buf, 0) - len - 1;
+       return 0;
+}
+
+
 int wpas_send_mscs_req(struct wpa_supplicant *wpa_s)
 {
        struct wpabuf *buf;
@@ -101,6 +221,214 @@ int wpas_send_mscs_req(struct wpa_supplicant *wpa_s)
 }
 
 
+static size_t tclas_elem_len(const struct tclas_element *elem)
+{
+       size_t buf_len = 0;
+
+       buf_len += 2 +  /* TCLAS element header */
+               1 +     /* User Priority */
+               1 ;     /* Classifier Type */
+
+       if (elem->classifier_type == 4) {
+               enum ip_version ip_ver;
+
+               buf_len += 1 +  /* Classifier mask */
+                       1 +     /* IP version */
+                       1 +     /* user priority */
+                       2 +     /* src_port */
+                       2 +     /* dst_port */
+                       1 ;     /* dscp */
+               ip_ver = elem->frame_classifier.type4_param.ip_version;
+               if (ip_ver == IPV4) {
+                       buf_len += 4 +  /* src_ip */
+                               4 +     /* dst_ip */
+                               1 +     /* protocol */
+                               1 ;  /* Reserved */
+               } else if (ip_ver == IPV6) {
+                       buf_len += 16 +  /* src_ip */
+                               16 +  /* dst_ip */
+                               1  +  /* next_header */
+                               3  ;  /* flow_label */
+               } else {
+                       wpa_printf(MSG_ERROR, "%s: Incorrect IP version %d",
+                                  __func__, ip_ver);
+                       return 0;
+               }
+       } else if (elem->classifier_type == 10) {
+               buf_len += 1 +  /* protocol instance */
+                       1 +     /* protocol number */
+                       2 * elem->frame_classifier.type10_param.filter_len;
+       } else {
+               wpa_printf(MSG_ERROR, "%s: Incorrect classifier type %u",
+                          __func__, elem->classifier_type);
+               return 0;
+       }
+
+       return buf_len;
+}
+
+
+static struct wpabuf * allocate_scs_buf(struct scs_desc_elem *desc_elem,
+                                       unsigned int num_scs_desc)
+{
+       struct wpabuf *buf;
+       size_t buf_len = 0;
+       unsigned int i, j;
+
+       buf_len = 3; /* Action frame header */
+
+       for (i = 0; i < num_scs_desc; i++, desc_elem++) {
+               struct tclas_element *tclas_elem;
+
+               buf_len += 2 +  /* SCS descriptor IE header */
+                          1 +  /* SCSID */
+                          1 ;  /* Request type */
+
+               if (desc_elem->request_type == SCS_REQ_REMOVE)
+                       continue;
+
+               if (desc_elem->intra_access_priority || desc_elem->scs_up_avail)
+                       buf_len += 3;
+
+               tclas_elem = desc_elem->tclas_elems;
+               if (!tclas_elem) {
+                       wpa_printf(MSG_ERROR, "%s: TCLAS element null",
+                                  __func__);
+                       return NULL;
+               }
+
+               for (j = 0; j < desc_elem->num_tclas_elem; j++, tclas_elem++) {
+                       size_t elen;
+
+                       elen = tclas_elem_len(tclas_elem);
+                       if (elen == 0)
+                               return NULL;
+                       buf_len += elen;
+               }
+
+               if (desc_elem->num_tclas_elem > 1) {
+                       buf_len += 1 +  /* TCLAS Processing eid */
+                                  1 +  /* length */
+                                  1 ;  /* processing */
+               }
+       }
+
+       buf = wpabuf_alloc(buf_len);
+       if (!buf) {
+               wpa_printf(MSG_ERROR, "Failed to allocate SCS req");
+               return NULL;
+       }
+
+       return buf;
+}
+
+
+int wpas_send_scs_req(struct wpa_supplicant *wpa_s)
+{
+       struct wpabuf *buf = NULL;
+       struct scs_desc_elem *desc_elem = NULL;
+       int ret = -1;
+       unsigned int i;
+
+       if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
+               return -1;
+
+       if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_SCS)) {
+               wpa_dbg(wpa_s, MSG_INFO,
+                       "AP does not support SCS - could not send SCS Request");
+               return -1;
+       }
+
+       desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems;
+       if (!desc_elem)
+               return -1;
+
+       buf = allocate_scs_buf(desc_elem,
+                              wpa_s->scs_robust_av_req.num_scs_desc);
+       if (!buf)
+               return -1;
+
+       wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
+       wpabuf_put_u8(buf, ROBUST_AV_SCS_REQ);
+       wpa_s->scs_dialog_token++;
+       if (wpa_s->scs_dialog_token == 0)
+               wpa_s->scs_dialog_token++;
+       wpabuf_put_u8(buf, wpa_s->scs_dialog_token);
+
+       for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
+            i++, desc_elem++) {
+               /* SCS Descriptor element */
+               if (wpas_populate_scs_descriptor_ie(desc_elem, buf) < 0)
+                       goto end;
+       }
+
+       wpa_hexdump_buf(MSG_DEBUG, "SCS Request", buf);
+       ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+                                 wpa_s->own_addr, wpa_s->bssid,
+                                 wpabuf_head(buf), wpabuf_len(buf), 0);
+       if (ret < 0) {
+               wpa_dbg(wpa_s, MSG_ERROR, "SCS: Failed to send SCS Request");
+               wpa_s->scs_dialog_token--;
+       }
+end:
+       wpabuf_free(buf);
+       free_up_scs_desc(&wpa_s->scs_robust_av_req);
+
+       return ret;
+}
+
+
+void free_up_tclas_elem(struct scs_desc_elem *elem)
+{
+       struct tclas_element *tclas_elems = elem->tclas_elems;
+       unsigned int num_tclas_elem = elem->num_tclas_elem;
+       struct tclas_element *tclas_data;
+       unsigned int j;
+
+       elem->tclas_elems = NULL;
+       elem->num_tclas_elem = 0;
+
+       if (!tclas_elems)
+               return;
+
+       tclas_data = tclas_elems;
+       for (j = 0; j < num_tclas_elem; j++, tclas_data++) {
+               if (tclas_data->classifier_type != 10)
+                       continue;
+
+               os_free(tclas_data->frame_classifier.type10_param.filter_value);
+               os_free(tclas_data->frame_classifier.type10_param.filter_mask);
+       }
+
+       os_free(tclas_elems);
+}
+
+
+void free_up_scs_desc(struct scs_robust_av_data *data)
+{
+       struct scs_desc_elem *desc_elems = data->scs_desc_elems;
+       unsigned int num_scs_desc = data->num_scs_desc;
+       struct scs_desc_elem *desc_data;
+       unsigned int i;
+
+       data->scs_desc_elems = NULL;
+       data->num_scs_desc = 0;
+
+       if (!desc_elems)
+               return;
+
+       desc_data = desc_elems;
+       for (i = 0; i < num_scs_desc; i++, desc_data++) {
+               if (desc_data->request_type == SCS_REQ_REMOVE ||
+                   !desc_data->tclas_elems)
+                       continue;
+
+               free_up_tclas_elem(desc_data);
+       }
+       os_free(desc_elems);
+}
+
+
 void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
                                       const u8 *src, const u8 *buf, size_t len)
 {
index 066b624c173a08b5f11198778b0d054a8f042af3..a855b1f24975f22b50ebaf182a5065c4f150d32a 100644 (file)
@@ -743,6 +743,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 #ifdef CONFIG_PASN
        wpas_pasn_auth_stop(wpa_s);
 #endif /* CONFIG_PASN */
+       free_up_scs_desc(&wpa_s->scs_robust_av_req);
+       wpa_s->scs_dialog_token = 0;
 }
 
 
@@ -1915,6 +1917,7 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
 #endif /* CONFIG_MBO */
                break;
        case 6: /* Bits 48-55 */
+               *pos |= 0x40; /* Bit 54 - SCS */
                break;
        case 7: /* Bits 56-63 */
                break;
@@ -3963,6 +3966,9 @@ static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s,
        eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
        if (old_ssid != wpa_s->current_ssid)
                wpas_notify_network_changed(wpa_s);
+
+       free_up_scs_desc(&wpa_s->scs_robust_av_req);
+       wpa_s->scs_dialog_token = 0;
        eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
 }
 
index 60acb53c5c387e83623e0f0af10ca4223e8c42b1..3417cc11f62e09ed0aa95c150aabd40432c28435 100644 (file)
@@ -576,6 +576,80 @@ struct wpas_pasn {
 };
 #endif /* CONFIG_PASN */
 
+
+enum ip_version {
+       IPV4 = 4,
+       IPV6 = 6,
+};
+
+
+struct ipv4_params {
+       struct in_addr src_ip;
+       struct in_addr dst_ip;
+       u16 src_port;
+       u16 dst_port;
+       u8 dscp;
+       u8 protocol;
+};
+
+
+struct ipv6_params {
+       struct in6_addr src_ip;
+       struct in6_addr dst_ip;
+       u16 src_port;
+       u16 dst_port;
+       u8 dscp;
+       u8 next_header;
+       u8 flow_label[3];
+};
+
+
+struct type4_params {
+       u8 classifier_mask;
+       enum ip_version ip_version;
+       union {
+               struct ipv4_params v4;
+               struct ipv6_params v6;
+       } ip_params;
+};
+
+
+struct type10_params {
+       u8 prot_instance;
+       u8 prot_number;
+       u8 *filter_value;
+       u8 *filter_mask;
+       size_t filter_len;
+};
+
+
+struct tclas_element {
+       u8 user_priority;
+       u8 classifier_type;
+       union {
+               struct type4_params type4_param;
+               struct type10_params type10_param;
+       } frame_classifier;
+};
+
+
+struct scs_desc_elem {
+       u8 scs_id;
+       enum scs_request_type request_type;
+       u8 intra_access_priority;
+       bool scs_up_avail;
+       struct tclas_element *tclas_elems;
+       unsigned int num_tclas_elem;
+       u8 tclas_processing;
+};
+
+
+struct scs_robust_av_data {
+       struct scs_desc_elem *scs_desc_elems;
+       unsigned int num_scs_desc;
+};
+
+
 /**
  * struct wpa_supplicant - Internal data for wpa_supplicant interface
  *
@@ -1402,6 +1476,8 @@ struct wpa_supplicant {
        struct wpas_pasn pasn;
        struct wpa_radio_work *pasn_auth_work;
 #endif /* CONFIG_PASN */
+       struct scs_robust_av_data scs_robust_av_req;
+       u8 scs_dialog_token;
 };
 
 
@@ -1734,6 +1810,9 @@ void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
                                       size_t len);
 void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
                                 const u8 *ies, size_t ies_len);
+int wpas_send_scs_req(struct wpa_supplicant *wpa_s);
+void free_up_tclas_elem(struct scs_desc_elem *elem);
+void free_up_scs_desc(struct scs_robust_av_data *data);
 
 int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
                         const u8 *bssid, int akmp, int cipher,