[U-Boot] [RFC PATCH 4/8] lib: net_utils: add string_to_ip6

Joe Hershberger joe.hershberger at gmail.com
Mon Nov 2 21:43:24 CET 2015


Hi Chris,

On Mon, Oct 12, 2015 at 2:43 AM, Chris Packham <judge.packham at 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 at 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


More information about the U-Boot mailing list