#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,
+ ¶m->ip_params.v4.src_ip);
+ else
+ ret = inet_pton(AF_INET6, addr,
+ ¶m->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,
+ ¶m->ip_params.v4.dst_ip);
+ else
+ ret = inet_pton(AF_INET6, addr,
+ ¶m->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)
{
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;
}
+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;
}
+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)
{