Browse Source

qcacmn: Add MAC/IPV4/IPV6 address parsing to QDF

Add converged implementations of MAC, IPV4 and IPV6 address parsing to
QDF. This allows for reuse wherever address parsing is done. This also
supports the upcoming configuration component, which will support all
three address types.

Change-Id: I5158e2a40169da979a219363c6d2a25784c5a7e8
CRs-Fixed: 2174072
Dustin Brown 7 years ago
parent
commit
2e93d637cf
2 changed files with 447 additions and 15 deletions
  1. 105 15
      qdf/inc/qdf_types.h
  2. 342 0
      qdf/src/qdf_types.c

+ 105 - 15
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
+ */
+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 } }
+
+/**
+ * struct qdf_ipv4_addr - An IPV4 address
+ * @bytes: the raw address bytes array
+ */
+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
  */
-#define QDF_MAC_ADDR_BROADCAST_INITIALIZER \
-	{ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }
+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 } }
 
 /**
- * This macro is used to initialize a QDF MacAddress to zero
- * It is used like this...
+ * 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
  */
-#define QDF_MAC_ADDR_ZERO_INITIALIZER { { 0, 0, 0, 0, 0, 0 } }
+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)
 
 /**

+ 342 - 0
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;
+}
+