Commit c2e1b09ff237c0a3687b9a804cc8bf489743cffc
Committed by
Trond Myklebust
1 parent
babe80eb49
Exists in
master
and in
39 other branches
SUNRPC: Support registering IPv6 interfaces with local rpcbind daemon
Introduce a new API to register RPC services on IPv6 interfaces to allow the NFS server and lockd to advertise on IPv6 networks. Unlike rpcb_register(), the new rpcb_v4_register() function uses rpcbind protocol version 4 to contact the local rpcbind daemon. The version 4 SET/UNSET procedures allow services to register address families besides AF_INET, register at specific network interfaces, and register transport protocols besides UDP and TCP. All of this functionality is exposed via the new rpcb_v4_register() kernel API. A user-space rpcbind daemon implementation that supports version 4 of the rpcbind protocol is required in order to make use of this new API. Note that rpcbind version 3 is sufficient to support the new rpcbind facilities listed above, but most extant implementations use version 4. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Showing 2 changed files with 177 additions and 4 deletions Side-by-side Diff
include/linux/sunrpc/clnt.h
... | ... | @@ -125,6 +125,9 @@ |
125 | 125 | void rpc_release_client(struct rpc_clnt *); |
126 | 126 | |
127 | 127 | int rpcb_register(u32, u32, int, unsigned short, int *); |
128 | +int rpcb_v4_register(const u32 program, const u32 version, | |
129 | + const struct sockaddr *address, | |
130 | + const char *netid, int *result); | |
128 | 131 | int rpcb_getport_sync(struct sockaddr_in *, u32, u32, int); |
129 | 132 | void rpcb_getport_async(struct rpc_task *); |
130 | 133 |
net/sunrpc/rpcb_clnt.c
... | ... | @@ -87,6 +87,7 @@ |
87 | 87 | |
88 | 88 | static struct rpc_procinfo rpcb_procedures2[]; |
89 | 89 | static struct rpc_procinfo rpcb_procedures3[]; |
90 | +static struct rpc_procinfo rpcb_procedures4[]; | |
90 | 91 | |
91 | 92 | struct rpcb_info { |
92 | 93 | u32 rpc_vers; |
... | ... | @@ -122,6 +123,12 @@ |
122 | 123 | .sin_port = htons(RPCBIND_PORT), |
123 | 124 | }; |
124 | 125 | |
126 | +static const struct sockaddr_in6 rpcb_in6addr_loopback = { | |
127 | + .sin6_family = AF_INET6, | |
128 | + .sin6_addr = IN6ADDR_LOOPBACK_INIT, | |
129 | + .sin6_port = htons(RPCBIND_PORT), | |
130 | +}; | |
131 | + | |
125 | 132 | static struct rpc_clnt *rpcb_create_local(struct sockaddr *addr, |
126 | 133 | size_t addrlen, u32 version) |
127 | 134 | { |
128 | 135 | |
129 | 136 | |
130 | 137 | |
... | ... | @@ -196,13 +203,38 @@ |
196 | 203 | * rpcb_register - set or unset a port registration with the local rpcbind svc |
197 | 204 | * @prog: RPC program number to bind |
198 | 205 | * @vers: RPC version number to bind |
199 | - * @prot: transport protocol to use to make this request | |
206 | + * @prot: transport protocol to register | |
200 | 207 | * @port: port value to register |
201 | - * @okay: result code | |
208 | + * @okay: OUT: result code | |
202 | 209 | * |
203 | - * port == 0 means unregister, port != 0 means register. | |
210 | + * RPC services invoke this function to advertise their contact | |
211 | + * information via the system's rpcbind daemon. RPC services | |
212 | + * invoke this function once for each [program, version, transport] | |
213 | + * tuple they wish to advertise. | |
204 | 214 | * |
205 | - * This routine supports only rpcbind version 2. | |
215 | + * Callers may also unregister RPC services that are no longer | |
216 | + * available by setting the passed-in port to zero. This removes | |
217 | + * all registered transports for [program, version] from the local | |
218 | + * rpcbind database. | |
219 | + * | |
220 | + * Returns zero if the registration request was dispatched | |
221 | + * successfully and a reply was received. The rpcbind daemon's | |
222 | + * boolean result code is stored in *okay. | |
223 | + * | |
224 | + * Returns an errno value and sets *result to zero if there was | |
225 | + * some problem that prevented the rpcbind request from being | |
226 | + * dispatched, or if the rpcbind daemon did not respond within | |
227 | + * the timeout. | |
228 | + * | |
229 | + * This function uses rpcbind protocol version 2 to contact the | |
230 | + * local rpcbind daemon. | |
231 | + * | |
232 | + * Registration works over both AF_INET and AF_INET6, and services | |
233 | + * registered via this function are advertised as available for any | |
234 | + * address. If the local rpcbind daemon is listening on AF_INET6, | |
235 | + * services registered via this function will be advertised on | |
236 | + * IN6ADDR_ANY (ie available for all AF_INET and AF_INET6 | |
237 | + * addresses). | |
206 | 238 | */ |
207 | 239 | int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) |
208 | 240 | { |
... | ... | @@ -228,6 +260,144 @@ |
228 | 260 | return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback, |
229 | 261 | sizeof(rpcb_inaddr_loopback), |
230 | 262 | RPCBVERS_2, &msg, okay); |
263 | +} | |
264 | + | |
265 | +/* | |
266 | + * Fill in AF_INET family-specific arguments to register | |
267 | + */ | |
268 | +static int rpcb_register_netid4(struct sockaddr_in *address_to_register, | |
269 | + struct rpc_message *msg) | |
270 | +{ | |
271 | + struct rpcbind_args *map = msg->rpc_argp; | |
272 | + unsigned short port = ntohs(address_to_register->sin_port); | |
273 | + char buf[32]; | |
274 | + | |
275 | + /* Construct AF_INET universal address */ | |
276 | + snprintf(buf, sizeof(buf), | |
277 | + NIPQUAD_FMT".%u.%u", | |
278 | + NIPQUAD(address_to_register->sin_addr.s_addr), | |
279 | + port >> 8, port & 0xff); | |
280 | + map->r_addr = buf; | |
281 | + | |
282 | + dprintk("RPC: %sregistering [%u, %u, %s, '%s'] with " | |
283 | + "local rpcbind\n", (port ? "" : "un"), | |
284 | + map->r_prog, map->r_vers, | |
285 | + map->r_addr, map->r_netid); | |
286 | + | |
287 | + msg->rpc_proc = &rpcb_procedures4[RPCBPROC_UNSET]; | |
288 | + if (port) | |
289 | + msg->rpc_proc = &rpcb_procedures4[RPCBPROC_SET]; | |
290 | + | |
291 | + return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback, | |
292 | + sizeof(rpcb_inaddr_loopback), | |
293 | + RPCBVERS_4, msg, msg->rpc_resp); | |
294 | +} | |
295 | + | |
296 | +/* | |
297 | + * Fill in AF_INET6 family-specific arguments to register | |
298 | + */ | |
299 | +static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register, | |
300 | + struct rpc_message *msg) | |
301 | +{ | |
302 | + struct rpcbind_args *map = msg->rpc_argp; | |
303 | + unsigned short port = ntohs(address_to_register->sin6_port); | |
304 | + char buf[64]; | |
305 | + | |
306 | + /* Construct AF_INET6 universal address */ | |
307 | + snprintf(buf, sizeof(buf), | |
308 | + NIP6_FMT".%u.%u", | |
309 | + NIP6(address_to_register->sin6_addr), | |
310 | + port >> 8, port & 0xff); | |
311 | + map->r_addr = buf; | |
312 | + | |
313 | + dprintk("RPC: %sregistering [%u, %u, %s, '%s'] with " | |
314 | + "local rpcbind\n", (port ? "" : "un"), | |
315 | + map->r_prog, map->r_vers, | |
316 | + map->r_addr, map->r_netid); | |
317 | + | |
318 | + msg->rpc_proc = &rpcb_procedures4[RPCBPROC_UNSET]; | |
319 | + if (port) | |
320 | + msg->rpc_proc = &rpcb_procedures4[RPCBPROC_SET]; | |
321 | + | |
322 | + return rpcb_register_call((struct sockaddr *)&rpcb_in6addr_loopback, | |
323 | + sizeof(rpcb_in6addr_loopback), | |
324 | + RPCBVERS_4, msg, msg->rpc_resp); | |
325 | +} | |
326 | + | |
327 | +/** | |
328 | + * rpcb_v4_register - set or unset a port registration with the local rpcbind | |
329 | + * @program: RPC program number of service to (un)register | |
330 | + * @version: RPC version number of service to (un)register | |
331 | + * @address: address family, IP address, and port to (un)register | |
332 | + * @netid: netid of transport protocol to (un)register | |
333 | + * @result: result code from rpcbind RPC call | |
334 | + * | |
335 | + * RPC services invoke this function to advertise their contact | |
336 | + * information via the system's rpcbind daemon. RPC services | |
337 | + * invoke this function once for each [program, version, address, | |
338 | + * netid] tuple they wish to advertise. | |
339 | + * | |
340 | + * Callers may also unregister RPC services that are no longer | |
341 | + * available by setting the port number in the passed-in address | |
342 | + * to zero. Callers pass a netid of "" to unregister all | |
343 | + * transport netids associated with [program, version, address]. | |
344 | + * | |
345 | + * Returns zero if the registration request was dispatched | |
346 | + * successfully and a reply was received. The rpcbind daemon's | |
347 | + * result code is stored in *result. | |
348 | + * | |
349 | + * Returns an errno value and sets *result to zero if there was | |
350 | + * some problem that prevented the rpcbind request from being | |
351 | + * dispatched, or if the rpcbind daemon did not respond within | |
352 | + * the timeout. | |
353 | + * | |
354 | + * This function uses rpcbind protocol version 4 to contact the | |
355 | + * local rpcbind daemon. The local rpcbind daemon must support | |
356 | + * version 4 of the rpcbind protocol in order for these functions | |
357 | + * to register a service successfully. | |
358 | + * | |
359 | + * Supported netids include "udp" and "tcp" for UDP and TCP over | |
360 | + * IPv4, and "udp6" and "tcp6" for UDP and TCP over IPv6, | |
361 | + * respectively. | |
362 | + * | |
363 | + * The contents of @address determine the address family and the | |
364 | + * port to be registered. The usual practice is to pass INADDR_ANY | |
365 | + * as the raw address, but specifying a non-zero address is also | |
366 | + * supported by this API if the caller wishes to advertise an RPC | |
367 | + * service on a specific network interface. | |
368 | + * | |
369 | + * Note that passing in INADDR_ANY does not create the same service | |
370 | + * registration as IN6ADDR_ANY. The former advertises an RPC | |
371 | + * service on any IPv4 address, but not on IPv6. The latter | |
372 | + * advertises the service on all IPv4 and IPv6 addresses. | |
373 | + */ | |
374 | +int rpcb_v4_register(const u32 program, const u32 version, | |
375 | + const struct sockaddr *address, const char *netid, | |
376 | + int *result) | |
377 | +{ | |
378 | + struct rpcbind_args map = { | |
379 | + .r_prog = program, | |
380 | + .r_vers = version, | |
381 | + .r_netid = netid, | |
382 | + .r_owner = RPCB_OWNER_STRING, | |
383 | + }; | |
384 | + struct rpc_message msg = { | |
385 | + .rpc_argp = &map, | |
386 | + .rpc_resp = result, | |
387 | + }; | |
388 | + | |
389 | + *result = 0; | |
390 | + | |
391 | + switch (address->sa_family) { | |
392 | + case AF_INET: | |
393 | + return rpcb_register_netid4((struct sockaddr_in *)address, | |
394 | + &msg); | |
395 | + case AF_INET6: | |
396 | + return rpcb_register_netid6((struct sockaddr_in6 *)address, | |
397 | + &msg); | |
398 | + } | |
399 | + | |
400 | + return -EAFNOSUPPORT; | |
231 | 401 | } |
232 | 402 | |
233 | 403 | /** |