diff --git a/qdf/inc/qdf_types.h b/qdf/inc/qdf_types.h index 81c30bc7d8fd..5b994d7dd765 100644 --- a/qdf/inc/qdf_types.h +++ b/qdf/inc/qdf_types.h @@ -653,34 +653,124 @@ typedef enum { #endif #endif -#define QDF_MAC_ADDR_SIZE (6) -#define QDF_MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x" -#define QDF_MAC_ADDR_ARRAY(a) \ - (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define QDF_MAC_ADDR_SIZE 6 +#define QDF_MAC_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x" +#define QDF_MAC_ADDR_ARRAY(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define QDF_MAC_ADDR_BCAST_INIT { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } +#define QDF_MAC_ADDR_ZERO_INIT { { 0, 0, 0, 0, 0, 0 } } + +/* backwards compatibility; use QDF_MAC_ADDR_STR instead */ +#define QDF_MAC_ADDRESS_STR QDF_MAC_ADDR_STR +/* backwards compatibility; use QDF_MAC_ADDR_BCAST_INIT instead */ +#define QDF_MAC_ADDR_BROADCAST_INITIALIZER QDF_MAC_ADDR_BCAST_INIT +/* backwards compatibility; use QDF_MAC_ADDR_ZERO_INIT instead */ +#define QDF_MAC_ADDR_ZERO_INITIALIZER QDF_MAC_ADDR_ZERO_INIT /** - * struct qdf_mac_addr - mac address array - * @bytes: MAC address bytes + * struct qdf_mac_addr - A MAC address + * @bytes: the raw address bytes array */ struct qdf_mac_addr { uint8_t bytes[QDF_MAC_ADDR_SIZE]; }; /** - * This macro is used to initialize a QDF MacAddress to the broadcast - * MacAddress. It is used like this... + * qdf_mac_parse() - parse the given string as a MAC address + * @mac_str: the input MAC address string to parse + * @out_addr: the output MAC address value, populated on success + * + * A MAC address is a set of 6, colon-delimited, hexadecimal encoded octets. + * + * E.g. + * 00:00:00:00:00:00 (zero address) + * ff:ff:ff:ff:ff:ff (broadcast address) + * 12:34:56:78:90:ab (an arbitrary address) + * + * This implementation also accepts MAC addresses without colons. Historically, + * other delimiters and groupings have been used to represent MAC addresses, but + * these are not supported here. Hexadecimal digits may be in either upper or + * lower case. + * + * Return: QDF_STATUS */ -#define QDF_MAC_ADDR_BROADCAST_INITIALIZER \ - { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } +QDF_STATUS qdf_mac_parse(const char *mac_str, struct qdf_mac_addr *out_addr); + +#define QDF_IPV4_ADDR_SIZE 4 +#define QDF_IPV4_ADDR_STR "%d.%d.%d.%d" +#define QDF_IPV4_ADDR_ARRAY(a) (a)[0], (a)[1], (a)[2], (a)[3] +#define QDF_IPV4_ADDR_ZERO_INIT { { 0, 0, 0, 0 } } /** - * This macro is used to initialize a QDF MacAddress to zero - * It is used like this... + * struct qdf_ipv4_addr - An IPV4 address + * @bytes: the raw address bytes array */ -#define QDF_MAC_ADDR_ZERO_INITIALIZER { { 0, 0, 0, 0, 0, 0 } } +struct qdf_ipv4_addr { + uint8_t bytes[QDF_IPV4_ADDR_SIZE]; +}; + +/** + * qdf_ipv4_parse() - parse the given string as an IPV4 address + * @ipv4_str: the input IPV4 address string to parse + * @out_addr: the output IPV4 address value, populated on success + * + * An IPV4 address is a set of 4, dot-delimited, decimal encoded octets. + * + * E.g. + * 0.0.0.0 (wildcard address) + * 127.0.0.1 (loopback address) + * 255.255.255.255 (broadcast address) + * 192.168.0.1 (an arbitrary address) + * + * Historically, non-decimal encodings have also been used to represent IPV4 + * addresses, but these are not supported here. + * + * Return: QDF_STATUS + */ +QDF_STATUS qdf_ipv4_parse(const char *ipv4_str, struct qdf_ipv4_addr *out_addr); + +#define QDF_IPV6_ADDR_SIZE 16 +#define QDF_IPV6_ADDR_HEXTET_COUNT 8 +#define QDF_IPV6_ADDR_STR "%x:%x:%x:%x:%x:%x:%x:%x" +#define QDF_IPV6_ADDR_ARRAY(a) \ + ((a)[0] << 8) + (a)[1], ((a)[2] << 8) + (a)[3], \ + ((a)[4] << 8) + (a)[5], ((a)[6] << 8) + (a)[7], \ + ((a)[8] << 8) + (a)[9], ((a)[10] << 8) + (a)[11], \ + ((a)[12] << 8) + (a)[13], ((a)[14] << 8) + (a)[15] +#define QDF_IPV6_ADDR_ZERO_INIT \ + { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } + +/** + * struct qdf_ipv6_addr - An IPV6 address + * @bytes: the raw address bytes array + */ +struct qdf_ipv6_addr { + uint8_t bytes[QDF_IPV6_ADDR_SIZE]; +}; + +/** + * qdf_ipv6_parse() - parse the given string as an IPV6 address + * @ipv6_str: the input IPV6 address string to parse + * @out_addr: the output IPV6 address value, populated on success + * + * A hextet is a pair of octets. An IPV6 address is a set of 8, colon-delimited, + * hexadecimal encoded hextets. Each hextet may omit leading zeros. One or more + * zero-hextets may be "compressed" using a pair of colons ("::"). Up to one + * such zero-compression is allowed per address. + * + * E.g. + * 0:0:0:0:0:0:0:0 (unspecified address) + * :: (also the unspecified address) + * 0:0:0:0:0:0:0:1 (loopback address) + * ::1 (also the loopback address) + * 900a:ae7::6 (an arbitrary address) + * 900a:ae7:0:0:0:0:0:6 (the same arbitrary address) + * + * Hexadecimal digits may be in either upper or lower case. + * + * Return: QDF_STATUS + */ +QDF_STATUS qdf_ipv6_parse(const char *ipv6_str, struct qdf_ipv6_addr *out_addr); -#define QDF_IPV4_ADDR_SIZE (4) -#define QDF_IPV6_ADDR_SIZE (16) #define QDF_MAX_NUM_CHAN (128) /** diff --git a/qdf/src/qdf_types.c b/qdf/src/qdf_types.c new file mode 100644 index 000000000000..5b12e57c122e --- /dev/null +++ b/qdf/src/qdf_types.c @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "qdf_mem.h" +#include "qdf_status.h" +#include "qdf_trace.h" +#include "qdf_types.h" + +static QDF_STATUS qdf_consume_char(const char **str, char c) +{ + if (*str[0] != c) + return QDF_STATUS_E_FAILURE; + + (*str)++; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS qdf_consume_whitespace(const char **str, char *c) +{ + switch (*str[0]) { + case ' ': + case '\t': + case '\n': + case '\r': + *c = *str[0]; + (*str)++; + return QDF_STATUS_SUCCESS; + default: + return QDF_STATUS_E_FAILURE; + } +} + +static void qdf_skip_whitespace(const char **str) +{ + QDF_STATUS status; + char c; + + do { + status = qdf_consume_whitespace(str, &c); + } while (QDF_IS_STATUS_SUCCESS(status)); +} + +static QDF_STATUS qdf_consume_dec(const char **str, uint8_t *out_digit) +{ + uint8_t c = *str[0]; + + if (c >= '0' && c <= '9') + *out_digit = c - '0'; + else + return QDF_STATUS_E_FAILURE; + + (*str)++; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS qdf_consume_octet_dec(const char **str, uint8_t *out_octet) +{ + uint8_t len = 0; + uint16_t octet = 0; + int i; + + /* consume up to 3 decimal digits */ + for (i = 0; i < 3; i++) { + uint8_t digit; + + if (QDF_IS_STATUS_ERROR(qdf_consume_dec(str, &digit))) + break; + + len++; + octet = octet * 10 + digit; + } + + /* require at least 1 digit */ + if (!len) + return QDF_STATUS_E_FAILURE; + + if (octet > 255) { + (*str) -= len; + return QDF_STATUS_E_FAILURE; + } + + *out_octet = octet; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS qdf_consume_hex(const char **str, uint8_t *out_nibble) +{ + uint8_t c = *str[0]; + + if (c >= '0' && c <= '9') + *out_nibble = c - '0'; + else if (c >= 'a' && c <= 'f') + *out_nibble = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + *out_nibble = c - 'A' + 10; + else + return QDF_STATUS_E_FAILURE; + + (*str)++; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS qdf_consume_hex_pair(const char **str, uint8_t *out_byte) +{ + QDF_STATUS status; + uint8_t hi, low; + + status = qdf_consume_hex(str, &hi); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + status = qdf_consume_hex(str, &low); + if (QDF_IS_STATUS_ERROR(status)) { + (*str)--; + return status; + } + + *out_byte = hi << 4 | low; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS qdf_consume_hextet(const char **str, uint16_t *out_hextet) +{ + uint8_t len = 0; + uint16_t hextet = 0; + int i; + + /* consume up to 4 hex digits */ + for (i = 0; i < 4; i++) { + uint8_t digit; + + if (QDF_IS_STATUS_ERROR(qdf_consume_hex(str, &digit))) + break; + + len++; + hextet = (hextet << 4) + digit; + } + + /* require at least 1 digit */ + if (!len) + return QDF_STATUS_E_FAILURE; + + /* no need to check for overflow */ + + *out_hextet = hextet; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS qdf_mac_parse(const char *mac_str, struct qdf_mac_addr *out_addr) +{ + QDF_STATUS status; + struct qdf_mac_addr addr; + bool colons; + int i; + + QDF_BUG(mac_str); + if (!mac_str) + return QDF_STATUS_E_INVAL; + + QDF_BUG(out_addr); + if (!out_addr) + return QDF_STATUS_E_INVAL; + + qdf_skip_whitespace(&mac_str); + + /* parse leading hex pair */ + status = qdf_consume_hex_pair(&mac_str, &addr.bytes[0]); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + /* dynamically detect colons */ + colons = mac_str[0] == ':'; + + for (i = 1; i < QDF_MAC_ADDR_SIZE; i++) { + /* ensure colon separator if previously detected */ + if (colons) { + status = qdf_consume_char(&mac_str, ':'); + if (QDF_IS_STATUS_ERROR(status)) + return status; + } + + /* parse next hex pair */ + status = qdf_consume_hex_pair(&mac_str, &addr.bytes[i]); + if (QDF_IS_STATUS_ERROR(status)) + return status; + } + + qdf_skip_whitespace(&mac_str); + if (mac_str[0] != '\0') + return QDF_STATUS_E_FAILURE; + + *out_addr = addr; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS qdf_ipv4_parse(const char *ipv4_str, struct qdf_ipv4_addr *out_addr) +{ + QDF_STATUS status; + struct qdf_ipv4_addr addr; + int i; + + QDF_BUG(ipv4_str); + if (!ipv4_str) + return QDF_STATUS_E_INVAL; + + QDF_BUG(out_addr); + if (!out_addr) + return QDF_STATUS_E_INVAL; + + qdf_skip_whitespace(&ipv4_str); + + /* parse leading octet */ + status = qdf_consume_octet_dec(&ipv4_str, &addr.bytes[0]); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + for (i = 1; i < QDF_IPV4_ADDR_SIZE; i++) { + /* ensure dot separator */ + status = qdf_consume_char(&ipv4_str, '.'); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + /* parse next octet */ + status = qdf_consume_octet_dec(&ipv4_str, &addr.bytes[i]); + if (QDF_IS_STATUS_ERROR(status)) + return status; + } + + qdf_skip_whitespace(&ipv4_str); + if (ipv4_str[0] != '\0') + return QDF_STATUS_E_FAILURE; + + *out_addr = addr; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS qdf_ipv6_parse(const char *ipv6_str, struct qdf_ipv6_addr *out_addr) +{ + QDF_STATUS status; + struct qdf_ipv6_addr addr; + int8_t zero_comp = -1; + uint8_t hextets_found = 0; + + QDF_BUG(ipv6_str); + if (!ipv6_str) + return QDF_STATUS_E_INVAL; + + QDF_BUG(out_addr); + if (!out_addr) + return QDF_STATUS_E_INVAL; + + qdf_skip_whitespace(&ipv6_str); + + /* check for leading zero-compression ("::") */ + status = qdf_consume_char(&ipv6_str, ':'); + if (QDF_IS_STATUS_SUCCESS(status)) { + status = qdf_consume_char(&ipv6_str, ':'); + if (QDF_IS_STATUS_SUCCESS(status)) + zero_comp = 0; + else + return QDF_STATUS_E_FAILURE; + } + + while (hextets_found < QDF_IPV6_ADDR_HEXTET_COUNT) { + uint16_t hextet; + + /* parse hextet */ + status = qdf_consume_hextet(&ipv6_str, &hextet); + if (QDF_IS_STATUS_ERROR(status)) { + /* we must end with hextet or zero compression */ + if (hextets_found != zero_comp) + return QDF_STATUS_E_FAILURE; + + break; + } + + addr.bytes[hextets_found * 2] = hextet >> 8; + addr.bytes[hextets_found * 2 + 1] = hextet; + hextets_found++; + + /* parse ':' char */ + status = qdf_consume_char(&ipv6_str, ':'); + if (QDF_IS_STATUS_ERROR(status)) + break; + + /* check for zero compression ("::") */ + status = qdf_consume_char(&ipv6_str, ':'); + if (QDF_IS_STATUS_SUCCESS(status)) { + /* only one zero compression is allowed */ + if (zero_comp >= 0) + return QDF_STATUS_E_FAILURE; + + zero_comp = hextets_found; + } + } + + /* we must have max hextets or a zero compression */ + if (hextets_found < QDF_IPV6_ADDR_HEXTET_COUNT && zero_comp == -1) + return QDF_STATUS_E_FAILURE; + + qdf_skip_whitespace(&ipv6_str); + if (ipv6_str[0] != '\0') + return QDF_STATUS_E_FAILURE; + + /* shift lower hextets if zero compressed */ + if (zero_comp >= 0) { + uint8_t shift = QDF_IPV6_ADDR_HEXTET_COUNT - hextets_found; + void *to = &addr.bytes[(zero_comp + shift) * 2]; + void *from = &addr.bytes[zero_comp * 2]; + + qdf_mem_move(to, from, (hextets_found - zero_comp) * 2); + qdf_mem_set(from, shift * 2, 0); + } + + *out_addr = addr; + + return QDF_STATUS_SUCCESS; +} +