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