aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorDaniel Borkmann <dborkman@redhat.com>2013-06-28 13:49:39 -0400
committerDavid S. Miller <davem@davemloft.net>2013-07-02 02:22:13 -0400
commit1067964305df131ede2c08c2f3c9b3892640f1c6 (patch)
tree669ec54a6978c1301ea1003897fadd9b616879e4 /lib
parent936faf6c4937c50048e5779f6e49cfce566048d3 (diff)
lib: vsprintf: add IPv4/v6 generic %p[Ii]S[pfs] format specifier
In order to avoid making code that deals with printing both, IPv4 and IPv6 addresses, unnecessary complicated as for example ... if (sa.sa_family == AF_INET6) printk("... %pI6 ...", ..sin6_addr); else printk("... %pI4 ...", ..sin_addr.s_addr); ... it would be better to introduce a format specifier that can deal with those kind of situations internally; just as we have a "struct sockaddr" for generic mapping into "struct sockaddr_in" or "struct sockaddr_in6" as e.g. done in "union sctp_addr". Then, we could reduce the above statement into something like: printk("... %pIS ..", &sockaddr); In case our pointer is NULL, pointer() then deals with that already at an earlier point in time internally. While we're at it, support for both %piS/%pIS, where 'S' stands for sockaddr, comes (almost) for free. Additionally to that, postfix specifiers 'p', 'f' and 's' are supported as suggested and initially implemented in 2009 by Joe Perches [1]. Handling of those additional specifiers orientate on the initial RFC that was proposed. Also we support IPv6 compressed format specified by 'c' and various other IPv4 extensions as stated in the documentation part. Likely, there are many other areas than just SCTP in the kernel to make use of this extension as well. [1] http://patchwork.ozlabs.org/patch/31480/ Signed-off-by: Daniel Borkmann <dborkman@redhat.com> CC: Joe Perches <joe@perches.com> CC: linux-kernel@vger.kernel.org Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'lib')
-rw-r--r--lib/vsprintf.c124
1 files changed, 122 insertions, 2 deletions
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index e149c6416384..31febc0b70df 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -923,6 +923,103 @@ char *ip4_addr_string(char *buf, char *end, const u8 *addr,
923} 923}
924 924
925static noinline_for_stack 925static noinline_for_stack
926char *ip6_addr_string_sa(char *buf, char *end, const struct sockaddr_in6 *sa,
927 struct printf_spec spec, const char *fmt)
928{
929 bool have_p = false, have_s = false, have_f = false, have_c = false;
930 char ip6_addr[sizeof("[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255]") +
931 sizeof(":12345") + sizeof("/123456789") +
932 sizeof("%1234567890")];
933 char *p = ip6_addr, *pend = ip6_addr + sizeof(ip6_addr);
934 const u8 *addr = (const u8 *) &sa->sin6_addr;
935 char fmt6[2] = { fmt[0], '6' };
936 u8 off = 0;
937
938 fmt++;
939 while (isalpha(*++fmt)) {
940 switch (*fmt) {
941 case 'p':
942 have_p = true;
943 break;
944 case 'f':
945 have_f = true;
946 break;
947 case 's':
948 have_s = true;
949 break;
950 case 'c':
951 have_c = true;
952 break;
953 }
954 }
955
956 if (have_p || have_s || have_f) {
957 *p = '[';
958 off = 1;
959 }
960
961 if (fmt6[0] == 'I' && have_c)
962 p = ip6_compressed_string(ip6_addr + off, addr);
963 else
964 p = ip6_string(ip6_addr + off, addr, fmt6);
965
966 if (have_p || have_s || have_f)
967 *p++ = ']';
968
969 if (have_p) {
970 *p++ = ':';
971 p = number(p, pend, ntohs(sa->sin6_port), spec);
972 }
973 if (have_f) {
974 *p++ = '/';
975 p = number(p, pend, ntohl(sa->sin6_flowinfo &
976 IPV6_FLOWINFO_MASK), spec);
977 }
978 if (have_s) {
979 *p++ = '%';
980 p = number(p, pend, sa->sin6_scope_id, spec);
981 }
982 *p = '\0';
983
984 return string(buf, end, ip6_addr, spec);
985}
986
987static noinline_for_stack
988char *ip4_addr_string_sa(char *buf, char *end, const struct sockaddr_in *sa,
989 struct printf_spec spec, const char *fmt)
990{
991 bool have_p = false;
992 char *p, ip4_addr[sizeof("255.255.255.255") + sizeof(":12345")];
993 char *pend = ip4_addr + sizeof(ip4_addr);
994 const u8 *addr = (const u8 *) &sa->sin_addr.s_addr;
995 char fmt4[3] = { fmt[0], '4', 0 };
996
997 fmt++;
998 while (isalpha(*++fmt)) {
999 switch (*fmt) {
1000 case 'p':
1001 have_p = true;
1002 break;
1003 case 'h':
1004 case 'l':
1005 case 'n':
1006 case 'b':
1007 fmt4[2] = *fmt;
1008 break;
1009 }
1010 }
1011
1012 p = ip4_string(ip4_addr, addr, fmt4);
1013 if (have_p) {
1014 *p++ = ':';
1015 p = number(p, pend, ntohs(sa->sin_port), spec);
1016 }
1017 *p = '\0';
1018
1019 return string(buf, end, ip4_addr, spec);
1020}
1021
1022static noinline_for_stack
926char *uuid_string(char *buf, char *end, const u8 *addr, 1023char *uuid_string(char *buf, char *end, const u8 *addr,
927 struct printf_spec spec, const char *fmt) 1024 struct printf_spec spec, const char *fmt)
928{ 1025{
@@ -1007,11 +1104,17 @@ int kptr_restrict __read_mostly;
1007 * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way 1104 * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way
1008 * IPv4 uses dot-separated decimal without leading 0's (1.2.3.4) 1105 * IPv4 uses dot-separated decimal without leading 0's (1.2.3.4)
1009 * IPv6 uses colon separated network-order 16 bit hex with leading 0's 1106 * IPv6 uses colon separated network-order 16 bit hex with leading 0's
1107 * [S][pfs]
1108 * Generic IPv4/IPv6 address (struct sockaddr *) that falls back to
1109 * [4] or [6] and is able to print port [p], flowinfo [f], scope [s]
1010 * - 'i' [46] for 'raw' IPv4/IPv6 addresses 1110 * - 'i' [46] for 'raw' IPv4/IPv6 addresses
1011 * IPv6 omits the colons (01020304...0f) 1111 * IPv6 omits the colons (01020304...0f)
1012 * IPv4 uses dot-separated decimal with leading 0's (010.123.045.006) 1112 * IPv4 uses dot-separated decimal with leading 0's (010.123.045.006)
1013 * - '[Ii]4[hnbl]' IPv4 addresses in host, network, big or little endian order 1113 * [S][pfs]
1014 * - 'I6c' for IPv6 addresses printed as specified by 1114 * Generic IPv4/IPv6 address (struct sockaddr *) that falls back to
1115 * [4] or [6] and is able to print port [p], flowinfo [f], scope [s]
1116 * - '[Ii][4S][hnbl]' IPv4 addresses in host, network, big or little endian order
1117 * - 'I[6S]c' for IPv6 addresses printed as specified by
1015 * http://tools.ietf.org/html/rfc5952 1118 * http://tools.ietf.org/html/rfc5952
1016 * - 'U' For a 16 byte UUID/GUID, it prints the UUID/GUID in the form 1119 * - 'U' For a 16 byte UUID/GUID, it prints the UUID/GUID in the form
1017 * "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 1120 * "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
@@ -1093,6 +1196,21 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
1093 return ip6_addr_string(buf, end, ptr, spec, fmt); 1196 return ip6_addr_string(buf, end, ptr, spec, fmt);
1094 case '4': 1197 case '4':
1095 return ip4_addr_string(buf, end, ptr, spec, fmt); 1198 return ip4_addr_string(buf, end, ptr, spec, fmt);
1199 case 'S': {
1200 const union {
1201 struct sockaddr raw;
1202 struct sockaddr_in v4;
1203 struct sockaddr_in6 v6;
1204 } *sa = ptr;
1205
1206 switch (sa->raw.sa_family) {
1207 case AF_INET:
1208 return ip4_addr_string_sa(buf, end, &sa->v4, spec, fmt);
1209 case AF_INET6:
1210 return ip6_addr_string_sa(buf, end, &sa->v6, spec, fmt);
1211 default:
1212 return string(buf, end, "(invalid address)", spec);
1213 }}
1096 } 1214 }
1097 break; 1215 break;
1098 case 'U': 1216 case 'U':
@@ -1370,6 +1488,8 @@ qualifier:
1370 * %pI6 print an IPv6 address with colons 1488 * %pI6 print an IPv6 address with colons
1371 * %pi6 print an IPv6 address without colons 1489 * %pi6 print an IPv6 address without colons
1372 * %pI6c print an IPv6 address as specified by RFC 5952 1490 * %pI6c print an IPv6 address as specified by RFC 5952
1491 * %pIS depending on sa_family of 'struct sockaddr *' print IPv4/IPv6 address
1492 * %piS depending on sa_family of 'struct sockaddr *' print IPv4/IPv6 address
1373 * %pU[bBlL] print a UUID/GUID in big or little endian using lower or upper 1493 * %pU[bBlL] print a UUID/GUID in big or little endian using lower or upper
1374 * case. 1494 * case.
1375 * %*ph[CDN] a variable-length hex string with a separator (supports up to 64 1495 * %*ph[CDN] a variable-length hex string with a separator (supports up to 64