mld6.c revision 216586
1130561Sobrien/* $KAME: mld6.c,v 1.15 2003/04/02 11:29:54 suz Exp $ */ 2130561Sobrien 3130561Sobrien/* 4130561Sobrien * Copyright (C) 1998 WIDE Project. 5130561Sobrien * All rights reserved. 6130561Sobrien * 7130561Sobrien * Redistribution and use in source and binary forms, with or without 8130561Sobrien * modification, are permitted provided that the following conditions 9130561Sobrien * are met: 10130561Sobrien * 1. Redistributions of source code must retain the above copyright 11130561Sobrien * notice, this list of conditions and the following disclaimer. 12130561Sobrien * 2. Redistributions in binary form must reproduce the above copyright 13130561Sobrien * notice, this list of conditions and the following disclaimer in the 14130561Sobrien * documentation and/or other materials provided with the distribution. 15130561Sobrien * 3. Neither the name of the project nor the names of its contributors 16130561Sobrien * may be used to endorse or promote products derived from this software 17130561Sobrien * without specific prior written permission. 18130561Sobrien * 19130561Sobrien * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20130561Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21130561Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22130561Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23130561Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24130561Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25130561Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26130561Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27130561Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28130561Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29130561Sobrien * SUCH DAMAGE. 30130561Sobrien */ 31130561Sobrien 32130561Sobrien#include <sys/cdefs.h> 33130561Sobrien__FBSDID("$FreeBSD: head/usr.sbin/mld6query/mld6.c 216586 2010-12-20 08:54:30Z charnier $"); 34130561Sobrien 35130561Sobrien#include <sys/param.h> 36130561Sobrien#include <sys/uio.h> 37130561Sobrien#include <sys/socket.h> 38130561Sobrien#include <sys/types.h> 39130561Sobrien#include <sys/time.h> 40130561Sobrien#include <ifaddrs.h> 41130561Sobrien#include <unistd.h> 42130561Sobrien#include <signal.h> 43130561Sobrien 44130561Sobrien#include <net/if.h> 45130561Sobrien#include <net/if_var.h> 46130561Sobrien 47130561Sobrien#include <netinet/in.h> 48130561Sobrien#include <netinet/ip6.h> 49130561Sobrien#include <netinet/icmp6.h> 50130561Sobrien 51130561Sobrien#include <arpa/inet.h> 52130561Sobrien 53130561Sobrien#include <stdlib.h> 54130561Sobrien#include <stdio.h> 55130561Sobrien#include <string.h> 56130561Sobrien#include <err.h> 57130561Sobrien 58130561Sobrien/* portability with older KAME headers */ 59130561Sobrien#ifndef MLD_LISTENER_QUERY 60130561Sobrien#define MLD_LISTENER_QUERY MLD6_LISTENER_QUERY 61130561Sobrien#define MLD_LISTENER_REPORT MLD6_LISTENER_REPORT 62130561Sobrien#define MLD_LISTENER_DONE MLD6_LISTENER_DONE 63130561Sobrien#define MLD_MTRACE_RESP MLD6_MTRACE_RESP 64130561Sobrien#define MLD_MTRACE MLD6_MTRACE 65130561Sobrien#define mld_hdr mld6_hdr 66130561Sobrien#define mld_type mld6_type 67130561Sobrien#define mld_code mld6_code 68130561Sobrien#define mld_cksum mld6_cksum 69130561Sobrien#define mld_maxdelay mld6_maxdelay 70130561Sobrien#define mld_reserved mld6_reserved 71130561Sobrien#define mld_addr mld6_addr 72130561Sobrien#endif 73130561Sobrien#ifndef IP6OPT_ROUTER_ALERT 74130561Sobrien#define IP6OPT_ROUTER_ALERT IP6OPT_RTALERT 75130561Sobrien#endif 76130561Sobrien 77130561Sobrienstruct msghdr m; 78130561Sobrienstruct sockaddr_in6 dst; 79130561Sobrienstruct mld_hdr mldh; 80130561Sobrienstruct in6_addr maddr = IN6ADDR_ANY_INIT, any = IN6ADDR_ANY_INIT; 81130561Sobrienstruct ipv6_mreq mreq; 82130561Sobrienu_short ifindex; 83130561Sobrienint s; 84130561Sobrien 85130561Sobrien#define QUERY_RESPONSE_INTERVAL 10000 86130561Sobrien 87130561Sobrienvoid make_msg(int index, struct in6_addr *addr, u_int type); 88130561Sobrienvoid usage(void); 89130561Sobrienvoid dump(int); 90130561Sobrienvoid quit(int); 91130561Sobrien 92130561Sobrienint 93130561Sobrienmain(int argc, char *argv[]) 94130561Sobrien{ 95130561Sobrien int i; 96130561Sobrien struct icmp6_filter filt; 97130561Sobrien u_int hlim = 1; 98130561Sobrien fd_set fdset; 99130561Sobrien struct itimerval itimer; 100130561Sobrien u_int type; 101130561Sobrien int ch; 102130561Sobrien 103130561Sobrien type = MLD_LISTENER_QUERY; 104130561Sobrien while ((ch = getopt(argc, argv, "dr")) != -1) { 105130561Sobrien switch (ch) { 106130561Sobrien case 'd': 107130561Sobrien type = MLD_LISTENER_DONE; 108130561Sobrien break; 109130561Sobrien case 'r': 110130561Sobrien type = MLD_LISTENER_REPORT; 111130561Sobrien break; 112130561Sobrien default: 113130561Sobrien usage(); 114130561Sobrien /*NOTREACHED*/ 115130561Sobrien } 116130561Sobrien } 117130561Sobrien 118130561Sobrien argv += optind; 119130561Sobrien argc -= optind; 120130561Sobrien 121130561Sobrien if (argc != 1 && argc != 2) 122130561Sobrien usage(); 123130561Sobrien 124130561Sobrien ifindex = (u_short)if_nametoindex(argv[0]); 125130561Sobrien if (ifindex == 0) 126130561Sobrien usage(); 127130561Sobrien if (argc == 2 && inet_pton(AF_INET6, argv[1], &maddr) != 1) 128130561Sobrien usage(); 129130561Sobrien 130130561Sobrien if ((s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) 131130561Sobrien err(1, "socket"); 132130561Sobrien 133130561Sobrien if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hlim, 134130561Sobrien sizeof(hlim)) == -1) 135130561Sobrien err(1, "setsockopt(IPV6_MULTICAST_HOPS)"); 136130561Sobrien 137130561Sobrien mreq.ipv6mr_multiaddr = any; 138130561Sobrien mreq.ipv6mr_interface = ifindex; 139130561Sobrien if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, 140130561Sobrien sizeof(mreq)) == -1) 141130561Sobrien err(1, "setsockopt(IPV6_JOIN_GROUP)"); 142130561Sobrien 143130561Sobrien ICMP6_FILTER_SETBLOCKALL(&filt); 144130561Sobrien ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_QUERY, &filt); 145130561Sobrien ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REPORT, &filt); 146130561Sobrien ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REDUCTION, &filt); 147130561Sobrien if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 148130561Sobrien sizeof(filt)) < 0) 149130561Sobrien err(1, "setsockopt(ICMP6_FILTER)"); 150130561Sobrien 151130561Sobrien make_msg(ifindex, &maddr, type); 152130561Sobrien 153130561Sobrien if (sendmsg(s, &m, 0) < 0) 154130561Sobrien err(1, "sendmsg"); 155130561Sobrien 156130561Sobrien itimer.it_value.tv_sec = QUERY_RESPONSE_INTERVAL / 1000; 157130561Sobrien itimer.it_interval.tv_sec = 0; 158130561Sobrien itimer.it_interval.tv_usec = 0; 159130561Sobrien itimer.it_value.tv_usec = 0; 160130561Sobrien 161130561Sobrien (void)signal(SIGALRM, quit); 162130561Sobrien (void)setitimer(ITIMER_REAL, &itimer, NULL); 163130561Sobrien 164130561Sobrien FD_ZERO(&fdset); 165130561Sobrien if (s >= FD_SETSIZE) 166130561Sobrien errx(1, "descriptor too big"); 167130561Sobrien for (;;) { 168130561Sobrien FD_SET(s, &fdset); 169130561Sobrien if ((i = select(s + 1, &fdset, NULL, NULL, NULL)) < 0) 170130561Sobrien perror("select"); 171130561Sobrien if (i == 0) 172130561Sobrien continue; 173130561Sobrien else 174130561Sobrien dump(s); 175130561Sobrien } 176130561Sobrien} 177130561Sobrien 178130561Sobrienvoid 179130561Sobrienmake_msg(int index, struct in6_addr *addr, u_int type) 180130561Sobrien{ 181130561Sobrien static struct iovec iov[2]; 182130561Sobrien static u_char *cmsgbuf; 183130561Sobrien int cmsglen, hbhlen = 0; 184130561Sobrien#ifdef USE_RFC2292BIS 185130561Sobrien void *hbhbuf = NULL, *optp = NULL; 186130561Sobrien int currentlen; 187130561Sobrien#else 188130561Sobrien u_int8_t raopt[IP6OPT_RTALERT_LEN]; 189130561Sobrien#endif 190130561Sobrien struct in6_pktinfo *pi; 191130561Sobrien struct cmsghdr *cmsgp; 192130561Sobrien u_short rtalert_code = htons(IP6OPT_RTALERT_MLD); 193130561Sobrien struct ifaddrs *ifa, *ifap; 194130561Sobrien struct in6_addr src; 195130561Sobrien 196130561Sobrien dst.sin6_len = sizeof(dst); 197130561Sobrien dst.sin6_family = AF_INET6; 198130561Sobrien if (IN6_IS_ADDR_UNSPECIFIED(addr)) { 199130561Sobrien if (inet_pton(AF_INET6, "ff02::1", &dst.sin6_addr) != 1) 200130561Sobrien errx(1, "inet_pton failed"); 201130561Sobrien } 202130561Sobrien else 203130561Sobrien dst.sin6_addr = *addr; 204130561Sobrien m.msg_name = (caddr_t)&dst; 205130561Sobrien m.msg_namelen = dst.sin6_len; 206130561Sobrien iov[0].iov_base = (caddr_t)&mldh; 207130561Sobrien iov[0].iov_len = sizeof(mldh); 208130561Sobrien m.msg_iov = iov; 209130561Sobrien m.msg_iovlen = 1; 210130561Sobrien 211130561Sobrien bzero(&mldh, sizeof(mldh)); 212130561Sobrien mldh.mld_type = type & 0xff; 213130561Sobrien mldh.mld_maxdelay = htons(QUERY_RESPONSE_INTERVAL); 214130561Sobrien mldh.mld_addr = *addr; 215130561Sobrien 216130561Sobrien /* MLD packet should be advertised from linklocal address */ 217130561Sobrien getifaddrs(&ifa); 218130561Sobrien for (ifap = ifa; ifap; ifap = ifap->ifa_next) { 219130561Sobrien if (index != if_nametoindex(ifap->ifa_name)) 220130561Sobrien continue; 221130561Sobrien 222130561Sobrien if (ifap->ifa_addr->sa_family != AF_INET6) 223130561Sobrien continue; 224130561Sobrien if (!IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *) 225130561Sobrien ifap->ifa_addr)->sin6_addr)) 226130561Sobrien continue; 227130561Sobrien break; 228130561Sobrien } 229130561Sobrien if (ifap == NULL) 230130561Sobrien errx(1, "no linkocal address is available"); 231130561Sobrien memcpy(&src, &((struct sockaddr_in6 *)ifap->ifa_addr)->sin6_addr, 232130561Sobrien sizeof(src)); 233130561Sobrien freeifaddrs(ifa); 234130561Sobrien#ifdef __KAME__ 235130561Sobrien /* remove embedded ifindex */ 236130561Sobrien src.s6_addr[2] = src.s6_addr[3] = 0; 237130561Sobrien#endif 238130561Sobrien 239130561Sobrien#ifdef USE_RFC2292BIS 240130561Sobrien if ((hbhlen = inet6_opt_init(NULL, 0)) == -1) 241130561Sobrien errx(1, "inet6_opt_init(0) failed"); 242130561Sobrien if ((hbhlen = inet6_opt_append(NULL, 0, hbhlen, IP6OPT_ROUTER_ALERT, 2, 243130561Sobrien 2, NULL)) == -1) 244130561Sobrien errx(1, "inet6_opt_append(0) failed"); 245130561Sobrien if ((hbhlen = inet6_opt_finish(NULL, 0, hbhlen)) == -1) 246130561Sobrien errx(1, "inet6_opt_finish(0) failed"); 247130561Sobrien cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(hbhlen); 248130561Sobrien#else 249130561Sobrien hbhlen = sizeof(raopt); 250130561Sobrien cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + 251130561Sobrien inet6_option_space(hbhlen); 252130561Sobrien#endif 253130561Sobrien 254130561Sobrien if ((cmsgbuf = malloc(cmsglen)) == NULL) 255130561Sobrien errx(1, "can't allocate enough memory for cmsg"); 256130561Sobrien cmsgp = (struct cmsghdr *)cmsgbuf; 257130561Sobrien m.msg_control = (caddr_t)cmsgbuf; 258130561Sobrien m.msg_controllen = cmsglen; 259130561Sobrien /* specify the outgoing interface */ 260130561Sobrien cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 261130561Sobrien cmsgp->cmsg_level = IPPROTO_IPV6; 262130561Sobrien cmsgp->cmsg_type = IPV6_PKTINFO; 263130561Sobrien pi = (struct in6_pktinfo *)CMSG_DATA(cmsgp); 264130561Sobrien pi->ipi6_ifindex = index; 265130561Sobrien memcpy(&pi->ipi6_addr, &src, sizeof(pi->ipi6_addr)); 266130561Sobrien /* specifiy to insert router alert option in a hop-by-hop opt hdr. */ 267130561Sobrien cmsgp = CMSG_NXTHDR(&m, cmsgp); 268130561Sobrien#ifdef USE_RFC2292BIS 269130561Sobrien cmsgp->cmsg_len = CMSG_LEN(hbhlen); 270130561Sobrien cmsgp->cmsg_level = IPPROTO_IPV6; 271130561Sobrien cmsgp->cmsg_type = IPV6_HOPOPTS; 272130561Sobrien hbhbuf = CMSG_DATA(cmsgp); 273130561Sobrien if ((currentlen = inet6_opt_init(hbhbuf, hbhlen)) == -1) 274130561Sobrien errx(1, "inet6_opt_init(len = %d) failed", hbhlen); 275130561Sobrien if ((currentlen = inet6_opt_append(hbhbuf, hbhlen, currentlen, 276130561Sobrien IP6OPT_ROUTER_ALERT, 2, 277130561Sobrien 2, &optp)) == -1) 278130561Sobrien errx(1, "inet6_opt_append(currentlen = %d, hbhlen = %d) failed", 279130561Sobrien currentlen, hbhlen); 280130561Sobrien (void)inet6_opt_set_val(optp, 0, &rtalert_code, sizeof(rtalert_code)); 281130561Sobrien if ((currentlen = inet6_opt_finish(hbhbuf, hbhlen, currentlen)) == -1) 282130561Sobrien errx(1, "inet6_opt_finish(buf) failed"); 283130561Sobrien#else /* old advanced API */ 284130561Sobrien if (inet6_option_init((void *)cmsgp, &cmsgp, IPV6_HOPOPTS)) 285130561Sobrien errx(1, "inet6_option_init failed\n"); 286130561Sobrien raopt[0] = IP6OPT_ROUTER_ALERT; 287130561Sobrien raopt[1] = IP6OPT_RTALERT_LEN - 2; 288130561Sobrien memcpy(&raopt[2], (caddr_t)&rtalert_code, sizeof(u_short)); 289130561Sobrien if (inet6_option_append(cmsgp, raopt, 4, 0)) 290130561Sobrien errx(1, "inet6_option_append failed\n"); 291130561Sobrien#endif 292130561Sobrien} 293130561Sobrien 294130561Sobrienvoid 295130561Sobriendump(int s) 296130561Sobrien{ 297130561Sobrien int i; 298130561Sobrien struct mld_hdr *mld; 299130561Sobrien u_char buf[1024]; 300130561Sobrien struct sockaddr_in6 from; 301130561Sobrien int from_len = sizeof(from); 302130561Sobrien char ntop_buf[256]; 303130561Sobrien 304130561Sobrien if ((i = recvfrom(s, buf, sizeof(buf), 0, 305130561Sobrien (struct sockaddr *)&from, 306130561Sobrien &from_len)) < 0) 307130561Sobrien return; 308130561Sobrien 309130561Sobrien if (i < sizeof(struct mld_hdr)) { 310130561Sobrien printf("too short!\n"); 311130561Sobrien return; 312130561Sobrien } 313130561Sobrien 314130561Sobrien mld = (struct mld_hdr *)buf; 315130561Sobrien 316130561Sobrien printf("from %s, ", inet_ntop(AF_INET6, &from.sin6_addr, 317130561Sobrien ntop_buf, sizeof(ntop_buf))); 318130561Sobrien 319130561Sobrien switch (mld->mld_type) { 320130561Sobrien case ICMP6_MEMBERSHIP_QUERY: 321130561Sobrien printf("type=Multicast Listener Query, "); 322130561Sobrien break; 323130561Sobrien case ICMP6_MEMBERSHIP_REPORT: 324130561Sobrien printf("type=Multicast Listener Report, "); 325130561Sobrien break; 326130561Sobrien case ICMP6_MEMBERSHIP_REDUCTION: 327130561Sobrien printf("type=Multicast Listener Done, "); 328130561Sobrien break; 329130561Sobrien } 330130561Sobrien printf("addr=%s\n", inet_ntop(AF_INET6, &mld->mld_addr, 331130561Sobrien ntop_buf, sizeof(ntop_buf))); 332130561Sobrien 333130561Sobrien fflush(stdout); 334130561Sobrien} 335130561Sobrien 336130561Sobrienvoid 337130561Sobrienquit(int signum __unused) 338130561Sobrien{ 339130561Sobrien mreq.ipv6mr_multiaddr = any; 340130561Sobrien mreq.ipv6mr_interface = ifindex; 341130561Sobrien if (setsockopt(s, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, 342130561Sobrien sizeof(mreq)) == -1) 343130561Sobrien err(1, "setsockopt(IPV6_LEAVE_GROUP)"); 344130561Sobrien 345130561Sobrien exit(0); 346130561Sobrien} 347130561Sobrien 348130561Sobrienvoid 349130561Sobrienusage(void) 350130561Sobrien{ 351130561Sobrien (void)fprintf(stderr, "usage: mld6query ifname [addr]\n"); 352130561Sobrien exit(1); 353130561Sobrien} 354130561Sobrien