All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chris Packham <judge.packham@gmail.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [RFC PATCH v2 04/11] lib: net_utils: add string_to_ip6
Date: Mon,  9 Nov 2015 20:38:49 +1300	[thread overview]
Message-ID: <1447054736-27658-5-git-send-email-judge.packham@gmail.com> (raw)
In-Reply-To: <1447054736-27658-1-git-send-email-judge.packham@gmail.com>

string_to_ip6 parses an IPv6 address from a string. Parsing v6 addresses
is a bit more complicated than parsing v4 because there are a number of
different formats that can be used.

Signed-off-by: Chris Packham <judge.packham@gmail.com>

---
I'm sure the parsing can be better and done in less code with only a
single pass but I haven't yet figured it out. The main problem is that
"::" can represent a variable number of contiguous "0000:" so when
parsing "::" we can't tell how many half words to skip.

Changes in v2:
- Wrap code in CONFIG_NET6

 include/net6.h  |   3 ++
 lib/net_utils.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 124 insertions(+)

diff --git a/include/net6.h b/include/net6.h
index 1b82c25..a41eb87 100644
--- a/include/net6.h
+++ b/include/net6.h
@@ -58,4 +58,7 @@ static inline int ipv6_addr_is_isatap(const struct in6_addr *a)
 	return (a->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE);
 }
 
+/* Convert a string to an ipv6 address */
+int string_to_ip6(const char *s, struct in6_addr *addr);
+
 #endif /* __NET6_H__ */
diff --git a/lib/net_utils.c b/lib/net_utils.c
index f148b8a..7422c27 100644
--- a/lib/net_utils.c
+++ b/lib/net_utils.c
@@ -11,6 +11,8 @@
  */
 
 #include <common.h>
+#include <net6.h>
+#include <linux/ctype.h>
 
 struct in_addr string_to_ip(const char *s)
 {
@@ -39,3 +41,122 @@ struct in_addr string_to_ip(const char *s)
 	addr.s_addr = htonl(addr.s_addr);
 	return addr;
 }
