Hi Andrew,
On 8/6/21 9:10 PM, Andrew Zaborowski wrote:
---
src/ie.c | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/ie.h | 38 +++++++
2 files changed, 374 insertions(+)
diff --git a/src/ie.c b/src/ie.c
index a73d5bbc..27950025 100644
--- a/src/ie.c
+++ b/src/ie.c
@@ -25,6 +25,7 @@
#endif
#include <errno.h>
+#include <arpa/inet.h>
#include <ell/ell.h>
@@ -2103,3 +2104,338 @@ bool ie_rsnxe_capable(const uint8_t *rsnxe, unsigned int bit)
return test_bit(rsnxe + 2, bit);
}
+
+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 (bit_field(data[0], 0, 2) == 1)
+ return -EINVAL;
+ else if (bit_field(data[0], 0, 2) >= 2) {
+ info.ipv4 = true;
+ ipv4_specific_addr = (bit_field(data[0], 0, 2) == 3);
+ }
+
+ if (bit_field(data[0], 2, 2) == 1)
+ return -EINVAL;
+ else if (bit_field(data[0], 2, 2) >= 2) {
+ info.ipv6 = true;
+ ipv6_specific_addr = (bit_field(data[0], 2, 2) == 3);
+ }
+
+ info.dns = test_bit(data++, 4);
+
+ if (len < 1 + (ipv4_specific_addr ? 4u : 0u) +
+ (ipv4_specific_addr ? 16u : 0u))
ipv6_specific_addr?
+ 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;
+
+ *to++ = IE_TYPE_EXTENSION;
+ len = to++;
+ *to++ = IE_TYPE_FILS_IP_ADDRESS & 0xff;
+ *to++ = ((info->ipv4 ? info->ipv4_requested_addr ? 3 : 2 : 0) << 0) |
+ ((info->ipv6 ? !l_memeqzero(info->ipv6_requested_addr, 16) ?
+ 3 : 2 : 0) << 2) |
+ ((info->dns ? 1 : 0) << 4);
I think it would look a lot nicer if you used set_bit instead. Maybe avoid an
extra l_memeqzero too...
+
+ if (info->ipv4_requested_addr) {
+ l_put_u32(info->ipv4_requested_addr, to);
+ to += 4;
+ }
+
+ if (!l_memeqzero(info->ipv6_requested_addr, 16)) {
+ memcpy(to, info->ipv6_requested_addr, 16);
+ to += 16;
+ }
+
+ *len = to - (len + 1);
+}
+
+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 *response_ctrl;
+ const uint8_t *dns_ctrl;
+
+ if (len < 2)
+ return -EMSGSIZE;
+
+ info.response_pending = test_bit(data, 0);
+
+ if (info.response_pending) {
+ info.response_timeout = bit_field(data[0], 1, 6); /* seconds */
+ return 0;
+ }
+
+ response_ctrl = data++;
+ dns_ctrl = data++;
+ len -= 2;
+
+ if (test_bit(response_ctrl, 1)) {
+ uint32_t netmask;
+
+ if (len < 8)
+ return -EMSGSIZE;
+
+ info.ipv4_addr = l_get_u32(data);
+ netmask = l_get_be32(data + 4);
+ info.ipv4_prefix_len = __builtin_popcount(netmask);
+ data += 8;
+ len -= 8;
+
+ if (!info.ipv4_addr || info.ipv4_prefix_len > 30 || netmask !=
+ util_netmask_from_prefix(info.ipv4_prefix_len))
+ return -EINVAL;
+ }
+
+ if (test_bit(response_ctrl, 2)) {
+ if (len < 10)
+ return -EMSGSIZE;
+
+ info.ipv4_gateway = l_get_u32(data);
+ memcpy(info.ipv4_gateway_mac, data + 4, 6);
+ data += 10;
+ len -= 10;
+
+ /* 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 (test_bit(response_ctrl, 3)) {
+ if (len < 17)
+ return -EMSGSIZE;
+
+ memcpy(info.ipv6_addr, data, 16);
+ info.ipv6_prefix_len = data[16];
+ data += 17;
+ len -= 17;
+
+ if (l_memeqzero(info.ipv6_addr, 16) ||
+ info.ipv6_prefix_len > 126)
+ return -EINVAL;
+ }
+
+ if (test_bit(response_ctrl, 4)) {
+ if (len < 22)
+ return -EMSGSIZE;
+
+ memcpy(info.ipv6_gateway, data, 16);
+ memcpy(info.ipv6_gateway_mac, data + 16, 6);
+ data += 22;
+ len -= 22;
+
+ /* 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 (test_bit(response_ctrl, 5)) {
+ if (len < 1)
+ return -EMSGSIZE;
+
+ info.ipv4_lifetime = *data++;
+ len--;
+ }
+
+ if (test_bit(response_ctrl, 6)) {
+ if (len < 1)
+ return -EMSGSIZE;
+
+ info.ipv6_lifetime = *data++;
+ len--;
+ }
+
+ if (test_bit(dns_ctrl, 0)) {
+ if (len < 4)
+ return -EMSGSIZE;
+
+ info.ipv4_dns = l_get_u32(data);
+ data += 4;
+ len -= 4;
+
+ if (!info.ipv4_dns)
+ return -EINVAL;
+ }
+
+ if (test_bit(dns_ctrl, 1)) {
+ if (len < 16)
+ return -EMSGSIZE;
+
+ memcpy(info.ipv6_dns, data, 16);
+ data += 16;
+ len -= 16;
+
+ if (l_memeqzero(info.ipv6_dns, 16))
+ return -EINVAL;
+ }
+
+ if (test_bit(dns_ctrl, 2)) {
+ if (len < 6)
+ return -EMSGSIZE;
+
+ memcpy(info.ipv4_dns_mac, data, 6);
+ data += 6;
+ len -= 6;
Can we come up with a clever macro that would automate this somewhat? Similar
to RSNE_ADVANCE maybe?
+ }
+
+ if (test_bit(dns_ctrl, 3)) {
+ if (len < 6)
+ return -EMSGSIZE;
+
+ memcpy(info.ipv6_dns_mac, data, 6);
+ data += 6;
+ len -= 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 *response_ctrl;
+ uint8_t *dns_ctrl;
+
+ *to++ = IE_TYPE_EXTENSION;
+ len = to++;
+ *to++ = IE_TYPE_FILS_IP_ADDRESS & 0xff;
+ response_ctrl = to++;
+ dns_ctrl = to++;
+
+ *response_ctrl = 0;
+ *dns_ctrl = 0;
+
+ if (info->response_pending) {
+ *response_ctrl |= 1 << 0;
+ *response_ctrl |= info->response_timeout << 1;
+ goto done;
+ }
+
+ if (info->ipv4_addr) {
+ uint32_t netmask =
+ util_netmask_from_prefix(info->ipv4_prefix_len);
+
+ *response_ctrl |= 1 << 1;
set_bit please. May even want to make a private enum for the bits too for
easier reading. Similar to enum ie_rsnx_capability maybe?
+
+ l_put_u32(info->ipv4_addr, to);
+ l_put_u32(htonl(netmask), to + 4);
+ to += 8;
+ }
+
+ if (info->ipv4_gateway) {
+ *response_ctrl |= 1 << 2;
+
+ l_put_u32(info->ipv4_gateway, to);
+ memcpy(to + 4, info->ipv4_gateway_mac, 6);
+ to += 10;
+ }
+
+ if (!l_memeqzero(info->ipv6_addr, 16)) {
+ *response_ctrl |= 1 << 3;
+
+ memcpy(to, info->ipv6_addr, 16);
+ to[16] = info->ipv6_prefix_len;
+ to += 17;
+ }
+
+ if (!l_memeqzero(info->ipv6_gateway, 16)) {
+ *response_ctrl |= 1 << 4;
+
+ memcpy(to, info->ipv6_gateway, 16);
+ memcpy(to + 16, info->ipv6_gateway_mac, 6);
+ to += 22;
+ }
+
+ if (info->ipv4_lifetime) {
+ *response_ctrl |= 1 << 5;
+
+ *to++ = info->ipv4_lifetime;
+ }
+
+ if (info->ipv6_lifetime) {
+ *response_ctrl |= 1 << 6;
+
+ *to++ = info->ipv6_lifetime;
+ }
+
+ if (info->ipv4_dns) {
+ *dns_ctrl |= 1 << 0;
+
+ l_put_u32(info->ipv4_dns, to);
+ to += 4;
+ }
+
+ if (!l_memeqzero(info->ipv6_dns, 16)) {
+ *dns_ctrl |= 1 << 1;
+
+ memcpy(to, info->ipv6_dns, 16);
+ to += 16;
+ }
+
+ if (!l_memeqzero(info->ipv4_dns_mac, 6)) {
+ *dns_ctrl |= 1 << 2;
+
+ memcpy(to, info->ipv4_dns_mac, 6);
+ to += 6;
+ }
+
+ if (!l_memeqzero(info->ipv6_dns_mac, 6)) {
+ *dns_ctrl |= 1 << 3;
+
+ memcpy(to, info->ipv6_dns_mac, 6);
+ to += 6;
+ }
+
+done:
+ *len = to - (len + 1);
+}
diff --git a/src/ie.h b/src/ie.h
index 25b56302..05afce39 100644
--- a/src/ie.h
+++ b/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);
Regards,
-Denis