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: releng/11.0/usr.sbin/mld6query/mld6.c 281143 2015-04-06 09:42:23Z glebius $"); 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 4662914Sume#include <netinet/in.h> 4762914Sume#include <netinet/ip6.h> 4862914Sume#include <netinet/icmp6.h> 4962914Sume 5062914Sume#include <arpa/inet.h> 5162914Sume 5262914Sume#include <stdlib.h> 5362914Sume#include <stdio.h> 5462914Sume#include <string.h> 5562914Sume#include <err.h> 5662914Sume 57121472Sume/* portability with older KAME headers */ 58121472Sume#ifndef MLD_LISTENER_QUERY 59121472Sume#define MLD_LISTENER_QUERY MLD6_LISTENER_QUERY 60121472Sume#define MLD_LISTENER_REPORT MLD6_LISTENER_REPORT 61121472Sume#define MLD_LISTENER_DONE MLD6_LISTENER_DONE 62121472Sume#define MLD_MTRACE_RESP MLD6_MTRACE_RESP 63121472Sume#define MLD_MTRACE MLD6_MTRACE 64121472Sume#define mld_hdr mld6_hdr 65121472Sume#define mld_type mld6_type 66121472Sume#define mld_code mld6_code 67121472Sume#define mld_cksum mld6_cksum 68121472Sume#define mld_maxdelay mld6_maxdelay 69121472Sume#define mld_reserved mld6_reserved 70121472Sume#define mld_addr mld6_addr 71121472Sume#endif 72121472Sume#ifndef IP6OPT_ROUTER_ALERT 73121472Sume#define IP6OPT_ROUTER_ALERT IP6OPT_RTALERT 74121472Sume#endif 75121472Sume 7662914Sumestruct msghdr m; 7762914Sumestruct sockaddr_in6 dst; 78121472Sumestruct mld_hdr mldh; 7962914Sumestruct in6_addr maddr = IN6ADDR_ANY_INIT, any = IN6ADDR_ANY_INIT; 8062914Sumestruct ipv6_mreq mreq; 8162914Sumeu_short ifindex; 8262914Sumeint s; 8362914Sume 8462914Sume#define QUERY_RESPONSE_INTERVAL 10000 8562914Sume 8662914Sumevoid make_msg(int index, struct in6_addr *addr, u_int type); 8762914Sumevoid usage(void); 8862914Sumevoid dump(int); 8962914Sumevoid quit(int); 9062914Sume 9162914Sumeint 9262914Sumemain(int argc, char *argv[]) 9362914Sume{ 9462914Sume int i; 9562914Sume struct icmp6_filter filt; 9662914Sume u_int hlim = 1; 9762914Sume fd_set fdset; 9862914Sume struct itimerval itimer; 9962914Sume u_int type; 10062914Sume int ch; 10162914Sume 102121472Sume type = MLD_LISTENER_QUERY; 10378064Sume while ((ch = getopt(argc, argv, "dr")) != -1) { 10462914Sume switch (ch) { 10562914Sume case 'd': 106121472Sume type = MLD_LISTENER_DONE; 10762914Sume break; 10862914Sume case 'r': 109121472Sume type = MLD_LISTENER_REPORT; 11062914Sume break; 11162914Sume default: 11262914Sume usage(); 11362914Sume /*NOTREACHED*/ 11462914Sume } 11562914Sume } 11662914Sume 11762914Sume argv += optind; 11862914Sume argc -= optind; 11962914Sume 12062914Sume if (argc != 1 && argc != 2) 12162914Sume usage(); 12262914Sume 12362914Sume ifindex = (u_short)if_nametoindex(argv[0]); 12462914Sume if (ifindex == 0) 12562914Sume usage(); 12678064Sume if (argc == 2 && inet_pton(AF_INET6, argv[1], &maddr) != 1) 12762914Sume usage(); 12862914Sume 12962914Sume if ((s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) 13062914Sume err(1, "socket"); 13162914Sume 13262914Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hlim, 13362914Sume sizeof(hlim)) == -1) 13462914Sume err(1, "setsockopt(IPV6_MULTICAST_HOPS)"); 13562914Sume 13662914Sume mreq.ipv6mr_multiaddr = any; 13762914Sume mreq.ipv6mr_interface = ifindex; 13862914Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, 13962914Sume sizeof(mreq)) == -1) 14062914Sume err(1, "setsockopt(IPV6_JOIN_GROUP)"); 14162914Sume 14262914Sume ICMP6_FILTER_SETBLOCKALL(&filt); 14362914Sume ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_QUERY, &filt); 14462914Sume ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REPORT, &filt); 14562914Sume ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REDUCTION, &filt); 14662914Sume if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 14762914Sume sizeof(filt)) < 0) 14862914Sume err(1, "setsockopt(ICMP6_FILTER)"); 14962914Sume 15062914Sume make_msg(ifindex, &maddr, type); 15162914Sume 15262914Sume if (sendmsg(s, &m, 0) < 0) 15362914Sume err(1, "sendmsg"); 15462914Sume 15562914Sume itimer.it_value.tv_sec = QUERY_RESPONSE_INTERVAL / 1000; 15662914Sume itimer.it_interval.tv_sec = 0; 15762914Sume itimer.it_interval.tv_usec = 0; 15862914Sume itimer.it_value.tv_usec = 0; 15962914Sume 16062914Sume (void)signal(SIGALRM, quit); 16162914Sume (void)setitimer(ITIMER_REAL, &itimer, NULL); 16262914Sume 16362914Sume FD_ZERO(&fdset); 164121472Sume if (s >= FD_SETSIZE) 165121472Sume errx(1, "descriptor too big"); 16662914Sume for (;;) { 16762914Sume FD_SET(s, &fdset); 16862914Sume if ((i = select(s + 1, &fdset, NULL, NULL, NULL)) < 0) 16962914Sume perror("select"); 17062914Sume if (i == 0) 17162914Sume continue; 17262914Sume else 17362914Sume dump(s); 17462914Sume } 17562914Sume} 17662914Sume 17762914Sumevoid 17862914Sumemake_msg(int index, struct in6_addr *addr, u_int type) 17962914Sume{ 18062914Sume static struct iovec iov[2]; 18162914Sume static u_char *cmsgbuf; 18262914Sume int cmsglen, hbhlen = 0; 183121472Sume#ifdef USE_RFC2292BIS 184121472Sume void *hbhbuf = NULL, *optp = NULL; 185121472Sume int currentlen; 186121472Sume#else 18762914Sume u_int8_t raopt[IP6OPT_RTALERT_LEN]; 188121472Sume#endif 18962914Sume struct in6_pktinfo *pi; 19062914Sume struct cmsghdr *cmsgp; 19162914Sume u_short rtalert_code = htons(IP6OPT_RTALERT_MLD); 192121472Sume struct ifaddrs *ifa, *ifap; 193121472Sume struct in6_addr src; 19462914Sume 19562914Sume dst.sin6_len = sizeof(dst); 19662914Sume dst.sin6_family = AF_INET6; 19762914Sume if (IN6_IS_ADDR_UNSPECIFIED(addr)) { 19862914Sume if (inet_pton(AF_INET6, "ff02::1", &dst.sin6_addr) != 1) 19962914Sume errx(1, "inet_pton failed"); 20062914Sume } 20162914Sume else 20262914Sume dst.sin6_addr = *addr; 20362914Sume m.msg_name = (caddr_t)&dst; 20462914Sume m.msg_namelen = dst.sin6_len; 20562914Sume iov[0].iov_base = (caddr_t)&mldh; 20662914Sume iov[0].iov_len = sizeof(mldh); 20762914Sume m.msg_iov = iov; 20862914Sume m.msg_iovlen = 1; 20962914Sume 21062914Sume bzero(&mldh, sizeof(mldh)); 211121472Sume mldh.mld_type = type & 0xff; 212121472Sume mldh.mld_maxdelay = htons(QUERY_RESPONSE_INTERVAL); 213121472Sume mldh.mld_addr = *addr; 21462914Sume 215121472Sume /* MLD packet should be advertised from linklocal address */ 216121472Sume getifaddrs(&ifa); 217121472Sume for (ifap = ifa; ifap; ifap = ifap->ifa_next) { 218121472Sume if (index != if_nametoindex(ifap->ifa_name)) 219121472Sume continue; 220121472Sume 221121472Sume if (ifap->ifa_addr->sa_family != AF_INET6) 222121472Sume continue; 223121472Sume if (!IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *) 224121472Sume ifap->ifa_addr)->sin6_addr)) 225121472Sume continue; 226121472Sume break; 227121472Sume } 228121472Sume if (ifap == NULL) 229121472Sume errx(1, "no linkocal address is available"); 230121472Sume memcpy(&src, &((struct sockaddr_in6 *)ifap->ifa_addr)->sin6_addr, 231121472Sume sizeof(src)); 232121472Sume freeifaddrs(ifa); 233121472Sume#ifdef __KAME__ 234121472Sume /* remove embedded ifindex */ 235121472Sume src.s6_addr[2] = src.s6_addr[3] = 0; 236121472Sume#endif 237121472Sume 238121472Sume#ifdef USE_RFC2292BIS 239121472Sume if ((hbhlen = inet6_opt_init(NULL, 0)) == -1) 240121472Sume errx(1, "inet6_opt_init(0) failed"); 241121472Sume if ((hbhlen = inet6_opt_append(NULL, 0, hbhlen, IP6OPT_ROUTER_ALERT, 2, 242121472Sume 2, NULL)) == -1) 243121472Sume errx(1, "inet6_opt_append(0) failed"); 244121472Sume if ((hbhlen = inet6_opt_finish(NULL, 0, hbhlen)) == -1) 245121472Sume errx(1, "inet6_opt_finish(0) failed"); 246121472Sume cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(hbhlen); 247121472Sume#else 24862914Sume hbhlen = sizeof(raopt); 24962914Sume cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + 25062914Sume inet6_option_space(hbhlen); 251121472Sume#endif 25262914Sume 25362914Sume if ((cmsgbuf = malloc(cmsglen)) == NULL) 25462914Sume errx(1, "can't allocate enough memory for cmsg"); 25562914Sume cmsgp = (struct cmsghdr *)cmsgbuf; 25662914Sume m.msg_control = (caddr_t)cmsgbuf; 25762914Sume m.msg_controllen = cmsglen; 25862914Sume /* specify the outgoing interface */ 25978064Sume cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 26062914Sume cmsgp->cmsg_level = IPPROTO_IPV6; 26162914Sume cmsgp->cmsg_type = IPV6_PKTINFO; 26262914Sume pi = (struct in6_pktinfo *)CMSG_DATA(cmsgp); 26362914Sume pi->ipi6_ifindex = index; 264121472Sume memcpy(&pi->ipi6_addr, &src, sizeof(pi->ipi6_addr)); 26562914Sume /* specifiy to insert router alert option in a hop-by-hop opt hdr. */ 26662914Sume cmsgp = CMSG_NXTHDR(&m, cmsgp); 267121472Sume#ifdef USE_RFC2292BIS 268121472Sume cmsgp->cmsg_len = CMSG_LEN(hbhlen); 269121472Sume cmsgp->cmsg_level = IPPROTO_IPV6; 270121472Sume cmsgp->cmsg_type = IPV6_HOPOPTS; 271121472Sume hbhbuf = CMSG_DATA(cmsgp); 272121472Sume if ((currentlen = inet6_opt_init(hbhbuf, hbhlen)) == -1) 273121472Sume errx(1, "inet6_opt_init(len = %d) failed", hbhlen); 274121472Sume if ((currentlen = inet6_opt_append(hbhbuf, hbhlen, currentlen, 275121472Sume IP6OPT_ROUTER_ALERT, 2, 276121472Sume 2, &optp)) == -1) 277121472Sume errx(1, "inet6_opt_append(currentlen = %d, hbhlen = %d) failed", 278121472Sume currentlen, hbhlen); 279121472Sume (void)inet6_opt_set_val(optp, 0, &rtalert_code, sizeof(rtalert_code)); 280121472Sume if ((currentlen = inet6_opt_finish(hbhbuf, hbhlen, currentlen)) == -1) 281121472Sume errx(1, "inet6_opt_finish(buf) failed"); 282121472Sume#else /* old advanced API */ 28362914Sume if (inet6_option_init((void *)cmsgp, &cmsgp, IPV6_HOPOPTS)) 28462914Sume errx(1, "inet6_option_init failed\n"); 285121472Sume raopt[0] = IP6OPT_ROUTER_ALERT; 28662914Sume raopt[1] = IP6OPT_RTALERT_LEN - 2; 28762914Sume memcpy(&raopt[2], (caddr_t)&rtalert_code, sizeof(u_short)); 28862914Sume if (inet6_option_append(cmsgp, raopt, 4, 0)) 28962914Sume errx(1, "inet6_option_append failed\n"); 290121472Sume#endif 29162914Sume} 29262914Sume 29362914Sumevoid 29462914Sumedump(int s) 29562914Sume{ 29662914Sume int i; 297121472Sume struct mld_hdr *mld; 29862914Sume u_char buf[1024]; 29962914Sume struct sockaddr_in6 from; 30062914Sume int from_len = sizeof(from); 30162914Sume char ntop_buf[256]; 30262914Sume 30362914Sume if ((i = recvfrom(s, buf, sizeof(buf), 0, 30462914Sume (struct sockaddr *)&from, 30562914Sume &from_len)) < 0) 30662914Sume return; 30762914Sume 308121472Sume if (i < sizeof(struct mld_hdr)) { 30962914Sume printf("too short!\n"); 31062914Sume return; 31162914Sume } 31262914Sume 313121472Sume mld = (struct mld_hdr *)buf; 31462914Sume 31562914Sume printf("from %s, ", inet_ntop(AF_INET6, &from.sin6_addr, 31662914Sume ntop_buf, sizeof(ntop_buf))); 31762914Sume 318121472Sume switch (mld->mld_type) { 31962914Sume case ICMP6_MEMBERSHIP_QUERY: 32062914Sume printf("type=Multicast Listener Query, "); 32162914Sume break; 32262914Sume case ICMP6_MEMBERSHIP_REPORT: 32362914Sume printf("type=Multicast Listener Report, "); 32462914Sume break; 32562914Sume case ICMP6_MEMBERSHIP_REDUCTION: 32662914Sume printf("type=Multicast Listener Done, "); 32762914Sume break; 32862914Sume } 329121472Sume printf("addr=%s\n", inet_ntop(AF_INET6, &mld->mld_addr, 33062914Sume ntop_buf, sizeof(ntop_buf))); 33162914Sume 33262914Sume fflush(stdout); 33362914Sume} 33462914Sume 33562914Sumevoid 336216586Scharnierquit(int signum __unused) 337121472Sume{ 33862914Sume mreq.ipv6mr_multiaddr = any; 33962914Sume mreq.ipv6mr_interface = ifindex; 34062914Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, 34162914Sume sizeof(mreq)) == -1) 34262914Sume err(1, "setsockopt(IPV6_LEAVE_GROUP)"); 34362914Sume 34462914Sume exit(0); 34562914Sume} 34662914Sume 34762914Sumevoid 348216586Scharnierusage(void) 34962914Sume{ 35062914Sume (void)fprintf(stderr, "usage: mld6query ifname [addr]\n"); 35162914Sume exit(1); 35262914Sume} 353