[U-Boot] [RFC PATCH v2 2/5] lib/vsprintf.c: add IPv6 compressed format %pI6c

Chris Packham
2013-01-18T01:35:29Z

From: Chris Packham <chris.packham@alliedtelesis.co.nz>

Add support for "human friendly" IPv6 address representations as
specified in
http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-00

This code has been adapted from Linux kernel with minimal modification.

Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>

---
Changes in v2:
-Add support for printing mapped and ISATAP addresses

include/net6.h | 13 ++++++
lib/vsprintf.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++--------
2 files changed, 136 insertions(+), 20 deletions(-)

diff --git a/include/net6.h b/include/net6.h
index 03bccb2..bdb4326 100644
--- a/include/net6.h
+++ b/include/net6.h
@@ -40,4 +40,17 @@ struct ip6_hdr {
IP6addr_t daddr;
};

+/* ::ffff:0:0/96 is reserved for v4 mapped addresses */
+static inline int ipv6_addr_v4mapped(const IP6addr_t *a)
+{
+ return (a->u6_addr32[0] | a->u6_addr32[1] |
+ (a->u6_addr32[2] ^ htonl(0x0000ffff))) == 0;
+}
+
+/* Intra-Site Automatic Tunnel Addressing Protocol Address */
+static inline int ipv6_addr_is_isatap(const IP6addr_t *a)
+{
+ return (a->u6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE);
+}
+
#endif /* __NET6_H__ */
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 3c432f8..21d9f2a 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -23,6 +23,7 @@
#endif

#include <div64.h>
+#include <net6.h>
#define noinline __attribute__((noinline))

/* some reluctance to put this into a new limits.h, so it is here */
@@ -276,6 +277,7 @@ static noinline char *put_dec(char *buf, u64 num)
#define LEFT 16 /* left justified */
#define SMALL 32 /* Must be 32 == 0x20 */
#define SPECIAL 64 /* 0x */
+#define COMPRESSED 128 /* use compressed format */

#ifdef CONFIG_SYS_VSNPRINTF
/*
@@ -430,12 +432,107 @@ 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,
- int precision, int flags)
+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 *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;
+ IP6addr_t in6;
+
+ memcpy(&in6, addr, sizeof(IP6addr_t));
+
+ 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.u6_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.u6_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.u6_addr8[12]);
+ }
+ *p = '\0';
+
+ return p;
+}
+
+static char *ip6_string(char *p, u8 *addr, int flags)
{
- /* (8 * 4 hex digits), 7 colons and trailing zero */
- char ip6_addr[8 * 5];
- char *p = ip6_addr;
int i;

for (i = 0; i < 8; i++) {
@@ -446,6 +543,19 @@ static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width,
}
*p = '\0';

+ return p;
+}
+
+static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width,
+ int precision, int flags)
+{
+ char ip6_addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
+
+ if (flags & COMPRESSED)
+ ip6_compressed_string(ip6_addr, addr);
+ else
+ ip6_string(ip6_addr, addr, flags);
+
return string(buf, end, ip6_addr, field_width, precision,
flags & ~SPECIAL);
}
@@ -453,21 +563,9 @@ static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width,
static char *ip4_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 ip4_addr[sizeof("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';
+ ip4_string(ip4_addr, addr);

return string(buf, end, ip4_addr, field_width, precision,
flags & ~SPECIAL);
@@ -487,6 +585,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
@@ -517,9 +617,12 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr,
flags |= SPECIAL;
/* Fallthrough */
case 'I':
- if (fmt[1] == '6')
+ if (fmt[1] == '6') {
+ if (fmt[2] == 'c')
+ flags |= COMPRESSED;
return ip6_addr_string(buf, end, ptr, field_width,
precision, flags);
+ }
if (fmt[1] == '4')
return ip4_addr_string(buf, end, ptr, field_width,
precision, flags);
--
1.7.12.rc2.16.g034161a