]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pim6dd/mld6.c
This commit was generated by cvs2svn to compensate for changes in r69836,
[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.14 2000/10/05 22:20:38 itojun 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 = NULL;
104 static int                      rcvcmsglen;
105
106 #ifndef USE_RFC2292BIS
107 u_int8_t raopt[IP6OPT_RTALERT_LEN];
108 #endif 
109 static char *sndcmsgbuf;
110 static int ctlbuflen = 0;
111 static u_short rtalert_code;
112
113 /* local functions */
114
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));
119
120 #ifndef IP6OPT_ROUTER_ALERT     /* XXX to be compatible older systems */
121 #define IP6OPT_ROUTER_ALERT IP6OPT_RTALERT
122 #endif
123
124 /*
125  * Open and initialize the MLD socket.
126  */
127 void
128 init_mld6()
129 {
130     struct icmp6_filter filt;
131     int             on;
132
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");
138
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");
143     
144     IF_DEBUG(DEBUG_KERN)
145         log(LOG_DEBUG,0,"%d octets allocated for the emit/recept buffer mld6",RECV_BUF_SIZE);
146
147     if ((mld6_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
148                 log(LOG_ERR, errno, "MLD6 socket");
149
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     */
154
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");
160
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,
169                    sizeof(filt)) < 0)
170         log(LOG_ERR, errno, "setsockopt(ICMP6_FILTER)");
171
172     /* specify to tell receiving interface */
173     on = 1;
174 #ifdef IPV6_RECVPKTINFO
175     if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
176                    sizeof(on)) < 0)
177         log(LOG_ERR, errno, "setsockopt(IPV6_RECVPKTINFO)");
178 #else  /* old adv. API */
179     if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_PKTINFO, &on,
180                    sizeof(on)) < 0)
181         log(LOG_ERR, errno, "setsockopt(IPV6_PKTINFO)");
182 #endif 
183     on = 1;
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,
187                    sizeof(on)) < 0)
188         log(LOG_ERR, errno, "setsockopt(IPV6_RECVHOPLIMIT)");
189 #else  /* old adv. API */
190     if (setsockopt(mld6_socket, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
191                    sizeof(on)) < 0)
192         log(LOG_ERR, errno, "setsockopt(IPV6_HOPLIMIT)");
193 #endif 
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;
203
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));
214 #endif 
215
216     /* register MLD message handler */
217     if (register_input_handler(mld6_socket, mld6_read) < 0)
218         log(LOG_ERR, 0,
219             "Couldn't register mld6_read as an input handler");
220 }
221
222 /* Read an MLD message */
223 static void
224 mld6_read(i, rfd)
225     int             i;
226     fd_set         *rfd;
227 {
228     register int    mld6_recvlen;
229
230     mld6_recvlen = recvmsg(mld6_socket, &rcvmh, 0);
231
232     if (mld6_recvlen < 0)
233     {
234         if (errno != EINTR)
235             log(LOG_ERR, errno, "MLD6 recvmsg");
236         return;
237     }
238
239     /* TODO: make it as a thread in the future releases */
240     accept_mld6(mld6_recvlen);
241 }
242
243 /*
244  * Process a newly received MLD6 packet that is sitting in the input packet
245  * buffer.
246  */
247 static void
248 accept_mld6(recvlen)
249 int recvlen;
250 {
251         struct in6_addr *group, *dst = NULL;
252         struct mld6_hdr *mldh;
253         struct cmsghdr *cm;
254         struct in6_pktinfo *pi = NULL;
255         int *hlimp = NULL;
256         int ifindex = 0;
257         struct sockaddr_in6 *src = (struct sockaddr_in6 *) rcvmh.msg_name;
258
259         if (recvlen < sizeof(struct mld6_hdr))
260         {
261                 log(LOG_WARNING, 0,
262                     "received packet too short (%u bytes) for MLD header",
263                     recvlen);
264                 return;
265         }
266         mldh = (struct mld6_hdr *) rcvmh.msg_iov[0].iov_base;
267
268         /*
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.
272          */
273         if (mldh->mld6_type == 0) {
274                 /* XXX: msg_controllen must be reset in this case. */
275                 rcvmh.msg_controllen = rcvcmsglen;
276
277                 process_kernel_call();
278                 return;
279         }
280
281         group = &mldh->mld6_addr;
282
283         /* extract optional information via Advanced API */
284         for (cm = (struct cmsghdr *) CMSG_FIRSTHDR(&rcvmh);
285              cm;
286              cm = (struct cmsghdr *) CMSG_NXTHDR(&rcvmh, cm))
287         {
288                 if (cm->cmsg_level == IPPROTO_IPV6 &&
289                     cm->cmsg_type == IPV6_PKTINFO &&
290                     cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
291                 {
292                         pi = (struct in6_pktinfo *) (CMSG_DATA(cm));
293                         ifindex = pi->ipi6_ifindex;
294                         dst = &pi->ipi6_addr;
295                 }
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);
300         }
301         if (hlimp == NULL)
302         {
303                 log(LOG_WARNING, 0,
304                     "failed to get receiving hop limit");
305                 return;
306         }
307
308         /* TODO: too noisy. Remove it? */
309 //#define NOSUCHDEF
310 #ifdef NOSUCHDEF
311         IF_DEBUG(DEBUG_PKT | debug_kind(IPPROTO_ICMPV6, mldh->mld6_type,
312                                         mldh->mld6_code))
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 */
318
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));
323                 return;
324         }
325
326         /* hop limit check */
327         if (*hlimp != 1)
328         {
329                 log(LOG_WARNING, 0,
330                     "received an MLD6 message with illegal hop limit(%d) from %s",
331                     *hlimp, inet6_fmt(&src->sin6_addr));
332                 /* but accept the packet */
333         }
334         if (ifindex == 0)
335         {
336                 log(LOG_WARNING, 0, "failed to get receiving interface");
337                 return;
338         }
339
340         /* scope check */
341         if (IN6_IS_ADDR_MC_NODELOCAL(&mldh->mld6_addr))
342         {
343                 log(LOG_INFO, 0,
344                     "RECV %s with an invalid scope: %s from %s",
345                     packet_kind(IPPROTO_ICMPV6, mldh->mld6_type,
346                                 mldh->mld6_code),
347                     inet6_fmt(&mldh->mld6_addr),
348                     inet6_fmt(&src->sin6_addr));
349                 return;                 /* discard */
350         }
351
352         /* source address check */
353         if (!IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr))
354         {
355                 log(LOG_INFO, 0,
356                     "RECV %s from a non link local address: %s",
357                     packet_kind(IPPROTO_ICMPV6, mldh->mld6_type,
358                                 mldh->mld6_code),
359                     inet6_fmt(&src->sin6_addr));
360                 return;
361         }
362
363         switch (mldh->mld6_type)
364         {
365         case MLD6_LISTENER_QUERY:
366                 accept_listener_query(src, dst, group,
367                                       ntohs(mldh->mld6_maxdelay));
368                 return;
369
370         case MLD6_LISTENER_REPORT:
371                 accept_listener_report(src, dst, group);
372                 return;
373
374         case MLD6_LISTENER_DONE:
375                 accept_listener_done(src, dst, group);
376                 return;
377
378         default:
379                 /* This must be impossible since we set a type filter */
380                 log(LOG_INFO, 0,
381                     "ignoring unknown ICMPV6 message type %x from %s to %s",
382                     mldh->mld6_type, inet6_fmt(&src->sin6_addr),
383                     inet6_fmt(dst));
384                 return;
385         }
386 }
387
388 static void
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;
393 {
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;
397
398     switch(type) {
399     case MLD6_MTRACE:
400     case MLD6_MTRACE_RESP:
401         sndmh.msg_name = (caddr_t)dst;
402         break;
403     default:
404         if (IN6_IS_ADDR_UNSPECIFIED(group))
405             dst_sa.sin6_addr = allnodes_group.sin6_addr;
406         else
407             dst_sa.sin6_addr = *group;
408         sndmh.msg_name = (caddr_t)&dst_sa;
409         datalen = sizeof(struct mld6_hdr);
410         break;
411     }
412    
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;
418
419     sndiov[0].iov_len = datalen;
420
421     /* estimate total ancillary data length */
422     ctllen = 0;
423     if (ifindex != -1 || src)
424             ctllen += CMSG_SPACE(sizeof(struct in6_pktinfo));
425     if (alert) {
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,
430                                        2, NULL)) == -1)
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));
437         ctllen += hbhlen;
438 #endif
439         
440     }
441     /* extend ancillary data space (if necessary) */
442     if (ctlbuflen < ctllen) {
443             if (sndcmsgbuf)
444                     free(sndcmsgbuf);
445             if ((sndcmsgbuf = malloc(ctllen)) == NULL)
446                     log(LOG_ERR, 0, "make_mld6_msg: malloc failed"); /* assert */
447             ctlbuflen = ctllen;
448     }
449     /* store ancillary data */
450     if ((sndmh.msg_controllen = ctllen) > 0) {
451             struct cmsghdr *cmsgp;
452
453             sndmh.msg_control = sndcmsgbuf;
454             cmsgp = CMSG_FIRSTHDR(&sndmh);
455
456             if (ifindex != -1 || src) {
457                     struct in6_pktinfo *pktinfo;
458
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));
464                     if (ifindex != -1)
465                             pktinfo->ipi6_ifindex = ifindex;
466                     if (src)
467                             pktinfo->ipi6_addr = src->sin6_addr;
468                     cmsgp = CMSG_NXTHDR(&sndmh, cmsgp);
469             }
470             if (alert) {
471 #ifdef USE_RFC2292BIS
472                     int currentlen;
473                     void *hbhbuf, *optp = NULL;
474
475                     cmsgp->cmsg_len = CMSG_SPACE(hbhlen);
476                     cmsgp->cmsg_level = IPPROTO_IPV6;
477                     cmsgp->cmsg_type = IPV6_HOPOPTS;
478                     hbhbuf = CMSG_DATA(cmsgp);
479
480                     if ((currentlen = inet6_opt_init(hbhbuf, hbhlen)) == -1)
481                             log(LOG_ERR, 0, "inet6_opt_init(len = %d) failed",
482                                 hbhlen);
483                     if ((currentlen = inet6_opt_append(hbhbuf, hbhlen,
484                                                        currentlen,
485                                                        IP6OPT_ROUTER_ALERT, 2,
486                                                        2, &optp)) == -1)
487                             log(LOG_ERR, 0,
488                                 "inet6_opt_append(len = %d) failed",
489                                 currentlen, hbhlen);
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");
501 #endif 
502                     cmsgp = CMSG_NXTHDR(&sndmh, cmsgp);
503             }
504     }
505     else
506             sndmh.msg_control = NULL; /* clear for safety */
507 }
508
509 void
510 send_mld6(type, code, src, dst, group, index, delay, datalen, alert)
511     int type;
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 */
518 {
519     int setloop = 0;
520     struct sockaddr_in6 *dstp;
521         
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)) {
525         setloop = 1;
526         k_set_loop(mld6_socket, TRUE);
527     }
528     if (sendmsg(mld6_socket, &sndmh, 0) < 0) {
529         if (errno == ENETDOWN)
530             check_vif_state();
531         else
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)",
536                 ifindex2str(index));
537
538         if (setloop)
539             k_set_loop(mld6_socket, FALSE);
540         return;
541     }
542     
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));
548 }