]> git.decadent.org.uk Git - nfs-utils.git/blob - support/nfs/svc_create.c
Merge branch 'sid'
[nfs-utils.git] / support / nfs / svc_create.c
1 /*
2  * Copyright 2009 Oracle.  All rights reserved.
3  *
4  * This file is part of nfs-utils.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <memory.h>
28 #include <signal.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <netdb.h>
32
33 #include <netinet/in.h>
34
35 #include <sys/socket.h>
36 #include <sys/resource.h>
37
38 #include <rpc/rpc.h>
39 #include <rpc/svc.h>
40
41 #ifdef HAVE_TCP_WRAPPER
42 #include "tcpwrapper.h"
43 #endif
44
45 #include "sockaddr.h"
46 #include "rpcmisc.h"
47 #include "xlog.h"
48
49 #ifdef HAVE_LIBTIRPC
50
51 #define SVC_CREATE_XPRT_CACHE_SIZE      (8)
52 static SVCXPRT *svc_create_xprt_cache[SVC_CREATE_XPRT_CACHE_SIZE] = { NULL, };
53
54 /*
55  * Cache an SVC xprt, in case there are more programs or versions to
56  * register against it.
57  */
58 static void
59 svc_create_cache_xprt(SVCXPRT *xprt)
60 {
61         unsigned int i;
62
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)
66                         return;
67
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;
72                         return;
73                 }
74
75         xlog(L_ERROR, "%s: Failed to cache an xprt", __func__);
76 }
77
78 /*
79  * Find a previously cached SVC xprt structure with the given bind address
80  * and transport semantics.
81  *
82  * Returns pointer to a cached SVC xprt.
83  *
84  * If no matching SVC XPRT can be found, NULL is returned.
85  */
86 static SVCXPRT *
87 svc_create_find_xprt(const struct sockaddr *bindaddr, const struct netconfig *nconf)
88 {
89         unsigned int i;
90
91         for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++) {
92                 SVCXPRT *xprt = svc_create_xprt_cache[i];
93                 struct sockaddr *sap;
94
95                 if (xprt == NULL)
96                         continue;
97                 if (strcmp(nconf->nc_netid, xprt->xp_netid) != 0)
98                         continue;
99                 sap = (struct sockaddr *)xprt->xp_ltaddr.buf;
100                 if (!nfs_compare_sockaddr(bindaddr, sap))
101                         continue;
102                 return xprt;
103         }
104         return NULL;
105 }
106
107 /*
108  * Set up an appropriate bind address, given @port and @nconf.
109  *
110  * Returns getaddrinfo(3) results if successful.  Caller must
111  * invoke freeaddrinfo(3) on these results.
112  *
113  * Otherwise NULL is returned if an error occurs.
114  */
115 __attribute_malloc__
116 static struct addrinfo *
117 svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
118 {
119         struct addrinfo *ai = NULL;
120         struct addrinfo hint = {
121                 .ai_flags       = AI_PASSIVE | AI_NUMERICSERV,
122         };
123         char buf[8];
124         int error;
125
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 */
132         else {
133                 xlog(D_GENERAL, "Unrecognized bind address family: %s",
134                         nconf->nc_protofmly);
135                 return NULL;
136         }
137
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;
142         else {
143                 xlog(D_GENERAL, "Unrecognized bind address protocol: %s",
144                         nconf->nc_proto);
145                 return NULL;
146         }
147
148         (void)snprintf(buf, sizeof(buf), "%u", port);
149         error = getaddrinfo(NULL, buf, &hint, &ai);
150         if (error != 0) {
151                 xlog(L_ERROR, "Failed to construct bind address: %s",
152                         gai_strerror(error));
153                 return NULL;
154         }
155
156         return ai;
157 }
158
159 /*
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.
163  *
164  * Returns an open, bound, and possibly listening network
165  * socket on success.
166  *
167  * Otherwise returns -1 if some error occurs.
168  */
169 static int
170 svc_create_sock(const struct sockaddr *sap, socklen_t salen,
171                 struct netconfig *nconf)
172 {
173         int fd, type, protocol;
174         int one = 1;
175
176         switch(nconf->nc_semantics) {
177         case NC_TPI_CLTS:
178                 type = SOCK_DGRAM;
179                 break;
180         case NC_TPI_COTS_ORD:
181                 type = SOCK_STREAM;
182                 break;
183         default:
184                 xlog(D_GENERAL, "%s: Unrecognized bind address semantics: %u",
185                         __func__, nconf->nc_semantics);
186                 return -1;
187         }
188
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;
193         else {
194                 xlog(D_GENERAL, "%s: Unrecognized bind address protocol: %s",
195                         __func__, nconf->nc_proto);
196                 return -1;
197         }
198
199         fd = socket((int)sap->sa_family, type, protocol);
200         if (fd == -1) {
201                 xlog(L_ERROR, "Could not make a socket: (%d) %m",
202                         errno);
203                 return -1;
204         }
205
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",
211                                 errno);
212                         (void)close(fd);
213                         return -1;
214                 }
215         }
216 #endif  /* IPV6_SUPPORTED */
217
218         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
219                        &one, sizeof(one)) == -1) {
220                 xlog(L_ERROR, "Failed to set SO_REUSEADDR: (%d) %m",
221                         errno);
222                 (void)close(fd);
223                 return -1;
224         }
225
226         if (bind(fd, sap, salen) == -1) {
227                 xlog(L_ERROR, "Could not bind socket: (%d) %m",
228                         errno);
229                 (void)close(fd);
230                 return -1;
231         }
232
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",
236                                 errno);
237                         (void)close(fd);
238                         return -1;
239                 }
240
241         return fd;
242 }
243
244 /*
245  * The simple case is allowing the TI-RPC library to create a
246  * transport itself, given just the bind address and transport
247  * semantics.
248  *
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.
252  *
253  * Returns the count of started listeners (one or zero).
254  */
255 static unsigned int
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)
260 {
261         struct t_bind bindaddr;
262         struct addrinfo *ai;
263         SVCXPRT *xprt;
264
265         ai = svc_create_bindaddr(nconf, 0);
266         if (ai == NULL)
267                 return 0;
268
269         bindaddr.addr.buf = ai->ai_addr;
270         bindaddr.qlen = SOMAXCONN;
271
272         xprt = svc_tli_create(RPC_ANYFD, nconf, &bindaddr, 0, 0);
273         freeaddrinfo(ai);
274         if (xprt == NULL) {
275                 xlog(D_GENERAL, "Failed to create listener xprt "
276                         "(%s, %u, %s)", name, version, nconf->nc_netid);
277                 return 0;
278         }
279
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);
284                 return 0;
285         }
286
287         return 1;
288 }
289
290 /*
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).
296  *
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.
303  *
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.
310  *
311  * The xprt cache implemented here is local to a process.  Two
312  * separate RPC daemons can not share a set of listeners.
313  *
314  * Returns the count of started listeners (one or zero).
315  */
316 static unsigned int
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)
321 {
322         struct addrinfo *ai;
323         SVCXPRT *xprt;
324
325         ai = svc_create_bindaddr(nconf, port);
326         if (ai == NULL)
327                 return 0;
328
329         xprt = svc_create_find_xprt(ai->ai_addr, nconf);
330         if (xprt == NULL) {
331                 int fd;
332
333                 fd = svc_create_sock(ai->ai_addr, ai->ai_addrlen, nconf);
334                 if (fd == -1)
335                         goto out_free;
336
337                 xprt = svc_tli_create(fd, nconf, NULL, 0, 0);
338                 if (xprt == NULL) {
339                         xlog(D_GENERAL, "Failed to create listener xprt "
340                                 "(%s, %u, %s)", name, version, nconf->nc_netid);
341                         (void)close(fd);
342                         goto out_free;
343                 }
344         }
345
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);
350                 goto out_free;
351         }
352
353         svc_create_cache_xprt(xprt);
354
355         freeaddrinfo(ai);
356         return 1;
357
358 out_free:
359         freeaddrinfo(ai);
360         return 0;
361 }
362
363 static unsigned int
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)
368 {
369         if (port != 0)
370                 return svc_create_nconf_fixed_port(name, program,
371                         version, dispatch, port, nconf);
372
373         return svc_create_nconf_rand_port(name, program,
374                         version, dispatch, nconf);
375 }
376
377 /**
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
384  *
385  * Sets up network transports for receiving RPC requests, and starts
386  * the RPC dispatcher.  Returns the number of started network transports.
387  */
388 unsigned int
389 nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
390                 void (*dispatch)(struct svc_req *, SVCXPRT *),
391                 const uint16_t port)
392 {
393         const struct sigaction create_sigaction = {
394                 .sa_handler     = SIG_IGN,
395         };
396         unsigned int visible, up, servport;
397         struct netconfig *nconf;
398         void *handlep;
399
400         /*
401          * Ignore SIGPIPE to avoid exiting sideways when peers
402          * close their TCP connection while we're trying to reply
403          * to them.
404          */
405         (void)sigaction(SIGPIPE, &create_sigaction, NULL);
406
407         handlep = setnetconfig();
408         if (handlep == NULL) {
409                 xlog(L_ERROR, "Failed to access local netconfig database: %s",
410                         nc_sperror());
411                 return 0;
412         }
413
414         visible = 0;
415         up = 0;
416         while ((nconf = getnetconfig(handlep)) != NULL) {
417                 if (!(nconf->nc_flag & NC_VISIBLE))
418                         continue;
419                 visible++;
420                 if (port == 0)
421                         servport = getservport(program, nconf->nc_proto);
422                 else
423                         servport = port;
424
425                 up += svc_create_nconf(name, program, version, dispatch,
426                                                 servport, nconf);
427         }
428
429         if (visible == 0)
430                 xlog(L_ERROR, "Failed to find any visible netconfig entries");
431
432         if (endnetconfig(handlep) == -1)
433                 xlog(L_ERROR, "Failed to close local netconfig database: %s",
434                         nc_sperror());
435
436         return up;
437 }
438
439 /**
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
443  *
444  * Removes all registrations for [ @program, @version ] .
445  */
446 void
447 nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
448 {
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);
452 }
453
454 #else   /* !HAVE_LIBTIRPC */
455
456 /**
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
463  *
464  * Sets up network transports for receiving RPC requests, and starts
465  * the RPC dispatcher.  Returns the number of started network transports.
466  */
467 unsigned int
468 nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
469                 void (*dispatch)(struct svc_req *, SVCXPRT *),
470                 const uint16_t port)
471 {
472         rpc_init(name, (int)program, (int)version, dispatch, (int)port);
473         return 1;
474 }
475
476 /**
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
480  *
481  * Removes all registrations for [ @program, @version ] .
482  */
483 void
484 nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
485 {
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);
489 }
490
491 #endif  /* !HAVE_LIBTIRPC */