if.c revision 300281
1144046Sjcamou/*	$FreeBSD: stable/10/usr.sbin/rtadvd/if.c 300281 2016-05-20 07:00:11Z truckman $	*/
2144046Sjcamou/*	$KAME: if.c,v 1.17 2001/01/21 15:27:30 itojun Exp $	*/
3144046Sjcamou
4144046Sjcamou/*
5144046Sjcamou * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6144046Sjcamou * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org>
7144046Sjcamou * All rights reserved.
8144046Sjcamou *
9144046Sjcamou * Redistribution and use in source and binary forms, with or without
10144046Sjcamou * modification, are permitted provided that the following conditions
11144046Sjcamou * are met:
12144046Sjcamou * 1. Redistributions of source code must retain the above copyright
13144046Sjcamou *    notice, this list of conditions and the following disclaimer.
14144046Sjcamou * 2. Redistributions in binary form must reproduce the above copyright
15144046Sjcamou *    notice, this list of conditions and the following disclaimer in the
16144046Sjcamou *    documentation and/or other materials provided with the distribution.
17144046Sjcamou * 3. Neither the name of the project nor the names of its contributors
18144046Sjcamou *    may be used to endorse or promote products derived from this software
19144046Sjcamou *    without specific prior written permission.
20144046Sjcamou *
21144046Sjcamou * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22144046Sjcamou * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23144046Sjcamou * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24144046Sjcamou * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25144046Sjcamou * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26144046Sjcamou * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27164752Sjoel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28144046Sjcamou * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29144046Sjcamou * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30144046Sjcamou * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31144046Sjcamou * SUCH DAMAGE.
32144046Sjcamou */
33144046Sjcamou
34152984Sjoel#include <sys/param.h>
35152984Sjoel#include <sys/socket.h>
36152984Sjoel#include <sys/sysctl.h>
37144046Sjcamou#include <sys/ioctl.h>
38144046Sjcamou#include <net/if.h>
39152984Sjoel#include <net/if_dl.h>
40152984Sjoel#include <net/if_types.h>
41152984Sjoel#include <net/if_var.h>
42152984Sjoel#include <net/ethernet.h>
43152984Sjoel#include <net/route.h>
44152984Sjoel#include <netinet/in.h>
45152984Sjoel#include <netinet/in_var.h>
46152984Sjoel#include <netinet/ip6.h>
47144046Sjcamou#include <netinet/icmp6.h>
48144046Sjcamou#include <netinet6/nd6.h>
49144046Sjcamou#include <unistd.h>
50144046Sjcamou#include <errno.h>
51144046Sjcamou#include <netdb.h>
52144046Sjcamou#include <stdlib.h>
53144046Sjcamou#include <string.h>
54144046Sjcamou#include <syslog.h>
55164752Sjoel
56164752Sjoel#include "pathnames.h"
57164752Sjoel#include "rtadvd.h"
58164752Sjoel#include "if.h"
59164752Sjoel
60164752Sjoel#define ROUNDUP(a, size)					\
61164752Sjoel	(((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
62164752Sjoel
63164752Sjoel#define	NEXT_SA(ap)							\
64164752Sjoel	(ap) = (struct sockaddr *)((caddr_t)(ap) +			\
65164752Sjoel	    ((ap)->sa_len ? ROUNDUP((ap)->sa_len, sizeof(u_long)) :	\
66164752Sjoel	    sizeof(u_long)))
67164752Sjoel
68164752Sjoelstruct sockaddr_in6 sin6_linklocal_allnodes = {
69169495Sbrueffer        .sin6_len =     sizeof(sin6_linklocal_allnodes),
70144046Sjcamou        .sin6_family =  AF_INET6,
71144046Sjcamou        .sin6_addr =    IN6ADDR_LINKLOCAL_ALLNODES_INIT,
72144046Sjcamou};
73144046Sjcamou
74144046Sjcamoustruct sockaddr_in6 sin6_linklocal_allrouters = {
75144046Sjcamou        .sin6_len =     sizeof(sin6_linklocal_allrouters),
76144046Sjcamou        .sin6_family =  AF_INET6,
77144046Sjcamou        .sin6_addr =    IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,
78144046Sjcamou};
79144046Sjcamou
80144046Sjcamoustruct sockaddr_in6 sin6_sitelocal_allrouters = {
81144046Sjcamou        .sin6_len =     sizeof(sin6_sitelocal_allrouters),
82144046Sjcamou        .sin6_family =  AF_INET6,
83144046Sjcamou        .sin6_addr =    IN6ADDR_SITELOCAL_ALLROUTERS_INIT,
84144046Sjcamou};
85144046Sjcamou
86158014Sbruefferstruct sockinfo sock = { .si_fd = -1, .si_name = NULL };
87158014Sbruefferstruct sockinfo rtsock = { .si_fd = -1, .si_name = NULL };
88144046Sjcamoustruct sockinfo ctrlsock = { .si_fd = -1, .si_name = _PATH_CTRL_SOCK };
89144046Sjcamou
90144046Sjcamouchar *mcastif;
91144046Sjcamou
92144046Sjcamoustatic void		get_rtaddrs(int, struct sockaddr *,
93144046Sjcamou			    struct sockaddr **);
94144046Sjcamoustatic struct if_msghdr	*get_next_msghdr(struct if_msghdr *,
95144046Sjcamou			    struct if_msghdr *);
96152890Sjoel
97152890Sjoelstatic void
98152890Sjoelget_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
99144046Sjcamou{
100147432Sru	int i;
101144046Sjcamou
102147432Sru	for (i = 0; i < RTAX_MAX; i++) {
103144046Sjcamou		if (addrs & (1 << i)) {
104144046Sjcamou			rti_info[i] = sa;
105144046Sjcamou			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		strlcpy(ifi->ifi_ifname, ifname, sizeof(ifi->ifi_ifname));
363		ifi->ifi_rainfo = NULL;
364		ifi->ifi_state = IFI_STATE_UNCONFIGURED;
365		TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next);
366	}
367
368	ifi->ifi_persist = 1;
369
370	syslog(LOG_DEBUG, "<%s> %s is marked PERSIST", __func__,
371	    ifi->ifi_ifname);
372	syslog(LOG_DEBUG, "<%s> %s is state = %d", __func__,
373	    ifi->ifi_ifname, ifi->ifi_state);
374	return (ifi);
375}
376
377int
378update_ifinfo_nd_flags(struct ifinfo *ifi)
379{
380	struct in6_ndireq nd;
381	int s;
382	int error;
383
384	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
385		syslog(LOG_ERR,
386		    "<%s> socket() failed.", __func__);
387		return (1);
388	}
389	/* ND flags */
390	memset(&nd, 0, sizeof(nd));
391	strlcpy(nd.ifname, ifi->ifi_ifname,
392	    sizeof(nd.ifname));
393	error = ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd);
394	if (error) {
395		close(s);
396		if (errno != EPFNOSUPPORT)
397			syslog(LOG_ERR, "<%s> ioctl() failed.", __func__);
398		return (1);
399	}
400	ifi->ifi_nd_flags = nd.ndi.flags;
401	close(s);
402
403	return (0);
404}
405
406struct ifinfo *
407update_ifinfo(struct ifilist_head_t *ifi_head, int ifindex)
408{
409	struct if_msghdr *ifm;
410	struct ifinfo *ifi = NULL;
411	struct sockaddr *sa;
412	struct sockaddr *rti_info[RTAX_MAX];
413	char *msg;
414	size_t len;
415	char *lim;
416	int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_IFLIST, 0 };
417	int error;
418
419	syslog(LOG_DEBUG, "<%s> enter", __func__);
420
421	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), NULL, &len, NULL, 0) <
422	    0) {
423		syslog(LOG_ERR,
424		    "<%s> sysctl: NET_RT_IFLIST size get failed", __func__);
425		exit(1);
426	}
427	if ((msg = malloc(len)) == NULL) {
428		syslog(LOG_ERR, "<%s> malloc failed", __func__);
429		exit(1);
430	}
431	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), msg, &len, NULL, 0) <
432	    0) {
433		syslog(LOG_ERR,
434		    "<%s> sysctl: NET_RT_IFLIST get failed", __func__);
435		exit(1);
436	}
437
438	lim = msg + len;
439	for (ifm = (struct if_msghdr *)msg;
440	     ifm != NULL && ifm < (struct if_msghdr *)lim;
441	     ifm = get_next_msghdr(ifm,(struct if_msghdr *)lim)) {
442		int ifi_new;
443
444		syslog(LOG_DEBUG, "<%s> ifm = %p, lim = %p, diff = %zu",
445		    __func__, ifm, lim, (char *)lim - (char *)ifm);
446
447		if (ifm->ifm_version != RTM_VERSION) {
448			syslog(LOG_ERR,
449			    "<%s> ifm_vesrion mismatch", __func__);
450			exit(1);
451		}
452		if (ifm->ifm_msglen == 0) {
453			syslog(LOG_WARNING,
454			    "<%s> ifm_msglen is 0", __func__);
455			free(msg);
456			return (NULL);
457		}
458
459		ifi_new = 0;
460		if (ifm->ifm_type == RTM_IFINFO) {
461			struct ifreq ifr;
462			int s;
463			char ifname[IFNAMSIZ];
464
465			syslog(LOG_DEBUG, "<%s> RTM_IFINFO found. "
466			    "ifm_index = %d, ifindex = %d",
467			    __func__, ifm->ifm_index, ifindex);
468
469			/* when ifindex is specified */
470			if (ifindex != UPDATE_IFINFO_ALL &&
471			    ifindex != ifm->ifm_index)
472				continue;
473
474			/* lookup an entry with the same ifindex */
475			TAILQ_FOREACH(ifi, ifi_head, ifi_next) {
476				if (ifm->ifm_index == ifi->ifi_ifindex)
477					break;
478				if_indextoname(ifm->ifm_index, ifname);
479				if (strncmp(ifname, ifi->ifi_ifname,
480					sizeof(ifname)) == 0)
481					break;
482			}
483			if (ifi == NULL) {
484				syslog(LOG_DEBUG,
485				    "<%s> new entry for idx=%d",
486				    __func__, ifm->ifm_index);
487				ELM_MALLOC(ifi, exit(1));
488				ifi->ifi_rainfo = NULL;
489				ifi->ifi_state = IFI_STATE_UNCONFIGURED;
490				ifi->ifi_persist = 0;
491				ifi_new = 1;
492			}
493			/* ifindex */
494			ifi->ifi_ifindex = ifm->ifm_index;
495
496			/* ifname */
497			if_indextoname(ifm->ifm_index, ifi->ifi_ifname);
498			if (ifi->ifi_ifname == NULL) {
499				syslog(LOG_WARNING,
500				    "<%s> ifname not found (idx=%d)",
501				    __func__, ifm->ifm_index);
502				if (ifi_new)
503					free(ifi);
504				continue;
505			}
506
507			if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
508				syslog(LOG_ERR,
509				    "<%s> socket() failed.", __func__);
510				if (ifi_new)
511					free(ifi);
512				continue;
513			}
514
515			/* MTU  */
516			ifi->ifi_phymtu = ifm->ifm_data.ifi_mtu;
517			if (ifi->ifi_phymtu == 0) {
518				memset(&ifr, 0, sizeof(ifr));
519				ifr.ifr_addr.sa_family = AF_INET6;
520				strlcpy(ifr.ifr_name, ifi->ifi_ifname,
521				    sizeof(ifr.ifr_name));
522				error = ioctl(s, SIOCGIFMTU, (caddr_t)&ifr);
523				if (error) {
524					close(s);
525					syslog(LOG_ERR,
526					    "<%s> ioctl() failed.",
527					    __func__);
528					if (ifi_new)
529						free(ifi);
530					continue;
531				}
532				ifi->ifi_phymtu = ifr.ifr_mtu;
533				if (ifi->ifi_phymtu == 0) {
534					syslog(LOG_WARNING,
535					    "<%s> no interface mtu info"
536					    " on %s.  %d will be used.",
537					    __func__, ifi->ifi_ifname,
538					    IPV6_MMTU);
539					ifi->ifi_phymtu = IPV6_MMTU;
540				}
541			}
542			close(s);
543
544			/* ND flags */
545			error = update_ifinfo_nd_flags(ifi);
546			if (error) {
547				if (ifi_new)
548					free(ifi);
549				continue;
550			}
551
552			/* SDL */
553			sa = (struct sockaddr *)(ifm + 1);
554			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
555			if ((sa = rti_info[RTAX_IFP]) != NULL) {
556				if (sa->sa_family == AF_LINK) {
557					memcpy(&ifi->ifi_sdl,
558					    (struct sockaddr_dl *)sa,
559					    sizeof(ifi->ifi_sdl));
560				}
561			} else
562				memset(&ifi->ifi_sdl, 0,
563				    sizeof(ifi->ifi_sdl));
564
565			/* flags */
566			ifi->ifi_flags = ifm->ifm_flags;
567
568			/* type */
569			ifi->ifi_type = ifm->ifm_type;
570		} else {
571			syslog(LOG_ERR,
572			    "out of sync parsing NET_RT_IFLIST\n"
573			    "expected %d, got %d\n msglen = %d\n",
574			    RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen);
575			exit(1);
576		}
577
578		if (ifi_new) {
579			syslog(LOG_DEBUG,
580			    "<%s> adding %s(idx=%d) to ifilist",
581			    __func__, ifi->ifi_ifname, ifi->ifi_ifindex);
582			TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next);
583		}
584	}
585	free(msg);
586
587	if (mcastif != NULL) {
588		error = sock_mc_rr_update(&sock, mcastif);
589		if (error)
590			exit(1);
591	}
592
593	return (ifi);
594}
595
596static struct if_msghdr *
597get_next_msghdr(struct if_msghdr *ifm, struct if_msghdr *lim)
598{
599	struct ifa_msghdr *ifam;
600
601	for (ifam = (struct ifa_msghdr *)((char *)ifm + ifm->ifm_msglen);
602	     ifam < (struct ifa_msghdr *)lim;
603	     ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen)) {
604		if (!ifam->ifam_msglen) {
605			syslog(LOG_WARNING,
606			    "<%s> ifa_msglen is 0", __func__);
607			return (NULL);
608		}
609		if (ifam->ifam_type != RTM_NEWADDR)
610			break;
611	}
612
613	return ((struct if_msghdr *)ifam);
614}
615
616int
617getinet6sysctl(int code)
618{
619	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
620	int value;
621	size_t size;
622
623	mib[3] = code;
624	size = sizeof(value);
625	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
626	    < 0) {
627		syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
628		    __func__, code,
629		    strerror(errno));
630		return (-1);
631	}
632	else
633		return (value);
634}
635
636
637int
638sock_mc_join(struct sockinfo *s, int ifindex)
639{
640	struct ipv6_mreq mreq;
641	char ifname[IFNAMSIZ];
642
643	syslog(LOG_DEBUG, "<%s> enter", __func__);
644
645	if (ifindex == 0)
646		return (1);
647
648	/*
649	 * join all routers multicast address on each advertising
650	 * interface.
651	 */
652	memset(&mreq, 0, sizeof(mreq));
653	/* XXX */
654	memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
655	    &sin6_linklocal_allrouters.sin6_addr,
656	    sizeof(mreq.ipv6mr_multiaddr.s6_addr));
657
658	mreq.ipv6mr_interface = ifindex;
659	if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
660		sizeof(mreq)) < 0) {
661		syslog(LOG_ERR,
662		    "<%s> IPV6_JOIN_GROUP(link) on %s: %s",
663		    __func__, if_indextoname(ifindex, ifname),
664		    strerror(errno));
665		return (1);
666	}
667	syslog(LOG_DEBUG,
668	    "<%s> %s: join link-local all-routers MC group",
669	    __func__, if_indextoname(ifindex, ifname));
670
671	return (0);
672}
673
674int
675sock_mc_leave(struct sockinfo *s, int ifindex)
676{
677	struct ipv6_mreq mreq;
678	char ifname[IFNAMSIZ];
679
680	syslog(LOG_DEBUG, "<%s> enter", __func__);
681
682	if (ifindex == 0)
683		return (1);
684
685	/*
686	 * join all routers multicast address on each advertising
687	 * interface.
688	 */
689
690	memset(&mreq, 0, sizeof(mreq));
691	/* XXX */
692	memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
693	    &sin6_linklocal_allrouters.sin6_addr,
694	    sizeof(mreq.ipv6mr_multiaddr.s6_addr));
695
696	mreq.ipv6mr_interface = ifindex;
697	if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq,
698		sizeof(mreq)) < 0) {
699		syslog(LOG_ERR,
700		    "<%s> IPV6_JOIN_LEAVE(link) on %s: %s",
701		    __func__, if_indextoname(ifindex, ifname),
702		    strerror(errno));
703		return (1);
704	}
705	syslog(LOG_DEBUG,
706	    "<%s> %s: leave link-local all-routers MC group",
707	    __func__, if_indextoname(ifindex, ifname));
708
709	return (0);
710}
711
712int
713sock_mc_rr_update(struct sockinfo *s, char *mif)
714{
715	struct ipv6_mreq mreq;
716
717	syslog(LOG_DEBUG, "<%s> enter", __func__);
718
719	if (mif == NULL)
720		return (1);
721	/*
722	 * When attending router renumbering, join all-routers site-local
723	 * multicast group.
724	 */
725	/* XXX */
726	memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
727	    &sin6_sitelocal_allrouters.sin6_addr,
728	    sizeof(mreq.ipv6mr_multiaddr.s6_addr));
729	if ((mreq.ipv6mr_interface = if_nametoindex(mif)) == 0) {
730		syslog(LOG_ERR,
731		    "<%s> invalid interface: %s",
732		    __func__, mif);
733		return (1);
734	}
735
736	if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
737		&mreq, sizeof(mreq)) < 0) {
738		syslog(LOG_ERR,
739		    "<%s> IPV6_JOIN_GROUP(site) on %s: %s",
740		    __func__, mif, strerror(errno));
741		return (1);
742	}
743
744	syslog(LOG_DEBUG,
745	    "<%s> %s: join site-local all-routers MC group",
746	    __func__, mif);
747
748	return (0);
749}
750