[v3,2/4] rte_ether_unformat: accept more inputs

Message ID 20231003202909.391330-3-stephen@networkplumber.org (mailing list archive)
State Accepted, archived
Delegated to: Ferruh Yigit
Headers
Series rte_ether_unformat_addr related changes |

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Stephen Hemminger Oct. 3, 2023, 8:29 p.m. UTC
  This updates rte_ether_addr_unformat() to accept more types
formats for MAC address. It allows IEEE, IETF and Cisco
formats. Leading zeros are allowed for byte formats.

Acked-by: Morten Brørup <mb@smartsharesystems.com>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 lib/net/rte_ether.c | 85 ++++++++++++++++++++++++++++++++++-----------
 lib/net/rte_ether.h | 10 ++++--
 2 files changed, 73 insertions(+), 22 deletions(-)
  

Patch

diff --git a/lib/net/rte_ether.c b/lib/net/rte_ether.c
index 66d9a9d0699a..f59c20289d37 100644
--- a/lib/net/rte_ether.c
+++ b/lib/net/rte_ether.c
@@ -38,7 +38,8 @@  static int8_t get_xdigit(char ch)
 }
 
 /* Convert 00:11:22:33:44:55 to ethernet address */
-static bool get_ether_addr6(const char *s0, struct rte_ether_addr *ea)
+static bool get_ether_addr6(const char *s0, struct rte_ether_addr *ea,
+			    const char sep)
 {
 	const char *s = s0;
 	int i;
@@ -48,25 +49,29 @@  static bool get_ether_addr6(const char *s0, struct rte_ether_addr *ea)
 
 		x = get_xdigit(*s++);
 		if (x < 0)
-			return false;
+			return false;	/* not a hex digit */
 
-		ea->addr_bytes[i] = x << 4;
-		x = get_xdigit(*s++);
-		if (x < 0)
-			return false;
-		ea->addr_bytes[i] |= x;
+		ea->addr_bytes[i] = x;
+		if (*s != sep && *s != '\0') {
+			x = get_xdigit(*s++);
+			if (x < 0)
+				return false;	/* not a hex digit */
+			ea->addr_bytes[i] <<= 4;
+			ea->addr_bytes[i] |= x;
+		}
 
 		if (i < RTE_ETHER_ADDR_LEN - 1 &&
-		    *s++ != ':')
-			return false;
+		    *s++ != sep)
+			return false;	/* premature end of string */
 	}
 
-	/* return true if at end of string */
+	/* return true if no trailing characters */
 	return *s == '\0';
 }
 
 /* Convert 0011:2233:4455 to ethernet address */
-static bool get_ether_addr3(const char *s, struct rte_ether_addr *ea)
+static bool get_ether_addr3(const char *s, struct rte_ether_addr *ea,
+			    const char sep)
 {
 	int i, j;
 
@@ -78,14 +83,15 @@  static bool get_ether_addr3(const char *s, struct rte_ether_addr *ea)
 
 			x = get_xdigit(*s++);
 			if (x < 0)
-				return false;
+				return false;	/* not a hex digit */
 			w = (w << 4) | x;
 		}
+
 		ea->addr_bytes[i] = w >> 8;
 		ea->addr_bytes[i + 1] = w & 0xff;
 
 		if (i < RTE_ETHER_ADDR_LEN - 2 &&
-		    *s++ != ':')
+		    *s++ != sep)
 			return false;
 	}
 
@@ -93,17 +99,56 @@  static bool get_ether_addr3(const char *s, struct rte_ether_addr *ea)
 }
 
 /*
- * Like ether_aton_r but can handle either
- * XX:XX:XX:XX:XX:XX or XXXX:XXXX:XXXX
- * and is more restrictive.
+ * Scan input to see if separated by dash, colon or period
+ * Returns separator and number of matches
+ * If separators are mixed will return
+ */
+static unsigned int get_ether_sep(const char *s, char *sep)
+{
+	static const char separators[] = "-:.";
+	unsigned int count = 0;
+	const char *cp;
+
+	cp = strpbrk(s, separators);
+	if (cp == NULL)
+		return 0;	/* no separator found */
+
+	*sep = *cp;		/* return the separator */
+	do {
+		++count;
+		/* find next instance of separator */
+		cp = strchr(cp + 1, *sep);
+	} while (cp != NULL);
+
+	return count;
+}
+
+/*
+ * Be liberal in accepting a wide variety of notational formats
+ * for MAC address including:
+ *  - Linux format six groups of hexadecimal digits separated by colon
+ *  - Windows format six groups separated by hyphen
+ *  - two groups hexadecimal digits
  */
 int
 rte_ether_unformat_addr(const char *s, struct rte_ether_addr *ea)
 {
-	if (get_ether_addr6(s, ea))
-		return 0;
-	if (get_ether_addr3(s, ea))
-		return 0;
+	unsigned int count;
+	char sep = '\0';
+
+	count = get_ether_sep(s, &sep);
+	switch (count) {
+	case 5:	/* i.e 01:23:45:67:89:AB */
+		if (get_ether_addr6(s, ea, sep))
+			return 0;
+		break;
+	case 2: /* i.e 0123.4567.89AB */
+		if (get_ether_addr3(s, ea, sep))
+			return 0;
+		break;
+	default:
+		break;
+	}
 
 	rte_errno = EINVAL;
 	return -1;
diff --git a/lib/net/rte_ether.h b/lib/net/rte_ether.h
index b35c72c7b0e0..ce073ea818a2 100644
--- a/lib/net/rte_ether.h
+++ b/lib/net/rte_ether.h
@@ -254,9 +254,15 @@  rte_ether_format_addr(char *buf, uint16_t size,
  *
  * @param str
  *   A pointer to buffer contains the formatted MAC address.
- *   The supported formats are:
- *     XX:XX:XX:XX:XX:XX or XXXX:XXXX:XXXX
+ *   Accepts either byte or word format separated by colon,
+ *   hyphen or period.
+ *
+ *   The example formats are:
+ *     XX:XX:XX:XX:XX:XX - Canonical form
+ *     XX-XX-XX-XX-XX-XX - Windows and IEEE 802
+ *     XXXX.XXXX.XXXX    - Cisco
  *   where XX is a hex digit: 0-9, a-f, or A-F.
+ *   In the byte format, leading zeros are optional.
  * @param eth_addr
  *   A pointer to a ether_addr structure.
  * @return