mld6.c revision 78064
178064Sume/* $KAME: mld6.c,v 1.11 2001/05/13 15:45:07 suz Exp $ */ 278064Sume/* $FreeBSD: head/usr.sbin/mld6query/mld6.c 78064 2001-06-11 12:39:29Z ume $ */ 378064Sume 462914Sume/* 562914Sume * Copyright (C) 1998 WIDE Project. 662914Sume * All rights reserved. 762914Sume * 862914Sume * Redistribution and use in source and binary forms, with or without 962914Sume * modification, are permitted provided that the following conditions 1062914Sume * are met: 1162914Sume * 1. Redistributions of source code must retain the above copyright 1262914Sume * notice, this list of conditions and the following disclaimer. 1362914Sume * 2. Redistributions in binary form must reproduce the above copyright 1462914Sume * notice, this list of conditions and the following disclaimer in the 1562914Sume * documentation and/or other materials provided with the distribution. 1662914Sume * 3. Neither the name of the project nor the names of its contributors 1762914Sume * may be used to endorse or promote products derived from this software 1862914Sume * without specific prior written permission. 1962914Sume * 2062914Sume * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2162914Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2262914Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2362914Sume * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2462914Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2562914Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2662914Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2762914Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2862914Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2962914Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3062914Sume * SUCH DAMAGE. 3162914Sume */ 3262914Sume#include <sys/param.h> 3362914Sume#include <sys/uio.h> 3462914Sume#include <sys/socket.h> 3562914Sume#include <sys/types.h> 3662914Sume#include <sys/time.h> 3762914Sume#include <unistd.h> 3862914Sume#include <signal.h> 3962914Sume 4062914Sume#include <net/if.h> 4162914Sume#include <net/if_var.h> 4262914Sume 4362914Sume#include <netinet/in.h> 4462914Sume#include <netinet/ip6.h> 4562914Sume#include <netinet/icmp6.h> 4662914Sume 4762914Sume#include <arpa/inet.h> 4862914Sume 4962914Sume#include <stdlib.h> 5062914Sume#include <stdio.h> 5162914Sume#include <string.h> 5262914Sume#include <err.h> 5362914Sume 5462914Sumestruct msghdr m; 5562914Sumestruct sockaddr_in6 dst; 5662914Sumestruct mld6_hdr mldh; 5762914Sumestruct in6_addr maddr = IN6ADDR_ANY_INIT, any = IN6ADDR_ANY_INIT; 5862914Sumestruct ipv6_mreq mreq; 5962914Sumeu_short ifindex; 6062914Sumeint s; 6162914Sume 6262914Sume#define QUERY_RESPONSE_INTERVAL 10000 6362914Sume 6462914Sumevoid make_msg(int index, struct in6_addr *addr, u_int type); 6562914Sumevoid usage(void); 6662914Sumevoid dump(int); 6762914Sumevoid quit(int); 6862914Sume 6962914Sumeint 7062914Sumemain(int argc, char *argv[]) 7162914Sume{ 7262914Sume int i; 7362914Sume struct icmp6_filter filt; 7462914Sume u_int hlim = 1; 7562914Sume fd_set fdset; 7662914Sume struct itimerval itimer; 7762914Sume u_int type; 7862914Sume int ch; 7962914Sume 8062914Sume type = MLD6_LISTENER_QUERY; 8178064Sume while ((ch = getopt(argc, argv, "dr")) != -1) { 8262914Sume switch (ch) { 8362914Sume case 'd': 8462914Sume type = MLD6_LISTENER_DONE; 8562914Sume break; 8662914Sume case 'r': 8762914Sume type = MLD6_LISTENER_REPORT; 8862914Sume break; 8962914Sume default: 9062914Sume usage(); 9162914Sume /*NOTREACHED*/ 9262914Sume } 9362914Sume } 9462914Sume 9562914Sume argv += optind; 9662914Sume argc -= optind; 9762914Sume 9862914Sume if (argc != 1 && argc != 2) 9962914Sume usage(); 10062914Sume 10162914Sume ifindex = (u_short)if_nametoindex(argv[0]); 10262914Sume if (ifindex == 0) 10362914Sume usage(); 10478064Sume if (argc == 2 && inet_pton(AF_INET6, argv[1], &maddr) != 1) 10562914Sume usage(); 10662914Sume 10762914Sume if ((s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) 10862914Sume err(1, "socket"); 10962914Sume 11062914Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hlim, 11162914Sume sizeof(hlim)) == -1) 11262914Sume err(1, "setsockopt(IPV6_MULTICAST_HOPS)"); 11362914Sume 11462914Sume mreq.ipv6mr_multiaddr = any; 11562914Sume mreq.ipv6mr_interface = ifindex; 11662914Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, 11762914Sume sizeof(mreq)) == -1) 11862914Sume err(1, "setsockopt(IPV6_JOIN_GROUP)"); 11962914Sume 12062914Sume ICMP6_FILTER_SETBLOCKALL(&filt); 12162914Sume ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_QUERY, &filt); 12262914Sume ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REPORT, &filt); 12362914Sume ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REDUCTION, &filt); 12462914Sume if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 12562914Sume sizeof(filt)) < 0) 12662914Sume err(1, "setsockopt(ICMP6_FILTER)"); 12762914Sume 12862914Sume make_msg(ifindex, &maddr, type); 12962914Sume 13062914Sume if (sendmsg(s, &m, 0) < 0) 13162914Sume err(1, "sendmsg"); 13262914Sume 13362914Sume itimer.it_value.tv_sec = QUERY_RESPONSE_INTERVAL / 1000; 13462914Sume itimer.it_interval.tv_sec = 0; 13562914Sume itimer.it_interval.tv_usec = 0; 13662914Sume itimer.it_value.tv_usec = 0; 13762914Sume 13862914Sume (void)signal(SIGALRM, quit); 13962914Sume (void)setitimer(ITIMER_REAL, &itimer, NULL); 14062914Sume 14162914Sume FD_ZERO(&fdset); 14262914Sume for (;;) { 14362914Sume FD_SET(s, &fdset); 14462914Sume if ((i = select(s + 1, &fdset, NULL, NULL, NULL)) < 0) 14562914Sume perror("select"); 14662914Sume if (i == 0) 14762914Sume continue; 14862914Sume else 14962914Sume dump(s); 15062914Sume } 15162914Sume} 15262914Sume 15362914Sumevoid 15462914Sumemake_msg(int index, struct in6_addr *addr, u_int type) 15562914Sume{ 15662914Sume static struct iovec iov[2]; 15762914Sume static u_char *cmsgbuf; 15862914Sume int cmsglen, hbhlen = 0; 15962914Sume u_int8_t raopt[IP6OPT_RTALERT_LEN]; 16062914Sume struct in6_pktinfo *pi; 16162914Sume struct cmsghdr *cmsgp; 16262914Sume u_short rtalert_code = htons(IP6OPT_RTALERT_MLD); 16362914Sume 16462914Sume dst.sin6_len = sizeof(dst); 16562914Sume dst.sin6_family = AF_INET6; 16662914Sume if (IN6_IS_ADDR_UNSPECIFIED(addr)) { 16762914Sume if (inet_pton(AF_INET6, "ff02::1", &dst.sin6_addr) != 1) 16862914Sume errx(1, "inet_pton failed"); 16962914Sume } 17062914Sume else 17162914Sume dst.sin6_addr = *addr; 17262914Sume m.msg_name = (caddr_t)&dst; 17362914Sume m.msg_namelen = dst.sin6_len; 17462914Sume iov[0].iov_base = (caddr_t)&mldh; 17562914Sume iov[0].iov_len = sizeof(mldh); 17662914Sume m.msg_iov = iov; 17762914Sume m.msg_iovlen = 1; 17862914Sume 17962914Sume bzero(&mldh, sizeof(mldh)); 18062914Sume mldh.mld6_type = type & 0xff; 18162914Sume mldh.mld6_maxdelay = htons(QUERY_RESPONSE_INTERVAL); 18262914Sume mldh.mld6_addr = *addr; 18362914Sume 18462914Sume hbhlen = sizeof(raopt); 18562914Sume cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + 18662914Sume inet6_option_space(hbhlen); 18762914Sume 18862914Sume if ((cmsgbuf = malloc(cmsglen)) == NULL) 18962914Sume errx(1, "can't allocate enough memory for cmsg"); 19062914Sume cmsgp = (struct cmsghdr *)cmsgbuf; 19162914Sume m.msg_control = (caddr_t)cmsgbuf; 19262914Sume m.msg_controllen = cmsglen; 19362914Sume /* specify the outgoing interface */ 19478064Sume cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 19562914Sume cmsgp->cmsg_level = IPPROTO_IPV6; 19662914Sume cmsgp->cmsg_type = IPV6_PKTINFO; 19762914Sume pi = (struct in6_pktinfo *)CMSG_DATA(cmsgp); 19862914Sume pi->ipi6_ifindex = index; 19962914Sume memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); 20062914Sume /* specifiy to insert router alert option in a hop-by-hop opt hdr. */ 20162914Sume cmsgp = CMSG_NXTHDR(&m, cmsgp); 20262914Sume if (inet6_option_init((void *)cmsgp, &cmsgp, IPV6_HOPOPTS)) 20362914Sume errx(1, "inet6_option_init failed\n"); 20462914Sume raopt[0] = IP6OPT_RTALERT; 20562914Sume raopt[1] = IP6OPT_RTALERT_LEN - 2; 20662914Sume memcpy(&raopt[2], (caddr_t)&rtalert_code, sizeof(u_short)); 20762914Sume if (inet6_option_append(cmsgp, raopt, 4, 0)) 20862914Sume errx(1, "inet6_option_append failed\n"); 20962914Sume} 21062914Sume 21162914Sumevoid 21262914Sumedump(int s) 21362914Sume{ 21462914Sume int i; 21562914Sume struct mld6_hdr *mld; 21662914Sume u_char buf[1024]; 21762914Sume struct sockaddr_in6 from; 21862914Sume int from_len = sizeof(from); 21962914Sume char ntop_buf[256]; 22062914Sume 22162914Sume if ((i = recvfrom(s, buf, sizeof(buf), 0, 22262914Sume (struct sockaddr *)&from, 22362914Sume &from_len)) < 0) 22462914Sume return; 22562914Sume 22662914Sume if (i < sizeof(struct mld6_hdr)) { 22762914Sume printf("too short!\n"); 22862914Sume return; 22962914Sume } 23062914Sume 23162914Sume mld = (struct mld6_hdr *)buf; 23262914Sume 23362914Sume printf("from %s, ", inet_ntop(AF_INET6, &from.sin6_addr, 23462914Sume ntop_buf, sizeof(ntop_buf))); 23562914Sume 23662914Sume switch (mld->mld6_type) { 23762914Sume case ICMP6_MEMBERSHIP_QUERY: 23862914Sume printf("type=Multicast Listener Query, "); 23962914Sume break; 24062914Sume case ICMP6_MEMBERSHIP_REPORT: 24162914Sume printf("type=Multicast Listener Report, "); 24262914Sume break; 24362914Sume case ICMP6_MEMBERSHIP_REDUCTION: 24462914Sume printf("type=Multicast Listener Done, "); 24562914Sume break; 24662914Sume } 24762914Sume printf("addr=%s\n", inet_ntop(AF_INET6, &mld->mld6_addr, 24862914Sume ntop_buf, sizeof(ntop_buf))); 24962914Sume 25062914Sume fflush(stdout); 25162914Sume} 25262914Sume 25362914Sume/* ARGSUSED */ 25462914Sumevoid 25562914Sumequit(int signum) { 25662914Sume mreq.ipv6mr_multiaddr = any; 25762914Sume mreq.ipv6mr_interface = ifindex; 25862914Sume if (setsockopt(s, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, 25962914Sume sizeof(mreq)) == -1) 26062914Sume err(1, "setsockopt(IPV6_LEAVE_GROUP)"); 26162914Sume 26262914Sume exit(0); 26362914Sume} 26462914Sume 26562914Sumevoid 26662914Sumeusage() 26762914Sume{ 26862914Sume (void)fprintf(stderr, "usage: mld6query ifname [addr]\n"); 26962914Sume exit(1); 27062914Sume} 271