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