mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2025-01-03 19:02:34 +01:00
ie: Add FILS IP Address Assignment parsers and builders
This commit is contained in:
parent
3045ef0770
commit
8f5f62575d
347
src/ie.c
347
src/ie.c
@ -25,6 +25,7 @@
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <ell/ell.h>
|
||||
|
||||
@ -2103,3 +2104,349 @@ bool ie_rsnxe_capable(const uint8_t *rsnxe, unsigned int bit)
|
||||
|
||||
return test_bit(rsnxe + 2, bit);
|
||||
}
|
||||
|
||||
/* 802.11ai-2016 Tables 9-589r, 9-262d, 9-262e */
|
||||
enum ie_fils_ip_addr_req_ctrl_bits {
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV4_MASK = 3 << 0,
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV4_NONE = 0 << 0,
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV4_NEW = 2 << 0,
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV4_SPECIFIC = 3 << 0,
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV6_MASK = 3 << 2,
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV6_NONE = 0 << 2,
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV6_NEW = 2 << 2,
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV6_SPECIFIC = 3 << 2,
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_DNS = 1 << 4,
|
||||
};
|
||||
|
||||
/* 802.11ai-2016 Table 9-262f */
|
||||
enum ie_fils_ip_addr_resp_ctrl_bits {
|
||||
IE_FILS_IP_ADDR_RESP_CTRL_IP_PENDING = 1 << 0,
|
||||
IE_FILS_IP_ADDR_RESP_CTRL_IPV4_ASSIGNED = 1 << 1,
|
||||
IE_FILS_IP_ADDR_RESP_CTRL_IPV4_GW_INCLUDED = 1 << 2,
|
||||
IE_FILS_IP_ADDR_RESP_CTRL_IPV6_ASSIGNED = 1 << 3,
|
||||
IE_FILS_IP_ADDR_RESP_CTRL_IPV6_GW_INCLUDED = 1 << 4,
|
||||
IE_FILS_IP_ADDR_RESP_CTRL_IPV4_LIFETIME_INCLUDED = 1 << 5,
|
||||
IE_FILS_IP_ADDR_RESP_CTRL_IPV6_LIFETIME_INCLUDED = 1 << 6,
|
||||
};
|
||||
|
||||
/* 802.11ai-2016 Table 9-262h */
|
||||
enum ie_fils_ip_addr_resp_dns_ctrl_bits {
|
||||
IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV4_DNS_INCLUDED = 1 << 0,
|
||||
IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV6_DNS_INCLUDED = 1 << 1,
|
||||
IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV4_DNS_MAC_INCLUDED = 1 << 2,
|
||||
IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV6_DNS_MAC_INCLUDED = 1 << 3,
|
||||
};
|
||||
|
||||
int ie_parse_fils_ip_addr_request(struct ie_tlv_iter *iter,
|
||||
struct ie_fils_ip_addr_request_info *out)
|
||||
{
|
||||
unsigned int len = ie_tlv_iter_get_length(iter);
|
||||
const uint8_t *data = ie_tlv_iter_get_data(iter);
|
||||
struct ie_fils_ip_addr_request_info info = {};
|
||||
bool ipv4_specific_addr = false;
|
||||
bool ipv6_specific_addr = false;
|
||||
|
||||
if (len < 1)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (L_IN_SET(data[0] & IE_FILS_IP_ADDR_REQ_CTRL_IPV4_MASK,
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV4_NEW,
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV4_SPECIFIC)) {
|
||||
info.ipv4 = true;
|
||||
ipv4_specific_addr =
|
||||
(data[0] & IE_FILS_IP_ADDR_REQ_CTRL_IPV4_MASK) ==
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV4_SPECIFIC;
|
||||
} else if ((data[0] & IE_FILS_IP_ADDR_REQ_CTRL_IPV4_MASK) !=
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV4_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
if (L_IN_SET(data[0] & IE_FILS_IP_ADDR_REQ_CTRL_IPV6_MASK,
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV6_NEW,
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV6_SPECIFIC)) {
|
||||
info.ipv6 = true;
|
||||
ipv6_specific_addr =
|
||||
(data[0] & IE_FILS_IP_ADDR_REQ_CTRL_IPV6_MASK) ==
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV6_SPECIFIC;
|
||||
} else if ((data[0] & IE_FILS_IP_ADDR_REQ_CTRL_IPV6_MASK) !=
|
||||
IE_FILS_IP_ADDR_REQ_CTRL_IPV6_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
info.dns = !!(*data++ & IE_FILS_IP_ADDR_REQ_CTRL_DNS);
|
||||
|
||||
if (len < 1 + (ipv4_specific_addr ? 4u : 0u) +
|
||||
(ipv6_specific_addr ? 16u : 0u))
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (ipv4_specific_addr) {
|
||||
info.ipv4_requested_addr = l_get_u32(data);
|
||||
data += 4;
|
||||
|
||||
if (!info.ipv4_requested_addr)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ipv6_specific_addr) {
|
||||
memcpy(info.ipv6_requested_addr, data, 16);
|
||||
data += 16;
|
||||
|
||||
if (l_memeqzero(info.ipv6_requested_addr, 16))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(out, &info, sizeof(info));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ie_build_fils_ip_addr_request(
|
||||
const struct ie_fils_ip_addr_request_info *info,
|
||||
uint8_t *to)
|
||||
{
|
||||
uint8_t *len;
|
||||
uint8_t *ctrl;
|
||||
|
||||
*to++ = IE_TYPE_EXTENSION;
|
||||
len = to++;
|
||||
*to++ = IE_TYPE_FILS_IP_ADDRESS & 0xff;
|
||||
ctrl = to++;
|
||||
|
||||
*ctrl = info->dns ? IE_FILS_IP_ADDR_REQ_CTRL_DNS : 0;
|
||||
|
||||
if (info->ipv4) {
|
||||
if (info->ipv4_requested_addr) {
|
||||
l_put_u32(info->ipv4_requested_addr, to);
|
||||
to += 4;
|
||||
*ctrl |= IE_FILS_IP_ADDR_REQ_CTRL_IPV4_SPECIFIC;
|
||||
} else
|
||||
*ctrl |= IE_FILS_IP_ADDR_REQ_CTRL_IPV4_NEW;
|
||||
}
|
||||
|
||||
if (info->ipv6) {
|
||||
if (!l_memeqzero(info->ipv6_requested_addr, 16)) {
|
||||
memcpy(to, info->ipv6_requested_addr, 16);
|
||||
to += 16;
|
||||
*ctrl |= IE_FILS_IP_ADDR_REQ_CTRL_IPV6_SPECIFIC;
|
||||
} else
|
||||
*ctrl |= IE_FILS_IP_ADDR_REQ_CTRL_IPV6_NEW;
|
||||
}
|
||||
|
||||
*len = to - (len + 1);
|
||||
}
|
||||
|
||||
#define NEXT_FIELD(data, len, size) (__extension__ ({ \
|
||||
const uint8_t *_ptr = data; \
|
||||
\
|
||||
if (len < size) \
|
||||
return -EMSGSIZE; \
|
||||
\
|
||||
data += size; \
|
||||
len -= size; \
|
||||
_ptr; }))
|
||||
|
||||
int ie_parse_fils_ip_addr_response(struct ie_tlv_iter *iter,
|
||||
struct ie_fils_ip_addr_response_info *out)
|
||||
{
|
||||
unsigned int len = ie_tlv_iter_get_length(iter);
|
||||
const uint8_t *data = ie_tlv_iter_get_data(iter);
|
||||
struct ie_fils_ip_addr_response_info info = {};
|
||||
const uint8_t *resp_ctrl;
|
||||
const uint8_t *dns_ctrl;
|
||||
const uint8_t *ptr;
|
||||
|
||||
resp_ctrl = NEXT_FIELD(data, len, 2);
|
||||
dns_ctrl = resp_ctrl + 1;
|
||||
|
||||
info.response_pending =
|
||||
!!(*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IP_PENDING);
|
||||
|
||||
if (info.response_pending) {
|
||||
info.response_timeout =
|
||||
bit_field(*resp_ctrl, 1, 6); /* seconds */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV4_ASSIGNED) {
|
||||
uint32_t netmask;
|
||||
|
||||
ptr = NEXT_FIELD(data, len, 8);
|
||||
info.ipv4_addr = l_get_u32(ptr);
|
||||
netmask = l_get_be32(ptr + 4);
|
||||
info.ipv4_prefix_len = __builtin_popcount(netmask);
|
||||
|
||||
if (!info.ipv4_addr || info.ipv4_prefix_len > 30 || netmask !=
|
||||
util_netmask_from_prefix(info.ipv4_prefix_len))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV4_GW_INCLUDED) {
|
||||
ptr = NEXT_FIELD(data, len, 10);
|
||||
info.ipv4_gateway = l_get_u32(ptr);
|
||||
memcpy(info.ipv4_gateway_mac, ptr + 4, 6);
|
||||
|
||||
/* Check gateway is on the same subnet */
|
||||
if (info.ipv4_addr && (ntohl(info.ipv4_addr ^ info.ipv4_gateway) &
|
||||
util_netmask_from_prefix(info.ipv4_prefix_len)))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV6_ASSIGNED) {
|
||||
ptr = NEXT_FIELD(data, len, 17);
|
||||
memcpy(info.ipv6_addr, ptr, 16);
|
||||
info.ipv6_prefix_len = ptr[16];
|
||||
|
||||
if (l_memeqzero(info.ipv6_addr, 16) ||
|
||||
info.ipv6_prefix_len > 126)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV6_GW_INCLUDED) {
|
||||
ptr = NEXT_FIELD(data, len, 22);
|
||||
memcpy(info.ipv6_gateway, ptr, 16);
|
||||
memcpy(info.ipv6_gateway_mac, ptr + 16, 6);
|
||||
|
||||
/* Check gateway is on the same subnet */
|
||||
if (!l_memeqzero(info.ipv6_addr, 12)) {
|
||||
int n = info.ipv6_prefix_len / 8;
|
||||
uint8_t mask = (1 << (info.ipv6_prefix_len & 7)) - 1;
|
||||
|
||||
if (n && memcmp(info.ipv6_addr, info.ipv6_gateway, n))
|
||||
return -EINVAL;
|
||||
|
||||
if (mask && ((info.ipv6_addr[n] ^
|
||||
info.ipv6_gateway[n]) & mask))
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV4_LIFETIME_INCLUDED)
|
||||
info.ipv4_lifetime = *NEXT_FIELD(data, len, 1); /* seconds */
|
||||
|
||||
if (*resp_ctrl & IE_FILS_IP_ADDR_RESP_CTRL_IPV6_LIFETIME_INCLUDED)
|
||||
info.ipv6_lifetime = *NEXT_FIELD(data, len, 1); /* seconds */
|
||||
|
||||
if (*dns_ctrl & IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV4_DNS_INCLUDED) {
|
||||
info.ipv4_dns = l_get_u32(NEXT_FIELD(data, len, 4));
|
||||
|
||||
if (!info.ipv4_dns)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (*dns_ctrl & IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV6_DNS_INCLUDED) {
|
||||
memcpy(info.ipv6_dns, NEXT_FIELD(data, len, 16), 16);
|
||||
|
||||
if (l_memeqzero(info.ipv6_dns, 16))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (*dns_ctrl & IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV4_DNS_MAC_INCLUDED)
|
||||
memcpy(info.ipv4_dns_mac, NEXT_FIELD(data, len, 6), 6);
|
||||
|
||||
if (*dns_ctrl & IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV6_DNS_MAC_INCLUDED)
|
||||
memcpy(info.ipv6_dns_mac, NEXT_FIELD(data, len, 6), 6);
|
||||
|
||||
memcpy(out, &info, sizeof(info));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ie_build_fils_ip_addr_response(
|
||||
const struct ie_fils_ip_addr_response_info *info,
|
||||
uint8_t *to)
|
||||
{
|
||||
uint8_t *len;
|
||||
uint8_t *resp_ctrl;
|
||||
uint8_t *dns_ctrl;
|
||||
|
||||
*to++ = IE_TYPE_EXTENSION;
|
||||
len = to++;
|
||||
*to++ = IE_TYPE_FILS_IP_ADDRESS & 0xff;
|
||||
resp_ctrl = to++;
|
||||
dns_ctrl = to++;
|
||||
|
||||
*resp_ctrl = 0;
|
||||
*dns_ctrl = 0;
|
||||
|
||||
if (info->response_pending) {
|
||||
*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IP_PENDING;
|
||||
*resp_ctrl |= info->response_timeout << 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (info->ipv4_addr) {
|
||||
uint32_t netmask =
|
||||
util_netmask_from_prefix(info->ipv4_prefix_len);
|
||||
|
||||
*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IPV4_ASSIGNED;
|
||||
|
||||
l_put_u32(info->ipv4_addr, to);
|
||||
l_put_u32(htonl(netmask), to + 4);
|
||||
to += 8;
|
||||
}
|
||||
|
||||
if (info->ipv4_gateway) {
|
||||
*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IPV4_GW_INCLUDED;
|
||||
|
||||
l_put_u32(info->ipv4_gateway, to);
|
||||
memcpy(to + 4, info->ipv4_gateway_mac, 6);
|
||||
to += 10;
|
||||
}
|
||||
|
||||
if (!l_memeqzero(info->ipv6_addr, 16)) {
|
||||
*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IPV6_ASSIGNED;
|
||||
|
||||
memcpy(to, info->ipv6_addr, 16);
|
||||
to[16] = info->ipv6_prefix_len;
|
||||
to += 17;
|
||||
}
|
||||
|
||||
if (!l_memeqzero(info->ipv6_gateway, 16)) {
|
||||
*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IPV6_GW_INCLUDED;
|
||||
|
||||
memcpy(to, info->ipv6_gateway, 16);
|
||||
memcpy(to + 16, info->ipv6_gateway_mac, 6);
|
||||
to += 22;
|
||||
}
|
||||
|
||||
if (info->ipv4_lifetime) {
|
||||
*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IPV4_LIFETIME_INCLUDED;
|
||||
|
||||
*to++ = info->ipv4_lifetime;
|
||||
}
|
||||
|
||||
if (info->ipv6_lifetime) {
|
||||
*resp_ctrl |= IE_FILS_IP_ADDR_RESP_CTRL_IPV6_LIFETIME_INCLUDED;
|
||||
|
||||
*to++ = info->ipv6_lifetime;
|
||||
}
|
||||
|
||||
if (info->ipv4_dns) {
|
||||
*dns_ctrl |= IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV4_DNS_INCLUDED;
|
||||
|
||||
l_put_u32(info->ipv4_dns, to);
|
||||
to += 4;
|
||||
}
|
||||
|
||||
if (!l_memeqzero(info->ipv6_dns, 16)) {
|
||||
*dns_ctrl |= IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV6_DNS_INCLUDED;
|
||||
|
||||
memcpy(to, info->ipv6_dns, 16);
|
||||
to += 16;
|
||||
}
|
||||
|
||||
if (!l_memeqzero(info->ipv4_dns_mac, 6)) {
|
||||
*dns_ctrl |=
|
||||
IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV4_DNS_MAC_INCLUDED;
|
||||
|
||||
memcpy(to, info->ipv4_dns_mac, 6);
|
||||
to += 6;
|
||||
}
|
||||
|
||||
if (!l_memeqzero(info->ipv6_dns_mac, 6)) {
|
||||
*dns_ctrl |=
|
||||
IE_FILS_IP_ADDR_RESP_DNS_CTRL_IPV6_DNS_MAC_INCLUDED;
|
||||
|
||||
memcpy(to, info->ipv6_dns_mac, 6);
|
||||
to += 6;
|
||||
}
|
||||
|
||||
done:
|
||||
*len = to - (len + 1);
|
||||
}
|
||||
|
38
src/ie.h
38
src/ie.h
@ -459,6 +459,33 @@ struct ie_neighbor_report_info {
|
||||
bool bss_transition_pref_present : 1;
|
||||
};
|
||||
|
||||
struct ie_fils_ip_addr_request_info {
|
||||
bool ipv4 : 1;
|
||||
uint32_t ipv4_requested_addr; /* Zero if none */
|
||||
bool ipv6 : 1;
|
||||
uint8_t ipv6_requested_addr[16]; /* Zero if none */
|
||||
bool dns : 1;
|
||||
};
|
||||
|
||||
struct ie_fils_ip_addr_response_info {
|
||||
bool response_pending : 1;
|
||||
uint8_t response_timeout; /* Seconds */
|
||||
uint32_t ipv4_addr; /* Zero if not provided */
|
||||
uint8_t ipv4_prefix_len;
|
||||
uint32_t ipv4_gateway; /* Zero if not provided */
|
||||
uint8_t ipv4_gateway_mac[6];
|
||||
uint32_t ipv4_dns; /* Zero if not provided */
|
||||
uint8_t ipv4_dns_mac[6]; /* Zero if not provided */
|
||||
uint8_t ipv4_lifetime; /* Zero if not provided */
|
||||
uint8_t ipv6_addr[16]; /* Zero if not provided */
|
||||
uint8_t ipv6_prefix_len;
|
||||
uint8_t ipv6_gateway[16]; /* Zero if not provided */
|
||||
uint8_t ipv6_gateway_mac[6];
|
||||
uint8_t ipv6_dns[16]; /* Zero if not provided */
|
||||
uint8_t ipv6_dns_mac[6]; /* Zero if not provided */
|
||||
uint8_t ipv6_lifetime; /* Zero if not provided */
|
||||
};
|
||||
|
||||
extern const unsigned char ieee_oui[3];
|
||||
extern const unsigned char microsoft_oui[3];
|
||||
extern const unsigned char wifi_alliance_oui[3];
|
||||
@ -586,3 +613,14 @@ enum ie_rsnx_capability {
|
||||
};
|
||||
|
||||
bool ie_rsnxe_capable(const uint8_t *rsnxe, unsigned int bit);
|
||||
|
||||
int ie_parse_fils_ip_addr_request(struct ie_tlv_iter *iter,
|
||||
struct ie_fils_ip_addr_request_info *out);
|
||||
void ie_build_fils_ip_addr_request(
|
||||
const struct ie_fils_ip_addr_request_info *info,
|
||||
uint8_t *to);
|
||||
int ie_parse_fils_ip_addr_response(struct ie_tlv_iter *iter,
|
||||
struct ie_fils_ip_addr_response_info *out);
|
||||
void ie_build_fils_ip_addr_response(
|
||||
const struct ie_fils_ip_addr_response_info *info,
|
||||
uint8_t *to);
|
||||
|
Loading…
Reference in New Issue
Block a user