]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/rtadvd/if.c
MFC r346960:
[FreeBSD/FreeBSD.git] / usr.sbin / rtadvd / if.c
1 /*      $FreeBSD$       */
2 /*      $KAME: if.c,v 1.17 2001/01/21 15:27:30 itojun Exp $     */
3
4 /*
5  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6  * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the project nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include <sys/param.h>
35 #include <sys/socket.h>
36 #include <sys/sysctl.h>
37 #include <sys/ioctl.h>
38 #include <net/if.h>
39 #include <net/if_dl.h>
40 #include <net/if_types.h>
41 #include <net/ethernet.h>
42 #include <net/route.h>
43 #include <netinet/in.h>
44 #include <netinet/in_var.h>
45 #include <netinet/ip6.h>
46 #include <netinet/icmp6.h>
47 #include <netinet6/nd6.h>
48 #include <unistd.h>
49 #include <errno.h>
50 #include <netdb.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <syslog.h>
54
55 #include "pathnames.h"
56 #include "rtadvd.h"
57 #include "if.h"
58
59 #define ROUNDUP(a, size)                                        \
60         (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
61
62 #define NEXT_SA(ap)                                                     \
63         (ap) = (struct sockaddr *)((caddr_t)(ap) +                      \
64             ((ap)->sa_len ? ROUNDUP((ap)->sa_len, sizeof(u_long)) :     \
65             sizeof(u_long)))
66
67 struct sockaddr_in6 sin6_linklocal_allnodes = {
68         .sin6_len =     sizeof(sin6_linklocal_allnodes),
69         .sin6_family =  AF_INET6,
70         .sin6_addr =    IN6ADDR_LINKLOCAL_ALLNODES_INIT,
71 };
72
73 struct sockaddr_in6 sin6_linklocal_allrouters = {
74         .sin6_len =     sizeof(sin6_linklocal_allrouters),
75         .sin6_family =  AF_INET6,
76         .sin6_addr =    IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,
77 };
78
79 struct sockaddr_in6 sin6_sitelocal_allrouters = {
80         .sin6_len =     sizeof(sin6_sitelocal_allrouters),
81         .sin6_family =  AF_INET6,
82         .sin6_addr =    IN6ADDR_SITELOCAL_ALLROUTERS_INIT,
83 };
84
85 struct sockinfo sock = { .si_fd = -1, .si_name = NULL };
86 struct sockinfo rtsock = { .si_fd = -1, .si_name = NULL };
87 struct sockinfo ctrlsock = { .si_fd = -1, .si_name = _PATH_CTRL_SOCK };
88
89 char *mcastif;
90
91 static void             get_rtaddrs(int, struct sockaddr *,
92                             struct sockaddr **);
93 static struct if_msghdr *get_next_msghdr(struct if_msghdr *,
94                             struct if_msghdr *);
95
96 static void
97 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
98 {
99         int i;
100
101         for (i = 0; i < RTAX_MAX; i++) {
102                 if (addrs & (1 << i)) {
103                         rti_info[i] = sa;
104                         NEXT_SA(sa);
105                 }
106                 else
107                         rti_info[i] = NULL;
108         }
109 }
110
111 #define ROUNDUP8(a) (1 + (((a) - 1) | 7))
112 int
113 lladdropt_length(struct sockaddr_dl *sdl)
114 {
115         switch (sdl->sdl_type) {
116         case IFT_ETHER:
117         case IFT_L2VLAN:
118         case IFT_BRIDGE:
119                 return (ROUNDUP8(ETHER_ADDR_LEN + 2));
120         default:
121                 return (0);
122         }
123 }
124
125 void
126 lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
127 {
128         char *addr;
129
130         ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
131
132         switch (sdl->sdl_type) {
133         case IFT_ETHER:
134         case IFT_L2VLAN:
135         case IFT_BRIDGE:
136                 ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
137                 addr = (char *)(ndopt + 1);
138                 memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
139                 break;
140         default:
141                 syslog(LOG_ERR, "<%s> unsupported link type(%d)",
142                     __func__, sdl->sdl_type);
143                 exit(1);
144         }
145
146         return;
147 }
148
149 int
150 rtbuf_len(void)
151 {
152         size_t len;
153         int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0};
154
155         if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
156                 return (-1);
157
158         return (len);
159 }
160
161 #define FILTER_MATCH(type, filter) ((0x1 << type) & filter)
162 #define SIN6(s) ((struct sockaddr_in6 *)(s))
163 #define SDL(s) ((struct sockaddr_dl *)(s))
164 char *
165 get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter)
166 {
167         struct rt_msghdr *rtm;
168         struct ifa_msghdr *ifam;
169         struct sockaddr *sa, *dst, *gw, *ifa, *rti_info[RTAX_MAX];
170
171         *lenp = 0;
172         for (rtm = (struct rt_msghdr *)buf;
173              rtm < (struct rt_msghdr *)lim;
174              rtm = (struct rt_msghdr *)(((char *)rtm) + rtm->rtm_msglen)) {
175                 /* just for safety */
176                 if (!rtm->rtm_msglen) {
177                         syslog(LOG_WARNING, "<%s> rtm_msglen is 0 "
178                             "(buf=%p lim=%p rtm=%p)", __func__,
179                             buf, lim, rtm);
180                         break;
181                 }
182                 if (((struct rt_msghdr *)buf)->rtm_version != RTM_VERSION) {
183                         syslog(LOG_WARNING,
184                             "<%s> routing message version mismatch "
185                             "(buf=%p lim=%p rtm=%p)", __func__,
186                             buf, lim, rtm);
187                         continue;
188                 }
189
190                 if (FILTER_MATCH(rtm->rtm_type, filter) == 0)
191                         continue;
192
193                 switch (rtm->rtm_type) {
194                 case RTM_GET:
195                 case RTM_ADD:
196                 case RTM_DELETE:
197                         /* address related checks */
198                         sa = (struct sockaddr *)(rtm + 1);
199                         get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
200                         if ((dst = rti_info[RTAX_DST]) == NULL ||
201                             dst->sa_family != AF_INET6)
202                                 continue;
203
204                         if (IN6_IS_ADDR_LINKLOCAL(&SIN6(dst)->sin6_addr) ||
205                             IN6_IS_ADDR_MULTICAST(&SIN6(dst)->sin6_addr))
206                                 continue;
207
208                         if ((gw = rti_info[RTAX_GATEWAY]) == NULL ||
209                             gw->sa_family != AF_LINK)
210                                 continue;
211                         if (ifindex && SDL(gw)->sdl_index != ifindex)
212                                 continue;
213
214                         if (rti_info[RTAX_NETMASK] == NULL)
215                                 continue;
216
217                         /* found */
218                         *lenp = rtm->rtm_msglen;
219                         return (char *)rtm;
220                         /* NOTREACHED */
221                 case RTM_NEWADDR:
222                 case RTM_DELADDR:
223                         ifam = (struct ifa_msghdr *)rtm;
224
225                         /* address related checks */
226                         sa = (struct sockaddr *)(ifam + 1);
227                         get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
228                         if ((ifa = rti_info[RTAX_IFA]) == NULL ||
229                             (ifa->sa_family != AF_INET &&
230                              ifa->sa_family != AF_INET6))
231                                 continue;
232
233                         if (ifa->sa_family == AF_INET6 &&
234                             (IN6_IS_ADDR_LINKLOCAL(&SIN6(ifa)->sin6_addr) ||
235                              IN6_IS_ADDR_MULTICAST(&SIN6(ifa)->sin6_addr)))
236                                 continue;
237
238                         if (ifindex && ifam->ifam_index != ifindex)
239                                 continue;
240
241                         /* found */
242                         *lenp = ifam->ifam_msglen;
243                         return (char *)rtm;
244                         /* NOTREACHED */
245                 case RTM_IFINFO:
246                 case RTM_IFANNOUNCE:
247                         /* found */
248                         *lenp = rtm->rtm_msglen;
249                         return (char *)rtm;
250                         /* NOTREACHED */
251                 }
252         }
253
254         return ((char *)rtm);
255 }
256 #undef FILTER_MATCH
257
258 struct in6_addr *
259 get_addr(char *buf)
260 {
261         struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
262         struct sockaddr *sa, *rti_info[RTAX_MAX];
263
264         sa = (struct sockaddr *)(rtm + 1);
265         get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
266
267         return (&SIN6(rti_info[RTAX_DST])->sin6_addr);
268 }
269
270 int
271 get_rtm_ifindex(char *buf)
272 {
273         struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
274         struct sockaddr *sa, *rti_info[RTAX_MAX];
275
276         sa = (struct sockaddr *)(rtm + 1);
277         get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
278
279         return (((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index);
280 }
281
282 int
283 get_prefixlen(char *buf)
284 {
285         struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
286         struct sockaddr *sa, *rti_info[RTAX_MAX];
287         char *p, *lim;
288
289         sa = (struct sockaddr *)(rtm + 1);
290         get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
291         sa = rti_info[RTAX_NETMASK];
292
293         p = (char *)(&SIN6(sa)->sin6_addr);
294         lim = (char *)sa + sa->sa_len;
295         return prefixlen(p, lim);
296 }
297
298 int
299 prefixlen(unsigned char *p, unsigned char *lim)
300 {
301         int masklen;
302
303         for (masklen = 0; p < lim; p++) {
304                 switch (*p) {
305                 case 0xff:
306                         masklen += 8;
307                         break;
308                 case 0xfe:
309                         masklen += 7;
310                         break;
311                 case 0xfc:
312                         masklen += 6;
313                         break;
314                 case 0xf8:
315                         masklen += 5;
316                         break;
317                 case 0xf0:
318                         masklen += 4;
319                         break;
320                 case 0xe0:
321                         masklen += 3;
322                         break;
323                 case 0xc0:
324                         masklen += 2;
325                         break;
326                 case 0x80:
327                         masklen += 1;
328                         break;
329                 case 0x00:
330                         break;
331                 default:
332                         return (-1);
333                 }
334         }
335
336         return (masklen);
337 }
338
339 struct ifinfo *
340 update_persist_ifinfo(struct ifilist_head_t *ifi_head, const char *ifname)
341 {
342         struct ifinfo *ifi;
343         int ifindex;
344
345         ifi = NULL;
346         ifindex = if_nametoindex(ifname);
347         TAILQ_FOREACH(ifi, ifi_head, ifi_next) {
348                 if (ifindex != 0) {
349                         if (ifindex == ifi->ifi_ifindex)
350                                 break;
351                 } else {
352                         if (strncmp(ifname, ifi->ifi_ifname,
353                                 sizeof(ifi->ifi_ifname)) == 0)
354                                 break;
355                 }
356         }
357
358         if (ifi == NULL) {
359                 /* A new ifinfo element is needed. */
360                 syslog(LOG_DEBUG, "<%s> new entry: %s", __func__,
361                     ifname);
362
363                 ELM_MALLOC(ifi, exit(1));
364                 ifi->ifi_ifindex = 0;
365                 strlcpy(ifi->ifi_ifname, ifname, sizeof(ifi->ifi_ifname));
366                 ifi->ifi_rainfo = NULL;
367                 ifi->ifi_state = IFI_STATE_UNCONFIGURED;
368                 TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next);
369         }
370
371         ifi->ifi_persist = 1;
372
373         syslog(LOG_DEBUG, "<%s> %s is marked PERSIST", __func__,
374             ifi->ifi_ifname);
375         syslog(LOG_DEBUG, "<%s> %s is state = %d", __func__,
376             ifi->ifi_ifname, ifi->ifi_state);
377         return (ifi);
378 }
379
380 int
381 update_ifinfo_nd_flags(struct ifinfo *ifi)
382 {
383         struct in6_ndireq nd;
384         int s;
385         int error;
386
387         if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
388                 syslog(LOG_ERR,
389                     "<%s> socket() failed.", __func__);
390                 return (1);
391         }
392         /* ND flags */
393         memset(&nd, 0, sizeof(nd));
394         strlcpy(nd.ifname, ifi->ifi_ifname,
395             sizeof(nd.ifname));
396         error = ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd);
397         if (error) {
398                 close(s);
399                 if (errno != EPFNOSUPPORT)
400                         syslog(LOG_ERR, "<%s> ioctl() failed.", __func__);
401                 return (1);
402         }
403         ifi->ifi_nd_flags = nd.ndi.flags;
404         close(s);
405
406         return (0);
407 }
408
409 #define MAX_SYSCTL_TRY 5
410
411 struct ifinfo *
412 update_ifinfo(struct ifilist_head_t *ifi_head, int ifindex)
413 {
414         struct if_msghdr *ifm;
415         struct ifinfo *ifi = NULL;
416         struct sockaddr *sa;
417         struct sockaddr *rti_info[RTAX_MAX];
418         char *msg;
419         size_t len;
420         char *lim;
421         int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_IFLIST, 0 };
422         int error, ntry;
423
424         syslog(LOG_DEBUG, "<%s> enter", __func__);
425
426         ntry = 0;
427         do {
428                 /*
429                  * We'll try to get addresses several times in case that
430                  * the number of addresses is unexpectedly increased during
431                  * the two sysctl calls.  This should rarely happen.
432                  * Portability note: since FreeBSD does not add margin of
433                  * memory at the first sysctl, the possibility of failure on
434                  * the second sysctl call is a bit higher.
435                  */
436
437                 if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) < 0) {
438                         syslog(LOG_ERR,
439                             "<%s> sysctl: NET_RT_IFLIST size get failed",
440                             __func__);
441                         exit(1);
442                 }
443                 if ((msg = malloc(len)) == NULL) {
444                         syslog(LOG_ERR, "<%s> malloc failed", __func__);
445                         exit(1);
446                 }
447                 if (sysctl(mib, nitems(mib), msg, &len, NULL, 0) < 0) {
448                         if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
449                                 free(msg);
450                                 syslog(LOG_ERR,
451                                     "<%s> sysctl: NET_RT_IFLIST get failed",
452                                     __func__);
453                                 exit(1);
454                         }
455                         free(msg);
456                         msg = NULL;
457                 }
458         } while (msg == NULL);
459
460         lim = msg + len;
461         for (ifm = (struct if_msghdr *)msg;
462              ifm != NULL && ifm < (struct if_msghdr *)lim;
463              ifm = get_next_msghdr(ifm,(struct if_msghdr *)lim)) {
464                 int ifi_new;
465
466                 syslog(LOG_DEBUG, "<%s> ifm = %p, lim = %p, diff = %zu",
467                     __func__, ifm, lim, (char *)lim - (char *)ifm);
468
469                 if (ifm->ifm_version != RTM_VERSION) {
470                         syslog(LOG_ERR,
471                             "<%s> ifm_vesrion mismatch", __func__);
472                         exit(1);
473                 }
474                 if (ifm->ifm_msglen == 0) {
475                         syslog(LOG_WARNING,
476                             "<%s> ifm_msglen is 0", __func__);
477                         free(msg);
478                         return (NULL);
479                 }
480
481                 ifi_new = 0;
482                 if (ifm->ifm_type == RTM_IFINFO) {
483                         struct ifreq ifr;
484                         int s;
485                         char ifname[IFNAMSIZ];
486
487                         syslog(LOG_DEBUG, "<%s> RTM_IFINFO found. "
488                             "ifm_index = %d, ifindex = %d",
489                             __func__, ifm->ifm_index, ifindex);
490
491                         /* when ifindex is specified */
492                         if (ifindex != UPDATE_IFINFO_ALL &&
493                             ifindex != ifm->ifm_index)
494                                 continue;
495
496                         /* ifname */
497                         if (if_indextoname(ifm->ifm_index, ifname) == NULL) {
498                                 syslog(LOG_WARNING,
499                                     "<%s> ifname not found (idx=%d)",
500                                     __func__, ifm->ifm_index);
501                                 continue;
502                         }
503
504                         /* lookup an entry with the same ifindex */
505                         TAILQ_FOREACH(ifi, ifi_head, ifi_next) {
506                                 if (ifm->ifm_index == ifi->ifi_ifindex)
507                                         break;
508                                 if (strncmp(ifname, ifi->ifi_ifname,
509                                         sizeof(ifname)) == 0)
510                                         break;
511                         }
512                         if (ifi == NULL) {
513                                 syslog(LOG_DEBUG,
514                                     "<%s> new entry for idx=%d",
515                                     __func__, ifm->ifm_index);
516                                 ELM_MALLOC(ifi, exit(1));
517                                 ifi->ifi_rainfo = NULL;
518                                 ifi->ifi_state = IFI_STATE_UNCONFIGURED;
519                                 ifi->ifi_persist = 0;
520                                 ifi_new = 1;
521                         }
522                         /* ifindex */
523                         ifi->ifi_ifindex = ifm->ifm_index;
524
525                         /* ifname */
526                         strlcpy(ifi->ifi_ifname, ifname, IFNAMSIZ);
527
528                         if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
529                                 syslog(LOG_ERR,
530                                     "<%s> socket() failed.", __func__);
531                                 if (ifi_new)
532                                         free(ifi);
533                                 continue;
534                         }
535
536                         /* MTU  */
537                         ifi->ifi_phymtu = ifm->ifm_data.ifi_mtu;
538                         if (ifi->ifi_phymtu == 0) {
539                                 memset(&ifr, 0, sizeof(ifr));
540                                 ifr.ifr_addr.sa_family = AF_INET6;
541                                 strlcpy(ifr.ifr_name, ifi->ifi_ifname,
542                                     sizeof(ifr.ifr_name));
543                                 error = ioctl(s, SIOCGIFMTU, (caddr_t)&ifr);
544                                 if (error) {
545                                         close(s);
546                                         syslog(LOG_ERR,
547                                             "<%s> ioctl() failed.",
548                                             __func__);
549                                         if (ifi_new)
550                                                 free(ifi);
551                                         continue;
552                                 }
553                                 ifi->ifi_phymtu = ifr.ifr_mtu;
554                                 if (ifi->ifi_phymtu == 0) {
555                                         syslog(LOG_WARNING,
556                                             "<%s> no interface mtu info"
557                                             " on %s.  %d will be used.",
558                                             __func__, ifi->ifi_ifname,
559                                             IPV6_MMTU);
560                                         ifi->ifi_phymtu = IPV6_MMTU;
561                                 }
562                         }
563                         close(s);
564
565                         /* ND flags */
566                         error = update_ifinfo_nd_flags(ifi);
567                         if (error) {
568                                 if (ifi_new)
569                                         free(ifi);
570                                 continue;
571                         }
572
573                         /* SDL */
574                         sa = (struct sockaddr *)(ifm + 1);
575                         get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
576                         if ((sa = rti_info[RTAX_IFP]) != NULL) {
577                                 if (sa->sa_family == AF_LINK) {
578                                         memcpy(&ifi->ifi_sdl,
579                                             (struct sockaddr_dl *)sa,
580                                             sizeof(ifi->ifi_sdl));
581                                 }
582                         } else
583                                 memset(&ifi->ifi_sdl, 0,
584                                     sizeof(ifi->ifi_sdl));
585
586                         /* flags */
587                         ifi->ifi_flags = ifm->ifm_flags;
588
589                         /* type */
590                         ifi->ifi_type = ifm->ifm_type;
591                 } else {
592                         syslog(LOG_ERR,
593                             "out of sync parsing NET_RT_IFLIST\n"
594                             "expected %d, got %d\n msglen = %d\n",
595                             RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen);
596                         exit(1);
597                 }
598
599                 if (ifi_new) {
600                         syslog(LOG_DEBUG,
601                             "<%s> adding %s(idx=%d) to ifilist",
602                             __func__, ifi->ifi_ifname, ifi->ifi_ifindex);
603                         TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next);
604                 }
605         }
606         free(msg);
607
608         if (mcastif != NULL) {
609                 error = sock_mc_rr_update(&sock, mcastif);
610                 if (error)
611                         exit(1);
612         }
613
614         return (ifi);
615 }
616
617 static struct if_msghdr *
618 get_next_msghdr(struct if_msghdr *ifm, struct if_msghdr *lim)
619 {
620         struct ifa_msghdr *ifam;
621
622         for (ifam = (struct ifa_msghdr *)((char *)ifm + ifm->ifm_msglen);
623              ifam < (struct ifa_msghdr *)lim;
624              ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen)) {
625                 if (!ifam->ifam_msglen) {
626                         syslog(LOG_WARNING,
627                             "<%s> ifa_msglen is 0", __func__);
628                         return (NULL);
629                 }
630                 if (ifam->ifam_type != RTM_NEWADDR)
631                         break;
632         }
633
634         return ((struct if_msghdr *)ifam);
635 }
636
637 int
638 getinet6sysctl(int code)
639 {
640         int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
641         int value;
642         size_t size;
643
644         mib[3] = code;
645         size = sizeof(value);
646         if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
647             < 0) {
648                 syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
649                     __func__, code,
650                     strerror(errno));
651                 return (-1);
652         }
653         else
654                 return (value);
655 }
656
657
658 int
659 sock_mc_join(struct sockinfo *s, int ifindex)
660 {
661         struct ipv6_mreq mreq;
662         char ifname[IFNAMSIZ];
663
664         syslog(LOG_DEBUG, "<%s> enter", __func__);
665
666         if (ifindex == 0)
667                 return (1);
668
669         /*
670          * join all routers multicast address on each advertising
671          * interface.
672          */
673         memset(&mreq, 0, sizeof(mreq));
674         /* XXX */
675         memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
676             &sin6_linklocal_allrouters.sin6_addr,
677             sizeof(mreq.ipv6mr_multiaddr.s6_addr));
678
679         mreq.ipv6mr_interface = ifindex;
680         if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
681                 sizeof(mreq)) < 0) {
682                 syslog(LOG_ERR,
683                     "<%s> IPV6_JOIN_GROUP(link) on %s: %s",
684                     __func__, if_indextoname(ifindex, ifname),
685                     strerror(errno));
686                 return (1);
687         }
688         syslog(LOG_DEBUG,
689             "<%s> %s: join link-local all-routers MC group",
690             __func__, if_indextoname(ifindex, ifname));
691
692         return (0);
693 }
694
695 int
696 sock_mc_leave(struct sockinfo *s, int ifindex)
697 {
698         struct ipv6_mreq mreq;
699         char ifname[IFNAMSIZ];
700
701         syslog(LOG_DEBUG, "<%s> enter", __func__);
702
703         if (ifindex == 0)
704                 return (1);
705
706         /*
707          * join all routers multicast address on each advertising
708          * interface.
709          */
710
711         memset(&mreq, 0, sizeof(mreq));
712         /* XXX */
713         memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
714             &sin6_linklocal_allrouters.sin6_addr,
715             sizeof(mreq.ipv6mr_multiaddr.s6_addr));
716
717         mreq.ipv6mr_interface = ifindex;
718         if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq,
719                 sizeof(mreq)) < 0) {
720                 syslog(LOG_ERR,
721                     "<%s> IPV6_JOIN_LEAVE(link) on %s: %s",
722                     __func__, if_indextoname(ifindex, ifname),
723                     strerror(errno));
724                 return (1);
725         }
726         syslog(LOG_DEBUG,
727             "<%s> %s: leave link-local all-routers MC group",
728             __func__, if_indextoname(ifindex, ifname));
729
730         return (0);
731 }
732
733 int
734 sock_mc_rr_update(struct sockinfo *s, char *mif)
735 {
736         struct ipv6_mreq mreq;
737
738         syslog(LOG_DEBUG, "<%s> enter", __func__);
739
740         if (mif == NULL)
741                 return (1);
742         /*
743          * When attending router renumbering, join all-routers site-local
744          * multicast group.
745          */
746         /* XXX */
747         memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
748             &sin6_sitelocal_allrouters.sin6_addr,
749             sizeof(mreq.ipv6mr_multiaddr.s6_addr));
750         if ((mreq.ipv6mr_interface = if_nametoindex(mif)) == 0) {
751                 syslog(LOG_ERR,
752                     "<%s> invalid interface: %s",
753                     __func__, mif);
754                 return (1);
755         }
756
757         if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
758                 &mreq, sizeof(mreq)) < 0) {
759                 syslog(LOG_ERR,
760                     "<%s> IPV6_JOIN_GROUP(site) on %s: %s",
761                     __func__, mif, strerror(errno));
762                 return (1);
763         }
764
765         syslog(LOG_DEBUG,
766             "<%s> %s: join site-local all-routers MC group",
767             __func__, mif);
768
769         return (0);
770 }