[PATCH 05/17] net: ipv6: Add string_to_ip6 converter
Simon Glass
sjg at chromium.org
Thu Sep 1 04:27:02 CEST 2022
Hi Viacheslav,
On Tue, 30 Aug 2022 at 07:01, 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 | 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
>
I won't mention the function/struct comments again, but please check
this for the whole series.
Regards,
Simon
More information about the U-Boot
mailing list