SCS: Sending of SCS Request frames
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>
This commit is contained in:
parent
445dbe2cdb
commit
c005283c48
4 changed files with 866 additions and 0 deletions
|
@ -10887,6 +10887,456 @@ static int wpas_ctrl_iface_pasn_deauthenticate(struct wpa_supplicant *wpa_s,
|
||||||
#endif /* CONFIG_PASN */
|
#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 * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
|
||||||
char *buf, size_t *resp_len)
|
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)
|
if (wpas_ctrl_iface_pasn_deauthenticate(wpa_s, buf + 12) < 0)
|
||||||
reply_len = -1;
|
reply_len = -1;
|
||||||
#endif /* CONFIG_PASN */
|
#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 {
|
} else {
|
||||||
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
|
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
|
||||||
reply_len = 16;
|
reply_len = 16;
|
||||||
|
|
|
@ -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)
|
int wpas_send_mscs_req(struct wpa_supplicant *wpa_s)
|
||||||
{
|
{
|
||||||
struct wpabuf *buf;
|
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,
|
void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
|
||||||
const u8 *src, const u8 *buf, size_t len)
|
const u8 *src, const u8 *buf, size_t len)
|
||||||
{
|
{
|
||||||
|
|
|
@ -743,6 +743,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
|
||||||
#ifdef CONFIG_PASN
|
#ifdef CONFIG_PASN
|
||||||
wpas_pasn_auth_stop(wpa_s);
|
wpas_pasn_auth_stop(wpa_s);
|
||||||
#endif /* CONFIG_PASN */
|
#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 */
|
#endif /* CONFIG_MBO */
|
||||||
break;
|
break;
|
||||||
case 6: /* Bits 48-55 */
|
case 6: /* Bits 48-55 */
|
||||||
|
*pos |= 0x40; /* Bit 54 - SCS */
|
||||||
break;
|
break;
|
||||||
case 7: /* Bits 56-63 */
|
case 7: /* Bits 56-63 */
|
||||||
break;
|
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);
|
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
|
||||||
if (old_ssid != wpa_s->current_ssid)
|
if (old_ssid != wpa_s->current_ssid)
|
||||||
wpas_notify_network_changed(wpa_s);
|
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);
|
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -576,6 +576,80 @@ struct wpas_pasn {
|
||||||
};
|
};
|
||||||
#endif /* CONFIG_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
|
* struct wpa_supplicant - Internal data for wpa_supplicant interface
|
||||||
*
|
*
|
||||||
|
@ -1402,6 +1476,8 @@ struct wpa_supplicant {
|
||||||
struct wpas_pasn pasn;
|
struct wpas_pasn pasn;
|
||||||
struct wpa_radio_work *pasn_auth_work;
|
struct wpa_radio_work *pasn_auth_work;
|
||||||
#endif /* CONFIG_PASN */
|
#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);
|
size_t len);
|
||||||
void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
|
void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
|
||||||
const u8 *ies, size_t ies_len);
|
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,
|
int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
|
||||||
const u8 *bssid, int akmp, int cipher,
|
const u8 *bssid, int akmp, int cipher,
|
||||||
|
|
Loading…
Reference in a new issue