2 * Copyright (C) 1998 WIDE Project.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the project nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * Copyright (c) 1998 by the University of Southern California.
32 * All rights reserved.
34 * Permission to use, copy, modify, and distribute this software and
35 * its documentation in source and binary forms for lawful
36 * purposes and without fee is hereby granted, provided
37 * that the above copyright notice appear in all copies and that both
38 * the copyright notice and this permission notice appear in supporting
39 * documentation, and that any documentation, advertising materials,
40 * and other materials related to such distribution and use acknowledge
41 * that the software was developed by the University of Southern
42 * California and/or Information Sciences Institute.
43 * The name of the University of Southern California may not
44 * be used to endorse or promote products derived from this software
45 * without specific prior written permission.
47 * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
48 * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
49 * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
50 * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
51 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
54 * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
55 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
56 * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
57 * THE USE OR PERFORMANCE OF THIS SOFTWARE.
59 * Other copyrights might apply to parts of this software and are so
60 * noted when applicable.
63 * Questions concerning this software should be directed to
64 * Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
66 * $Id: mld6.c,v 1.14 2000/10/05 22:20:38 itojun Exp $
69 * Part of this program has been derived from mrouted.
70 * The mrouted program is covered by the license in the accompanying file
71 * named "LICENSE.mrouted".
73 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
74 * Leland Stanford Junior University.
86 char *mld6_recv_buf; /* input packet buffer */
87 char *mld6_send_buf; /* output packet buffer */
88 int mld6_socket; /* socket for all network I/O */
89 struct sockaddr_in6 allrouters_group = {sizeof(struct sockaddr_in6), AF_INET6};
90 struct sockaddr_in6 allnodes_group = {sizeof(struct sockaddr_in6), AF_INET6};
94 extern struct in6_addr in6addr_linklocal_allnodes;
96 /* local variables. */
97 static struct sockaddr_in6 dst = {sizeof(dst), AF_INET6};
98 static struct msghdr sndmh,
100 static struct iovec sndiov[2];
101 static struct iovec rcviov[2];
102 static struct sockaddr_in6 from;
103 static u_char *rcvcmsgbuf = NULL;
104 static int rcvcmsglen;
106 #ifndef USE_RFC2292BIS
107 u_int8_t raopt[IP6OPT_RTALERT_LEN];
109 static char *sndcmsgbuf;
110 static int ctlbuflen = 0;
111 static u_short rtalert_code;
113 /* local functions */
115 static void mld6_read __P((int i, fd_set * fds));
116 static void accept_mld6 __P((int len));
117 static void make_mld6_msg __P((int, int, struct sockaddr_in6 *,
118 struct sockaddr_in6 *, struct in6_addr *, int, int, int, int));
120 #ifndef IP6OPT_ROUTER_ALERT /* XXX to be compatible older systems */
121 #define IP6OPT_ROUTER_ALERT IP6OPT_RTALERT
125 * Open and initialize the MLD socket.
130 struct icmp6_filter filt;
133 rtalert_code = htons(IP6OPT_RTALERT_MLD);
134 if (!mld6_recv_buf && (mld6_recv_buf = malloc(RECV_BUF_SIZE)) == NULL)
135 log(LOG_ERR, 0, "malloc failed");
136 if (!mld6_send_buf && (mld6_send_buf = malloc(RECV_BUF_SIZE)) == NULL)
137 log(LOG_ERR, 0, "malloc failed");
139 rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
140 CMSG_SPACE(sizeof(int));
141 if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL)
142 log(LOG_ERR, 0,"malloc failed");
145 log(LOG_DEBUG,0,"%d octets allocated for the emit/recept buffer mld6",RECV_BUF_SIZE);
147 if ((mld6_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
148 log(LOG_ERR, errno, "MLD6 socket");
150 k_set_rcvbuf(mld6_socket, SO_RECV_BUF_SIZE_MAX,
151 SO_RECV_BUF_SIZE_MIN); /* lots of input buffering */
152 k_set_hlim(mld6_socket, MINHLIM); /* restrict multicasts to one hop */
153 k_set_loop(mld6_socket, FALSE); /* disable multicast loopback */
155 /* address initialization */
156 allnodes_group.sin6_addr = in6addr_linklocal_allnodes;
157 if (inet_pton(AF_INET6, "ff02::2",
158 (void *) &allrouters_group.sin6_addr) != 1)
159 log(LOG_ERR, 0, "inet_pton failed for ff02::2");
161 /* filter all non-MLD ICMP messages */
162 ICMP6_FILTER_SETBLOCKALL(&filt);
163 ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_QUERY, &filt);
164 ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REPORT, &filt);
165 ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REDUCTION, &filt);
166 ICMP6_FILTER_SETPASS(MLD6_MTRACE_RESP, &filt);
167 ICMP6_FILTER_SETPASS(MLD6_MTRACE, &filt);
168 if (setsockopt(mld6_socket, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
170 log(LOG_ERR, errno, "setsockopt(ICMP6_FILTER)");
172 /* specify to tell receiving interface */
174 #ifdef IPV6_RECVPKTINFO
175 if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
177 log(LOG_ERR, errno, "setsockopt(IPV6_RECVPKTINFO)");
178 #else /* old adv. API */
179 if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_PKTINFO, &on,
181 log(LOG_ERR, errno, "setsockopt(IPV6_PKTINFO)");
184 /* specify to tell value of hoplimit field of received IP6 hdr */
185 #ifdef IPV6_RECVHOPLIMIT
186 if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
188 log(LOG_ERR, errno, "setsockopt(IPV6_RECVHOPLIMIT)");
189 #else /* old adv. API */
190 if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
192 log(LOG_ERR, errno, "setsockopt(IPV6_HOPLIMIT)");
194 /* initialize msghdr for receiving packets */
195 rcviov[0].iov_base = (caddr_t) mld6_recv_buf;
196 rcviov[0].iov_len = RECV_BUF_SIZE;
197 rcvmh.msg_name = (caddr_t) & from;
198 rcvmh.msg_namelen = sizeof(from);
199 rcvmh.msg_iov = rcviov;
200 rcvmh.msg_iovlen = 1;
201 rcvmh.msg_control = (caddr_t) rcvcmsgbuf;
202 rcvmh.msg_controllen = rcvcmsglen;
204 /* initialize msghdr for sending packets */
205 sndiov[0].iov_base = (caddr_t)mld6_send_buf;
206 sndmh.msg_namelen = sizeof(struct sockaddr_in6);
207 sndmh.msg_iov = sndiov;
208 sndmh.msg_iovlen = 1;
209 /* specifiy to insert router alert option in a hop-by-hop opt hdr. */
210 #ifndef USE_RFC2292BIS
211 raopt[0] = IP6OPT_ROUTER_ALERT;
212 raopt[1] = IP6OPT_RTALERT_LEN - 2;
213 memcpy(&raopt[2], (caddr_t) & rtalert_code, sizeof(u_short));
216 /* register MLD message handler */
217 if (register_input_handler(mld6_socket, mld6_read) < 0)
219 "Couldn't register mld6_read as an input handler");
222 /* Read an MLD message */
228 register int mld6_recvlen;
230 mld6_recvlen = recvmsg(mld6_socket, &rcvmh, 0);
232 if (mld6_recvlen < 0)
235 log(LOG_ERR, errno, "MLD6 recvmsg");
239 /* TODO: make it as a thread in the future releases */
240 accept_mld6(mld6_recvlen);
244 * Process a newly received MLD6 packet that is sitting in the input packet
251 struct in6_addr *group, *dst = NULL;
252 struct mld6_hdr *mldh;
254 struct in6_pktinfo *pi = NULL;
257 struct sockaddr_in6 *src = (struct sockaddr_in6 *) rcvmh.msg_name;
259 if (recvlen < sizeof(struct mld6_hdr))
262 "received packet too short (%u bytes) for MLD header",
266 mldh = (struct mld6_hdr *) rcvmh.msg_iov[0].iov_base;
269 * Packets sent up from kernel to daemon have ICMPv6 type = 0.
270 * Note that we set filters on the mld6_socket, so we should never
271 * see a "normal" ICMPv6 packet with type 0 of ICMPv6 type.
273 if (mldh->mld6_type == 0) {
274 /* XXX: msg_controllen must be reset in this case. */
275 rcvmh.msg_controllen = rcvcmsglen;
277 process_kernel_call();
281 group = &mldh->mld6_addr;
283 /* extract optional information via Advanced API */
284 for (cm = (struct cmsghdr *) CMSG_FIRSTHDR(&rcvmh);
286 cm = (struct cmsghdr *) CMSG_NXTHDR(&rcvmh, cm))
288 if (cm->cmsg_level == IPPROTO_IPV6 &&
289 cm->cmsg_type == IPV6_PKTINFO &&
290 cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
292 pi = (struct in6_pktinfo *) (CMSG_DATA(cm));
293 ifindex = pi->ipi6_ifindex;
294 dst = &pi->ipi6_addr;
296 if (cm->cmsg_level == IPPROTO_IPV6 &&
297 cm->cmsg_type == IPV6_HOPLIMIT &&
298 cm->cmsg_len == CMSG_LEN(sizeof(int)))
299 hlimp = (int *) CMSG_DATA(cm);
304 "failed to get receiving hop limit");
308 /* TODO: too noisy. Remove it? */
311 IF_DEBUG(DEBUG_PKT | debug_kind(IPPROTO_ICMPV6, mldh->mld6_type,
313 log(LOG_DEBUG, 0, "RECV %s from %s to %s",
314 packet_kind(IPPROTO_ICMPV6,
315 mldh->mld6_type, mldh->mld6_code),
316 inet6_fmt(&src->sin6_addr), inet6_fmt(dst));
317 #endif /* NOSUCHDEF */
319 /* for an mtrace message, we don't need strict checks */
320 if (mldh->mld6_type == MLD6_MTRACE) {
321 accept_mtrace(src, dst, group, ifindex, (char *)(mldh + 1),
322 mldh->mld6_code, recvlen - sizeof(struct mld6_hdr));
326 /* hop limit check */
330 "received an MLD6 message with illegal hop limit(%d) from %s",
331 *hlimp, inet6_fmt(&src->sin6_addr));
332 /* but accept the packet */
336 log(LOG_WARNING, 0, "failed to get receiving interface");
341 if (IN6_IS_ADDR_MC_NODELOCAL(&mldh->mld6_addr))
344 "RECV %s with an invalid scope: %s from %s",
345 packet_kind(IPPROTO_ICMPV6, mldh->mld6_type,
347 inet6_fmt(&mldh->mld6_addr),
348 inet6_fmt(&src->sin6_addr));
349 return; /* discard */
352 /* source address check */
353 if (!IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr))
356 "RECV %s from a non link local address: %s",
357 packet_kind(IPPROTO_ICMPV6, mldh->mld6_type,
359 inet6_fmt(&src->sin6_addr));
363 switch (mldh->mld6_type)
365 case MLD6_LISTENER_QUERY:
366 accept_listener_query(src, dst, group,
367 ntohs(mldh->mld6_maxdelay));
370 case MLD6_LISTENER_REPORT:
371 accept_listener_report(src, dst, group);
374 case MLD6_LISTENER_DONE:
375 accept_listener_done(src, dst, group);
379 /* This must be impossible since we set a type filter */
381 "ignoring unknown ICMPV6 message type %x from %s to %s",
382 mldh->mld6_type, inet6_fmt(&src->sin6_addr),
389 make_mld6_msg(type, code, src, dst, group, ifindex, delay, datalen, alert)
390 int type, code, ifindex, delay, datalen, alert;
391 struct sockaddr_in6 *src, *dst;
392 struct in6_addr *group;
394 static struct sockaddr_in6 dst_sa = {sizeof(dst_sa), AF_INET6};
395 struct mld6_hdr *mhp = (struct mld6_hdr *)mld6_send_buf;
396 int ctllen, hbhlen = 0;
400 case MLD6_MTRACE_RESP:
401 sndmh.msg_name = (caddr_t)dst;
404 if (IN6_IS_ADDR_UNSPECIFIED(group))
405 dst_sa.sin6_addr = allnodes_group.sin6_addr;
407 dst_sa.sin6_addr = *group;
408 sndmh.msg_name = (caddr_t)&dst_sa;
409 datalen = sizeof(struct mld6_hdr);
413 bzero(mhp, sizeof(*mhp));
414 mhp->mld6_type = type;
415 mhp->mld6_code = code;
416 mhp->mld6_maxdelay = htons(delay);
417 mhp->mld6_addr = *group;
419 sndiov[0].iov_len = datalen;
421 /* estimate total ancillary data length */
423 if (ifindex != -1 || src)
424 ctllen += CMSG_SPACE(sizeof(struct in6_pktinfo));
426 #ifdef USE_RFC2292BIS
427 if ((hbhlen = inet6_opt_init(NULL, 0)) == -1)
428 log(LOG_ERR, 0, "inet6_opt_init(0) failed");
429 if ((hbhlen = inet6_opt_append(NULL, 0, hbhlen, IP6OPT_ROUTER_ALERT, 2,
431 log(LOG_ERR, 0, "inet6_opt_append(0) failed");
432 if ((hbhlen = inet6_opt_finish(NULL, 0, hbhlen)) == -1)
433 log(LOG_ERR, 0, "inet6_opt_finish(0) failed");
434 ctllen += CMSG_SPACE(hbhlen);
435 #else /* old advanced API */
436 hbhlen = inet6_option_space(sizeof(raopt));
441 /* extend ancillary data space (if necessary) */
442 if (ctlbuflen < ctllen) {
445 if ((sndcmsgbuf = malloc(ctllen)) == NULL)
446 log(LOG_ERR, 0, "make_mld6_msg: malloc failed"); /* assert */
449 /* store ancillary data */
450 if ((sndmh.msg_controllen = ctllen) > 0) {
451 struct cmsghdr *cmsgp;
453 sndmh.msg_control = sndcmsgbuf;
454 cmsgp = CMSG_FIRSTHDR(&sndmh);
456 if (ifindex != -1 || src) {
457 struct in6_pktinfo *pktinfo;
459 cmsgp->cmsg_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
460 cmsgp->cmsg_level = IPPROTO_IPV6;
461 cmsgp->cmsg_type = IPV6_PKTINFO;
462 pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsgp);
463 memset((caddr_t)pktinfo, 0, sizeof(*pktinfo));
465 pktinfo->ipi6_ifindex = ifindex;
467 pktinfo->ipi6_addr = src->sin6_addr;
468 cmsgp = CMSG_NXTHDR(&sndmh, cmsgp);
471 #ifdef USE_RFC2292BIS
473 void *hbhbuf, *optp = NULL;
475 cmsgp->cmsg_len = CMSG_SPACE(hbhlen);
476 cmsgp->cmsg_level = IPPROTO_IPV6;
477 cmsgp->cmsg_type = IPV6_HOPOPTS;
478 hbhbuf = CMSG_DATA(cmsgp);
480 if ((currentlen = inet6_opt_init(hbhbuf, hbhlen)) == -1)
481 log(LOG_ERR, 0, "inet6_opt_init(len = %d) failed",
483 if ((currentlen = inet6_opt_append(hbhbuf, hbhlen,
485 IP6OPT_ROUTER_ALERT, 2,
488 "inet6_opt_append(len = %d) failed",
490 (void)inet6_opt_set_val(optp, 0, &rtalert_code,
491 sizeof(rtalert_code));
492 if (inet6_opt_finish(hbhbuf, hbhlen, currentlen) == -1)
493 log(LOG_ERR, 0, "inet6_opt_finish(buf) failed");
494 #else /* old advanced API */
495 if (inet6_option_init((void *)cmsgp, &cmsgp, IPV6_HOPOPTS))
496 log(LOG_ERR, 0, /* assert */
497 "make_mld6_msg: inet6_option_init failed");
498 if (inet6_option_append(cmsgp, raopt, 4, 0))
499 log(LOG_ERR, 0, /* assert */
500 "make_mld6_msg: inet6_option_append failed");
502 cmsgp = CMSG_NXTHDR(&sndmh, cmsgp);
506 sndmh.msg_control = NULL; /* clear for safety */
510 send_mld6(type, code, src, dst, group, index, delay, datalen, alert)
512 int code; /* for trace packets only */
513 struct sockaddr_in6 *src;
514 struct sockaddr_in6 *dst; /* may be NULL */
515 struct in6_addr *group;
516 int index, delay, alert;
517 int datalen; /* for trace packets only */
520 struct sockaddr_in6 *dstp;
522 make_mld6_msg(type, code, src, dst, group, index, delay, datalen, alert);
523 dstp = (struct sockaddr_in6 *)sndmh.msg_name;
524 if (IN6_ARE_ADDR_EQUAL(&dstp->sin6_addr, &allnodes_group.sin6_addr)) {
526 k_set_loop(mld6_socket, TRUE);
528 if (sendmsg(mld6_socket, &sndmh, 0) < 0) {
529 if (errno == ENETDOWN)
532 log(log_level(IPPROTO_ICMPV6, type, 0), errno,
533 "sendmsg to %s with src %s on %s",
534 inet6_fmt(&dstp->sin6_addr),
535 src ? inet6_fmt(&src->sin6_addr) : "(unspec)",
539 k_set_loop(mld6_socket, FALSE);
543 IF_DEBUG(DEBUG_PKT|debug_kind(IPPROTO_IGMP, type, 0))
544 log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
545 packet_kind(IPPROTO_ICMPV6, type, 0),
546 src ? inet6_fmt(&src->sin6_addr) : "unspec",
547 inet6_fmt(&dstp->sin6_addr));