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