1121472Sume/* $KAME: mld6.c,v 1.15 2003/04/02 11:29:54 suz Exp $ */ 278064Sume 362914Sume/* 462914Sume * Copyright (C) 1998 WIDE Project. 562914Sume * All rights reserved. 662914Sume * 762914Sume * Redistribution and use in source and binary forms, with or without 862914Sume * modification, are permitted provided that the following conditions 962914Sume * are met: 1062914Sume * 1. Redistributions of source code must retain the above copyright 1162914Sume * notice, this list of conditions and the following disclaimer. 1262914Sume * 2. Redistributions in binary form must reproduce the above copyright 1362914Sume * notice, this list of conditions and the following disclaimer in the 1462914Sume * documentation and/or other materials provided with the distribution. 1562914Sume * 3. Neither the name of the project nor the names of its contributors 1662914Sume * may be used to endorse or promote products derived from this software 1762914Sume * without specific prior written permission. 1862914Sume * 1962914Sume * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2062914Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2162914Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2262914Sume * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2362914Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2462914Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2562914Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2662914Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2762914Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2862914Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2962914Sume * SUCH DAMAGE. 3062914Sume */ 31216586Scharnier 32216586Scharnier#include <sys/cdefs.h> 33216586Scharnier__FBSDID("$FreeBSD$"); 34216586Scharnier 3562914Sume#include <sys/param.h> 3662914Sume#include <sys/uio.h> 3762914Sume#include <sys/socket.h> 3862914Sume#include <sys/types.h> 3962914Sume#include <sys/time.h> 40121472Sume#include <ifaddrs.h> 4162914Sume#include <unistd.h> 4262914Sume#include <signal.h> 4362914Sume 4462914Sume#include <net/if.h> 4562914Sume#include <net/if_var.h> 4662914Sume 4762914Sume#include <netinet/in.h> 4862914Sume#include <netinet/ip6.h> 4962914Sume#include <netinet/icmp6.h> 5062914Sume 5162914Sume#include <arpa/inet.h> 5262914Sume 5362914Sume#include <stdlib.h> 5462914Sume#include <stdio.h> 5562914Sume#include <string.h> 5662914Sume#include <err.h> 5762914Sume 58121472Sume/* portability with older KAME headers */ 59121472Sume#ifndef MLD_LISTENER_QUERY 60121472Sume#define MLD_LISTENER_QUERY MLD6_LISTENER_QUERY 61121472Sume#define MLD_LISTENER_REPORT MLD6_LISTENER_REPORT 62121472Sume#define MLD_LISTENER_DONE MLD6_LISTENER_DONE 63121472Sume#define MLD_MTRACE_RESP MLD6_MTRACE_RESP 64121472Sume#define MLD_MTRACE MLD6_MTRACE 65121472Sume#define mld_hdr mld6_hdr 66121472Sume#define mld_type mld6_type 67121472Sume#define mld_code mld6_code 68121472Sume#define mld_cksum mld6_cksum 69121472Sume#define mld_maxdelay mld6_maxdelay 70121472Sume#define mld_reserved mld6_reserved 71121472Sume#define mld_addr mld6_addr 72121472Sume#endif 73121472Sume#ifndef IP6OPT_ROUTER_ALERT 74121472Sume#define IP6OPT_ROUTER_ALERT IP6OPT_RTALERT 75121472Sume#endif 76121472Sume 7762914Sumestruct msghdr m; 7862914Sumestruct sockaddr_in6 dst; 79121472Sumestruct mld_hdr mldh; 8062914Sumestruct in6_addr maddr = IN6ADDR_ANY_INIT, any = IN6ADDR_ANY_INIT; 8162914Sumestruct ipv6_mreq mreq; 8262914Sumeu_short ifindex; 8362914Sumeint s; 8462914Sume 8562914Sume#define QUERY_RESPONSE_INTERVAL 10000 8662914Sume 8762914Sumevoid make_msg(int index, struct in6_addr *addr, u_int type); 8862914Sumevoid usage(void); 8962914Sumevoid dump(int); 9062914Sumevoid quit(int); 9162914Sume 9262914Sumeint 9362914Sumemain(int argc, char *argv[]) 9462914Sume{ 9562914Sume int i; 9662914Sume struct icmp6_filter filt; 9762914Sume u_int hlim = 1; 9862914Sume fd_set fdset; 9962914Sume struct itimerval itimer; 10062914Sume u_int type; 10162914Sume int ch; 10262914Sume 103121472Sume type = MLD_LISTENER_QUERY; 10478064Sume while ((ch = getopt(argc, argv, "dr")) != -1) { 10562914Sume switch (ch) { 10662914Sume case 'd': 107121472Sume type = MLD_LISTENER_DONE; 10862914Sume break; 10962914Sume case 'r': 110121472Sume type = MLD_LISTENER_REPORT; 11162914Sume break; 11262914Sume default: 11362914Sume usage(); 11462914Sume /*NOTREACHED*/ 11562914Sume } 11662914Sume } 11762914Sume 11862914Sume argv += optind; 11962914Sume argc -= optind; 12062914Sume 12162914Sume if (argc != 1 && argc != 2) 12262914Sume usage(); 12362914Sume 12462914Sume ifindex = (u_short)if_nametoindex(argv[0]); 12562914Sume if (ifindex == 0) 12662914Sume usage(); 12778064Sume if (argc == 2 && inet_pton(AF_INET6, argv[1], &maddr) != 1) 12862914Sume usage(); 12962914Sume 13062914Sume if ((s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) 13162914Sume err(1, "socket"); 13262914Sume 13362914Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hlim, 13462914Sume sizeof(hlim)) == -1) 13562914Sume err(1, "setsockopt(IPV6_MULTICAST_HOPS)"); 13662914Sume 13762914Sume mreq.ipv6mr_multiaddr = any; 13862914Sume mreq.ipv6mr_interface = ifindex; 13962914Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, 14062914Sume sizeof(mreq)) == -1) 14162914Sume err(1, "setsockopt(IPV6_JOIN_GROUP)"); 14262914Sume 14362914Sume ICMP6_FILTER_SETBLOCKALL(&filt); 14462914Sume ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_QUERY, &filt); 14562914Sume ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REPORT, &filt); 14662914Sume ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REDUCTION, &filt); 14762914Sume if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 14862914Sume sizeof(filt)) < 0) 14962914Sume err(1, "setsockopt(ICMP6_FILTER)"); 15062914Sume 15162914Sume make_msg(ifindex, &maddr, type); 15262914Sume 15362914Sume if (sendmsg(s, &m, 0) < 0) 15462914Sume err(1, "sendmsg"); 15562914Sume 15662914Sume itimer.it_value.tv_sec = QUERY_RESPONSE_INTERVAL / 1000; 15762914Sume itimer.it_interval.tv_sec = 0; 15862914Sume itimer.it_interval.tv_usec = 0; 15962914Sume itimer.it_value.tv_usec = 0; 16062914Sume 16162914Sume (void)signal(SIGALRM, quit); 16262914Sume (void)setitimer(ITIMER_REAL, &itimer, NULL); 16362914Sume 16462914Sume FD_ZERO(&fdset); 165121472Sume if (s >= FD_SETSIZE) 166121472Sume errx(1, "descriptor too big"); 16762914Sume for (;;) { 16862914Sume FD_SET(s, &fdset); 16962914Sume if ((i = select(s + 1, &fdset, NULL, NULL, NULL)) < 0) 17062914Sume perror("select"); 17162914Sume if (i == 0) 17262914Sume continue; 17362914Sume else 17462914Sume dump(s); 17562914Sume } 17662914Sume} 17762914Sume 17862914Sumevoid 17962914Sumemake_msg(int index, struct in6_addr *addr, u_int type) 18062914Sume{ 18162914Sume static struct iovec iov[2]; 18262914Sume static u_char *cmsgbuf; 18362914Sume int cmsglen, hbhlen = 0; 184121472Sume#ifdef USE_RFC2292BIS 185121472Sume void *hbhbuf = NULL, *optp = NULL; 186121472Sume int currentlen; 187121472Sume#else 18862914Sume u_int8_t raopt[IP6OPT_RTALERT_LEN]; 189121472Sume#endif 19062914Sume struct in6_pktinfo *pi; 19162914Sume struct cmsghdr *cmsgp; 19262914Sume u_short rtalert_code = htons(IP6OPT_RTALERT_MLD); 193121472Sume struct ifaddrs *ifa, *ifap; 194121472Sume struct in6_addr src; 19562914Sume 19662914Sume dst.sin6_len = sizeof(dst); 19762914Sume dst.sin6_family = AF_INET6; 19862914Sume if (IN6_IS_ADDR_UNSPECIFIED(addr)) { 19962914Sume if (inet_pton(AF_INET6, "ff02::1", &dst.sin6_addr) != 1) 20062914Sume errx(1, "inet_pton failed"); 20162914Sume } 20262914Sume else 20362914Sume dst.sin6_addr = *addr; 20462914Sume m.msg_name = (caddr_t)&dst; 20562914Sume m.msg_namelen = dst.sin6_len; 20662914Sume iov[0].iov_base = (caddr_t)&mldh; 20762914Sume iov[0].iov_len = sizeof(mldh); 20862914Sume m.msg_iov = iov; 20962914Sume m.msg_iovlen = 1; 21062914Sume 21162914Sume bzero(&mldh, sizeof(mldh)); 212121472Sume mldh.mld_type = type & 0xff; 213121472Sume mldh.mld_maxdelay = htons(QUERY_RESPONSE_INTERVAL); 214121472Sume mldh.mld_addr = *addr; 21562914Sume 216121472Sume /* MLD packet should be advertised from linklocal address */ 217121472Sume getifaddrs(&ifa); 218121472Sume for (ifap = ifa; ifap; ifap = ifap->ifa_next) { 219121472Sume if (index != if_nametoindex(ifap->ifa_name)) 220121472Sume continue; 221121472Sume 222121472Sume if (ifap->ifa_addr->sa_family != AF_INET6) 223121472Sume continue; 224121472Sume if (!IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *) 225121472Sume ifap->ifa_addr)->sin6_addr)) 226121472Sume continue; 227121472Sume break; 228121472Sume } 229121472Sume if (ifap == NULL) 230121472Sume errx(1, "no linkocal address is available"); 231121472Sume memcpy(&src, &((struct sockaddr_in6 *)ifap->ifa_addr)->sin6_addr, 232121472Sume sizeof(src)); 233121472Sume freeifaddrs(ifa); 234121472Sume#ifdef __KAME__ 235121472Sume /* remove embedded ifindex */ 236121472Sume src.s6_addr[2] = src.s6_addr[3] = 0; 237121472Sume#endif 238121472Sume 239121472Sume#ifdef USE_RFC2292BIS 240121472Sume if ((hbhlen = inet6_opt_init(NULL, 0)) == -1) 241121472Sume errx(1, "inet6_opt_init(0) failed"); 242121472Sume if ((hbhlen = inet6_opt_append(NULL, 0, hbhlen, IP6OPT_ROUTER_ALERT, 2, 243121472Sume 2, NULL)) == -1) 244121472Sume errx(1, "inet6_opt_append(0) failed"); 245121472Sume if ((hbhlen = inet6_opt_finish(NULL, 0, hbhlen)) == -1) 246121472Sume errx(1, "inet6_opt_finish(0) failed"); 247121472Sume cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(hbhlen); 248121472Sume#else 24962914Sume hbhlen = sizeof(raopt); 25062914Sume cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + 25162914Sume inet6_option_space(hbhlen); 252121472Sume#endif 25362914Sume 25462914Sume if ((cmsgbuf = malloc(cmsglen)) == NULL) 25562914Sume errx(1, "can't allocate enough memory for cmsg"); 25662914Sume cmsgp = (struct cmsghdr *)cmsgbuf; 25762914Sume m.msg_control = (caddr_t)cmsgbuf; 25862914Sume m.msg_controllen = cmsglen; 25962914Sume /* specify the outgoing interface */ 26078064Sume cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 26162914Sume cmsgp->cmsg_level = IPPROTO_IPV6; 26262914Sume cmsgp->cmsg_type = IPV6_PKTINFO; 26362914Sume pi = (struct in6_pktinfo *)CMSG_DATA(cmsgp); 26462914Sume pi->ipi6_ifindex = index; 265121472Sume memcpy(&pi->ipi6_addr, &src, sizeof(pi->ipi6_addr)); 26662914Sume /* specifiy to insert router alert option in a hop-by-hop opt hdr. */ 26762914Sume cmsgp = CMSG_NXTHDR(&m, cmsgp); 268121472Sume#ifdef USE_RFC2292BIS 269121472Sume cmsgp->cmsg_len = CMSG_LEN(hbhlen); 270121472Sume cmsgp->cmsg_level = IPPROTO_IPV6; 271121472Sume cmsgp->cmsg_type = IPV6_HOPOPTS; 272121472Sume hbhbuf = CMSG_DATA(cmsgp); 273121472Sume if ((currentlen = inet6_opt_init(hbhbuf, hbhlen)) == -1) 274121472Sume errx(1, "inet6_opt_init(len = %d) failed", hbhlen); 275121472Sume if ((currentlen = inet6_opt_append(hbhbuf, hbhlen, currentlen, 276121472Sume IP6OPT_ROUTER_ALERT, 2, 277121472Sume 2, &optp)) == -1) 278121472Sume errx(1, "inet6_opt_append(currentlen = %d, hbhlen = %d) failed", 279121472Sume currentlen, hbhlen); 280121472Sume (void)inet6_opt_set_val(optp, 0, &rtalert_code, sizeof(rtalert_code)); 281121472Sume if ((currentlen = inet6_opt_finish(hbhbuf, hbhlen, currentlen)) == -1) 282121472Sume errx(1, "inet6_opt_finish(buf) failed"); 283121472Sume#else /* old advanced API */ 28462914Sume if (inet6_option_init((void *)cmsgp, &cmsgp, IPV6_HOPOPTS)) 28562914Sume errx(1, "inet6_option_init failed\n"); 286121472Sume raopt[0] = IP6OPT_ROUTER_ALERT; 28762914Sume raopt[1] = IP6OPT_RTALERT_LEN - 2; 28862914Sume memcpy(&raopt[2], (caddr_t)&rtalert_code, sizeof(u_short)); 28962914Sume if (inet6_option_append(cmsgp, raopt, 4, 0)) 29062914Sume errx(1, "inet6_option_append failed\n"); 291121472Sume#endif 29262914Sume} 29362914Sume 29462914Sumevoid 29562914Sumedump(int s) 29662914Sume{ 29762914Sume int i; 298121472Sume struct mld_hdr *mld; 29962914Sume u_char buf[1024]; 30062914Sume struct sockaddr_in6 from; 30162914Sume int from_len = sizeof(from); 30262914Sume char ntop_buf[256]; 30362914Sume 30462914Sume if ((i = recvfrom(s, buf, sizeof(buf), 0, 30562914Sume (struct sockaddr *)&from, 30662914Sume &from_len)) < 0) 30762914Sume return; 30862914Sume 309121472Sume if (i < sizeof(struct mld_hdr)) { 31062914Sume printf("too short!\n"); 31162914Sume return; 31262914Sume } 31362914Sume 314121472Sume mld = (struct mld_hdr *)buf; 31562914Sume 31662914Sume printf("from %s, ", inet_ntop(AF_INET6, &from.sin6_addr, 31762914Sume ntop_buf, sizeof(ntop_buf))); 31862914Sume 319121472Sume switch (mld->mld_type) { 32062914Sume case ICMP6_MEMBERSHIP_QUERY: 32162914Sume printf("type=Multicast Listener Query, "); 32262914Sume break; 32362914Sume case ICMP6_MEMBERSHIP_REPORT: 32462914Sume printf("type=Multicast Listener Report, "); 32562914Sume break; 32662914Sume case ICMP6_MEMBERSHIP_REDUCTION: 32762914Sume printf("type=Multicast Listener Done, "); 32862914Sume break; 32962914Sume } 330121472Sume printf("addr=%s\n", inet_ntop(AF_INET6, &mld->mld_addr, 33162914Sume ntop_buf, sizeof(ntop_buf))); 33262914Sume 33362914Sume fflush(stdout); 33462914Sume} 33562914Sume 33662914Sumevoid 337216586Scharnierquit(int signum __unused) 338121472Sume{ 33962914Sume mreq.ipv6mr_multiaddr = any; 34062914Sume mreq.ipv6mr_interface = ifindex; 34162914Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, 34262914Sume sizeof(mreq)) == -1) 34362914Sume err(1, "setsockopt(IPV6_LEAVE_GROUP)"); 34462914Sume 34562914Sume exit(0); 34662914Sume} 34762914Sume 34862914Sumevoid 349216586Scharnierusage(void) 35062914Sume{ 35162914Sume (void)fprintf(stderr, "usage: mld6query ifname [addr]\n"); 35262914Sume exit(1); 35362914Sume} 354