[U-Boot] [RFC PATCH v3 02/11] lib: vsprintf: add IPv6 compressed format %pI6c
Chris Packham
judge.packham at gmail.com
Wed Jan 25 10:56:13 CET 2017
Add support for "human friendly" IPv6 address representations as
specified in https://www.rfc-editor.org/rfc/rfc5952.txt
This code has been adapted from Linux kernel with minimal modification.
Signed-off-by: Chris Packham <judge.packham at gmail.com>
---
Changes in v3: None
Changes in v2: None
include/net6.h | 13 +++++
lib/vsprintf.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 144 insertions(+), 23 deletions(-)
diff --git a/include/net6.h b/include/net6.h
index b62295181d45..1b82c25ec76e 100644
--- a/include/net6.h
+++ b/include/net6.h
@@ -45,4 +45,17 @@ struct ip6_hdr {
struct in6_addr daddr;
};
+/* ::ffff:0:0/96 is reserved for v4 mapped addresses */
+static inline int ipv6_addr_v4mapped(const struct in6_addr *a)
+{
+ return (a->s6_addr32[0] | a->s6_addr32[1] |
+ (a->s6_addr32[2] ^ htonl(0x0000ffff))) == 0;
+}
+
+/* Intra-Site Automatic Tunnel Addressing Protocol Address */
+static inline int ipv6_addr_is_isatap(const struct in6_addr *a)
+{
+ return (a->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE);
+}
+
#endif /* __NET6_H__ */
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 874a2951f705..54f1e632fdc1 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -19,6 +19,7 @@
#include <common.h>
#include <div64.h>
+#include <net6.h>
#define noinline __attribute__((noinline))
/* we use this so that we can do without the ctype library */
@@ -140,6 +141,7 @@ static noinline char *put_dec(char *buf, uint64_t num)
#define LEFT 16 /* left justified */
#define SMALL 32 /* Must be 32 == 0x20 */
#define SPECIAL 64 /* 0x */
+#define COMPRESSED 128 /* use compressed format */
/*
* Macro to add a new character to our output string, but only if it will
@@ -301,12 +303,121 @@ static char *mac_address_string(char *buf, char *end, u8 *addr, int field_width,
flags & ~SPECIAL);
}
-static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width,
+static char *ip4_string(char *p, u8 *addr)
+{
+ char temp[3]; /* hold each IP quad in reverse order */
+ int i, digits;
+
+ for (i = 0; i < 4; i++) {
+ digits = put_dec_trunc(temp, addr[i]) - temp;
+ /* reverse the digits in the quad */
+ while (digits--)
+ *p++ = temp[digits];
+ if (i != 3)
+ *p++ = '.';
+ }
+ *p = '\0';
+
+ return p;
+}
+
+static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width,
int precision, int flags)
{
- /* (8 * 4 hex digits), 7 colons and trailing zero */
- char ip6_addr[8 * 5];
- char *p = ip6_addr;
+ char ip4_addr[sizeof("255.255.255.255")];
+
+ ip4_string(ip4_addr, addr);
+
+ return string(buf, end, ip4_addr, field_width, precision,
+ flags & ~SPECIAL);
+}
+#endif
+
+#ifdef CONFIG_NET6
+static char *ip6_compressed_string(char *p, u8 *addr)
+{
+ int i, j, range;
+ unsigned char zerolength[8];
+ int longest = 1;
+ int colonpos = -1;
+ u16 word;
+ u8 hi, lo;
+ int needcolon = 0;
+ int useIPv4;
+ struct in6_addr in6;
+
+ memcpy(&in6, addr, sizeof(struct in6_addr));
+
+ useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6);
+
+ memset(zerolength, 0, sizeof(zerolength));
+
+ if (useIPv4)
+ range = 6;
+ else
+ range = 8;
+
+ /* find position of longest 0 run */
+ for (i = 0; i < range; i++) {
+ for (j = i; j < range; j++) {
+ if (in6.s6_addr16[j] != 0)
+ break;
+ zerolength[i]++;
+ }
+ }
+ for (i = 0; i < range; i++) {
+ if (zerolength[i] > longest) {
+ longest = zerolength[i];
+ colonpos = i;
+ }
+ }
+ if (longest == 1) /* don't compress a single 0 */
+ colonpos = -1;
+
+ /* emit address */
+ for (i = 0; i < range; i++) {
+ if (i == colonpos) {
+ if (needcolon || i == 0)
+ *p++ = ':';
+ *p++ = ':';
+ needcolon = 0;
+ i += longest - 1;
+ continue;
+ }
+ if (needcolon) {
+ *p++ = ':';
+ needcolon = 0;
+ }
+ /* hex u16 without leading 0s */
+ word = ntohs(in6.s6_addr16[i]);
+ hi = word >> 8;
+ lo = word & 0xff;
+ if (hi) {
+ if (hi > 0x0f)
+ p = pack_hex_byte(p, hi);
+ else
+ *p++ = hex_asc_lo(hi);
+ p = pack_hex_byte(p, lo);
+ } else if (lo > 0x0f) {
+ p = pack_hex_byte(p, lo);
+ } else {
+ *p++ = hex_asc_lo(lo);
+ }
+ needcolon = 1;
+ }
+
+ if (useIPv4) {
+ if (needcolon)
+ *p++ = ':';
+ p = ip4_string(p, &in6.s6_addr[12]);
+ }
+ *p = '\0';
+
+ return p;
+}
+
+static char *ip6_string(char *p, u8 *addr, int flags)
+{
int i;
for (i = 0; i < 8; i++) {
@@ -317,30 +428,20 @@ static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width,
}
*p = '\0';
- return string(buf, end, ip6_addr, field_width, precision,
- flags & ~SPECIAL);
+ return p;
}
-static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width,
+static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width,
int precision, int flags)
{
- /* (4 * 3 decimal digits), 3 dots and trailing zero */
- char ip4_addr[4 * 4];
- char temp[3]; /* hold each IP quad in reverse order */
- char *p = ip4_addr;
- int i, digits;
+ char ip6_addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
- for (i = 0; i < 4; i++) {
- digits = put_dec_trunc(temp, addr[i]) - temp;
- /* reverse the digits in the quad */
- while (digits--)
- *p++ = temp[digits];
- if (i != 3)
- *p++ = '.';
- }
- *p = '\0';
+ if (flags & COMPRESSED)
+ ip6_compressed_string(ip6_addr, addr);
+ else
+ ip6_string(ip6_addr, addr, flags);
- return string(buf, end, ip4_addr, field_width, precision,
+ return string(buf, end, ip6_addr, field_width, precision,
flags & ~SPECIAL);
}
#endif
@@ -358,6 +459,8 @@ static char *ip4_addr_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
+ * - 'I6c' for IPv6 addresses printed as specified by
+ * http://tools.ietf.org/html/rfc5952
*
* Note: The difference between 'S' and 'F' is that on ia64 and ppc64
* function pointers are really function descriptors, which contain a
@@ -401,9 +504,14 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
flags |= SPECIAL;
/* Fallthrough */
case 'I':
- if (fmt[1] == '6')
+#ifdef CONFIG_NET6
+ if (fmt[1] == '6') {
+ if (fmt[2] == 'c')
+ flags |= COMPRESSED;
return ip6_addr_string(buf, end, ptr, field_width,
precision, flags);
+ }
+#endif
if (fmt[1] == '4')
return ip4_addr_string(buf, end, ptr, field_width,
precision, flags);
--
2.11.0.24.ge6920cf
More information about the U-Boot
mailing list