On Sun, Apr 20, 2008 at 8:02 PM, J. Bruce Fields <bfields@xxxxxxxxxxxx> wrote: > > > Right, that's what would make the most sense to me. Janne, is there any > > > reason that wouldn't solve your problem? > > > > I didn't get the idea. So the idea is to use multiple sockets, > > one bound to LOOPBACK and one to external interface? > > I suppose so. One socket would be for communication for the local > kernel nfsd, one for communication with statd peers. Finally got around to it again. Attached patch takes a shot at the two socket approach. Patch is a draft to see what you guys would really think about this approach. -- // Janne
diff -Naurp nfs-utils-1.1.2.orig/utils/statd/rmtcall.c nfs-utils-1.1.2/utils/statd/rmtcall.c --- nfs-utils-1.1.2.orig/utils/statd/rmtcall.c 2008-03-14 11:46:29.000000000 -0400 +++ nfs-utils-1.1.2/utils/statd/rmtcall.c 2008-04-28 16:16:02.000000000 -0400 @@ -54,35 +54,34 @@ static unsigned long xid = 0; /* RPC XID counter */ static int sockfd = -1; /* notify socket */ +static int nlmfd = -1; /* lockd socket */ /* - * Initialize callback socket + * Initialize callback socket and bind it to addr */ int -statd_get_socket(void) +statd_reserve_socket(struct in_addr *addr) { - struct sockaddr_in sin; + struct sockaddr_in sin; struct servent *se; int loopcnt = 100; + int fd = -1; - if (sockfd >= 0) - return sockfd; - - while (loopcnt-- > 0) { + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + memcpy(&sin.sin_addr, addr, sizeof(struct in_addr)); - if (sockfd >= 0) close(sockfd); + while (loopcnt-- > 0) + { + if (fd >= 0) + close(fd); - if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { note(N_CRIT, "Can't create socket: %m"); return -1; } - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = INADDR_ANY; - - if (bindresvport(sockfd, &sin) < 0) { + if (bindresvport(fd, &sin) < 0) { dprintf(N_WARNING, "process_hosts: can't bind to reserved port\n"); break; @@ -92,12 +91,75 @@ statd_get_socket(void) break; /* rather not use that port, try again */ } - FD_SET(sockfd, &SVC_FDSET); - return sockfd; + FD_SET(fd, &SVC_FDSET); + return fd; +} + +/* + * Check required sockets and init + */ +int +statd_init_sockets(void) +{ + struct hostent *hp = 0; + struct in_addr addr; + + if (MY_NAME) + hp = gethostbyname(MY_NAME); + + if (hp) { + memcpy (&addr,hp->h_addr,sizeof(struct in_addr)); + sockfd = statd_reserve_socket(&addr); + + addr.s_addr = htonl(INADDR_LOOPBACK); + nlmfd = statd_reserve_socket(&addr); + } else { + addr.s_addr = INADDR_ANY; + sockfd = statd_reserve_socket(&addr); + nlmfd = sockfd; + } + if (sockfd < 0 || nlmfd < 0) { + close(sockfd); + close(nlmfd); + sockfd = 0; + nlmfd = 0; + return -1; + } + return 1; +} + +/* + * Get socket to destination. Local communications should + * use specific socket bound to loopback if we're running + * with -n. + */ +int +statd_get_socket(struct in_addr *dest) +{ + struct in_addr addr; + int res; + + if (!dest) + return -1; + + addr.s_addr = ntohl(dest->s_addr); + + if ((nlmfd > 0) && (addr.s_addr == INADDR_LOOPBACK)) + return nlmfd; + + if ((sockfd > 0) && (addr.s_addr != INADDR_LOOPBACK)) + return sockfd; + + res = statd_init_sockets(); + if (res < 0) { + note(N_WARNING, "statd_get_socket: could not initilize sockets\n"); + return -1; + } + return (addr.s_addr == INADDR_LOOPBACK) ? nlmfd : sockfd; } static unsigned long -xmit_call(int sockfd, struct sockaddr_in *sin, +xmit_call(int fd, struct sockaddr_in *sin, u_int32_t prog, u_int32_t vers, u_int32_t proc, xdrproc_t func, void *obj) /* __u32 prog, __u32 vers, __u32 proc, xdrproc_t func, void *obj) */ @@ -150,7 +212,7 @@ xmit_call(int sockfd, struct sockaddr_in /* Get overall length of datagram */ msglen = xdr_getpos(xdrs); - if ((err = sendto(sockfd, msgbuf, msglen, 0, + if ((err = sendto(fd, msgbuf, msglen, 0, (struct sockaddr *) sin, sizeof(*sin))) < 0) { dprintf(N_WARNING, "xmit_mesg: sendto failed: %m"); } else if (err != msglen) { @@ -163,7 +225,7 @@ xmit_call(int sockfd, struct sockaddr_in } static notify_list * -recv_rply(int sockfd, struct sockaddr_in *sin, u_long *portp) +recv_rply(int fd, struct sockaddr_in *sin, u_long *portp) { unsigned int msgbuf[MAXMSGSIZE], msglen; struct rpc_msg mesg; @@ -172,7 +234,7 @@ recv_rply(int sockfd, struct sockaddr_in socklen_t alen = sizeof(*sin); /* Receive message */ - if ((msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0, + if ((msglen = recvfrom(fd, msgbuf, sizeof(msgbuf), 0, (struct sockaddr *) sin, &alen)) < 0) { dprintf(N_WARNING, "recv_rply: recvfrom failed: %m"); return NULL; @@ -239,7 +301,7 @@ done: * Notify operation for a single list entry */ static int -process_entry(int sockfd, notify_list *lp) +process_entry(int fd, notify_list *lp) { struct sockaddr_in sin; struct status new_status; @@ -273,7 +335,7 @@ process_entry(int sockfd, notify_list *l new_status.state = NL_STATE(lp); memcpy(new_status.priv, NL_PRIV(lp), SM_PRIV_SIZE); - lp->xid = xmit_call(sockfd, &sin, prog, vers, proc, func, objp); + lp->xid = xmit_call(fd, &sin, prog, vers, proc, func, objp); if (!lp->xid) { note(N_WARNING, "notify_host: failed to notify port %d\n", ntohs(lp->port)); @@ -283,26 +345,20 @@ process_entry(int sockfd, notify_list *l return 1; } -/* - * Process a datagram received on the notify socket - */ int -process_reply(FD_SET_TYPE *rfds) +process_sock(int fd) { - struct sockaddr_in sin; - notify_list *lp; - u_long port; + struct sockaddr_in sin; + notify_list *lp; + u_long port; - if (sockfd == -1 || !FD_ISSET(sockfd, rfds)) - return 0; - - if (!(lp = recv_rply(sockfd, &sin, &port))) + if (!(lp = recv_rply(fd, &sin, &port))) return 1; if (lp->port == 0) { if (port != 0) { lp->port = htons((unsigned short) port); - process_entry(sockfd, lp); + process_entry(fd, lp); NL_WHEN(lp) = time(NULL) + NOTIFY_TIMEOUT; nlist_remove(¬ify, lp); nlist_insert_timer(¬ify, lp); @@ -319,6 +375,23 @@ process_reply(FD_SET_TYPE *rfds) } /* + * Process a datagram received on the notify socket + */ +int +process_reply(FD_SET_TYPE *rfds) +{ + int ret1 = 0, ret2 = 0; + + if (FD_ISSET(sockfd,rfds)) { + ret1 = process_sock(sockfd); + } + if ((nlmfd != sockfd) && (FD_ISSET(nlmfd,rfds))) { + ret2 = process_sock(nlmfd); + } + return ret1+ret2; +} + +/* * Process a notify list, either for notifying remote hosts after reboot * or for calling back (local) statd clients when the remote has notified * us of a crash. @@ -330,10 +403,9 @@ process_notify_list(void) time_t now; int fd; - if ((fd = statd_get_socket()) < 0) - return 0; - while ((entry = notify) != NULL && NL_WHEN(entry) < time(&now)) { + fd = statd_get_socket(&entry->addr); + if (process_entry(fd, entry)) { NL_WHEN(entry) = time(NULL) + NOTIFY_TIMEOUT; nlist_remove(¬ify, entry); diff -Naurp nfs-utils-1.1.2.orig/utils/statd/statd.c nfs-utils-1.1.2/utils/statd/statd.c --- nfs-utils-1.1.2.orig/utils/statd/statd.c 2008-03-14 11:46:29.000000000 -0400 +++ nfs-utils-1.1.2/utils/statd/statd.c 2008-04-28 14:47:15.000000000 -0400 @@ -75,7 +75,7 @@ static struct option longopts[] = }; extern void sm_prog_1 (struct svc_req *, register SVCXPRT *); -extern int statd_get_socket(void); +extern int statd_init_sockets(void); static void load_state_number(void); #ifdef SIMULATIONS @@ -477,7 +477,7 @@ int main (int argc, char **argv) } /* Make sure we have a privilege port for calling into the kernel */ - statd_get_socket(); + statd_init_sockets(); /* If sm-notify didn't take all the state files, load * state information into our notify-list so we can