]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/rmtcall.c
Assorted changes from Steve Dickson
[nfs-utils.git] / utils / statd / rmtcall.c
1 /*
2  * Copyright (C) 1996, 1999 Olaf Kirch
3  * Modified by Jeffrey A. Uphoff, 1997-1999.
4  * Modified by H.J. Lu, 1998.
5  * Modified by Lon Hohberger, Oct. 2000
6  *   - Bugfix handling client responses.
7  *   - Paranoia on NOTIFY_CALLBACK case
8  *
9  * NSM for Linux.
10  */
11
12 /*
13  * After reboot, notify all hosts on our notify list. In order not to
14  * hang statd with delivery to dead hosts, we perform all RPC calls in
15  * parallel.
16  *
17  * It would have been nice to use the portmapper's rmtcall feature,
18  * but that's not possible for security reasons (the portmapper would
19  * have to forward the call with root privs for most statd's, which
20  * it won't if it's worth its money).
21  */
22
23 #include "config.h"
24
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/time.h>
28 #include <netinet/in.h>
29 #include <net/if.h>
30 #include <arpa/inet.h>
31 #include <rpc/rpc.h>
32 #include <rpc/pmap_prot.h>
33 #include <rpc/pmap_rmt.h>
34 #include <time.h>
35 #include <netdb.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <ifaddrs.h>
39 #include "sm_inter.h"
40 #include "statd.h"
41 #include "notlist.h"
42 #include "log.h"
43 #include "ha-callout.h"
44
45 #define MAXMSGSIZE      (2048 / sizeof(unsigned int))
46
47 static unsigned long    xid = 0;        /* RPC XID counter */
48 static int              sockfd = -1;    /* notify socket */
49
50 /*
51  * Initialize callback socket
52  */
53 int
54 statd_get_socket(int port)
55 {
56         struct sockaddr_in      sin;
57
58         if (sockfd >= 0)
59                 return sockfd;
60
61         if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
62                 note(N_CRIT, "Can't create socket: %m");
63                 return -1;
64         }
65
66         FD_SET(sockfd, &SVC_FDSET);
67
68         memset(&sin, 0, sizeof(sin));
69         sin.sin_family = AF_INET;
70         sin.sin_addr.s_addr = INADDR_ANY;
71         /*
72          * If a local hostname is given (-n option to statd), bind to the address
73          * specified. This is required to support clients that ignore the mon_name in
74          * the statd protocol but use the source address from the request packet.
75          */
76         if (MY_NAME) {
77                 struct hostent *hp = gethostbyname(MY_NAME);
78                 if (hp)
79                         sin.sin_addr = *(struct in_addr *) hp->h_addr;
80         }
81         if (port != 0) {
82                 sin.sin_port = htons(port);
83                 if (bind(sockfd, &sin, sizeof(sin)) == 0)
84                         goto out_success;
85                 note(N_CRIT, "statd: failed to bind to outgoing port, %d\n"
86                                 "       falling back on randomly chosen port\n", port);
87         }
88         if (bindresvport(sockfd, &sin) < 0) {
89                 dprintf(N_WARNING,
90                         "process_hosts: can't bind to reserved port\n");
91         }
92 out_success:
93         return sockfd;
94 }
95 /*
96  * Using the NL_ADDR(lp), reset (if needed) the hostname
97  * that will be put in the SM_NOTIFY to the hostname
98  * that is associated with the network interface 
99  * that was monitored
100  */
101 static void
102 reset_my_name(notify_list *lp)
103 {
104         struct ifaddrs *ifa = NULL, *ifap;
105         struct in_addr netaddr, tmp;
106         struct sockaddr_in *sin, *nsin;
107         struct hostent *hp;
108
109         netaddr.s_addr = inet_netof(NL_ADDR(lp));
110         if (getifaddrs(&ifa) >= 0) {
111                 for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
112                         if (!(ifap->ifa_flags & IFF_UP))
113                                 continue;
114
115                         note(N_DEBUG, "ifa_name %s\n", ifap->ifa_name);
116                         if (ifap->ifa_addr == NULL)
117                                 continue;
118                         if (ifap->ifa_addr->sa_family != AF_INET)
119                                 continue;
120
121                         sin = (struct sockaddr_in *)ifap->ifa_addr;
122                         nsin = (struct sockaddr_in *)ifap->ifa_netmask;
123                         tmp.s_addr = sin->sin_addr.s_addr & nsin->sin_addr.s_addr;
124                         if (memcmp(&tmp.s_addr, &netaddr.s_addr, sizeof(netaddr.s_addr)))
125                                 continue;
126                         hp = gethostbyaddr((char *)&sin->sin_addr, 
127                                 sizeof(sin->sin_addr), AF_INET);
128                         if (hp == NULL)
129                                 continue;
130                         if (strcmp(NL_MY_NAME(lp), hp->h_name)) {
131                                 free(NL_MY_NAME(lp));
132                                 NL_MY_NAME(lp)= strdup(hp->h_name);
133                                 note(N_DEBUG, "NL_MY_NAME %s\n", NL_MY_NAME(lp));
134                         }
135                 }
136         }
137         return;
138 }
139 /*
140  * Try to resolve host name for notify/callback request
141  *
142  * When compiled with RESTRICTED_STATD defined, we expect all
143  * host names to be dotted quads. See monitor.c for details. --okir
144  */
145 #ifdef RESTRICTED_STATD
146 static int
147 try_to_resolve(notify_list *lp)
148 {
149         char            *hname;
150
151         if (NL_TYPE(lp) == NOTIFY_REBOOT)
152                 hname = NL_MON_NAME(lp);
153         else
154                 hname = NL_MY_NAME(lp);
155         if (!inet_aton(hname, &(NL_ADDR(lp)))) {
156                 note(N_ERROR, "%s is not an dotted-quad address", hname);
157                 NL_TIMES(lp) = 0;
158                 return 0;
159         }
160
161         /* XXX: In order to handle multi-homed hosts, we could do
162          * a reverse lookup, a forward lookup, and cycle through
163          * all the addresses.
164          */
165         return 1;
166 }
167 #else
168 static int
169 try_to_resolve(notify_list *lp)
170 {
171         struct hostent  *hp;
172         char            *hname;
173
174         if (NL_TYPE(lp) == NOTIFY_REBOOT)
175                 hname = NL_MON_NAME(lp);
176         else
177                 hname = NL_MY_NAME(lp);
178
179         dprintf(N_DEBUG, "Trying to resolve %s.", hname);
180         if (!(hp = gethostbyname(hname))) {
181                 herror("gethostbyname");
182                 NL_TIMES(lp) -= 1;
183                 return 0;
184         }
185
186         if (hp->h_addrtype != AF_INET) {
187                 note(N_ERROR, "%s is not an AF_INET address", hname);
188                 NL_TIMES(lp) = 0;
189                 return 0;
190         }
191
192         /* FIXME: should try all addresses for multi-homed hosts in
193          * alternation because one interface might be down/unreachable. */
194         NL_ADDR(lp) = *(struct in_addr *) hp->h_addr;
195
196         dprintf(N_DEBUG, "address of %s is %s", hname, inet_ntoa(NL_ADDR(lp)));
197         return 1;
198 }
199 #endif
200
201 static unsigned long
202 xmit_call(int sockfd, struct sockaddr_in *sin,
203           u_int32_t prog, u_int32_t vers, u_int32_t proc,
204           xdrproc_t func, void *obj)
205 /*              __u32 prog, __u32 vers, __u32 proc, xdrproc_t func, void *obj) */
206 {
207         unsigned int            msgbuf[MAXMSGSIZE], msglen;
208         struct rpc_msg          mesg;
209         struct pmap             pmap;
210         XDR                     xdr, *xdrs = &xdr;
211         int                     err;
212
213         if (!xid)
214                 xid = getpid() + time(NULL);
215
216         mesg.rm_xid = ++xid;
217         mesg.rm_direction = CALL;
218         mesg.rm_call.cb_rpcvers = 2;
219         if (sin->sin_port == 0) {
220                 sin->sin_port = htons(PMAPPORT);
221                 mesg.rm_call.cb_prog = PMAPPROG;
222                 mesg.rm_call.cb_vers = PMAPVERS;
223                 mesg.rm_call.cb_proc = PMAPPROC_GETPORT;
224                 pmap.pm_prog = prog;
225                 pmap.pm_vers = vers;
226                 pmap.pm_prot = IPPROTO_UDP;
227                 pmap.pm_port = 0;
228                 func = (xdrproc_t) xdr_pmap;
229                 obj  = &pmap;
230         } else {
231                 mesg.rm_call.cb_prog = prog;
232                 mesg.rm_call.cb_vers = vers;
233                 mesg.rm_call.cb_proc = proc;
234         }
235         mesg.rm_call.cb_cred.oa_flavor = AUTH_NULL;
236         mesg.rm_call.cb_cred.oa_base = (caddr_t) NULL;
237         mesg.rm_call.cb_cred.oa_length = 0;
238         mesg.rm_call.cb_verf.oa_flavor = AUTH_NULL;
239         mesg.rm_call.cb_verf.oa_base = (caddr_t) NULL;
240         mesg.rm_call.cb_verf.oa_length = 0;
241
242         /* Create XDR memory object for encoding */
243         xdrmem_create(xdrs, (caddr_t) msgbuf, sizeof(msgbuf), XDR_ENCODE);
244
245         /* Encode the RPC header part and payload */
246         if (!xdr_callmsg(xdrs, &mesg) || !func(xdrs, obj)) {
247                 dprintf(N_WARNING, "xmit_mesg: can't encode RPC message!\n");
248                 xdr_destroy(xdrs);
249                 return 0;
250         }
251
252         /* Get overall length of datagram */
253         msglen = xdr_getpos(xdrs);
254
255         if ((err = sendto(sockfd, msgbuf, msglen, 0,
256                         (struct sockaddr *) sin, sizeof(*sin))) < 0) {
257                 dprintf(N_WARNING, "xmit_mesg: sendto failed: %m");
258         } else if (err != msglen) {
259                 dprintf(N_WARNING, "xmit_mesg: short write: %m\n");
260         }
261
262         xdr_destroy(xdrs);
263
264         return err == msglen? xid : 0;
265 }
266
267 static notify_list *
268 recv_rply(int sockfd, struct sockaddr_in *sin, u_long *portp)
269 {
270         unsigned int            msgbuf[MAXMSGSIZE], msglen;
271         struct rpc_msg          mesg;
272         notify_list             *lp = NULL;
273         XDR                     xdr, *xdrs = &xdr;
274         int                     alen = sizeof(*sin);
275
276         /* Receive message */
277         if ((msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
278                         (struct sockaddr *) sin, &alen)) < 0) {
279                 dprintf(N_WARNING, "recv_rply: recvfrom failed: %m");
280                 return NULL;
281         }
282
283         /* Create XDR object for decoding buffer */
284         xdrmem_create(xdrs, (caddr_t) msgbuf, msglen, XDR_DECODE);
285
286         memset(&mesg, 0, sizeof(mesg));
287         mesg.rm_reply.rp_acpt.ar_results.where = NULL;
288         mesg.rm_reply.rp_acpt.ar_results.proc = (xdrproc_t) xdr_void;
289
290         if (!xdr_replymsg(xdrs, &mesg)) {
291                 note(N_WARNING, "recv_rply: can't decode RPC message!\n");
292                 goto done;
293         }
294
295         if (mesg.rm_reply.rp_stat != 0) {
296                 note(N_WARNING, "recv_rply: [%s] RPC status %d\n", 
297                                 inet_ntoa(sin->sin_addr),
298                                 mesg.rm_reply.rp_stat);
299                 goto done;
300         }
301         if (mesg.rm_reply.rp_acpt.ar_stat != 0) {
302                 note(N_WARNING, "recv_rply: [%s] RPC status %d\n",
303                                 inet_ntoa(sin->sin_addr),
304                                 mesg.rm_reply.rp_acpt.ar_stat);
305                 goto done;
306         }
307
308         for (lp = notify; lp != NULL; lp = lp->next) {
309                 /* LH - this was a bug... it should have been checking
310                  * the xid from the response message from the client,
311                  * not the static, internal xid */
312                 if (lp->xid != mesg.rm_xid)
313                         continue;
314                 if (lp->addr.s_addr != sin->sin_addr.s_addr) {
315                         char addr [18];
316                         strncpy (addr, inet_ntoa(lp->addr),
317                                  sizeof (addr) - 1);
318                         addr [sizeof (addr) - 1] = '\0';
319                         dprintf(N_WARNING, "address mismatch: "
320                                 "expected %s, got %s\n",
321                                 addr, inet_ntoa(sin->sin_addr));
322                 }
323                 if (lp->port == 0) {
324                         if (!xdr_u_long(xdrs, portp)) {
325                                 note(N_WARNING, "recv_rply: [%s] "
326                                         "can't decode reply body!\n",
327                                         inet_ntoa(sin->sin_addr));
328                                 lp = NULL;
329                                 goto done;
330                         }
331                 }
332                 break;
333         }
334
335 done:
336         xdr_destroy(xdrs);
337         return lp;
338 }
339
340 /*
341  * Notify operation for a single list entry
342  */
343 static int
344 process_entry(int sockfd, notify_list *lp)
345 {
346         struct sockaddr_in      sin;
347         struct status           new_status;
348         stat_chge               new_stat;
349         xdrproc_t               func;
350         void                    *objp;
351         u_int32_t               proc, vers, prog;
352 /*      __u32                   proc, vers, prog; */
353
354         if (lp->addr.s_addr == INADDR_ANY && !try_to_resolve(lp))
355                 return NL_TIMES(lp);
356         if (NL_TIMES(lp) == 0) {
357                 note(N_DEBUG, "Cannot notify %s, giving up.\n",
358                                         inet_ntoa(NL_ADDR(lp)));
359                 return 0;
360         }
361
362         memset(&sin, 0, sizeof(sin));
363         sin.sin_family = AF_INET;
364         sin.sin_port   = lp->port;
365         /* LH - moved address into switch */
366
367         switch (NL_TYPE(lp)) {
368         case NOTIFY_REBOOT:
369                 prog = SM_PROG;
370                 vers = SM_VERS;
371                 proc = SM_NOTIFY;
372
373                 /* Use source address for notify replies */
374                 sin.sin_addr   = lp->addr;
375                 /* 
376                  * Unless a static hostname has been defined
377                  * set the NL_MY_NAME(lp) hostname to the 
378                  * one associated with the network interface
379                  */
380                 if (!(run_mode & STATIC_HOSTNAME))
381                         reset_my_name(lp);
382
383                 func = (xdrproc_t) xdr_stat_chge;
384                 new_stat.state = MY_STATE;
385                 new_stat.mon_name = NL_MY_NAME(lp);
386
387                 objp = &new_stat;
388                 break;
389         case NOTIFY_CALLBACK:
390                 prog = NL_MY_PROG(lp);
391                 vers = NL_MY_VERS(lp);
392                 proc = NL_MY_PROC(lp);
393
394                 /* __FORCE__ loopback for callbacks to lockd ... */
395                 /* Just in case we somehow ignored it thus far */
396                 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
397
398                 func = (xdrproc_t) xdr_status;
399                 objp = &new_status;
400                 new_status.mon_name = NL_MON_NAME(lp);
401                 new_status.state    = NL_STATE(lp);
402                 memcpy(new_status.priv, NL_PRIV(lp), SM_PRIV_SIZE);
403                 break;
404         default:
405                 note(N_ERROR, "notify_host: unknown notify type %d",
406                                 NL_TYPE(lp));
407                 return 0;
408         }
409
410         lp->xid = xmit_call(sockfd, &sin, prog, vers, proc, func, objp);
411         if (!lp->xid) {
412                 note(N_WARNING, "notify_host: failed to notify %s\n",
413                                 inet_ntoa(lp->addr));
414         }
415         NL_TIMES(lp) -= 1;
416
417         return 1;
418 }
419
420 /*
421  * Process a datagram received on the notify socket
422  */
423 int
424 process_reply(FD_SET_TYPE *rfds)
425 {
426         struct sockaddr_in      sin;
427         notify_list             *lp;
428         u_long                  port;
429
430         if (sockfd == -1 || !FD_ISSET(sockfd, rfds))
431                 return 0;
432
433         if (!(lp = recv_rply(sockfd, &sin, &port)))
434                 return 1;
435
436         if (lp->port == 0) {
437                 if (port != 0) {
438                         lp->port = htons((unsigned short) port);
439                         process_entry(sockfd, lp);
440                         NL_WHEN(lp) = time(NULL) + NOTIFY_TIMEOUT;
441                         nlist_remove(&notify, lp);
442                         nlist_insert_timer(&notify, lp);
443                         return 1;
444                 }
445                 note(N_WARNING, "recv_rply: [%s] service %d not registered",
446                         inet_ntoa(lp->addr),
447                         NL_TYPE(lp) == NOTIFY_REBOOT? SM_PROG : NL_MY_PROG(lp));
448         } else if (NL_TYPE(lp) == NOTIFY_REBOOT) {
449                 dprintf(N_DEBUG, "Notification of %s succeeded.",
450                         NL_MON_NAME(lp));
451                 xunlink(SM_BAK_DIR, NL_MON_NAME(lp), 0);
452         } else {
453                 dprintf(N_DEBUG, "Callback to %s (for %d) succeeded.",
454                         NL_MY_NAME(lp), NL_MON_NAME(lp));
455         }
456         nlist_free(&notify, lp);
457         return 1;
458 }
459
460 /*
461  * Process a notify list, either for notifying remote hosts after reboot
462  * or for calling back (local) statd clients when the remote has notified
463  * us of a crash. 
464  */
465 int
466 process_notify_list(void)
467 {
468         notify_list     *entry;
469         time_t          now;
470         int             fd;
471
472         if ((fd = statd_get_socket(0)) < 0)
473                 return 0;
474
475         while ((entry = notify) != NULL && NL_WHEN(entry) < time(&now)) {
476                 if (process_entry(fd, entry)) {
477                         NL_WHEN(entry) = time(NULL) + NOTIFY_TIMEOUT;
478                         nlist_remove(&notify, entry);
479                         nlist_insert_timer(&notify, entry);
480                 } else if (NL_TYPE(entry) == NOTIFY_CALLBACK) {
481                         note(N_ERROR,
482                                 "Can't callback %s (%d,%d), giving up.",
483                                         NL_MY_NAME(entry),
484                                         NL_MY_PROG(entry),
485                                         NL_MY_VERS(entry));
486                         nlist_free(&notify, entry);
487                 } else {
488                         note(N_ERROR,
489                                 "Can't notify %s, giving up.",
490                                         NL_MON_NAME(entry));
491                         /* PRC: do the HA callout */
492                         ha_callout("del-client", NL_MON_NAME(entry), NL_MY_NAME(entry), -1);
493                         xunlink(SM_BAK_DIR, NL_MON_NAME(entry), 0);
494                         nlist_free(&notify, entry);
495                 }
496         }
497
498         return 1;
499 }