Index: usr.sbin/rwhod/rwhod.c =================================================================== --- usr.sbin/rwhod/rwhod.c +++ usr.sbin/rwhod/rwhod.c @@ -88,17 +88,36 @@ #define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */ + +/* + * Note that 224.0.1.3(ipv4) and FF0X::103(ipv6) are reserved for + * rwhod. In case of ipv6, X defines the scope. + * We use X=5 because 5 is for site-local scope. + */ + #define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */ /* (belongs in protocols/rwhod.h) */ +#define IN6ADDR_WHOD_GROUP_INIT \ + {{{ 0xff, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03 }}} /* ff05::103 */ + +const struct in6_addr in6addr_whod_group = IN6ADDR_WHOD_GROUP_INIT; + +int enable_v4 = 0; +int enable_v6 = 0; 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_in6 multicast_addr6 = + { sizeof(multicast_addr6), AF_INET6, 0, 0, {{{0}}}, 0 }; + /* * Sleep interval. Don't forget to change the down time check in ruptime * if this is changed. @@ -120,9 +139,9 @@ int n_flags; /* should forward?, interface flags */ }; -struct neighbor *neighbors; +struct neighbor *neighbors = NULL; struct whod mywd; -struct servent *sp; +in_port_t whod_port; int s; int fdp; pid_t pid_child_receiver; @@ -178,9 +197,9 @@ int main(int argc, char *argv[]) { - int on; - char *cp; - struct sockaddr_in soin; + int on, err, ch; + char *cp, *endptr; + struct addrinfo hints, *res; uid_t unpriv_uid; gid_t unpriv_gid; @@ -190,46 +209,77 @@ run_as(&unpriv_uid, &unpriv_gid); - argv++; - argc--; - while (argc > 0 && *argv[0] == '-') { - if (strcmp(*argv, "-m") == 0) { - if (argc > 1 && isdigit(*(argv + 1)[0])) { - argv++; - argc--; + while ((ch = getopt(argc, argv, "46ilpm::")) != -1) + switch (ch) { + case '4': + enable_v4 = 1; + break; + case '6': + enable_v6 = 1; + break; + case 'i': + insecure_mode = 1; + break; + case 'l': + quiet_mode = 1; + break; + case 'p': + iff_flag = 0; + break; + case 'm': + if (optarg != NULL) { multicast_mode = SCOPED_MULTICAST; - multicast_scope = atoi(*argv); - if (multicast_scope > MAX_MULTICAST_SCOPE) { + multicast_scope = (int)strtol(optarg, &endptr, 10); + if (*endptr != '\0') { + errx(1, "invalid ttl: %s", endptr); + } else if (multicast_scope > MAX_MULTICAST_SCOPE) { errx(1, "ttl must not exceed %u", MAX_MULTICAST_SCOPE); } } else { multicast_mode = PER_INTERFACE_MULTICAST; } - } else if (strcmp(*argv, "-i") == 0) { - insecure_mode = 1; - } else if (strcmp(*argv, "-l") == 0) { - quiet_mode = 1; - } else if (strcmp(*argv, "-p") == 0) { - iff_flag = 0; - } else { + break; + case '?': + default: usage(); } - argv++; - argc--; - } - if (argc > 0) - usage(); + + argc -= optind; + argv += optind; + + /* if neither -4 nor -6 was specified, enable both */ + if (enable_v4 == 0 && enable_v6 == 0) + enable_v4 = enable_v6 = 1; + #ifndef DEBUG daemon(1, 0); #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_protocol = IPPROTO_UDP; + if (enable_v6 == 1) + hints.ai_family = AF_INET6; + else + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + err = getaddrinfo(NULL, "who", &hints, &res); + if (err != 0) { + syslog(LOG_ERR, "who/udp: %s", gai_strerror(err)); exit(1); } + + if (res->ai_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr; + whod_port = htons(sin6->sin6_port); + } else if (res->ai_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)res->ai_addr; + whod_port = htons(sin->sin_port); + } + if (chdir(_PATH_RWHODIR) < 0) { syslog(LOG_ERR, "%s: %m", _PATH_RWHODIR); exit(1); @@ -245,22 +295,29 @@ *cp = '\0'; strlcpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname)); getboottime(0); - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + + if ((s = socket(res->ai_family, res->ai_socktype, 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); - } - 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) { + + /* + * ipv6 doesn't support broadcast, but maybe + * multicast at FF02::1 can be used, 2 being scope for link-local + */ + if (enable_v6 == 0) + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { + syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); + exit(1); + } + + if (bind(s, res->ai_addr, res->ai_addrlen) < 0) { syslog(LOG_ERR, "bind: %m"); exit(1); } + + freeaddrinfo(res); + if (setgid(unpriv_gid) != 0) { syslog(LOG_ERR, "setgid: %m"); exit(1); @@ -299,7 +356,7 @@ usage(void) { - fprintf(stderr, "usage: rwhod [-i] [-p] [-l] [-m [ttl]]\n"); + fprintf(stderr, "usage: rwhod [-4] [-6] [-i] [-p] [-l] [-m [ttl]]\n"); exit(1); } @@ -351,7 +408,7 @@ void receiver_process(void) { - struct sockaddr_in from; + struct sockaddr_storage from; struct stat st; cap_rights_t rights; char path[64]; @@ -360,6 +417,7 @@ socklen_t len; int cc, whod; time_t t; + char ip[INET6_ADDRSTRLEN]; len = sizeof(from); dirfd = open(".", O_RDONLY | O_DIRECTORY); @@ -385,14 +443,26 @@ 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)); - continue; + if (from.ss_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)&from; + if (sin->sin_port != whod_port && !insecure_mode) { + syslog(LOG_WARNING, "%d: bad source port from %s", + ntohs(sin->sin_port), + inet_ntop(from.ss_family, &(sin->sin_addr), ip, sizeof(ip))); + continue; + } + } else if (from.ss_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&from; + if (sin6->sin6_port != whod_port && !insecure_mode) { + syslog(LOG_WARNING, "%d: bad source port from %s", + ntohs(sin6->sin6_port), + inet_ntop(from.ss_family, &(sin6->sin6_addr), ip, sizeof(ip))); + continue; + } } + if (cc < WHDRSIZE) { - syslog(LOG_WARNING, "short packet from %s", - inet_ntoa(from.sin_addr)); + syslog(LOG_WARNING, "short packet from %s", ip); continue; } if (wd.wd_vers != WHODVERSION) @@ -400,8 +470,7 @@ if (wd.wd_type != WHODTYPE_STATUS) continue; if (!verify(wd.wd_hostname, sizeof(wd.wd_hostname))) { - syslog(LOG_WARNING, "malformed host name from %s", - inet_ntoa(from.sin_addr)); + syslog(LOG_WARNING, "malformed host name from %s", ip); continue; } (void) snprintf(path, sizeof(path), "whod.%s", wd.wd_hostname); @@ -600,30 +669,63 @@ 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 (enable_v4 == 1) { + multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP); + multicast_addr.sin_port = whod_port; + } + if (enable_v6 == 1) { + multicast_addr6.sin6_addr = in6addr_whod_group; + multicast_addr6.sin6_port = whod_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(so, IPPROTO_IP, IP_ADD_MEMBERSHIP, - &mreq, sizeof(mreq)) < 0) { - syslog(LOG_ERR, - "setsockopt IP_ADD_MEMBERSHIP: %m"); - return (0); + + if (enable_v4 == 1) { + struct ip_mreq mreq; + uint8_t 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); + } + 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); + if (enable_v6 == 1) { + struct ipv6_mreq mreq6; + uint8_t hops; + + mreq6.ipv6mr_multiaddr = in6addr_whod_group; + mreq6.ipv6mr_interface = 0; /* choose default multicast interface, same as above in ipv4*/ + + if (setsockopt(so, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq6, sizeof(mreq6)) < 0) { + syslog(LOG_ERR, + "setsockopt IPV6_ADD_MEMBERSHIP: %m"); + return (0); + } + hops = multicast_scope; + if (setsockopt(so, IPPROTO_IP, IPV6_MULTICAST_HOPS, &hops, + sizeof(hops)) < 0) { + syslog(LOG_ERR, + "setsockopt IPV6_MULTICAST_HOPS: %m"); + return (0); + } + return (1); } - return (1); + //assert(enable_ipv4 == 1 || enable_ipv6 == 1); } mib[0] = CTL_NET; @@ -668,7 +770,7 @@ #define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port if (dstaddr == 0 || dstaddr->sa_family != AF_INET) continue; - PORT_SA(dstaddr) = sp->s_port; + PORT_SA(dstaddr) = whod_port; for (np = neighbors; np != NULL; np = np->n_next) { if (memcmp(sdl->sdl_data, np->n_name, sdl->sdl_nlen) == 0 &&