]>
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> | |
5976687a JL |
20 | #include <linux/sunrpc/addr.h> |
21 | #include <linux/sunrpc/msg_prot.h> | |
5a0e3ad6 | 22 | #include <linux/slab.h> |
bc3b2d7f | 23 | #include <linux/export.h> |
a02d6926 | 24 | |
dfd56b8b | 25 | #if IS_ENABLED(CONFIG_IPV6) |
a02d6926 CL |
26 | |
27 | 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 | const struct in6_addr *addr = &sin6->sin6_addr; | |
32 | ||
33 | /* | |
34 | * RFC 4291, Section 2.2.2 | |
35 | * | |
36 | * Shorthanded ANY address | |
37 | */ | |
38 | if (ipv6_addr_any(addr)) | |
39 | return snprintf(buf, buflen, "::"); | |
40 | ||
41 | /* | |
42 | * RFC 4291, Section 2.2.2 | |
43 | * | |
44 | * Shorthanded loopback address | |
45 | */ | |
46 | if (ipv6_addr_loopback(addr)) | |
47 | 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 | if (ipv6_addr_v4mapped(addr)) | |
56 | return snprintf(buf, buflen, "::ffff:%pI4", | |
57 | &addr->s6_addr32[3]); | |
58 | ||
59 | /* | |
60 | * RFC 4291, Section 2.2.1 | |
a02d6926 | 61 | */ |
dd1fd90f | 62 | return snprintf(buf, buflen, "%pI6c", addr); |
a02d6926 CL |
63 | } |
64 | ||
65 | 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 | len = rpc_ntop6_noscopeid(sap, buf, buflen); | |
74 | if (unlikely(len == 0)) | |
75 | return len; | |
76 | ||
f1a89a11 | 77 | if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)) |
a02d6926 | 78 | return len; |
7a88efe9 CL |
79 | if (sin6->sin6_scope_id == 0) |
80 | return len; | |
a02d6926 CL |
81 | |
82 | rc = snprintf(scopebuf, sizeof(scopebuf), "%c%u", | |
83 | IPV6_SCOPE_DELIMITER, sin6->sin6_scope_id); | |
84 | if (unlikely((size_t)rc > sizeof(scopebuf))) | |
85 | return 0; | |
86 | ||
87 | len += rc; | |
88 | if (unlikely(len > buflen)) | |
89 | return 0; | |
90 | ||
91 | strcat(buf, scopebuf); | |
92 | return len; | |
93 | } | |
94 | ||
dfd56b8b | 95 | #else /* !IS_ENABLED(CONFIG_IPV6) */ |
a02d6926 CL |
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 | ||
dfd56b8b | 109 | #endif /* !IS_ENABLED(CONFIG_IPV6) */ |
a02d6926 CL |
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 | 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 | size_t rpc_ntop(const struct sockaddr *sap, char *buf, const size_t buflen) | |
129 | { | |
130 | switch (sap->sa_family) { | |
131 | case AF_INET: | |
132 | return rpc_ntop4(sap, buf, buflen); | |
133 | case AF_INET6: | |
134 | return rpc_ntop6(sap, buf, buflen); | |
135 | } | |
136 | ||
137 | return 0; | |
138 | } | |
139 | EXPORT_SYMBOL_GPL(rpc_ntop); | |
140 | ||
141 | 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 | u8 *addr = (u8 *)&sin->sin_addr.s_addr; | |
146 | ||
147 | if (buflen > INET_ADDRSTRLEN || salen < sizeof(struct sockaddr_in)) | |
148 | return 0; | |
149 | ||
150 | memset(sap, 0, sizeof(struct sockaddr_in)); | |
151 | ||
152 | if (in4_pton(buf, buflen, addr, '\0', NULL) == 0) | |
153 | return 0; | |
154 | ||
155 | sin->sin_family = AF_INET; | |
6eab04a8 | 156 | return sizeof(struct sockaddr_in); |
a02d6926 CL |
157 | } |
158 | ||
dfd56b8b | 159 | #if IS_ENABLED(CONFIG_IPV6) |
3065f1e2 SK |
160 | 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) | |
a02d6926 CL |
163 | { |
164 | char *p; | |
165 | size_t len; | |
166 | ||
167 | if ((buf + buflen) == delim) | |
168 | return 1; | |
169 | ||
170 | if (*delim != IPV6_SCOPE_DELIMITER) | |
171 | return 0; | |
172 | ||
f1a89a11 | 173 | if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)) |
a02d6926 CL |
174 | return 0; |
175 | ||
176 | len = (buf + buflen) - delim - 1; | |
177 | p = kstrndup(delim + 1, len, GFP_KERNEL); | |
178 | if (p) { | |
179 | unsigned long scope_id = 0; | |
180 | struct net_device *dev; | |
181 | ||
3065f1e2 | 182 | dev = dev_get_by_name(net, p); |
a02d6926 CL |
183 | if (dev != NULL) { |
184 | scope_id = dev->ifindex; | |
185 | dev_put(dev); | |
186 | } else { | |
187 | if (strict_strtoul(p, 10, &scope_id) == 0) { | |
188 | kfree(p); | |
189 | return 0; | |
190 | } | |
191 | } | |
192 | ||
193 | kfree(p); | |
194 | ||
195 | sin6->sin6_scope_id = scope_id; | |
196 | return 1; | |
197 | } | |
198 | ||
199 | return 0; | |
200 | } | |
201 | ||
8b147f74 | 202 | static size_t rpc_pton6(struct net *net, const char *buf, const size_t buflen, |
a02d6926 CL |
203 | struct sockaddr *sap, const size_t salen) |
204 | { | |
205 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; | |
206 | u8 *addr = (u8 *)&sin6->sin6_addr.in6_u; | |
207 | const char *delim; | |
208 | ||
209 | if (buflen > (INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN) || | |
210 | salen < sizeof(struct sockaddr_in6)) | |
211 | return 0; | |
212 | ||
213 | memset(sap, 0, sizeof(struct sockaddr_in6)); | |
214 | ||
215 | if (in6_pton(buf, buflen, addr, IPV6_SCOPE_DELIMITER, &delim) == 0) | |
216 | return 0; | |
217 | ||
8b147f74 | 218 | if (!rpc_parse_scope_id(net, buf, buflen, delim, sin6)) |
a02d6926 CL |
219 | return 0; |
220 | ||
221 | sin6->sin6_family = AF_INET6; | |
222 | return sizeof(struct sockaddr_in6); | |
223 | } | |
224 | #else | |
8b147f74 | 225 | static size_t rpc_pton6(struct net *net, const char *buf, const size_t buflen, |
a02d6926 CL |
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 | |
90100b17 | 234 | * @net: applicable network namespace |
a02d6926 CL |
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 | */ | |
90100b17 | 247 | size_t rpc_pton(struct net *net, const char *buf, const size_t buflen, |
a02d6926 CL |
248 | struct sockaddr *sap, const size_t salen) |
249 | { | |
250 | unsigned int i; | |
251 | ||
252 | for (i = 0; i < buflen; i++) | |
253 | if (buf[i] == ':') | |
90100b17 | 254 | return rpc_pton6(net, buf, buflen, sap, salen); |
a02d6926 CL |
255 | 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 | |
d77385f2 | 262 | * @gfp_flags: allocation mode |
a02d6926 CL |
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 | */ | |
d77385f2 | 268 | char *rpc_sockaddr2uaddr(const struct sockaddr *sap, gfp_t gfp_flags) |
a02d6926 CL |
269 | { |
270 | char portbuf[RPCBIND_MAXUADDRPLEN]; | |
271 | char addrbuf[RPCBIND_MAXUADDRLEN]; | |
272 | unsigned short port; | |
273 | ||
274 | switch (sap->sa_family) { | |
275 | case AF_INET: | |
276 | if (rpc_ntop4(sap, addrbuf, sizeof(addrbuf)) == 0) | |
277 | return NULL; | |
278 | port = ntohs(((struct sockaddr_in *)sap)->sin_port); | |
279 | break; | |
280 | case AF_INET6: | |
281 | if (rpc_ntop6_noscopeid(sap, addrbuf, sizeof(addrbuf)) == 0) | |
282 | return NULL; | |
283 | port = ntohs(((struct sockaddr_in6 *)sap)->sin6_port); | |
284 | break; | |
285 | default: | |
286 | return NULL; | |
287 | } | |
288 | ||
289 | if (snprintf(portbuf, sizeof(portbuf), | |
290 | ".%u.%u", port >> 8, port & 0xff) > (int)sizeof(portbuf)) | |
291 | return NULL; | |
292 | ||
293 | if (strlcat(addrbuf, portbuf, sizeof(addrbuf)) > sizeof(addrbuf)) | |
294 | return NULL; | |
295 | ||
d77385f2 | 296 | return kstrdup(addrbuf, gfp_flags); |
a02d6926 | 297 | } |
a02d6926 CL |
298 | |
299 | /** | |
300 | * rpc_uaddr2sockaddr - convert a universal address to a socket address. | |
f2ac4dc9 | 301 | * @net: applicable network namespace |
a02d6926 CL |
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 | * | |
1e360a60 CL |
307 | * @uaddr does not have to be '\0'-terminated, but strict_strtoul() and |
308 | * rpc_pton() require proper string termination to be successful. | |
309 | * | |
a02d6926 CL |
310 | * Returns the size of the socket address if successful; otherwise |
311 | * zero is returned. | |
312 | */ | |
f2ac4dc9 SK |
313 | size_t rpc_uaddr2sockaddr(struct net *net, const char *uaddr, |
314 | const size_t uaddr_len, struct sockaddr *sap, | |
315 | const size_t salen) | |
a02d6926 | 316 | { |
1e360a60 | 317 | char *c, buf[RPCBIND_MAXUADDRLEN + sizeof('\0')]; |
a02d6926 CL |
318 | unsigned long portlo, porthi; |
319 | unsigned short port; | |
320 | ||
1e360a60 | 321 | if (uaddr_len > RPCBIND_MAXUADDRLEN) |
a02d6926 CL |
322 | return 0; |
323 | ||
324 | memcpy(buf, uaddr, uaddr_len); | |
325 | ||
1e360a60 | 326 | buf[uaddr_len] = '\0'; |
a02d6926 CL |
327 | c = strrchr(buf, '.'); |
328 | if (unlikely(c == NULL)) | |
329 | return 0; | |
330 | if (unlikely(strict_strtoul(c + 1, 10, &portlo) != 0)) | |
331 | return 0; | |
332 | if (unlikely(portlo > 255)) | |
333 | return 0; | |
334 | ||
1e360a60 | 335 | *c = '\0'; |
a02d6926 CL |
336 | c = strrchr(buf, '.'); |
337 | if (unlikely(c == NULL)) | |
338 | return 0; | |
339 | if (unlikely(strict_strtoul(c + 1, 10, &porthi) != 0)) | |
340 | return 0; | |
341 | if (unlikely(porthi > 255)) | |
342 | return 0; | |
343 | ||
344 | port = (unsigned short)((porthi << 8) | portlo); | |
345 | ||
1e360a60 | 346 | *c = '\0'; |
f2ac4dc9 | 347 | if (rpc_pton(net, buf, strlen(buf), sap, salen) == 0) |
a02d6926 CL |
348 | return 0; |
349 | ||
350 | switch (sap->sa_family) { | |
351 | case AF_INET: | |
352 | ((struct sockaddr_in *)sap)->sin_port = htons(port); | |
353 | return sizeof(struct sockaddr_in); | |
354 | case AF_INET6: | |
355 | ((struct sockaddr_in6 *)sap)->sin6_port = htons(port); | |
356 | return sizeof(struct sockaddr_in6); | |
357 | } | |
358 | ||
359 | return 0; | |
360 | } | |
361 | EXPORT_SYMBOL_GPL(rpc_uaddr2sockaddr); |