diff options
Diffstat (limited to 'lib/vsprintf.c')
| -rw-r--r-- | lib/vsprintf.c | 126 | 
1 files changed, 123 insertions, 3 deletions
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index e149c6416384..739a36366b79 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -670,7 +670,7 @@ static noinline_for_stack  char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,  		 const char *fmt)  { -	int i, len = 1;		/* if we pass '%ph[CDN]', field witdh remains +	int i, len = 1;		/* if we pass '%ph[CDN]', field width remains  				   negative value, fallback to the default */  	char separator; @@ -923,6 +923,103 @@ char *ip4_addr_string(char *buf, char *end, const u8 *addr,  }  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  | 
