Root/
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 | #include <linux/slab.h> |
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 |
59 | */ |
60 | return snprintf(buf, buflen, "%pI6c", addr); |
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 | |
75 | if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)) |
76 | return len; |
77 | if (sin6->sin6_scope_id == 0) |
78 | return len; |
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 | |
170 | if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)) |
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 | * |
302 | * @uaddr does not have to be '\0'-terminated, but strict_strtoul() and |
303 | * rpc_pton() require proper string termination to be successful. |
304 | * |
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 | { |
311 | char *c, buf[RPCBIND_MAXUADDRLEN + sizeof('\0')]; |
312 | unsigned long portlo, porthi; |
313 | unsigned short port; |
314 | |
315 | if (uaddr_len > RPCBIND_MAXUADDRLEN) |
316 | return 0; |
317 | |
318 | memcpy(buf, uaddr, uaddr_len); |
319 | |
320 | buf[uaddr_len] = '\0'; |
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 | |
329 | *c = '\0'; |
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 | |
340 | *c = '\0'; |
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); |
356 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9