]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pim6dd/mld6.c
This commit was generated by cvs2svn to compensate for changes in r56893,
[FreeBSD/FreeBSD.git] / usr.sbin / pim6dd / mld6.c
1 /*
2  * Copyright (C) 1998 WIDE Project.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
16  *
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
27  * SUCH DAMAGE.
28  */
29
30 /*
31  *  Copyright (c) 1998 by the University of Southern California.
32  *  All rights reserved.
33  *
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.
46  *
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
52  *  NON-INFRINGEMENT.
53  *
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.
58  *
59  *  Other copyrights might apply to parts of this software and are so
60  *  noted when applicable.
61  */
62 /*
63  *  Questions concerning this software should be directed to
64  *  Pavlin Ivanov Radoslavov (pavlin@catarina.usc.edu)
65  *
66  *  $Id: mld6.c,v 1.7 2000/01/04 17:17:21 jinmei Exp $
67  */
68 /*
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".
72  *
73  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
74  * Leland Stanford Junior University.
75  *
76  * $FreeBSD$
77  */
78
79 #include "defs.h"
80 #include <sys/uio.h>
81
82 /*
83  * Exported variables.
84  */
85
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};
91
92 /* Extenals */
93
94 extern struct in6_addr in6addr_linklocal_allnodes;
95
96 /* local variables. */
97 static struct sockaddr_in6      dst = {sizeof(dst), AF_INET6};
98 static struct msghdr            sndmh,
99                                 rcvmh;
100 static struct iovec             sndiov[2];
101 static struct iovec             rcviov[2];
102 static struct sockaddr_in6      from;
103 static u_char                   rcvcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +
104                                 CMSG_SPACE(sizeof(int))];
105 #ifndef USE_RFC2292BIS
106 u_int8_t raopt[IP6OPT_RTALERT_LEN];
107 #endif
108 static char *sndcmsgbuf;
109 static int ctlbuflen = 0;
110 static u_short rtalert_code;
111
112 /* local functions */
113
114 static void mld6_read __P((int i, fd_set * fds));
115 static void accept_mld6 __P((int len));
116 static void make_mld6_msg __P((int, int, struct sockaddr_in6 *,
117         struct sockaddr_in6 *, struct in6_addr *, int, int, int, int));
118
119 #ifndef IP6OPT_ROUTER_ALERT
120 #define IP6OPT_ROUTER_ALERT IP6OPT_RTALERT
121 #endif
122
123 /*
124  * Open and initialize the MLD socket.
125  */
126 void
127 init_mld6()
128 {
129     struct icmp6_filter filt;
130     int             on;
131
132     rtalert_code = htons(IP6OPT_RTALERT_MLD);
133     if (!mld6_recv_buf && (mld6_recv_buf = malloc(RECV_BUF_SIZE)) == NULL)
134             log(LOG_ERR, 0, "malloca failed");
135     if (!mld6_send_buf && (mld6_send_buf = malloc(RECV_BUF_SIZE)) == NULL)
136             log(LOG_ERR, 0, "malloca failed");
137
138     IF_DEBUG(DEBUG_KERN)
139         log(LOG_DEBUG,0,"%d octets allocated for the emit/recept buffer mld6",RECV_BUF_SIZE);
140
141     if ((mld6_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
142                 log(LOG_ERR, errno, "MLD6 socket");
143
144     k_set_rcvbuf(mld6_socket, SO_RECV_BUF_SIZE_MAX,
145                  SO_RECV_BUF_SIZE_MIN); /* lots of input buffering */
146     k_set_hlim(mld6_socket, MINHLIM);   /* restrict multicasts to one hop */
147     k_set_loop(mld6_socket, FALSE);     /* disable multicast loopback     */
148
149     /* address initialization */
150     allnodes_group.sin6_addr = in6addr_linklocal_allnodes;
151     if (inet_pton(AF_INET6, "ff02::2",
152                   (void *) &allrouters_group.sin6_addr) != 1)
153         log(LOG_ERR, 0, "inet_pton failed for ff02::2");
154
155     /* filter all non-MLD ICMP messages */
156     ICMP6_FILTER_SETBLOCKALL(&filt);
157     ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_QUERY, &filt);
158     ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REPORT, &filt);
159     ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REDUCTION, &filt);
160     ICMP6_FILTER_SETPASS(MLD6_MTRACE_RESP, &filt);
161     ICMP6_FILTER_SETPASS(MLD6_MTRACE, &filt);
162     if (setsockopt(mld6_socket, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
163                    sizeof(filt)) < 0)
164         log(LOG_ERR, errno, "setsockopt(ICMP6_FILTER)");
165
166     /* specify to tell receiving interface */
167     on = 1;
168 #ifdef IPV6_RECVPKTINFO
169     if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
170                    sizeof(on)) < 0)
171         log(LOG_ERR, errno, "setsockopt(IPV6_RECVPKTINFO)");
172 #else  /* old adv. API */
173     if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_PKTINFO, &on,
174                    sizeof(on)) < 0)
175         log(LOG_ERR, errno, "setsockopt(IPV6_PKTINFO)");
176 #endif
177     on = 1;
178     /* specify to tell value of hoplimit field of received IP6 hdr */
179 #ifdef IPV6_RECVHOPLIMIT
180     if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
181                    sizeof(on)) < 0)
182         log(LOG_ERR, errno, "setsockopt(IPV6_RECVHOPLIMIT)");
183 #else  /* old adv. API */
184     if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
185                    sizeof(on)) < 0)
186         log(LOG_ERR, errno, "setsockopt(IPV6_HOPLIMIT)");
187 #endif
188     /* initialize msghdr for receiving packets */
189     rcviov[0].iov_base = (caddr_t) mld6_recv_buf;
190     rcviov[0].iov_len = RECV_BUF_SIZE;
191     rcvmh.msg_name = (caddr_t) & from;
192     rcvmh.msg_namelen = sizeof(from);
193     rcvmh.msg_iov = rcviov;
194     rcvmh.msg_iovlen = 1;
195     rcvmh.msg_control = (caddr_t) rcvcmsgbuf;
196     rcvmh.msg_controllen = sizeof(rcvcmsgbuf);
197
198     /* initialize msghdr for sending packets */
199     sndiov[0].iov_base = (caddr_t)mld6_send_buf;
200     sndmh.msg_namelen = sizeof(struct sockaddr_in6);
201     sndmh.msg_iov = sndiov;
202     sndmh.msg_iovlen = 1;
203     /* specifiy to insert router alert option in a hop-by-hop opt hdr. */
204 #ifndef USE_RFC2292BIS
205     raopt[0] = IP6OPT_ROUTER_ALERT;
206     raopt[1] = IP6OPT_RTALERT_LEN - 2;
207     memcpy(&raopt[2], (caddr_t) & rtalert_code, sizeof(u_short));
208 #endif
209
210     /* register MLD message handler */
211     if (register_input_handler(mld6_socket, mld6_read) < 0)
212         log(LOG_ERR, 0,
213             "Couldn't register mld6_read as an input handler");
214 }
215
216 /* Read an MLD message */
217 static void
218 mld6_read(i, rfd)
219     int             i;
220     fd_set         *rfd;
221 {
222     register int    mld6_recvlen;
223
224     mld6_recvlen = recvmsg(mld6_socket, &rcvmh, 0);
225
226     if (mld6_recvlen < 0)
227     {
228         if (errno != EINTR)
229             log(LOG_ERR, errno, "MLD6 recvmsg");
230         return;
231     }
232
233     /* TODO: make it as a thread in the future releases */
234     accept_mld6(mld6_recvlen);
235 }
236
237 /*
238  * Process a newly received MLD6 packet that is sitting in the input packet
239  * buffer.
240  */
241 static void
242 accept_mld6(recvlen)
243 int recvlen;
244 {
245         struct in6_addr *group, *dst = NULL;
246         struct mld6_hdr *mldh;
247         struct cmsghdr *cm;
248         struct in6_pktinfo *pi = NULL;
249         int *hlimp = NULL;
250         int ifindex = 0;
251         struct sockaddr_in6 *src = (struct sockaddr_in6 *) rcvmh.msg_name;
252
253         /*
254          * If control length is zero, it must be an upcall from the kernel
255          * multicast forwarding engine.
256          * XXX: can we trust it?
257          */
258         if (rcvmh.msg_controllen == 0) {
259                 /* XXX: msg_controllen must be reset in this case. */
260                 rcvmh.msg_controllen = sizeof(rcvcmsgbuf);
261
262                 process_kernel_call();
263                 return;
264         }
265
266         if (recvlen < sizeof(struct mld6_hdr))
267         {
268                 log(LOG_WARNING, 0,
269                     "received packet too short (%u bytes) for MLD header",
270                     recvlen);
271                 return;
272         }
273         mldh = (struct mld6_hdr *) rcvmh.msg_iov[0].iov_base;
274         group = &mldh->mld6_addr;
275
276         /* extract optional information via Advanced API */
277         for (cm = (struct cmsghdr *) CMSG_FIRSTHDR(&rcvmh);
278              cm;
279              cm = (struct cmsghdr *) CMSG_NXTHDR(&rcvmh, cm))
280         {
281                 if (cm->cmsg_level == IPPROTO_IPV6 &&
282                     cm->cmsg_type == IPV6_PKTINFO &&
283                     cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
284                 {
285                         pi = (struct in6_pktinfo *) (CMSG_DATA(cm));
286                         ifindex = pi->ipi6_ifindex;
287                         dst = &pi->ipi6_addr;
288                 }
289                 if (cm->cmsg_level == IPPROTO_IPV6 &&
290                     cm->cmsg_type == IPV6_HOPLIMIT &&
291                     cm->cmsg_len == CMSG_LEN(sizeof(int)))
292                         hlimp = (int *) CMSG_DATA(cm);
293         }
294         if (hlimp == NULL)
295         {
296                 log(LOG_WARNING, 0,
297                     "failed to get receiving hop limit");
298                 return;
299         }
300
301         /* TODO: too noisy. Remove it? */
302 //#define NOSUCHDEF
303 #ifdef NOSUCHDEF
304         IF_DEBUG(DEBUG_PKT | debug_kind(IPPROTO_ICMPV6, mldh->mld6_type,
305                                         mldh->mld6_code))
306                 log(LOG_DEBUG, 0, "RECV %s from %s to %s",
307                     packet_kind(IPPROTO_ICMPV6,
308                                 mldh->mld6_type, mldh->mld6_code),
309                     inet6_fmt(&src->sin6_addr), inet6_fmt(dst));
310 #endif                          /* NOSUCHDEF */
311
312         /* for an mtrace message, we don't need strict checks */
313         if (mldh->mld6_type == MLD6_MTRACE) {
314                 accept_mtrace(src, dst, group, ifindex, (char *)(mldh + 1),
315                               mldh->mld6_code, recvlen - sizeof(struct mld6_hdr));
316                 return;
317         }
318
319         /* hop limit check */
320         if (*hlimp != 1)
321         {
322                 log(LOG_WARNING, 0,
323                     "received an MLD6 message with illegal hop limit(%d) from %s",
324                     *hlimp, inet6_fmt(&src->sin6_addr));
325                 /* but accept the packet */
326         }
327         if (ifindex == 0)
328         {
329                 log(LOG_WARNING, 0, "failed to get receiving interface");
330                 return;
331         }
332
333         /* scope check */
334         if (IN6_IS_ADDR_MC_NODELOCAL(&mldh->mld6_addr))
335         {
336                 log(LOG_INFO, 0,
337                     "RECV %s with an invalid scope: %s from %s",
338                     inet6_fmt(&mldh->mld6_addr),
339                     inet6_fmt(&src->sin6_addr));
340                 return;                 /* discard */
341         }
342
343         /* source address check */
344         if (!IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr))
345         {
346                 log(LOG_INFO, 0,
347                     "RECV %s from a non link local address: %s",
348                     packet_kind(IPPROTO_ICMPV6, mldh->mld6_type,
349                                 mldh->mld6_code),
350                     inet6_fmt(&src->sin6_addr));
351                 return;
352         }
353
354         switch (mldh->mld6_type)
355         {
356         case MLD6_LISTENER_QUERY:
357                 accept_listener_query(src, dst, group,
358                                       ntohs(mldh->mld6_maxdelay));
359                 return;
360
361         case MLD6_LISTENER_REPORT:
362                 accept_listener_report(src, dst, group);
363                 return;
364
365         case MLD6_LISTENER_DONE:
366                 accept_listener_done(src, dst, group);
367                 return;
368
369         default:
370                 /* This must be impossible since we set a type filter */
371                 log(LOG_INFO, 0,
372                     "ignoring unknown ICMPV6 message type %x from %s to %s",
373                     mldh->mld6_type, inet6_fmt(&src->sin6_addr),
374                     inet6_fmt(dst));
375                 return;
376         }
377 }
378
379 static void
380 make_mld6_msg(type, code, src, dst, group, ifindex, delay, datalen, alert)
381     int type, code, ifindex, delay, datalen, alert;
382     struct sockaddr_in6 *src, *dst;
383     struct in6_addr *group;
384 {
385     static struct sockaddr_in6 dst_sa = {sizeof(dst_sa), AF_INET6};
386     struct mld6_hdr *mhp = (struct mld6_hdr *)mld6_send_buf;
387     int ctllen, hbhlen = 0;
388
389     switch(type) {
390     case MLD6_MTRACE:
391     case MLD6_MTRACE_RESP:
392         sndmh.msg_name = (caddr_t)dst;
393         break;
394     default:
395         if (IN6_IS_ADDR_UNSPECIFIED(group))
396             dst_sa.sin6_addr = allnodes_group.sin6_addr;
397         else
398             dst_sa.sin6_addr = *group;
399         sndmh.msg_name = (caddr_t)&dst_sa;
400         datalen = sizeof(struct mld6_hdr);
401         break;
402     }
403
404     bzero(mhp, sizeof(*mhp));
405     mhp->mld6_type = type;
406     mhp->mld6_code = code;
407     mhp->mld6_maxdelay = htons(delay);
408     mhp->mld6_addr = *group;
409
410     sndiov[0].iov_len = datalen;
411
412     /* estimate total ancillary data length */
413     ctllen = 0;
414     if (ifindex != -1 || src)
415             ctllen += CMSG_SPACE(sizeof(struct in6_pktinfo));
416     if (alert) {
417 #ifdef USE_RFC2292BIS
418         if ((hbhlen = inet6_opt_init(NULL, 0)) == -1)
419                 log(LOG_ERR, 0, "inet6_opt_init(0) failed");
420         if ((hbhlen = inet6_opt_append(NULL, 0, hbhlen, IP6OPT_ROUTER_ALERT, 2,
421                                        2, NULL)) == -1)
422                 log(LOG_ERR, 0, "inet6_opt_append(0) failed");
423         if ((hbhlen = inet6_opt_finish(NULL, 0, hbhlen)) == -1)
424                 log(LOG_ERR, 0, "inet6_opt_finish(0) failed");
425 #else  /* old advanced API */
426             hbhlen = inet6_option_space(sizeof(raopt));
427 #endif
428             ctllen += CMSG_SPACE(hbhlen);
429     }
430     /* extend ancillary data space (if necessary) */
431     if (ctlbuflen < ctllen) {
432             if (sndcmsgbuf)
433                     free(sndcmsgbuf);
434             if ((sndcmsgbuf = malloc(ctllen)) == NULL)
435                     log(LOG_ERR, 0, "make_mld6_msg: malloc failed"); /* assert */
436             ctlbuflen = ctllen;
437     }
438     /* store ancillary data */
439     if ((sndmh.msg_controllen = ctllen) > 0) {
440             struct cmsghdr *cmsgp;
441
442             sndmh.msg_control = sndcmsgbuf;
443             cmsgp = CMSG_FIRSTHDR(&sndmh);
444
445             if (ifindex != -1 || src) {
446                     struct in6_pktinfo *pktinfo;
447
448                     cmsgp->cmsg_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
449                     cmsgp->cmsg_level = IPPROTO_IPV6;
450                     cmsgp->cmsg_type = IPV6_PKTINFO;
451                     pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsgp);
452                     memset((caddr_t)pktinfo, 0, sizeof(*pktinfo));
453                     if (ifindex != -1)
454                             pktinfo->ipi6_ifindex = ifindex;
455                     if (src)
456                             pktinfo->ipi6_addr = src->sin6_addr;
457                     cmsgp = CMSG_NXTHDR(&sndmh, cmsgp);
458             }
459             if (alert) {
460 #ifdef USE_RFC2292BIS
461                     int currentlen;
462                     void *hbhbuf, *optp = NULL;
463
464                     cmsgp->cmsg_len = CMSG_SPACE(hbhlen);
465                     cmsgp->cmsg_level = IPPROTO_IPV6;
466                     cmsgp->cmsg_type = IPV6_HOPOPTS;
467                     hbhbuf = CMSG_DATA(cmsgp);
468
469                     if ((currentlen = inet6_opt_init(hbhbuf, hbhlen)) == -1)
470                             log(LOG_ERR, 0, "inet6_opt_init(len = %d) failed",
471                                 hbhlen);
472                     if ((currentlen = inet6_opt_append(hbhbuf, hbhlen,
473                                                        currentlen,
474                                                        IP6OPT_ROUTER_ALERT, 2,
475                                                        2, &optp)) == -1)
476                             log(LOG_ERR, 0,
477                                 "inet6_opt_append(len = %d) failed",
478                                 currentlen, hbhlen);
479                     (void)inet6_opt_set_val(optp, 0, &rtalert_code,
480                                             sizeof(rtalert_code));
481                     if (inet6_opt_finish(hbhbuf, hbhlen, currentlen) == -1)
482                             log(LOG_ERR, 0, "inet6_opt_finish(buf) failed");
483 #else  /* old advanced API */
484                     if (inet6_option_init((void *)cmsgp, &cmsgp, IPV6_HOPOPTS))
485                             log(LOG_ERR, 0, /* assert */
486                                 "make_mld6_msg: inet6_option_init failed");
487                     if (inet6_option_append(cmsgp, raopt, 4, 0))
488                             log(LOG_ERR, 0, /* assert */
489                                 "make_mld6_msg: inet6_option_append failed");
490 #endif
491                     cmsgp = CMSG_NXTHDR(&sndmh, cmsgp);
492             }
493     }
494     else
495             sndmh.msg_control = NULL; /* clear for safety */
496 }
497
498 void
499 send_mld6(type, code, src, dst, group, index, delay, datalen, alert)
500     int type;
501     int code;           /* for trace packets only */
502     struct sockaddr_in6 *src;
503     struct sockaddr_in6 *dst; /* may be NULL */
504     struct in6_addr *group;
505     int index, delay, alert;
506     int datalen;                /* for trace packets only */
507 {
508     int setloop = 0;
509     struct sockaddr_in6 *dstp;
510
511     make_mld6_msg(type, code, src, dst, group, index, delay, datalen, alert);
512     dstp = (struct sockaddr_in6 *)sndmh.msg_name;
513     if (IN6_ARE_ADDR_EQUAL(&dstp->sin6_addr, &allnodes_group.sin6_addr)) {
514         setloop = 1;
515         k_set_loop(mld6_socket, TRUE);
516     }
517     if (sendmsg(mld6_socket, &sndmh, 0) < 0) {
518         if (errno == ENETDOWN)
519             check_vif_state();
520         else
521             log(log_level(IPPROTO_ICMPV6, type, 0), errno,
522                 "sendmsg to %s with src %s on %s",
523                 inet6_fmt(&dstp->sin6_addr),
524                 src ? inet6_fmt(&src->sin6_addr) : "(unspec)",
525                 ifindex2str(index));
526
527         if (setloop)
528             k_set_loop(mld6_socket, FALSE);
529         return;
530     }
531
532     IF_DEBUG(DEBUG_PKT|debug_kind(IPPROTO_IGMP, type, 0))
533         log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
534             packet_kind(IPPROTO_ICMPV6, type, 0),
535             src ? inet6_fmt(&src->sin6_addr) : "unspec",
536             inet6_fmt(&dstp->sin6_addr));
537 }