1 | // SPDX-License-Identifier: GPL-2.0-only |
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> |
21 | #include <linux/sunrpc/addr.h> |
22 | #include <linux/sunrpc/msg_prot.h> |
23 | #include <linux/slab.h> |
24 | #include <linux/export.h> |
25 | |
26 | #if IS_ENABLED(CONFIG_IPV6) |
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(a: addr)) |
40 | return snprintf(buf, size: buflen, fmt: "::" ); |
41 | |
42 | /* |
43 | * RFC 4291, Section 2.2.2 |
44 | * |
45 | * Shorthanded loopback address |
46 | */ |
47 | if (ipv6_addr_loopback(a: addr)) |
48 | return snprintf(buf, size: buflen, fmt: "::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(a: addr)) |
57 | return snprintf(buf, size: buflen, fmt: "::ffff:%pI4" , |
58 | &addr->s6_addr32[3]); |
59 | |
60 | /* |
61 | * RFC 4291, Section 2.2.1 |
62 | */ |
63 | return snprintf(buf, size: buflen, fmt: "%pI6c" , addr); |
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 | |
78 | if (!(ipv6_addr_type(addr: &sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)) |
79 | return len; |
80 | if (sin6->sin6_scope_id == 0) |
81 | return len; |
82 | |
83 | rc = snprintf(buf: scopebuf, size: sizeof(scopebuf), fmt: "%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(p: buf, q: scopebuf); |
93 | return len; |
94 | } |
95 | |
96 | #else /* !IS_ENABLED(CONFIG_IPV6) */ |
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 | |
110 | #endif /* !IS_ENABLED(CONFIG_IPV6) */ |
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, size: buflen, fmt: "%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(src: buf, srclen: buflen, dst: addr, delim: '\0', NULL) == 0) |
154 | return 0; |
155 | |
156 | sin->sin_family = AF_INET; |
157 | return sizeof(struct sockaddr_in); |
158 | } |
159 | |
160 | #if IS_ENABLED(CONFIG_IPV6) |
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) |
164 | { |
165 | char p[IPV6_SCOPE_ID_LEN + 1]; |
166 | size_t len; |
167 | u32 scope_id = 0; |
168 | struct net_device *dev; |
169 | |
170 | if ((buf + buflen) == delim) |
171 | return 1; |
172 | |
173 | if (*delim != IPV6_SCOPE_DELIMITER) |
174 | return 0; |
175 | |
176 | if (!(ipv6_addr_type(addr: &sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)) |
177 | return 0; |
178 | |
179 | len = (buf + buflen) - delim - 1; |
180 | if (len > IPV6_SCOPE_ID_LEN) |
181 | return 0; |
182 | |
183 | memcpy(p, delim + 1, len); |
184 | p[len] = 0; |
185 | |
186 | dev = dev_get_by_name(net, name: p); |
187 | if (dev != NULL) { |
188 | scope_id = dev->ifindex; |
189 | dev_put(dev); |
190 | } else { |
191 | if (kstrtou32(s: p, base: 10, res: &scope_id) != 0) |
192 | return 0; |
193 | } |
194 | |
195 | sin6->sin6_scope_id = scope_id; |
196 | return 1; |
197 | } |
198 | |
199 | static size_t rpc_pton6(struct net *net, const char *buf, const size_t buflen, |
200 | struct sockaddr *sap, const size_t salen) |
201 | { |
202 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; |
203 | u8 *addr = (u8 *)&sin6->sin6_addr.in6_u; |
204 | const char *delim; |
205 | |
206 | if (buflen > (INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN) || |
207 | salen < sizeof(struct sockaddr_in6)) |
208 | return 0; |
209 | |
210 | memset(sap, 0, sizeof(struct sockaddr_in6)); |
211 | |
212 | if (in6_pton(src: buf, srclen: buflen, dst: addr, IPV6_SCOPE_DELIMITER, end: &delim) == 0) |
213 | return 0; |
214 | |
215 | if (!rpc_parse_scope_id(net, buf, buflen, delim, sin6)) |
216 | return 0; |
217 | |
218 | sin6->sin6_family = AF_INET6; |
219 | return sizeof(struct sockaddr_in6); |
220 | } |
221 | #else |
222 | static size_t rpc_pton6(struct net *net, const char *buf, const size_t buflen, |
223 | struct sockaddr *sap, const size_t salen) |
224 | { |
225 | return 0; |
226 | } |
227 | #endif |
228 | |
229 | /** |
230 | * rpc_pton - Construct a sockaddr in @sap |
231 | * @net: applicable network namespace |
232 | * @buf: C string containing presentation format IP address |
233 | * @buflen: length of presentation address in bytes |
234 | * @sap: buffer into which to plant socket address |
235 | * @salen: size of buffer in bytes |
236 | * |
237 | * Returns the size of the socket address if successful; otherwise |
238 | * zero is returned. |
239 | * |
240 | * Plants a socket address in @sap and returns the size of the |
241 | * socket address, if successful. Returns zero if an error |
242 | * occurred. |
243 | */ |
244 | size_t rpc_pton(struct net *net, const char *buf, const size_t buflen, |
245 | struct sockaddr *sap, const size_t salen) |
246 | { |
247 | unsigned int i; |
248 | |
249 | for (i = 0; i < buflen; i++) |
250 | if (buf[i] == ':') |
251 | return rpc_pton6(net, buf, buflen, sap, salen); |
252 | return rpc_pton4(buf, buflen, sap, salen); |
253 | } |
254 | EXPORT_SYMBOL_GPL(rpc_pton); |
255 | |
256 | /** |
257 | * rpc_sockaddr2uaddr - Construct a universal address string from @sap. |
258 | * @sap: socket address |
259 | * @gfp_flags: allocation mode |
260 | * |
261 | * Returns a %NUL-terminated string in dynamically allocated memory; |
262 | * otherwise NULL is returned if an error occurred. Caller must |
263 | * free the returned string. |
264 | */ |
265 | char *rpc_sockaddr2uaddr(const struct sockaddr *sap, gfp_t gfp_flags) |
266 | { |
267 | char portbuf[RPCBIND_MAXUADDRPLEN]; |
268 | char addrbuf[RPCBIND_MAXUADDRLEN]; |
269 | unsigned short port; |
270 | |
271 | switch (sap->sa_family) { |
272 | case AF_INET: |
273 | if (rpc_ntop4(sap, buf: addrbuf, buflen: sizeof(addrbuf)) == 0) |
274 | return NULL; |
275 | port = ntohs(((struct sockaddr_in *)sap)->sin_port); |
276 | break; |
277 | case AF_INET6: |
278 | if (rpc_ntop6_noscopeid(sap, buf: addrbuf, buflen: sizeof(addrbuf)) == 0) |
279 | return NULL; |
280 | port = ntohs(((struct sockaddr_in6 *)sap)->sin6_port); |
281 | break; |
282 | default: |
283 | return NULL; |
284 | } |
285 | |
286 | if (snprintf(buf: portbuf, size: sizeof(portbuf), |
287 | fmt: ".%u.%u" , port >> 8, port & 0xff) > (int)sizeof(portbuf)) |
288 | return NULL; |
289 | |
290 | if (strlcat(p: addrbuf, q: portbuf, avail: sizeof(addrbuf)) > sizeof(addrbuf)) |
291 | return NULL; |
292 | |
293 | return kstrdup(s: addrbuf, gfp: gfp_flags); |
294 | } |
295 | |
296 | /** |
297 | * rpc_uaddr2sockaddr - convert a universal address to a socket address. |
298 | * @net: applicable network namespace |
299 | * @uaddr: C string containing universal address to convert |
300 | * @uaddr_len: length of universal address string |
301 | * @sap: buffer into which to plant socket address |
302 | * @salen: size of buffer |
303 | * |
304 | * @uaddr does not have to be '\0'-terminated, but kstrtou8() and |
305 | * rpc_pton() require proper string termination to be successful. |
306 | * |
307 | * Returns the size of the socket address if successful; otherwise |
308 | * zero is returned. |
309 | */ |
310 | size_t rpc_uaddr2sockaddr(struct net *net, const char *uaddr, |
311 | const size_t uaddr_len, struct sockaddr *sap, |
312 | const size_t salen) |
313 | { |
314 | char *c, buf[RPCBIND_MAXUADDRLEN + sizeof('\0')]; |
315 | u8 portlo, porthi; |
316 | unsigned short port; |
317 | |
318 | if (uaddr_len > RPCBIND_MAXUADDRLEN) |
319 | return 0; |
320 | |
321 | memcpy(buf, uaddr, uaddr_len); |
322 | |
323 | buf[uaddr_len] = '\0'; |
324 | c = strrchr(buf, '.'); |
325 | if (unlikely(c == NULL)) |
326 | return 0; |
327 | if (unlikely(kstrtou8(c + 1, 10, &portlo) != 0)) |
328 | return 0; |
329 | |
330 | *c = '\0'; |
331 | c = strrchr(buf, '.'); |
332 | if (unlikely(c == NULL)) |
333 | return 0; |
334 | if (unlikely(kstrtou8(c + 1, 10, &porthi) != 0)) |
335 | return 0; |
336 | |
337 | port = (unsigned short)((porthi << 8) | portlo); |
338 | |
339 | *c = '\0'; |
340 | if (rpc_pton(net, buf, strlen(buf), sap, salen) == 0) |
341 | return 0; |
342 | |
343 | switch (sap->sa_family) { |
344 | case AF_INET: |
345 | ((struct sockaddr_in *)sap)->sin_port = htons(port); |
346 | return sizeof(struct sockaddr_in); |
347 | case AF_INET6: |
348 | ((struct sockaddr_in6 *)sap)->sin6_port = htons(port); |
349 | return sizeof(struct sockaddr_in6); |
350 | } |
351 | |
352 | return 0; |
353 | } |
354 | EXPORT_SYMBOL_GPL(rpc_uaddr2sockaddr); |
355 | |