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