2 * Copyright 2009 Oracle. All rights reserved.
4 * This file is part of nfs-utils.
6 * nfs-utils is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * nfs-utils is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with nfs-utils. If not, see <http://www.gnu.org/licenses/>.
33 #include <netinet/in.h>
35 #include <sys/socket.h>
36 #include <sys/resource.h>
41 #ifdef HAVE_TCP_WRAPPER
42 #include "tcpwrapper.h"
51 #define SVC_CREATE_XPRT_CACHE_SIZE (8)
52 static SVCXPRT *svc_create_xprt_cache[SVC_CREATE_XPRT_CACHE_SIZE] = { NULL, };
55 * Cache an SVC xprt, in case there are more programs or versions to
56 * register against it.
59 svc_create_cache_xprt(SVCXPRT *xprt)
63 /* Check if we've already got this one... */
64 for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++)
65 if (svc_create_xprt_cache[i] == xprt)
68 /* No, we don't. Cache it. */
69 for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++)
70 if (svc_create_xprt_cache[i] == NULL) {
71 svc_create_xprt_cache[i] = xprt;
75 xlog(L_ERROR, "%s: Failed to cache an xprt", __func__);
79 * Find a previously cached SVC xprt structure with the given bind address
80 * and transport semantics.
82 * Returns pointer to a cached SVC xprt.
84 * If no matching SVC XPRT can be found, NULL is returned.
87 svc_create_find_xprt(const struct sockaddr *bindaddr, const struct netconfig *nconf)
91 for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++) {
92 SVCXPRT *xprt = svc_create_xprt_cache[i];
97 if (strcmp(nconf->nc_netid, xprt->xp_netid) != 0)
99 sap = (struct sockaddr *)xprt->xp_ltaddr.buf;
100 if (!nfs_compare_sockaddr(bindaddr, sap))
108 * Set up an appropriate bind address, given @port and @nconf.
110 * Returns getaddrinfo(3) results if successful. Caller must
111 * invoke freeaddrinfo(3) on these results.
113 * Otherwise NULL is returned if an error occurs.
116 static struct addrinfo *
117 svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
119 struct addrinfo *ai = NULL;
120 struct addrinfo hint = {
121 .ai_flags = AI_PASSIVE | AI_NUMERICSERV,
126 if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
127 hint.ai_family = AF_INET;
128 #ifdef IPV6_SUPPORTED
129 else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
130 hint.ai_family = AF_INET6;
131 #endif /* IPV6_SUPPORTED */
133 xlog(D_GENERAL, "Unrecognized bind address family: %s",
134 nconf->nc_protofmly);
138 if (strcmp(nconf->nc_proto, NC_UDP) == 0)
139 hint.ai_protocol = (int)IPPROTO_UDP;
140 else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
141 hint.ai_protocol = (int)IPPROTO_TCP;
143 xlog(D_GENERAL, "Unrecognized bind address protocol: %s",
148 (void)snprintf(buf, sizeof(buf), "%u", port);
149 error = getaddrinfo(NULL, buf, &hint, &ai);
151 xlog(L_ERROR, "Failed to construct bind address: %s",
152 gai_strerror(error));
160 * Create a listener socket on a specific bindaddr, and set
161 * special socket options to allow it to share the same port
162 * as other listeners.
164 * Returns an open, bound, and possibly listening network
167 * Otherwise returns -1 if some error occurs.
170 svc_create_sock(const struct sockaddr *sap, socklen_t salen,
171 struct netconfig *nconf)
173 int fd, type, protocol;
176 switch(nconf->nc_semantics) {
180 case NC_TPI_COTS_ORD:
184 xlog(D_GENERAL, "%s: Unrecognized bind address semantics: %u",
185 __func__, nconf->nc_semantics);
189 if (strcmp(nconf->nc_proto, NC_UDP) == 0)
190 protocol = (int)IPPROTO_UDP;
191 else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
192 protocol = (int)IPPROTO_TCP;
194 xlog(D_GENERAL, "%s: Unrecognized bind address protocol: %s",
195 __func__, nconf->nc_proto);
199 fd = socket((int)sap->sa_family, type, protocol);
201 xlog(L_ERROR, "Could not make a socket: (%d) %m",
206 #ifdef IPV6_SUPPORTED
207 if (sap->sa_family == AF_INET6) {
208 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
209 &one, sizeof(one)) == -1) {
210 xlog(L_ERROR, "Failed to set IPV6_V6ONLY: (%d) %m",
216 #endif /* IPV6_SUPPORTED */
218 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
219 &one, sizeof(one)) == -1) {
220 xlog(L_ERROR, "Failed to set SO_REUSEADDR: (%d) %m",
226 if (bind(fd, sap, salen) == -1) {
227 xlog(L_ERROR, "Could not bind socket: (%d) %m",
233 if (nconf->nc_semantics == NC_TPI_COTS_ORD)
234 if (listen(fd, SOMAXCONN) == -1) {
235 xlog(L_ERROR, "Could not listen on socket: (%d) %m",
245 * The simple case is allowing the TI-RPC library to create a
246 * transport itself, given just the bind address and transport
249 * Our local xprt cache is ignored in this path, since the
250 * caller is not interested in sharing listeners or ports, and
251 * the library automatically avoids ports already in use.
253 * Returns the count of started listeners (one or zero).
256 svc_create_nconf_rand_port(const char *name, const rpcprog_t program,
257 const rpcvers_t version,
258 void (*dispatch)(struct svc_req *, SVCXPRT *),
259 struct netconfig *nconf)
261 struct t_bind bindaddr;
265 ai = svc_create_bindaddr(nconf, 0);
269 bindaddr.addr.buf = ai->ai_addr;
270 bindaddr.qlen = SOMAXCONN;
272 xprt = svc_tli_create(RPC_ANYFD, nconf, &bindaddr, 0, 0);
275 xlog(D_GENERAL, "Failed to create listener xprt "
276 "(%s, %u, %s)", name, version, nconf->nc_netid);
280 if (!svc_reg(xprt, program, version, dispatch, nconf)) {
281 /* svc_reg(3) destroys @xprt in this case */
282 xlog(D_GENERAL, "Failed to register (%s, %u, %s)",
283 name, version, nconf->nc_netid);
291 * If a port is specified on the command line, that port value will be
292 * the same for all listeners created here. Create each listener
293 * socket in advance and set SO_REUSEADDR, rather than allowing the
294 * RPC library to create the listeners for us on a randomly chosen
295 * port via svc_tli_create(RPC_ANYFD).
297 * Some callers want to listen for more than one RPC version using the
298 * same port number. For example, mountd could want to listen for MNT
299 * version 1, 2, and 3 requests. This means mountd must use the same
300 * set of listener sockets for multiple RPC versions, since, on one
301 * system, you can't have two listener sockets with the exact same
302 * bind address (and port) and transport protocol.
304 * To accomplish this, this function caches xprts as they are created.
305 * This cache is checked to see if a previously created xprt can be
306 * used, before creating a new xprt for this [program, version]. If
307 * there is a cached xprt with the same bindaddr and transport
308 * semantics, we simply register the new version with that xprt,
309 * rather than creating a fresh xprt for it.
311 * The xprt cache implemented here is local to a process. Two
312 * separate RPC daemons can not share a set of listeners.
314 * Returns the count of started listeners (one or zero).
317 svc_create_nconf_fixed_port(const char *name, const rpcprog_t program,
318 const rpcvers_t version,
319 void (*dispatch)(struct svc_req *, SVCXPRT *),
320 const uint16_t port, struct netconfig *nconf)
325 ai = svc_create_bindaddr(nconf, port);
329 xprt = svc_create_find_xprt(ai->ai_addr, nconf);
333 fd = svc_create_sock(ai->ai_addr, ai->ai_addrlen, nconf);
337 xprt = svc_tli_create(fd, nconf, NULL, 0, 0);
339 xlog(D_GENERAL, "Failed to create listener xprt "
340 "(%s, %u, %s)", name, version, nconf->nc_netid);
346 if (!svc_reg(xprt, program, version, dispatch, nconf)) {
347 /* svc_reg(3) destroys @xprt in this case */
348 xlog(D_GENERAL, "Failed to register (%s, %u, %s)",
349 name, version, nconf->nc_netid);
353 svc_create_cache_xprt(xprt);
364 svc_create_nconf(const char *name, const rpcprog_t program,
365 const rpcvers_t version,
366 void (*dispatch)(struct svc_req *, SVCXPRT *),
367 const uint16_t port, struct netconfig *nconf)
370 return svc_create_nconf_fixed_port(name, program,
371 version, dispatch, port, nconf);
373 return svc_create_nconf_rand_port(name, program,
374 version, dispatch, nconf);
378 * nfs_svc_create - start up RPC svc listeners
379 * @name: C string containing name of new service
380 * @program: RPC program number to register
381 * @version: RPC version number to register
382 * @dispatch: address of function that handles incoming RPC requests
383 * @port: if not zero, transport listens on this port
385 * Sets up network transports for receiving RPC requests, and starts
386 * the RPC dispatcher. Returns the number of started network transports.
389 nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
390 void (*dispatch)(struct svc_req *, SVCXPRT *),
393 const struct sigaction create_sigaction = {
394 .sa_handler = SIG_IGN,
396 unsigned int visible, up, servport;
397 struct netconfig *nconf;
401 * Ignore SIGPIPE to avoid exiting sideways when peers
402 * close their TCP connection while we're trying to reply
405 (void)sigaction(SIGPIPE, &create_sigaction, NULL);
407 handlep = setnetconfig();
408 if (handlep == NULL) {
409 xlog(L_ERROR, "Failed to access local netconfig database: %s",
416 while ((nconf = getnetconfig(handlep)) != NULL) {
417 if (!(nconf->nc_flag & NC_VISIBLE))
421 servport = getservport(program, nconf->nc_proto);
425 up += svc_create_nconf(name, program, version, dispatch,
430 xlog(L_ERROR, "Failed to find any visible netconfig entries");
432 if (endnetconfig(handlep) == -1)
433 xlog(L_ERROR, "Failed to close local netconfig database: %s",
440 * nfs_svc_unregister - remove service registrations from local rpcbind database
441 * @program: RPC program number to unregister
442 * @version: RPC version number to unregister
444 * Removes all registrations for [ @program, @version ] .
447 nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
449 if (rpcb_unset(program, version, NULL) == FALSE)
450 xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
451 (unsigned long)program, (unsigned long)version);
454 #else /* !HAVE_LIBTIRPC */
457 * nfs_svc_create - start up RPC svc listeners
458 * @name: C string containing name of new service
459 * @program: RPC program number to register
460 * @version: RPC version number to register
461 * @dispatch: address of function that handles incoming RPC requests
462 * @port: if not zero, transport listens on this port
464 * Sets up network transports for receiving RPC requests, and starts
465 * the RPC dispatcher. Returns the number of started network transports.
468 nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
469 void (*dispatch)(struct svc_req *, SVCXPRT *),
472 rpc_init(name, (int)program, (int)version, dispatch, (int)port);
477 * nfs_svc_unregister - remove service registrations from local rpcbind database
478 * @program: RPC program number to unregister
479 * @version: RPC version number to unregister
481 * Removes all registrations for [ @program, @version ] .
484 nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
486 if (pmap_unset((unsigned long)program, (unsigned long)version) == FALSE)
487 xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
488 (unsigned long)program, (unsigned long)version);
491 #endif /* !HAVE_LIBTIRPC */