From 2f7f2f2aa9d310c2937bd753df1a2bd6953194d1 Mon Sep 17 00:00:00 2001 From: Viacheslav Mitrofanov Date: Fri, 2 Dec 2022 12:18:02 +0300 Subject: net: ipv6: Add string_to_ip6 converter 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) Series-changes: 3 - Added function description - Added length parameter to string_to_ip6() Series-changes: 4 - Fixed function description style Signed-off-by: Viacheslav Mitrofanov Reviewed-by: Ramon Fried Reviewed-by: Simon Glass --- lib/net_utils.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) (limited to 'lib') diff --git a/lib/net_utils.c b/lib/net_utils.c index 72a3b098a70..4283c13a31d 100644 --- a/lib/net_utils.c +++ b/lib/net_utils.c @@ -11,6 +11,7 @@ #include #include +#include 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; -- cgit v1.2.3 From d807199da7a85dd299b25b380ba820a68d6a0db5 Mon Sep 17 00:00:00 2001 From: Viacheslav Mitrofanov Date: Fri, 2 Dec 2022 12:18:03 +0300 Subject: net: ipv6: Enable IPv6 typeconversion specifier Add the possibility to recognize IPv6 address in print function. To output IPv6 address use %pI6 specifier. Series-changes: 3 - Substituted #if (...) for if (...) to get better readability Signed-off-by: Viacheslav Mitrofanov Reviewed-by: Ramon Fried Reviewed-by: Simon Glass --- lib/vsprintf.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index fe06aa2d711..530d8088c7f 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -450,10 +450,6 @@ static char *uuid_string(char *buf, char *end, u8 *addr, int field_width, * decimal for v4 and colon separated network-order 16 bit hex for v6) * - 'i' [46] for 'raw' IPv4/IPv6 addresses, IPv6 omits the colons, IPv4 is * currently the same - * - * Note: IPv6 support is currently if(0)'ed out. If you ever need - * %pI6, please add an IPV6 Kconfig knob, make your code select or - * depend on that, and change the 0 below to CONFIG_IS_ENABLED(IPV6). */ static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags) @@ -498,8 +494,7 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr, flags |= SPECIAL; /* Fallthrough */ case 'I': - /* %pI6 currently unused */ - if (0 && fmt[1] == '6') + if (IS_ENABLED(CONFIG_IPV6) && fmt[1] == '6') return ip6_addr_string(buf, end, ptr, field_width, precision, flags); if (fmt[1] == '4') -- cgit v1.2.3