[PATCH 05/17] net: ipv6: Add string_to_ip6 converter

Ramon Fried rfried.dev at gmail.com
Mon Sep 12 08:53:38 CEST 2022


On Tue, Sep 6, 2022 at 6:10 PM Viacheslav Mitrofanov
<v.v.mitrofanov at yadro.com> wrote:
>
> 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.mitrofanov at yadro.com>
> ---
>  include/net6.h  |   4 ++
>  lib/net_utils.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 113 insertions(+)
>
> diff --git a/include/net6.h b/include/net6.h
> index 80236bd5ac..d36fac26e1 100644
> --- a/include/net6.h
> +++ b/include/net6.h
> @@ -174,6 +174,7 @@ 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 IPv6 string addr to inner IPV6 addr format
>   *
> @@ -193,11 +194,14 @@ extern bool use_ip6;
>   * @param addr converted IPv6 addr
>   * @return 0 if conversion successful, -EINVAL if fail
>   */
> +int string_to_ip6(const char *s, size_t len, struct in6_addr *addr);
> +#else
>  static inline int
>  string_to_ip6(const char *s, size_t len, struct in6_addr *addr)
>  {
>         return -EINVAL;
>  }
> +#endif
>
>  /**
>   * Check if IPv6 addr is not set i.e. is zero
> diff --git a/lib/net_utils.c b/lib/net_utils.c
> index 72a3b098a7..74e8eef879 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,114 @@ struct in_addr string_to_ip(const char *s)
>         return addr;
>  }
>
> +#if IS_ENABLED(CONFIG_IPV6)
> +int string_to_ip6(const char *str, size_t len, struct in6_addr *addr)
> +{
> +       int colon_count = 0;
> +       int found_double_colon = 0;
> +       int xstart = 0;         /* first zero (double colon) */
> +       int section_num = 7;    /* num words the double colon represents */
> +       int i;
> +       const char *s = str;
> +       const char *const e = s + len;
> +       struct in_addr zero_ip = {.s_addr = 0};
> +
> +       if (!str)
> +               return -1;
> +
> +       /* First pass, verify the syntax and locate the double colon */
> +       while (s < e) {
> +               while (s < e && isxdigit((int)*s))
> +                       s++;
> +               if (*s == '\0')
> +                       break;
> +               if (*s != ':') {
> +                       if (*s == '.' && section_num >= 2) {
> +                               struct in_addr v4;
> +
> +                               while (s != str && *(s - 1) != ':')
> +                                       --s;
> +                               v4 = string_to_ip(s);
> +                               if (memcmp(&zero_ip, &v4,
> +                                          sizeof(struct in_addr) != 0)) {
> +                                       section_num -= 2;
> +                                       break;
> +                               }
> +                       }
> +                       /* This could be a valid address */
> +                       break;
> +               }
> +               if (s == str) {
> +                       /* 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)
> +                               section_num--;
> +                       else
> +                               xstart++;
> +               }
> +
> +               if (*s == ':') {
> +                       if (found_double_colon)
> +                               /* Two double colons are not allowed */
> +                               goto out_err;
> +                       found_double_colon = 1;
> +                       section_num -= xstart;
> +                       s++;
> +               }
> +
> +               if (++colon_count == 7)
> +                       /* Found all colons */
> +                       break;
> +               ++s;
> +       }
> +
> +       if (colon_count == 0)
> +               goto out_err;
> +       if (*--s == ':')
> +               section_num++;
> +
> +       /* Second pass, read the address */
> +       s = str;
> +       for (i = 0; i < 8; i++) {
> +               int val = 0;
> +               char *end;
> +
> +               if (found_double_colon &&
> +                   i >= xstart && i < xstart + section_num) {
> +                       addr->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 */
> +                               addr->s6_addr32[3] = v4.s_addr;
> +                               break;
> +                       }
> +               }
> +
> +               val = simple_strtoul(s, &end, 16);
> +               if (end != e && *end != '\0' && *end != ':')
> +                       goto out_err;
> +               addr->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
>
Reviewed-by: Ramon Fried <rfried.dev at gmail.com>


More information about the U-Boot mailing list