+
+#ifdef CONFIG_NET6
+/**
+ * Parses an struct in6_addr from the given string. IPv6 address parsing is a bit
+ * more complicated than v4 due to the flexible format and some of the special
+ * cases (e.g. v4 mapped).
+ *
+ * Examples of valid strings:
+ *   2001:db8::0:1234:1
+ *   2001:0db8:0000:0000:0000:0000:1234:0001
+ *   ::1
+ *   ::ffff:192.168.1.1
+ *
+ * Examples of invalid strings
+ *   2001:db8::0::0          (:: can only appear once)
+ *   2001:db8:192.168.1.1::1 (v4 part can only appear at the end)
+ *   192.168.1.1             (we don't implicity map v4)
+ */
+int string_to_ip6(const char *strpt, struct in6_addr *addrpt)
+{
+	int colon_count = 0;
+	int found_double_colon = 0;
+	int xstart = 0;		/* first zero (double colon) */
+	int len = 7;		/* num words the double colon represents */
+	int i;
+	const char *s = strpt;
+	struct in_addr zero_ip = {.s_addr = 0};
+
+	if (strpt == NULL)
+		return -1;
+
+	/* First pass, verify the syntax and locate the double colon */
+	for (;;) {
+		while (isxdigit((int)*s))
+			s++;
+		if (*s == '\0')
+			break;
+		if (*s != ':') {
+			if (*s == '.' && len >= 2) {
+				struct in_addr v4;
+				while (s != strpt && *(s - 1) != ':')
+					--s;
+				v4 = string_to_ip(s);
+				if (memcmp(&zero_ip, &v4,
+					   sizeof(struct in_addr) != 0)) {
+					len -= 2;
+					break;
+				}
+			}
+			/* This could be a valid address */
+			break;
+		}
+		if (s == strpt) {
+			/* The address begins with a colon */
+			if (*++s != ':')
+				/* Must start with a double colon or a number */
+				goto out_err;
+		} else {
+			s++;
+			if (found_double_colon)
+				len--;
+			else
+				xstart++;
+		}
+
+		if (*s == ':') {
+			if (found_double_colon)
+				/* Two double colons are not allowed */
+				goto out_err;
+			found_double_colon = 1;
+			len -= xstart;
+			s++;
+		}
+
+		if (++colon_count == 7)
+			/* Found all colons */
+			break;
+	}
+
+	if (colon_count == 0)
+		goto out_err;
+	if (*--s == ':')
+		len++;
+
+	/* Second pass, read the address */
+	s = strpt;
+	for (i = 0; i < 8; i++) {
+		int val = 0;
+		char *end;
+
+		if (found_double_colon && i >= xstart && i < xstart + len) {
+			addrpt->s6_addr16[i] = 0;
+			continue;
+		}
+		while (*s == ':')
+			s++;
+
+		if (i == 6 && isdigit((int)*s)) {
+			struct in_addr v4 = string_to_ip(s);
+			if (memcmp(&zero_ip, &v4,
+				   sizeof(struct in_addr)) != 0) {
+				/* Ending with :IPv4-address */
+				addrpt->s6_addr32[3] = v4.s_addr;
+				break;
+			}
+		}
+
+		val = simple_strtoul(s, &end, 16);
+		if (*end != '\0' && *end != ':')
+			goto out_err;
+		addrpt->s6_addr16[i] = htons(val);
+		s = end;
+	}
+	return 0;
+
+out_err:
+	return -1;
+}
+#endif
-- 
2.5.3

  parent reply	other threads:[~2015-11-09  7:38 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-11-09  7:38 [U-Boot] [RFC PATCH v2 00/11] IPv6 support Chris Packham
2015-11-09  7:38 ` [U-Boot] [RFC PATCH v2 01/11] Initial net6.h Chris Packham
2015-11-24  1:05   ` Joe Hershberger
2015-11-09  7:38 ` [U-Boot] [RFC PATCH v2 02/11] lib: vsprintf: add IPv6 compressed format %pI6c Chris Packham
2015-11-24  1:06   ` Joe Hershberger
2015-11-09  7:38 ` [U-Boot] [RFC PATCH v2 03/11] lib: net_utils: make string_to_ip stricter Chris Packham
2015-11-24  1:06   ` Joe Hershberger
2015-11-24  9:37     ` Chris Packham
2015-11-09  7:38 ` Chris Packham [this message]
2015-11-24  1:06   ` [U-Boot] [RFC PATCH v2 04/11] lib: net_utils: add string_to_ip6 Joe Hershberger
2015-11-09  7:38 ` [U-Boot] [RFC PATCH v2 05/11] net: add definition of udp_hdr Chris Packham
2015-11-24  1:06   ` Joe Hershberger
2015-11-09  7:38 ` [U-Boot] [RFC PATCH v2 06/11] net: IPv6 skeleton and environment variables Chris Packham
2015-11-24  1:06   ` Joe Hershberger
2015-11-24  9:47     ` Chris Packham
2015-11-09  7:38 ` [U-Boot] [RFC PATCH v2 07/11] net: IPv6 support Chris Packham
2015-11-24  1:06   ` Joe Hershberger
2015-11-09  7:38 ` [U-Boot] [RFC PATCH v2 08/11] net: Add ping6 command and implementation Chris Packham
2015-11-24  1:06   ` Joe Hershberger
2015-11-09  7:38 ` [U-Boot] [RFC PATCH v2 09/11] net: TFTP over IPv6 Chris Packham
2015-11-24  1:07   ` Joe Hershberger
2015-11-09  7:38 ` [U-Boot] [RFC PATCH v2 10/11] net: IPv6 documentation Chris Packham
2015-11-09  7:38 ` [U-Boot] [RFC PATCH v2 11/11] net: e1000 enable multicast reception Chris Packham
2015-11-24  1:07   ` Joe Hershberger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1447054736-27658-5-git-send-email-judge.packham@gmail.com \
    --to=judge.packham@gmail.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.