|
@@ -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
|