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