diff --git a/usr.sbin/rwhod/Makefile b/usr.sbin/rwhod/Makefile --- a/usr.sbin/rwhod/Makefile +++ b/usr.sbin/rwhod/Makefile @@ -1,3 +1,4 @@ +.include PROG= rwhod MAN= rwhod.8 @@ -6,4 +7,11 @@ WARNS?= 3 +.if ${MK_INET_SUPPORT} != "no" +CFLAGS+= -DINET +.endif +.if ${MK_INET6_SUPPORT} != "no" +CFLAGS+= -DINET6 +.endif + .include diff --git a/usr.sbin/rwhod/rwhod.c b/usr.sbin/rwhod/rwhod.c --- a/usr.sbin/rwhod/rwhod.c +++ b/usr.sbin/rwhod/rwhod.c @@ -73,16 +73,21 @@ #define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */ +#ifdef INET #define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */ /* (belongs in protocols/rwhod.h) */ +#endif +#ifdef INET6 +#define IN6ADDR_WHOD_GROUP "ff02::103" /* link local address */ +#endif int insecure_mode; int quiet_mode; int iff_flag = IFF_POINTOPOINT; int multicast_mode = NO_MULTICAST; int multicast_scope; -struct sockaddr_in multicast_addr = - { sizeof(multicast_addr), AF_INET, 0, { 0 }, { 0 } }; +struct sockaddr_storage multicast_addr; +struct sockaddr *my_sa; /* * Sleep interval. Don't forget to change the down time check in ruptime @@ -107,7 +112,6 @@ struct neighbor *neighbors; struct whod mywd; -struct servent *sp; int s; int fdp; pid_t pid_child_receiver; @@ -163,9 +167,9 @@ int main(int argc, char *argv[]) { - int on; + int on, error, success; char *cp; - struct sockaddr_in soin; + struct addrinfo hints, *servinfo, *p; uid_t unpriv_uid; gid_t unpriv_gid; @@ -210,9 +214,13 @@ #endif (void) signal(SIGHUP, getboottime); openlog("rwhod", LOG_PID | LOG_NDELAY, LOG_DAEMON); - sp = getservbyname("who", "udp"); - if (sp == NULL) { - syslog(LOG_ERR, "who/udp: unknown service"); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(NULL, "who", &hints, &servinfo); + if (error != 0) { + syslog(LOG_ERR, "getaddrinfo: %s", gai_strerror(error)); exit(1); } if (chdir(_PATH_RWHODIR) < 0) { @@ -230,20 +238,50 @@ *cp = '\0'; strlcpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname)); getboottime(0); - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - syslog(LOG_ERR, "socket: %m"); - exit(1); - } - if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { - syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); - exit(1); + success = 0; + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((s = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) { + syslog(LOG_ERR, "socket: %m"); + continue; + } + switch (p->ai_family) { +#ifdef INET + case AF_INET: + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { + syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); + close(s); + continue; + } + break; +#endif +#ifdef INET6 + case AF_INET6: + if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &on, sizeof(on)) < 0) { + syslog(LOG_ERR, "setsockopt IPV6_MULTICAST_IF: %m"); + close(s); + continue; + } + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { + syslog(LOG_ERR, "setsockopt IPV6_V6ONLY: %m"); + close(s); + continue; + } + break; +#endif + default: + continue; + }; + if (bind(s, p->ai_addr, p->ai_addrlen) < 0) { + syslog(LOG_ERR, "bind: %m"); + close(s); + exit(1); + } + my_sa = p->ai_addr; + success = 1; + break; } - memset(&soin, 0, sizeof(soin)); - soin.sin_len = sizeof(soin); - soin.sin_family = AF_INET; - soin.sin_port = sp->s_port; - if (bind(s, (struct sockaddr *)&soin, sizeof(soin)) < 0) { - syslog(LOG_ERR, "bind: %m"); + if (success == 0) { + syslog(LOG_ERR, "No suitable address found for creating a socket"); exit(1); } if (setgid(unpriv_gid) != 0) { @@ -278,6 +316,7 @@ } else { receiver_process(); } + freeaddrinfo(servinfo); } static void @@ -336,14 +375,14 @@ void receiver_process(void) { - struct sockaddr_in from; + struct sockaddr from; struct stat st; cap_rights_t rights; - char path[64]; + char path[64], host[NI_MAXHOST], host_from[NI_MAXHOST], service[NI_MAXSERV], service_from[NI_MAXSERV]; int dirfd; struct whod wd; socklen_t len; - int cc, whod; + int cc, whod, error; time_t t; len = sizeof(from); @@ -362,22 +401,33 @@ syslog(LOG_ERR, "cap_enter: %m"); exit(1); } + error = getnameinfo(my_sa, my_sa->sa_len, host, NI_MAXHOST, service, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV); + if (error != 0) { + syslog(LOG_ERR, "getnameinfo: %s", gai_strerror(error)); + exit(1); + } for (;;) { - cc = recvfrom(s, &wd, sizeof(wd), 0, (struct sockaddr *)&from, + cc = recvfrom(s, &wd, sizeof(wd), 0, &from, &len); if (cc <= 0) { if (cc < 0 && errno != EINTR) syslog(LOG_WARNING, "recv: %m"); continue; } - if (from.sin_port != sp->s_port && !insecure_mode) { - syslog(LOG_WARNING, "%d: bad source port from %s", - ntohs(from.sin_port), inet_ntoa(from.sin_addr)); + error = getnameinfo(&from, len, host_from, NI_MAXHOST, service_from, + NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV); + if (error != 0) { + syslog(LOG_ERR, "getnameinfo: %s", gai_strerror(error)); + exit(1); + } + if (strcmp(service, service_from) != 0 && !insecure_mode) { + syslog(LOG_WARNING, "%s: bad source port from %s", + service_from, host_from); continue; } if (cc < WHDRSIZE) { syslog(LOG_WARNING, "short packet from %s", - inet_ntoa(from.sin_addr)); + host_from); continue; } if (wd.wd_vers != WHODVERSION) @@ -386,7 +436,7 @@ continue; if (!verify(wd.wd_hostname, sizeof(wd.wd_hostname))) { syslog(LOG_WARNING, "malformed host name from %s", - inet_ntoa(from.sin_addr)); + host_from); continue; } (void) snprintf(path, sizeof(path), "whod.%s", wd.wd_hostname); @@ -441,6 +491,7 @@ double avenrun[3]; time_t now; int i, cc, status; + unsigned int ifindex; struct utmpx *ut; struct stat stb; struct neighbor *np; @@ -497,13 +548,32 @@ * 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); + switch (np->n_addr->sa_family) { +#ifdef INET + case AF_INET: + 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); + } + break; +#endif +#ifdef INET6 + case AF_INET6: + ifindex = if_nametoindex(np->n_name); + if (setsockopt(s, IPPROTO_IPV6, + IPV6_MULTICAST_IF, + &ifindex, + sizeof(ifindex)) < 0) { + syslog(LOG_ERR, + "setsockopt IPV6_MULTICAST_IF: %m"); + exit(1); + } + break; +#endif } (void) sendto(s, (char *)&mywd, cc, 0, (struct sockaddr *)&multicast_addr, @@ -574,47 +644,87 @@ int configure(int so) { + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + struct ip_mreq mreq; + struct ipv6_mreq mreq6; struct neighbor *np; struct if_msghdr *ifm; struct ifa_msghdr *ifam; struct sockaddr_dl *sdl; size_t needed; - int mib[6], flags, lflags, len; - char *buf, *lim, *next; + int mib[6], flags, lflags, len, error; + char *buf, *lim, *next, host_dst[NI_MAXHOST], host_np[NI_MAXHOST]; struct rt_addrinfo info; flags = 0; - 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; + switch (my_sa->sa_family) { +#ifdef INET + case AF_INET: + if (multicast_mode != NO_MULTICAST) { + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP); + sin.sin_port = ((struct sockaddr_in *)(my_sa))->sin_port; + memcpy(&multicast_addr, (struct sockaddr *)&sin, sizeof(multicast_addr)); + } + if (multicast_mode == SCOPED_MULTICAST) { + unsigned char ttl; - mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - if (setsockopt(so, IPPROTO_IP, IP_ADD_MEMBERSHIP, - &mreq, sizeof(mreq)) < 0) { - syslog(LOG_ERR, - "setsockopt IP_ADD_MEMBERSHIP: %m"); - return (0); + mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt(so, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) < 0) { + syslog(LOG_ERR, + "setsockopt IP_ADD_MEMBERSHIP: %m"); + return (0); + } + ttl = multicast_scope; + if (setsockopt(so, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, + sizeof(ttl)) < 0) { + syslog(LOG_ERR, + "setsockopt IP_MULTICAST_TTL: %m"); + return (0); + } + return (1); } - ttl = multicast_scope; - if (setsockopt(so, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, - sizeof(ttl)) < 0) { - syslog(LOG_ERR, - "setsockopt IP_MULTICAST_TTL: %m"); - return (0); + break; +#endif +#ifdef INET6 + case AF_INET6: + if (multicast_mode != NO_MULTICAST) { + sin6.sin6_family = AF_INET6; + inet_pton(AF_INET6, IN6ADDR_WHOD_GROUP, &(sin6.sin6_addr)); + sin6.sin6_port = ((struct sockaddr_in6 *)(my_sa))->sin6_port; + memcpy(&multicast_addr, (struct sockaddr *)&sin6, sizeof(multicast_addr)); + } + if (multicast_mode == SCOPED_MULTICAST) { + int ttl; + + inet_pton(AF_INET6, IN6ADDR_WHOD_GROUP, &mreq6.ipv6mr_multiaddr); + mreq6.ipv6mr_interface = 0; + if (setsockopt(so, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, + sizeof(mreq6)) < 0) { + syslog(LOG_ERR, + "setsockopt IPV6_JOIN_GROUP: %m"); + return (0); + } + ttl = multicast_scope; + if (setsockopt(so, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + &ttl, sizeof(ttl)) < 0) { + syslog(LOG_ERR, + "setsockopt IPV6_MULTICAST_HOPS: %m"); + return (0); + } + return (1); } - return (1); + break; +#endif } mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; - mib[3] = AF_INET; + mib[3] = my_sa->sa_family; mib[4] = NET_RT_IFLIST; mib[5] = 0; if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0) @@ -649,15 +759,36 @@ /* 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) +#define PORT_SA6(x) ((struct sockaddr_in6 *)(x))->sin6_port + if (dstaddr == 0 || dstaddr->sa_family != my_sa->sa_family) continue; - PORT_SA(dstaddr) = sp->s_port; + switch (dstaddr->sa_family) { +#ifdef INET + case AF_INET: + PORT_SA(dstaddr) = PORT_SA(my_sa); + break; +#endif +#ifdef INET6 + case AF_INET6: + PORT_SA6(dstaddr) = PORT_SA6(my_sa); + break; +#endif + } + error = getnameinfo(dstaddr, dstaddr->sa_len, host_dst, NI_MAXHOST, NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); + if (error != 0) { + syslog(LOG_ERR, "getnameinfo: %s", gai_strerror(error)); + exit(1); + } for (np = neighbors; np != NULL; np = np->n_next) { + error = getnameinfo(np->n_addr, np->n_addr->sa_len, host_np, NI_MAXHOST, NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); + if (error != 0) { + syslog(LOG_ERR, "getnameinfo: %s", gai_strerror(error)); + exit(1); + } if (memcmp(sdl->sdl_data, np->n_name, sdl->sdl_nlen) == 0 && - IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr)) { + strcmp(host_np, host_dst) == 0) { break; } } @@ -677,23 +808,54 @@ if (multicast_mode == PER_INTERFACE_MULTICAST && (flags & IFF_MULTICAST) != 0 && (flags & IFF_LOOPBACK) == 0) { - 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"); + switch (my_sa->sa_family) { +#ifdef INET + case AF_INET: { + 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(np); - continue; + free(np); + continue; +#endif + } + break; + } +#endif +#ifdef INET6 + case AF_INET6: { + memcpy((char *)np->n_addr, (char *)ifaddr, + np->n_addrlen); + + inet_pton(AF_INET6, IN6ADDR_WHOD_GROUP, + &mreq6.ipv6mr_multiaddr); + mreq6.ipv6mr_interface = 0; + if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, + (const char *)&mreq6, + sizeof(mreq6)) < 0) { + syslog(LOG_ERR, + "setsockopt IPV6_JOIN_GROUP: %m"); +#if 0 + /* Fall back to broadcast on this if. */ + np->n_flags &= ~IFF_MULTICAST; +#else + free(np); + continue; +#endif + } + break; + } #endif } }