From 84f8341ef7643d94d6e9d02855c3fb6b10fde2cc Mon Sep 17 00:00:00 2001 From: "Jordan K. Hubbard" Date: Thu, 17 Aug 1995 00:51:40 +0000 Subject: [PATCH] Here are patches to add full multicast support to rwhod, and an updated man page. I tried all three modes (rwhod, rwhod -m, rwhod -m 32) on a machine with 2 ethernet interfaces and they all worked. Submitted by: Bill Fenner --- usr.sbin/rwhod/rwhod.8 | 44 ++++++++++- usr.sbin/rwhod/rwhod.c | 170 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 197 insertions(+), 17 deletions(-) diff --git a/usr.sbin/rwhod/rwhod.8 b/usr.sbin/rwhod/rwhod.8 index 2922bccf52f6..fc4b6283b1d5 100644 --- a/usr.sbin/rwhod/rwhod.8 +++ b/usr.sbin/rwhod/rwhod.8 @@ -39,6 +39,7 @@ .Nd system status server .Sh SYNOPSIS .Nm rwhod +.Op Fl m Op Ar ttl .Sh DESCRIPTION .Nm Rwhod is the server which maintains the database used by the @@ -47,19 +48,59 @@ and .Xr ruptime 1 programs. Its operation is predicated on the ability to .Em broadcast +or +.Em multicast messages on a network. .Pp .Nm Rwhod operates as both a producer and consumer of status information. As a producer of information it periodically queries the state of the system and constructs -status messages which are broadcast on a network. +status messages which are broadcasted or multicasted on a network. As a consumer of information, it listens for other .Nm rwhod servers' status messages, validating them, then recording them in a collection of files located in the directory .Pa /var/rwho . .Pp +The +.Fl m +option causes rwhod to use IP multicast (instead of +broadcast) on all interfaces that have +the IFF_MULTICAST flag set in their "ifnet" structs +(excluding the loopback interface). The multicast +reports are sent with a time-to-live of 1, to prevent +forwarding beyond the directly-connected subnet(s). +.Pp +If the optional +.Ar ttl +argument is supplied with the +.Fl m +flag, rwhod will send IP multicast datagrams with a +time-to-live of , via a SINGLE interface rather +than all interfaces. must be between 0 and +32 (or MAX_MULTICAST_SCOPE). Note that +.Fl m Ar 1 +is different than +.Fl m , +in that +.Fl m Ar 1 +specifies transmission on one interface only. +.Pp +When +.Fl m +is used without a +.Ar ttl +argument, the program accepts multicast +rwhod reports from all multicast-capable interfaces. If a +.Ar ttl +argument is given, it accepts multicast reports from only one interface, the +one on which reports are sent (which may be controlled via the host's routing +table). Regardless of the "-m" option, the program accepts broadcast or +unicast reports from all interfaces. Thus, this program will hear the +reports of old, non-multicasting rwhods, but, if multicasting is used, +those old rwhods won't hear the reports generated by this program. +.Pp The server transmits and receives messages at the port indicated in the ``rwho'' service specification; see .Xr services 5 . @@ -134,7 +175,6 @@ image currently operating. .Xr rwho 1 , .Xr ruptime 1 .Sh BUGS -There should be a way to relay status information between networks. Status information should be sent only upon request rather than continuously. People often interpret the server dying or network communication failures diff --git a/usr.sbin/rwhod/rwhod.c b/usr.sbin/rwhod/rwhod.c index 75c02ff0ef0c..66ead27ff5ea 100644 --- a/usr.sbin/rwhod/rwhod.c +++ b/usr.sbin/rwhod/rwhod.c @@ -66,6 +66,49 @@ static char sccsid[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93"; #include #include +/* + * This version of Berkeley's rwhod has been modified to use IP multicast + * datagrams, under control of a new command-line option: + * + * rwhod -m causes rwhod to use IP multicast (instead of + * broadcast or unicast) on all interfaces that have + * the IFF_MULTICAST flag set in their "ifnet" structs + * (excluding the loopback interface). The multicast + * reports are sent with a time-to-live of 1, to prevent + * forwarding beyond the directly-connected subnet(s). + * + * rwhod -m causes rwhod to send IP multicast datagrams with a + * time-to-live of , via a SINGLE interface rather + * than all interfaces. must be between 0 and + * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1" + * is different than "-m", in that "-m 1" specifies + * transmission on one interface only. + * + * When "-m" is used without a argument, the program accepts multicast + * rwhod reports from all multicast-capable interfaces. If a argument + * is given, it accepts multicast reports from only one interface, the one + * on which reports are sent (which may be controlled via the host's routing + * table). Regardless of the "-m" option, the program accepts broadcast or + * unicast reports from all interfaces. Thus, this program will hear the + * reports of old, non-multicasting rwhods, but, if multicasting is used, + * those old rwhods won't hear the reports generated by this program. + * + * -- Steve Deering, Stanford University, February 1989 + */ + +#define NO_MULTICAST 0 /* multicast modes */ +#define PER_INTERFACE_MULTICAST 1 +#define SCOPED_MULTICAST 2 + +#define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */ + +#define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */ + /* (belongs in protocols/rwhod.h) */ + +int multicast_mode = NO_MULTICAST; +int multicast_scope; +struct sockaddr_in multicast_addr = { sizeof multicast_addr, AF_INET }; + /* * Alarm interval. Don't forget to change the down time check in ruptime * if this is changed. @@ -109,7 +152,7 @@ void Sendto __P((int, char *, int, int, char *, int)); int main(argc, argv) int argc; - char argv[]; + char *argv[]; { struct sockaddr_in from; struct stat st; @@ -122,21 +165,43 @@ main(argc, argv) fprintf(stderr, "rwhod: not super user\n"); exit(1); } - sp = getservbyname("who", "udp"); - if (sp == NULL) { - fprintf(stderr, "rwhod: udp/who: unknown service\n"); + argv++; argc--; + while (argc > 0 && *argv[0] == '-') { + if (strcmp(*argv, "-m") == 0) { + if (argc > 1 && isdigit(*(argv + 1)[0])) { + argv++, argc--; + multicast_mode = SCOPED_MULTICAST; + multicast_scope = atoi(*argv); + if (multicast_scope > MAX_MULTICAST_SCOPE) { + fprintf(stderr, + "rwhod: ttl must not exceed %u\n", + MAX_MULTICAST_SCOPE); + exit(1); + } + } + else multicast_mode = PER_INTERFACE_MULTICAST; + } + else goto usage; + argv++, argc--; + } + if (argc > 0) { +usage: fprintf(stderr, "usage: rwhod [ -m [ ttl ] ]\n"); exit(1); } #ifndef DEBUG daemon(1, 0); #endif - if (chdir(_PATH_RWHODIR) < 0) { - (void)fprintf(stderr, "rwhod: %s: %s\n", - _PATH_RWHODIR, strerror(errno)); - exit(1); - } (void) signal(SIGHUP, getboottime); openlog("rwhod", LOG_PID, LOG_DAEMON); + sp = getservbyname("who", "udp"); + if (sp == NULL) { + syslog(LOG_ERR, "rwhod: udp/who: unknown service\n"); + exit(1); + } + if (chdir(_PATH_RWHODIR) < 0) { + syslog(LOG_ERR, "rwhod: %s: %m\n", _PATH_RWHODIR); + exit(1); + } /* * Establish host name as returned by system. */ @@ -162,6 +227,7 @@ main(argc, argv) exit(1); } memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; sin.sin_port = sp->s_port; if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { @@ -335,9 +401,30 @@ onalrm(signo) mywd.wd_sendtime = htonl(time(0)); mywd.wd_vers = WHODVERSION; mywd.wd_type = WHODTYPE_STATUS; - for (np = neighbors; np != NULL; np = np->n_next) - (void)sendto(s, (char *)&mywd, cc, 0, - np->n_addr, np->n_addrlen); + if (multicast_mode == SCOPED_MULTICAST) { + (void) sendto(s, (char *)&mywd, cc, 0, + (struct sockaddr *)&multicast_addr, + sizeof(multicast_addr)); + } + else for (np = neighbors; np != NULL; np = np->n_next) { + if (multicast_mode == PER_INTERFACE_MULTICAST && + np->n_flags & IFF_MULTICAST) { + /* + * Select the outgoing interface for the multicast. + */ + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, + &(((struct sockaddr_in *)np->n_addr)->sin_addr), + sizeof(struct in_addr)) < 0) { + syslog(LOG_ERR, + "setsockopt IP_MULTICAST_IF: %m"); + exit(1); + } + (void) sendto(s, (char *)&mywd, cc, 0, + (struct sockaddr *)&multicast_addr, + sizeof(multicast_addr)); + } else (void) sendto(s, (char *)&mywd, cc, 0, + np->n_addr, np->n_addrlen); + } if (utmpent && chdir(_PATH_RWHODIR)) { syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR); exit(1); @@ -410,6 +497,33 @@ configure(s) char *buf, *lim, *next; struct rt_addrinfo info; + if (multicast_mode != NO_MULTICAST) { + multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP); + multicast_addr.sin_port = sp->s_port; + } + + if (multicast_mode == SCOPED_MULTICAST) { + struct ip_mreq mreq; + unsigned char ttl; + + mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + syslog(LOG_ERR, + "setsockopt IP_ADD_MEMBERSHIP: %m"); + return(0); + } + ttl = multicast_scope; + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, + &ttl, sizeof(ttl)) < 0) { + syslog(LOG_ERR, + "setsockopt IP_MULTICAST_TTL: %m"); + return(0); + } + return(1); + } + mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; @@ -433,7 +547,9 @@ configure(s) continue; } if ((flags & IFF_UP) == 0 || - (flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) + (flags & (((multicast_mode == PER_INTERFACE_MULTICAST) ? + IFF_MULTICAST : 0) | + IFF_BROADCAST|IFF_POINTOPOINT)) == 0) continue; if (ifm->ifm_type != RTM_NEWADDR) quit("out of sync parsing NET_RT_IFLIST"); @@ -443,6 +559,7 @@ configure(s) &info); /* gag, wish we could get rid of Internet dependencies */ #define dstaddr info.rti_info[RTAX_BRD] +#define ifaddr info.rti_info[RTAX_IFA] #define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr #define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port if (dstaddr == 0 || dstaddr->sa_family != AF_INET) @@ -464,10 +581,33 @@ configure(s) np->n_addr = (struct sockaddr *)(np + 1); np->n_addrlen = dstaddr->sa_len; np->n_name = np->n_addrlen + (char *)np->n_addr; - np->n_next = neighbors; - neighbors = np; memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen); memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen); + if (multicast_mode == PER_INTERFACE_MULTICAST && + (flags & IFF_MULTICAST) && + !(flags & IFF_LOOPBACK)) { + struct ip_mreq mreq; + + memcpy((char *)np->n_addr, (char *)ifaddr, + np->n_addrlen); + mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); + mreq.imr_interface.s_addr = + ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr; + if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + syslog(LOG_ERR, + "setsockopt IP_ADD_MEMBERSHIP: %m"); +#if 0 + /* Fall back to broadcast on this if. */ + np->n_flags &= ~IFF_MULTICAST; +#else + free((char *)np); + continue; +#endif + } + } + np->n_next = neighbors; + neighbors = np; } free(buf); return (1);