Skip to content

Commit 1067964

Browse files
Daniel Borkmanndavem330
authored andcommitted
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 <[email protected]> CC: Joe Perches <[email protected]> CC: [email protected] Signed-off-by: David S. Miller <[email protected]>
1 parent 936faf6 commit 1067964

File tree

2 files changed

+154
-2
lines changed

2 files changed

+154
-2
lines changed

Documentation/printk-formats.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,38 @@ IPv6 addresses:
121121
print a compressed IPv6 address as described by
122122
http://tools.ietf.org/html/rfc5952
123123

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+
124156
UUID/GUID addresses:
125157

126158
%pUb 00010203-0405-0607-0809-0a0b0c0d0e0f

lib/vsprintf.c

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,103 @@ char *ip4_addr_string(char *buf, char *end, const u8 *addr,
922922
return string(buf, end, ip4_addr, spec);
923923
}
924924

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+
9251022
static noinline_for_stack
9261023
char *uuid_string(char *buf, char *end, const u8 *addr,
9271024
struct printf_spec spec, const char *fmt)
@@ -1007,11 +1104,17 @@ int kptr_restrict __read_mostly;
10071104
* - 'I' [46] for IPv4/IPv6 addresses printed in the usual way
10081105
* IPv4 uses dot-separated decimal without leading 0's (1.2.3.4)
10091106
* 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]
10101110
* - 'i' [46] for 'raw' IPv4/IPv6 addresses
10111111
* IPv6 omits the colons (01020304...0f)
10121112
* 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
1014-
* - 'I6c' for IPv6 addresses printed as specified by
1113+
* [S][pfs]
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
10151118
* http://tools.ietf.org/html/rfc5952
10161119
* - 'U' For a 16 byte UUID/GUID, it prints the UUID/GUID in the form
10171120
* "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
@@ -1093,6 +1196,21 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
10931196
return ip6_addr_string(buf, end, ptr, spec, fmt);
10941197
case '4':
10951198
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+
}}
10961214
}
10971215
break;
10981216
case 'U':
@@ -1370,6 +1488,8 @@ int format_decode(const char *fmt, struct printf_spec *spec)
13701488
* %pI6 print an IPv6 address with colons
13711489
* %pi6 print an IPv6 address without colons
13721490
* %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
13731493
* %pU[bBlL] print a UUID/GUID in big or little endian using lower or upper
13741494
* case.
13751495
* %*ph[CDN] a variable-length hex string with a separator (supports up to 64

0 commit comments

Comments
 (0)