
Hi Chris,
On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham judge.packham@gmail.com wrote:
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.
include/net6.h | 3 ++ lib/net_utils.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 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 0fca54d..b0d0364 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) { @@ -43,3 +45,120 @@ struct in_addr string_to_ip(const char *s) addr.s_addr = htonl(addr.s_addr); return addr; }
+/**
- 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 || colon_count > 7)
goto out_err;
Above you bail out if colon_count == 7 so how could it be greater here? My guess is you need to use strchr() to check for additional colons in the remaining string.
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;
+}
I tend to think we can revisit this to make it more concise in the future. Assuming this is functional, it is better to have a solution even if it's not the perfect solution.
Cheers, -Joe