Index: usr.sbin/rwhod/rwhod.c =================================================================== --- usr.sbin/rwhod/rwhod.c +++ usr.sbin/rwhod/rwhod.c @@ -104,15 +104,20 @@ 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. @@ -134,9 +139,9 @@ int n_flags; /* should forward?, interface flags */ }; -struct neighbor *neighbors; +struct neighbor *neighbors = NULL; struct whod mywd; -in_port_t who_port; +in_port_t whod_port; int s; int fdp; pid_t pid_child_receiver; @@ -192,8 +197,8 @@ int main(int argc, char *argv[]) { - int on, err; - char *cp; + int on, err, ch; + char *cp, *endptr; struct addrinfo hints, *res; uid_t unpriv_uid; gid_t unpriv_gid; @@ -204,36 +209,49 @@ 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 @@ -242,22 +260,24 @@ memset(&hints, 0, sizeof(hints)); hints.ai_protocol = IPPROTO_UDP; - hints.ai_family = AF_UNSPEC; + 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) { + if (err != 0) { syslog(LOG_ERR, "who/udp: %s", gai_strerror(err)); exit(1); } - if (res->ai_family == AF_INET) { - struct sockaddr_in *sin = (struct sockaddr_in *)res->ai_addr; - who_port = htons(sin->sin_port); - } - else if (res->ai_family == AF_INET6) { + if (res->ai_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr; - who_port = htons(sin6->sin6_port); + 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) { @@ -280,16 +300,24 @@ 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); - } - + + /* + * 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); @@ -328,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); } @@ -380,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]; @@ -389,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); @@ -414,14 +443,26 @@ syslog(LOG_WARNING, "recv: %m"); continue; } - if (from.sin_port != who_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) @@ -429,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); @@ -629,30 +669,63 @@ flags = 0; if (multicast_mode != NO_MULTICAST) { - multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP); - multicast_addr.sin_port = who_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; @@ -697,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) = who_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 &&