This functions is used as a converter from IPv6 address string notation to struct ip6_addr that is used everywhere in IPv6 implementation. For example it is used to parse and convert IPv6 address from tftpboot command. Conversion algorithm uses two passes, first to verify syntax and locate colons and second pass to read the address. In case of valid IPv6 address it returns 0.
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) Signed-off-by: Viacheslav Mitrofanov <v.v.mitrofa...@yadro.com> --- include/net6.h | 5 ++ lib/net_utils.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/include/net6.h b/include/net6.h index 71072d1416..68fa38adbb 100644 --- a/include/net6.h +++ b/include/net6.h @@ -177,10 +177,15 @@ extern u32 net_prefix_length; /* Our prefixlength (0 = unknown) */ extern struct in6_addr net_server_ip6; /* Server IPv6 addr (0 = unknown) */ extern bool use_ip6; +#if IS_ENABLED(CONFIG_IPV6) +/* Convert a string to an ipv6 address */ +int string_to_ip6(const char *s, struct in6_addr *addr); +#else static inline int string_to_ip6(const char *s, struct in6_addr *addr) { return -1; } +#endif static inline int ip6_is_unspecified_addr(struct in6_addr *addr) { diff --git a/lib/net_utils.c b/lib/net_utils.c index 72a3b098a7..09554c520b 100644 --- a/lib/net_utils.c +++ b/lib/net_utils.c @@ -11,6 +11,7 @@ #include <common.h> #include <net.h> +#include <net6.h> struct in_addr string_to_ip(const char *s) { @@ -43,6 +44,127 @@ struct in_addr string_to_ip(const char *s) 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) + */ +#if IS_ENABLED(CONFIG_IPV6) +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) + 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 + void string_to_enetaddr(const char *addr, uint8_t *enetaddr) { char *end; -- 2.25.1