Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
[cascardo/linux.git] / lib / vsprintf.c
index 7d84676..739a363 100644 (file)
@@ -922,6 +922,103 @@ char *ip4_addr_string(char *buf, char *end, const u8 *addr,
        return string(buf, end, ip4_addr, spec);
 }
 
+static noinline_for_stack
+char *ip6_addr_string_sa(char *buf, char *end, const struct sockaddr_in6 *sa,
+                        struct printf_spec spec, const char *fmt)
+{
+       bool have_p = false, have_s = false, have_f = false, have_c = false;
+       char ip6_addr[sizeof("[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255]") +
+                     sizeof(":12345") + sizeof("/123456789") +
+                     sizeof("%1234567890")];
+       char *p = ip6_addr, *pend = ip6_addr + sizeof(ip6_addr);
+       const u8 *addr = (const u8 *) &sa->sin6_addr;
+       char fmt6[2] = { fmt[0], '6' };
+       u8 off = 0;
+
+       fmt++;
+       while (isalpha(*++fmt)) {
+               switch (*fmt) {
+               case 'p':
+                       have_p = true;
+                       break;
+               case 'f':
+                       have_f = true;
+                       break;
+               case 's':
+                       have_s = true;
+                       break;
+               case 'c':
+                       have_c = true;
+                       break;
+               }
+       }
+
+       if (have_p || have_s || have_f) {
+               *p = '[';
+               off = 1;
+       }
+
+       if (fmt6[0] == 'I' && have_c)
+               p = ip6_compressed_string(ip6_addr + off, addr);
+       else
+               p = ip6_string(ip6_addr + off, addr, fmt6);
+
+       if (have_p || have_s || have_f)
+               *p++ = ']';
+
+       if (have_p) {
+               *p++ = ':';
+               p = number(p, pend, ntohs(sa->sin6_port), spec);
+       }
+       if (have_f) {
+               *p++ = '/';
+               p = number(p, pend, ntohl(sa->sin6_flowinfo &
+                                         IPV6_FLOWINFO_MASK), spec);
+       }
+       if (have_s) {
+               *p++ = '%';
+               p = number(p, pend, sa->sin6_scope_id, spec);
+       }
+       *p = '\0';
+
+       return string(buf, end, ip6_addr, spec);
+}
+
+static noinline_for_stack
+char *ip4_addr_string_sa(char *buf, char *end, const struct sockaddr_in *sa,
+                        struct printf_spec spec, const char *fmt)
+{
+       bool have_p = false;
+       char *p, ip4_addr[sizeof("255.255.255.255") + sizeof(":12345")];
+       char *pend = ip4_addr + sizeof(ip4_addr);
+       const u8 *addr = (const u8 *) &sa->sin_addr.s_addr;
+       char fmt4[3] = { fmt[0], '4', 0 };
+
+       fmt++;
+       while (isalpha(*++fmt)) {
+               switch (*fmt) {
+               case 'p':
+                       have_p = true;
+                       break;
+               case 'h':
+               case 'l':
+               case 'n':
+               case 'b':
+                       fmt4[2] = *fmt;
+                       break;
+               }
+       }
+
+       p = ip4_string(ip4_addr, addr, fmt4);
+       if (have_p) {
+               *p++ = ':';
+               p = number(p, pend, ntohs(sa->sin_port), spec);
+       }
+       *p = '\0';
+
+       return string(buf, end, ip4_addr, spec);
+}
+
 static noinline_for_stack
 char *uuid_string(char *buf, char *end, const u8 *addr,
                  struct printf_spec spec, const char *fmt)
@@ -1007,11 +1104,17 @@ int kptr_restrict __read_mostly;
  * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way
  *       IPv4 uses dot-separated decimal without leading 0's (1.2.3.4)
  *       IPv6 uses colon separated network-order 16 bit hex with leading 0's
+ *       [S][pfs]
+ *       Generic IPv4/IPv6 address (struct sockaddr *) that falls back to
+ *       [4] or [6] and is able to print port [p], flowinfo [f], scope [s]
  * - 'i' [46] for 'raw' IPv4/IPv6 addresses
  *       IPv6 omits the colons (01020304...0f)
  *       IPv4 uses dot-separated decimal with leading 0's (010.123.045.006)
- * - '[Ii]4[hnbl]' IPv4 addresses in host, network, big or little endian order
- * - 'I6c' for IPv6 addresses printed as specified by
+ *       [S][pfs]
+ *       Generic IPv4/IPv6 address (struct sockaddr *) that falls back to
+ *       [4] or [6] and is able to print port [p], flowinfo [f], scope [s]
+ * - '[Ii][4S][hnbl]' IPv4 addresses in host, network, big or little endian order
+ * - 'I[6S]c' for IPv6 addresses printed as specified by
  *       http://tools.ietf.org/html/rfc5952
  * - 'U' For a 16 byte UUID/GUID, it prints the UUID/GUID in the form
  *       "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
@@ -1093,6 +1196,21 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
                        return ip6_addr_string(buf, end, ptr, spec, fmt);
                case '4':
                        return ip4_addr_string(buf, end, ptr, spec, fmt);
+               case 'S': {
+                       const union {
+                               struct sockaddr         raw;
+                               struct sockaddr_in      v4;
+                               struct sockaddr_in6     v6;
+                       } *sa = ptr;
+
+                       switch (sa->raw.sa_family) {
+                       case AF_INET:
+                               return ip4_addr_string_sa(buf, end, &sa->v4, spec, fmt);
+                       case AF_INET6:
+                               return ip6_addr_string_sa(buf, end, &sa->v6, spec, fmt);
+                       default:
+                               return string(buf, end, "(invalid address)", spec);
+                       }}
                }
                break;
        case 'U':
@@ -1370,6 +1488,8 @@ qualifier:
  * %pI6 print an IPv6 address with colons
  * %pi6 print an IPv6 address without colons
  * %pI6c print an IPv6 address as specified by RFC 5952
+ * %pIS depending on sa_family of 'struct sockaddr *' print IPv4/IPv6 address
+ * %piS depending on sa_family of 'struct sockaddr *' print IPv4/IPv6 address
  * %pU[bBlL] print a UUID/GUID in big or little endian using lower or upper
  *   case.
  * %*ph[CDN] a variable-length hex string with a separator (supports up to 64