]>
Commit | Line | Data |
---|---|---|
457c8996 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
a02d6926 CL |
2 | /* |
3 | * Copyright 2009, Oracle. All rights reserved. | |
4 | * | |
5 | * Convert socket addresses to presentation addresses and universal | |
6 | * addresses, and vice versa. | |
7 | * | |
8 | * Universal addresses are introduced by RFC 1833 and further refined by | |
9 | * recent RFCs describing NFSv4. The universal address format is part | |
10 | * of the external (network) interface provided by rpcbind version 3 | |
11 | * and 4, and by NFSv4. Such an address is a string containing a | |
12 | * presentation format IP address followed by a port number in | |
13 | * "hibyte.lobyte" format. | |
14 | * | |
15 | * IPv6 addresses can also include a scope ID, typically denoted by | |
16 | * a '%' followed by a device name or a non-negative integer. Refer to | |
17 | * RFC 4291, Section 2.2 for details on IPv6 presentation formats. | |
18 | */ | |
19 | ||
20 | #include <net/ipv6.h> | |
5976687a JL |
21 | #include <linux/sunrpc/addr.h> |
22 | #include <linux/sunrpc/msg_prot.h> | |
5a0e3ad6 | 23 | #include <linux/slab.h> |
bc3b2d7f | 24 | #include <linux/export.h> |
a02d6926 | 25 | |
dfd56b8b | 26 | #if IS_ENABLED(CONFIG_IPV6) |
a02d6926 CL |
27 | |
28 | static size_t rpc_ntop6_noscopeid(const struct sockaddr *sap, | |
29 | char *buf, const int buflen) | |
30 | { | |
31 | const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; | |
32 | const struct in6_addr *addr = &sin6->sin6_addr; | |
33 | ||
34 | /* | |
35 | * RFC 4291, Section 2.2.2 | |
36 | * | |
37 | * Shorthanded ANY address | |
38 | */ | |
39 | if (ipv6_addr_any(addr)) | |
40 | return snprintf(buf, buflen, "::"); | |
41 | ||
42 | /* | |
43 | * RFC 4291, Section 2.2.2 | |
44 | * | |
45 | * Shorthanded loopback address | |
46 | */ | |
47 | if (ipv6_addr_loopback(addr)) | |
48 | return snprintf(buf, buflen, "::1"); | |
49 | ||
50 | /* | |
51 | * RFC 4291, Section 2.2.3 | |
52 | * | |
53 | * Special presentation address format for mapped v4 | |
54 | * addresses. | |
55 | */ | |
56 | if (ipv6_addr_v4mapped(addr)) | |
57 | return snprintf(buf, buflen, "::ffff:%pI4", | |
58 | &addr->s6_addr32[3]); | |
59 | ||
60 | /* | |
61 | * RFC 4291, Section 2.2.1 | |
a02d6926 | 62 | */ |
dd1fd90f | 63 | return snprintf(buf, buflen, "%pI6c", addr); |
a02d6926 CL |
64 | } |
65 | ||
66 | static size_t rpc_ntop6(const struct sockaddr *sap, | |
67 | char *buf, const size_t buflen) | |
68 | { | |
69 | const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; | |
70 | char scopebuf[IPV6_SCOPE_ID_LEN]; | |
71 | size_t len; | |
72 | int rc; | |
73 | ||
74 | len = rpc_ntop6_noscopeid(sap, buf, buflen); | |
75 | if (unlikely(len == 0)) | |
76 | return len; | |
77 | ||
f1a89a11 | 78 | if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)) |
a02d6926 | 79 | return len; |
7a88efe9 CL |
80 | if (sin6->sin6_scope_id == 0) |
81 | return len; | |
a02d6926 CL |
82 | |
83 | rc = snprintf(scopebuf, sizeof(scopebuf), "%c%u", | |
84 | IPV6_SCOPE_DELIMITER, sin6->sin6_scope_id); | |
85 | if (unlikely((size_t)rc > sizeof(scopebuf))) | |
86 | return 0; | |
87 | ||
88 | len += rc; | |
89 | if (unlikely(len > buflen)) | |
90 | return 0; | |
91 | ||
92 | strcat(buf, scopebuf); | |
93 | return len; | |
94 | } | |
95 | ||
dfd56b8b | 96 | #else /* !IS_ENABLED(CONFIG_IPV6) */ |
a02d6926 CL |
97 | |
98 | static size_t rpc_ntop6_noscopeid(const struct sockaddr *sap, | |
99 | char *buf, const int buflen) | |
100 | { | |
101 | return 0; | |
102 | } | |
103 | ||
104 | static size_t rpc_ntop6(const struct sockaddr *sap, | |
105 | char *buf, const size_t buflen) | |
106 | { | |
107 | return 0; | |
108 | } | |
109 | ||
dfd56b8b | 110 | #endif /* !IS_ENABLED(CONFIG_IPV6) */ |
a02d6926 CL |
111 | |
112 | static int rpc_ntop4(const struct sockaddr *sap, | |
113 | char *buf, const size_t buflen) | |
114 | { | |
115 | const struct sockaddr_in *sin = (struct sockaddr_in *)sap; | |
116 | ||
117 | return snprintf(buf, buflen, "%pI4", &sin->sin_addr); | |
118 | } | |
119 | ||
120 | /** | |
121 | * rpc_ntop - construct a presentation address in @buf | |
122 | * @sap: socket address | |
123 | * @buf: construction area | |
124 | * @buflen: size of @buf, in bytes | |
125 | * | |
126 | * Plants a %NUL-terminated string in @buf and returns the length | |
127 | * of the string, excluding the %NUL. Otherwise zero is returned. | |
128 | */ | |
129 | size_t rpc_ntop(const struct sockaddr *sap, char *buf, const size_t buflen) | |
130 | { | |
131 | switch (sap->sa_family) { | |
132 | case AF_INET: | |
133 | return rpc_ntop4(sap, buf, buflen); | |
134 | case AF_INET6: | |
135 | return rpc_ntop6(sap, buf, buflen); | |
136 | } | |
137 | ||
138 | return 0; | |
139 | } | |
140 | EXPORT_SYMBOL_GPL(rpc_ntop); | |
141 | ||
142 | static size_t rpc_pton4(const char *buf, const size_t buflen, | |
143 | struct sockaddr *sap, const size_t salen) | |
144 | { | |
145 | struct sockaddr_in *sin = (struct sockaddr_in *)sap; | |
146 | u8 *addr = (u8 *)&sin->sin_addr.s_addr; | |
147 | ||
148 | if (buflen > INET_ADDRSTRLEN || salen < sizeof(struct sockaddr_in)) | |
149 | return 0; | |
150 | ||
151 | memset(sap, 0, sizeof(struct sockaddr_in)); | |
152 | ||
153 | if (in4_pton(buf, buflen, addr, '\0', NULL) == 0) | |
154 | return 0; | |
155 | ||
156 | sin->sin_family = AF_INET; | |
6eab04a8 | 157 | return sizeof(struct sockaddr_in); |
a02d6926 CL |
158 | } |
159 | ||
dfd56b8b | 160 | #if IS_ENABLED(CONFIG_IPV6) |
3065f1e2 SK |
161 | static int rpc_parse_scope_id(struct net *net, const char *buf, |
162 | const size_t buflen, const char *delim, | |
163 | struct sockaddr_in6 *sin6) | |
a02d6926 CL |
164 | { |
165 | char *p; | |
166 | size_t len; | |
167 | ||
168 | if ((buf + buflen) == delim) | |
169 | return 1; | |
170 | ||
171 | if (*delim != IPV6_SCOPE_DELIMITER) | |
172 | return 0; | |
173 | ||
f1a89a11 | 174 | if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)) |
a02d6926 CL |
175 | return 0; |
176 | ||
177 | len = (buf + buflen) - delim - 1; | |
178 | p = kstrndup(delim + 1, len, GFP_KERNEL); | |
179 | if (p) { | |
00cfaa94 | 180 | u32 scope_id = 0; |
a02d6926 CL |
181 | struct net_device *dev; |
182 | ||
3065f1e2 | 183 | dev = dev_get_by_name(net, p); |
a02d6926 CL |
184 | if (dev != NULL) { |
185 | scope_id = dev->ifindex; | |
186 | dev_put(dev); | |
187 | } else { | |
00cfaa94 | 188 | if (kstrtou32(p, 10, &scope_id) == 0) { |
a02d6926 CL |
189 | kfree(p); |
190 | return 0; | |
191 | } | |
192 | } | |
193 | ||
194 | kfree(p); | |
195 | ||
196 | sin6->sin6_scope_id = scope_id; | |
197 | return 1; | |
198 | } | |
199 | ||
200 | return 0; | |
201 | } | |
202 | ||
8b147f74 | 203 | static size_t rpc_pton6(struct net *net, const char *buf, const size_t buflen, |
a02d6926 CL |
204 | struct sockaddr *sap, const size_t salen) |
205 | { | |
206 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; | |
207 | u8 *addr = (u8 *)&sin6->sin6_addr.in6_u; | |
208 | const char *delim; | |
209 | ||
210 | if (buflen > (INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN) || | |
211 | salen < sizeof(struct sockaddr_in6)) | |
212 | return 0; | |
213 | ||
214 | memset(sap, 0, sizeof(struct sockaddr_in6)); | |
215 | ||
216 | if (in6_pton(buf, buflen, addr, IPV6_SCOPE_DELIMITER, &delim) == 0) | |
217 | return 0; | |
218 | ||
8b147f74 | 219 | if (!rpc_parse_scope_id(net, buf, buflen, delim, sin6)) |
a02d6926 CL |
220 | return 0; |
221 | ||
222 | sin6->sin6_family = AF_INET6; | |
223 | return sizeof(struct sockaddr_in6); | |
224 | } | |
225 | #else | |
8b147f74 | 226 | static size_t rpc_pton6(struct net *net, const char *buf, const size_t buflen, |
a02d6926 CL |
227 | struct sockaddr *sap, const size_t salen) |
228 | { | |
229 | return 0; | |
230 | } | |
231 | #endif | |
232 | ||
233 | /** | |
234 | * rpc_pton - Construct a sockaddr in @sap | |
90100b17 | 235 | * @net: applicable network namespace |
a02d6926 CL |
236 | * @buf: C string containing presentation format IP address |
237 | * @buflen: length of presentation address in bytes | |
238 | * @sap: buffer into which to plant socket address | |
239 | * @salen: size of buffer in bytes | |
240 | * | |
241 | * Returns the size of the socket address if successful; otherwise | |
242 | * zero is returned. | |
243 | * | |
244 | * Plants a socket address in @sap and returns the size of the | |
245 | * socket address, if successful. Returns zero if an error | |
246 | * occurred. | |
247 | */ | |
90100b17 | 248 | size_t rpc_pton(struct net *net, const char *buf, const size_t buflen, |
a02d6926 CL |
249 | struct sockaddr *sap, const size_t salen) |
250 | { | |
251 | unsigned int i; | |
252 | ||
253 | for (i = 0; i < buflen; i++) | |
254 | if (buf[i] == ':') | |
90100b17 | 255 | return rpc_pton6(net, buf, buflen, sap, salen); |
a02d6926 CL |
256 | return rpc_pton4(buf, buflen, sap, salen); |
257 | } | |
258 | EXPORT_SYMBOL_GPL(rpc_pton); | |
259 | ||
260 | /** | |
261 | * rpc_sockaddr2uaddr - Construct a universal address string from @sap. | |
262 | * @sap: socket address | |
d77385f2 | 263 | * @gfp_flags: allocation mode |
a02d6926 CL |
264 | * |
265 | * Returns a %NUL-terminated string in dynamically allocated memory; | |
266 | * otherwise NULL is returned if an error occurred. Caller must | |
267 | * free the returned string. | |
268 | */ | |
d77385f2 | 269 | char *rpc_sockaddr2uaddr(const struct sockaddr *sap, gfp_t gfp_flags) |
a02d6926 CL |
270 | { |
271 | char portbuf[RPCBIND_MAXUADDRPLEN]; | |
272 | char addrbuf[RPCBIND_MAXUADDRLEN]; | |
273 | unsigned short port; | |
274 | ||
275 | switch (sap->sa_family) { | |
276 | case AF_INET: | |
277 | if (rpc_ntop4(sap, addrbuf, sizeof(addrbuf)) == 0) | |
278 | return NULL; | |
279 | port = ntohs(((struct sockaddr_in *)sap)->sin_port); | |
280 | break; | |
281 | case AF_INET6: | |
282 | if (rpc_ntop6_noscopeid(sap, addrbuf, sizeof(addrbuf)) == 0) | |
283 | return NULL; | |
284 | port = ntohs(((struct sockaddr_in6 *)sap)->sin6_port); | |
285 | break; | |
286 | default: | |
287 | return NULL; | |
288 | } | |
289 | ||
290 | if (snprintf(portbuf, sizeof(portbuf), | |
291 | ".%u.%u", port >> 8, port & 0xff) > (int)sizeof(portbuf)) | |
292 | return NULL; | |
293 | ||
294 | if (strlcat(addrbuf, portbuf, sizeof(addrbuf)) > sizeof(addrbuf)) | |
295 | return NULL; | |
296 | ||
d77385f2 | 297 | return kstrdup(addrbuf, gfp_flags); |
a02d6926 | 298 | } |
a02d6926 CL |
299 | |
300 | /** | |
301 | * rpc_uaddr2sockaddr - convert a universal address to a socket address. | |
f2ac4dc9 | 302 | * @net: applicable network namespace |
a02d6926 CL |
303 | * @uaddr: C string containing universal address to convert |
304 | * @uaddr_len: length of universal address string | |
305 | * @sap: buffer into which to plant socket address | |
306 | * @salen: size of buffer | |
307 | * | |
00cfaa94 | 308 | * @uaddr does not have to be '\0'-terminated, but kstrtou8() and |
1e360a60 CL |
309 | * rpc_pton() require proper string termination to be successful. |
310 | * | |
a02d6926 CL |
311 | * Returns the size of the socket address if successful; otherwise |
312 | * zero is returned. | |
313 | */ | |
f2ac4dc9 SK |
314 | size_t rpc_uaddr2sockaddr(struct net *net, const char *uaddr, |
315 | const size_t uaddr_len, struct sockaddr *sap, | |
316 | const size_t salen) | |
a02d6926 | 317 | { |
1e360a60 | 318 | char *c, buf[RPCBIND_MAXUADDRLEN + sizeof('\0')]; |
00cfaa94 | 319 | u8 portlo, porthi; |
a02d6926 CL |
320 | unsigned short port; |
321 | ||
1e360a60 | 322 | if (uaddr_len > RPCBIND_MAXUADDRLEN) |
a02d6926 CL |
323 | return 0; |
324 | ||
325 | memcpy(buf, uaddr, uaddr_len); | |
326 | ||
1e360a60 | 327 | buf[uaddr_len] = '\0'; |
a02d6926 CL |
328 | c = strrchr(buf, '.'); |
329 | if (unlikely(c == NULL)) | |
330 | return 0; | |
00cfaa94 | 331 | if (unlikely(kstrtou8(c + 1, 10, &portlo) != 0)) |
a02d6926 CL |
332 | return 0; |
333 | ||
1e360a60 | 334 | *c = '\0'; |
a02d6926 CL |
335 | c = strrchr(buf, '.'); |
336 | if (unlikely(c == NULL)) | |
337 | return 0; | |
00cfaa94 | 338 | if (unlikely(kstrtou8(c + 1, 10, &porthi) != 0)) |
a02d6926 CL |
339 | return 0; |
340 | ||
341 | port = (unsigned short)((porthi << 8) | portlo); | |
342 | ||
1e360a60 | 343 | *c = '\0'; |
f2ac4dc9 | 344 | if (rpc_pton(net, buf, strlen(buf), sap, salen) == 0) |
a02d6926 CL |
345 | return 0; |
346 | ||
347 | switch (sap->sa_family) { | |
348 | case AF_INET: | |
349 | ((struct sockaddr_in *)sap)->sin_port = htons(port); | |
350 | return sizeof(struct sockaddr_in); | |
351 | case AF_INET6: | |
352 | ((struct sockaddr_in6 *)sap)->sin6_port = htons(port); | |
353 | return sizeof(struct sockaddr_in6); | |
354 | } | |
355 | ||
356 | return 0; | |
357 | } | |
358 | EXPORT_SYMBOL_GPL(rpc_uaddr2sockaddr); |