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.pack...@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 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot