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