diff options
-rw-r--r-- | Documentation/printk-formats.txt | 32 | ||||
-rw-r--r-- | lib/vsprintf.c | 124 |
2 files changed, 154 insertions, 2 deletions
diff --git a/Documentation/printk-formats.txt b/Documentation/printk-formats.txt index 3af5ae6c9c11..3e8cb73ac43c 100644 --- a/Documentation/printk-formats.txt +++ b/Documentation/printk-formats.txt | |||
@@ -121,6 +121,38 @@ IPv6 addresses: | |||
121 | print a compressed IPv6 address as described by | 121 | print a compressed IPv6 address as described by |
122 | http://tools.ietf.org/html/rfc5952 | 122 | http://tools.ietf.org/html/rfc5952 |
123 | 123 | ||
124 | IPv4/IPv6 addresses (generic, with port, flowinfo, scope): | ||
125 | |||
126 | %pIS 1.2.3.4 or 0001:0002:0003:0004:0005:0006:0007:0008 | ||
127 | %piS 001.002.003.004 or 00010002000300040005000600070008 | ||
128 | %pISc 1.2.3.4 or 1:2:3:4:5:6:7:8 | ||
129 | %pISpc 1.2.3.4:12345 or [1:2:3:4:5:6:7:8]:12345 | ||
130 | %p[Ii]S[pfschnbl] | ||
131 | |||
132 | For printing an IP address without the need to distinguish whether it's | ||
133 | of type AF_INET or AF_INET6, a pointer to a valid 'struct sockaddr', | ||
134 | specified through 'IS' or 'iS', can be passed to this format specifier. | ||
135 | |||
136 | The additional 'p', 'f', and 's' specifiers are used to specify port | ||
137 | (IPv4, IPv6), flowinfo (IPv6) and scope (IPv6). Ports have a ':' prefix, | ||
138 | flowinfo a '/' and scope a '%', each followed by the actual value. | ||
139 | |||
140 | In case of an IPv6 address the compressed IPv6 address as described by | ||
141 | http://tools.ietf.org/html/rfc5952 is being used if the additional | ||
142 | specifier 'c' is given. The IPv6 address is surrounded by '[', ']' in | ||
143 | case of additional specifiers 'p', 'f' or 's' as suggested by | ||
144 | https://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-07 | ||
145 | |||
146 | In case of IPv4 addresses, the additional 'h', 'n', 'b', and 'l' | ||
147 | specifiers can be used as well and are ignored in case of an IPv6 | ||
148 | address. | ||
149 | |||
150 | Further examples: | ||
151 | |||
152 | %pISfc 1.2.3.4 or [1:2:3:4:5:6:7:8]/123456789 | ||
153 | %pISsc 1.2.3.4 or [1:2:3:4:5:6:7:8]%1234567890 | ||
154 | %pISpfc 1.2.3.4:12345 or [1:2:3:4:5:6:7:8]:12345/123456789 | ||
155 | |||
124 | UUID/GUID addresses: | 156 | UUID/GUID addresses: |
125 | 157 | ||
126 | %pUb 00010203-0405-0607-0809-0a0b0c0d0e0f | 158 | %pUb 00010203-0405-0607-0809-0a0b0c0d0e0f |
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 | ||
925 | static noinline_for_stack | 925 | static noinline_for_stack |
926 | char *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 | |||
987 | static noinline_for_stack | ||
988 | char *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 | |||
1022 | static noinline_for_stack | ||
926 | char *uuid_string(char *buf, char *end, const u8 *addr, | 1023 | char *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 |