171333Sitojun/*	$FreeBSD: stable/10/usr.sbin/rtadvd/if.c 308717 2016-11-16 03:54:41Z hrs $	*/
278064Sume/*	$KAME: if.c,v 1.17 2001/01/21 15:27:30 itojun Exp $	*/
362656Skris
455505Sshin/*
555505Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6224144Shrs * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org>
755505Sshin * All rights reserved.
8222732Shrs *
955505Sshin * Redistribution and use in source and binary forms, with or without
1055505Sshin * modification, are permitted provided that the following conditions
1155505Sshin * are met:
1255505Sshin * 1. Redistributions of source code must retain the above copyright
1355505Sshin *    notice, this list of conditions and the following disclaimer.
1455505Sshin * 2. Redistributions in binary form must reproduce the above copyright
1555505Sshin *    notice, this list of conditions and the following disclaimer in the
1655505Sshin *    documentation and/or other materials provided with the distribution.
1755505Sshin * 3. Neither the name of the project nor the names of its contributors
1855505Sshin *    may be used to endorse or promote products derived from this software
1955505Sshin *    without specific prior written permission.
20222732Shrs *
2155505Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2255505Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355505Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455505Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2555505Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655505Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755505Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855505Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955505Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055505Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155505Sshin * SUCH DAMAGE.
3255505Sshin */
3355505Sshin
3455505Sshin#include <sys/param.h>
3555505Sshin#include <sys/socket.h>
3655505Sshin#include <sys/sysctl.h>
3755505Sshin#include <sys/ioctl.h>
3855505Sshin#include <net/if.h>
39224144Shrs#include <net/if_dl.h>
4055505Sshin#include <net/if_types.h>
41224144Shrs#include <net/if_var.h>
42118787Sume#include <net/ethernet.h>
4355505Sshin#include <net/route.h>
4455505Sshin#include <netinet/in.h>
45224144Shrs#include <netinet/in_var.h>
46224144Shrs#include <netinet/ip6.h>
4755505Sshin#include <netinet/icmp6.h>
48224144Shrs#include <netinet6/nd6.h>
4955505Sshin#include <unistd.h>
5055505Sshin#include <errno.h>
51222732Shrs#include <netdb.h>
5255505Sshin#include <stdlib.h>
5355505Sshin#include <string.h>
5455505Sshin#include <syslog.h>
55224144Shrs
56224144Shrs#include "pathnames.h"
5755505Sshin#include "rtadvd.h"
5855505Sshin#include "if.h"
5955505Sshin
60222732Shrs#define ROUNDUP(a, size)					\
6155505Sshin	(((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
6255505Sshin
63222732Shrs#define	NEXT_SA(ap)							\
64222732Shrs	(ap) = (struct sockaddr *)((caddr_t)(ap) +			\
65222732Shrs	    ((ap)->sa_len ? ROUNDUP((ap)->sa_len, sizeof(u_long)) :	\
66222732Shrs	    sizeof(u_long)))
6755505Sshin
68224144Shrsstruct sockaddr_in6 sin6_linklocal_allnodes = {
69224144Shrs        .sin6_len =     sizeof(sin6_linklocal_allnodes),
70224144Shrs        .sin6_family =  AF_INET6,
71224144Shrs        .sin6_addr =    IN6ADDR_LINKLOCAL_ALLNODES_INIT,
72224144Shrs};
7355505Sshin
74224144Shrsstruct sockaddr_in6 sin6_linklocal_allrouters = {
75224144Shrs        .sin6_len =     sizeof(sin6_linklocal_allrouters),
76224144Shrs        .sin6_family =  AF_INET6,
77224144Shrs        .sin6_addr =    IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,
78224144Shrs};
7955505Sshin
80224144Shrsstruct sockaddr_in6 sin6_sitelocal_allrouters = {
81224144Shrs        .sin6_len =     sizeof(sin6_sitelocal_allrouters),
82224144Shrs        .sin6_family =  AF_INET6,
83224144Shrs        .sin6_addr =    IN6ADDR_SITELOCAL_ALLROUTERS_INIT,
84224144Shrs};
85224144Shrs
86224144Shrsstruct sockinfo sock = { .si_fd = -1, .si_name = NULL };
87224144Shrsstruct sockinfo rtsock = { .si_fd = -1, .si_name = NULL };
88224144Shrsstruct sockinfo ctrlsock = { .si_fd = -1, .si_name = _PATH_CTRL_SOCK };
89224144Shrs
90224144Shrschar *mcastif;
91224144Shrs
92224144Shrsstatic void		get_rtaddrs(int, struct sockaddr *,
93224144Shrs			    struct sockaddr **);
94224144Shrsstatic struct if_msghdr	*get_next_msghdr(struct if_msghdr *,
95224144Shrs			    struct if_msghdr *);
96224144Shrs
9755505Sshinstatic void
9855505Sshinget_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
9955505Sshin{
10055505Sshin	int i;
101222732Shrs
10255505Sshin	for (i = 0; i < RTAX_MAX; i++) {
10355505Sshin		if (addrs & (1 << i)) {
10455505Sshin			rti_info[i] = sa;
10555505Sshin			NEXT_SA(sa);
10655505Sshin		}
10755505Sshin		else
10855505Sshin			rti_info[i] = NULL;
10955505Sshin	}
11055505Sshin}
11155505Sshin
11262656Skris#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
11355505Sshinint
11455505Sshinlladdropt_length(struct sockaddr_dl *sdl)
11555505Sshin{
116118664Sume	switch (sdl->sdl_type) {
117118664Sume	case IFT_ETHER:
118308717Shrs	case IFT_L2VLAN:
119308717Shrs	case IFT_BRIDGE:
120222732Shrs		return (ROUNDUP8(ETHER_ADDR_LEN + 2));
121118664Sume	default:
122222732Shrs		return (0);
12355505Sshin	}
12455505Sshin}
12555505Sshin
12655505Sshinvoid
12755505Sshinlladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
12855505Sshin{
12955505Sshin	char *addr;
13055505Sshin
13155505Sshin	ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
13255505Sshin
133118664Sume	switch (sdl->sdl_type) {
134118664Sume	case IFT_ETHER:
135308717Shrs	case IFT_L2VLAN:
136308717Shrs	case IFT_BRIDGE:
137118664Sume		ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
138118664Sume		addr = (char *)(ndopt + 1);
139118664Sume		memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
140118664Sume		break;
141118664Sume	default:
142118664Sume		syslog(LOG_ERR, "<%s> unsupported link type(%d)",
143118664Sume		    __func__, sdl->sdl_type);
144118664Sume		exit(1);
14555505Sshin	}
14655505Sshin
14755505Sshin	return;
14855505Sshin}
14955505Sshin
15055505Sshinint
151222732Shrsrtbuf_len(void)
15255505Sshin{
15355505Sshin	size_t len;
15455505Sshin	int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0};
15555505Sshin
15655505Sshin	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
157222732Shrs		return (-1);
15855505Sshin
159222732Shrs	return (len);
16055505Sshin}
16155505Sshin
16262656Skris#define FILTER_MATCH(type, filter) ((0x1 << type) & filter)
16362656Skris#define SIN6(s) ((struct sockaddr_in6 *)(s))
16462656Skris#define SDL(s) ((struct sockaddr_dl *)(s))
16555505Sshinchar *
16655505Sshinget_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter)
16755505Sshin{
16855505Sshin	struct rt_msghdr *rtm;
16955505Sshin	struct ifa_msghdr *ifam;
17055505Sshin	struct sockaddr *sa, *dst, *gw, *ifa, *rti_info[RTAX_MAX];
17155505Sshin
17255505Sshin	*lenp = 0;
17355505Sshin	for (rtm = (struct rt_msghdr *)buf;
17455505Sshin	     rtm < (struct rt_msghdr *)lim;
17555505Sshin	     rtm = (struct rt_msghdr *)(((char *)rtm) + rtm->rtm_msglen)) {
17655505Sshin		/* just for safety */
17755505Sshin		if (!rtm->rtm_msglen) {
17855505Sshin			syslog(LOG_WARNING, "<%s> rtm_msglen is 0 "
179222732Shrs			    "(buf=%p lim=%p rtm=%p)", __func__,
180222732Shrs			    buf, lim, rtm);
18155505Sshin			break;
18255505Sshin		}
183222732Shrs		if (((struct rt_msghdr *)buf)->rtm_version != RTM_VERSION) {
184222732Shrs			syslog(LOG_WARNING,
185222732Shrs			    "<%s> routing message version mismatch "
186222732Shrs			    "(buf=%p lim=%p rtm=%p)", __func__,
187222732Shrs			    buf, lim, rtm);
18855505Sshin			continue;
18955505Sshin		}
19055505Sshin
191222732Shrs		if (FILTER_MATCH(rtm->rtm_type, filter) == 0)
192222732Shrs			continue;
193222732Shrs
19455505Sshin		switch (rtm->rtm_type) {
19555505Sshin		case RTM_GET:
19655505Sshin		case RTM_ADD:
19755505Sshin		case RTM_DELETE:
19855505Sshin			/* address related checks */
19955505Sshin			sa = (struct sockaddr *)(rtm + 1);
20055505Sshin			get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
20155505Sshin			if ((dst = rti_info[RTAX_DST]) == NULL ||
20255505Sshin			    dst->sa_family != AF_INET6)
20355505Sshin				continue;
20455505Sshin
20555505Sshin			if (IN6_IS_ADDR_LINKLOCAL(&SIN6(dst)->sin6_addr) ||
20655505Sshin			    IN6_IS_ADDR_MULTICAST(&SIN6(dst)->sin6_addr))
20755505Sshin				continue;
20855505Sshin
20955505Sshin			if ((gw = rti_info[RTAX_GATEWAY]) == NULL ||
21055505Sshin			    gw->sa_family != AF_LINK)
21155505Sshin				continue;
21255505Sshin			if (ifindex && SDL(gw)->sdl_index != ifindex)
21355505Sshin				continue;
21455505Sshin
21555505Sshin			if (rti_info[RTAX_NETMASK] == NULL)
21655505Sshin				continue;
21755505Sshin
21855505Sshin			/* found */
21955505Sshin			*lenp = rtm->rtm_msglen;
22055505Sshin			return (char *)rtm;
22155505Sshin			/* NOTREACHED */
22255505Sshin		case RTM_NEWADDR:
22355505Sshin		case RTM_DELADDR:
22455505Sshin			ifam = (struct ifa_msghdr *)rtm;
22555505Sshin
22655505Sshin			/* address related checks */
22755505Sshin			sa = (struct sockaddr *)(ifam + 1);
22855505Sshin			get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
22955505Sshin			if ((ifa = rti_info[RTAX_IFA]) == NULL ||
23055505Sshin			    (ifa->sa_family != AF_INET &&
23155505Sshin			     ifa->sa_family != AF_INET6))
23255505Sshin				continue;
23355505Sshin
23455505Sshin			if (ifa->sa_family == AF_INET6 &&
23555505Sshin			    (IN6_IS_ADDR_LINKLOCAL(&SIN6(ifa)->sin6_addr) ||
23655505Sshin			     IN6_IS_ADDR_MULTICAST(&SIN6(ifa)->sin6_addr)))
23755505Sshin				continue;
23855505Sshin
23955505Sshin			if (ifindex && ifam->ifam_index != ifindex)
24055505Sshin				continue;
24155505Sshin
24255505Sshin			/* found */
24355505Sshin			*lenp = ifam->ifam_msglen;
24455505Sshin			return (char *)rtm;
24555505Sshin			/* NOTREACHED */
24655505Sshin		case RTM_IFINFO:
247222732Shrs		case RTM_IFANNOUNCE:
24855505Sshin			/* found */
24955505Sshin			*lenp = rtm->rtm_msglen;
25055505Sshin			return (char *)rtm;
25155505Sshin			/* NOTREACHED */
25255505Sshin		}
25355505Sshin	}
25455505Sshin
255222732Shrs	return ((char *)rtm);
25655505Sshin}
25778064Sume#undef FILTER_MATCH
25855505Sshin
25955505Sshinstruct in6_addr *
26055505Sshinget_addr(char *buf)
26155505Sshin{
26255505Sshin	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
26355505Sshin	struct sockaddr *sa, *rti_info[RTAX_MAX];
26455505Sshin
26555505Sshin	sa = (struct sockaddr *)(rtm + 1);
26655505Sshin	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
26755505Sshin
268222732Shrs	return (&SIN6(rti_info[RTAX_DST])->sin6_addr);
26955505Sshin}
27055505Sshin
27155505Sshinint
27255505Sshinget_rtm_ifindex(char *buf)
27355505Sshin{
27455505Sshin	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
27555505Sshin	struct sockaddr *sa, *rti_info[RTAX_MAX];
27655505Sshin
27755505Sshin	sa = (struct sockaddr *)(rtm + 1);
27855505Sshin	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
27955505Sshin
280222732Shrs	return (((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index);
28155505Sshin}
28255505Sshin
28355505Sshinint
28455505Sshinget_prefixlen(char *buf)
28555505Sshin{
28655505Sshin	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
28755505Sshin	struct sockaddr *sa, *rti_info[RTAX_MAX];
288224144Shrs	char *p, *lim;
289222732Shrs
29055505Sshin	sa = (struct sockaddr *)(rtm + 1);
29155505Sshin	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
29255505Sshin	sa = rti_info[RTAX_NETMASK];
29355505Sshin
294224144Shrs	p = (char *)(&SIN6(sa)->sin6_addr);
295224144Shrs	lim = (char *)sa + sa->sa_len;
29667801Sume	return prefixlen(p, lim);
29767801Sume}
29867801Sume
29967801Sumeint
300224144Shrsprefixlen(unsigned char *p, unsigned char *lim)
30167801Sume{
30267801Sume	int masklen;
30367801Sume
30455505Sshin	for (masklen = 0; p < lim; p++) {
30555505Sshin		switch (*p) {
30655505Sshin		case 0xff:
30755505Sshin			masklen += 8;
30855505Sshin			break;
30955505Sshin		case 0xfe:
31055505Sshin			masklen += 7;
31155505Sshin			break;
31255505Sshin		case 0xfc:
31355505Sshin			masklen += 6;
31455505Sshin			break;
31555505Sshin		case 0xf8:
31655505Sshin			masklen += 5;
31755505Sshin			break;
31855505Sshin		case 0xf0:
31955505Sshin			masklen += 4;
32055505Sshin			break;
32155505Sshin		case 0xe0:
32255505Sshin			masklen += 3;
32355505Sshin			break;
32455505Sshin		case 0xc0:
32555505Sshin			masklen += 2;
32655505Sshin			break;
32755505Sshin		case 0x80:
32855505Sshin			masklen += 1;
32955505Sshin			break;
33055505Sshin		case 0x00:
33155505Sshin			break;
33255505Sshin		default:
333222732Shrs			return (-1);
33455505Sshin		}
33555505Sshin	}
33655505Sshin
337222732Shrs	return (masklen);
33855505Sshin}
33955505Sshin
340224144Shrsstruct ifinfo *
341224144Shrsupdate_persist_ifinfo(struct ifilist_head_t *ifi_head, const char *ifname)
34255505Sshin{
343224144Shrs	struct ifinfo *ifi;
344224144Shrs	int ifindex;
34555505Sshin
346224144Shrs	ifi = NULL;
347224144Shrs	ifindex = if_nametoindex(ifname);
348224144Shrs	TAILQ_FOREACH(ifi, ifi_head, ifi_next) {
349224144Shrs		if (ifindex != 0) {
350224144Shrs			if (ifindex == ifi->ifi_ifindex)
351224144Shrs				break;
352224144Shrs		} else {
353224144Shrs			if (strncmp(ifname, ifi->ifi_ifname,
354224144Shrs				sizeof(ifi->ifi_ifname)) == 0)
355224144Shrs				break;
356224144Shrs		}
357224144Shrs	}
35855505Sshin
359224144Shrs	if (ifi == NULL) {
360224144Shrs		/* A new ifinfo element is needed. */
361224144Shrs		syslog(LOG_DEBUG, "<%s> new entry: %s", __func__,
362224144Shrs		    ifname);
36355505Sshin
364224144Shrs		ELM_MALLOC(ifi, exit(1));
365224144Shrs		ifi->ifi_ifindex = 0;
366290853Sdelphij		strlcpy(ifi->ifi_ifname, ifname, sizeof(ifi->ifi_ifname));
367224144Shrs		ifi->ifi_rainfo = NULL;
368224144Shrs		ifi->ifi_state = IFI_STATE_UNCONFIGURED;
369224144Shrs		TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next);
370224144Shrs	}
371224144Shrs
372224144Shrs	ifi->ifi_persist = 1;
373224144Shrs
374224144Shrs	syslog(LOG_DEBUG, "<%s> %s is marked PERSIST", __func__,
375224144Shrs	    ifi->ifi_ifname);
376224144Shrs	syslog(LOG_DEBUG, "<%s> %s is state = %d", __func__,
377224144Shrs	    ifi->ifi_ifname, ifi->ifi_state);
378224144Shrs	return (ifi);
37955505Sshin}
38055505Sshin
38155505Sshinint
382224144Shrsupdate_ifinfo_nd_flags(struct ifinfo *ifi)
38355505Sshin{
384224144Shrs	struct in6_ndireq nd;
385224144Shrs	int s;
386224144Shrs	int error;
38755505Sshin
388224144Shrs	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
389224144Shrs		syslog(LOG_ERR,
390224144Shrs		    "<%s> socket() failed.", __func__);
391224144Shrs		return (1);
392224144Shrs	}
393224144Shrs	/* ND flags */
394224144Shrs	memset(&nd, 0, sizeof(nd));
395300281Struckman	strlcpy(nd.ifname, ifi->ifi_ifname,
396224144Shrs	    sizeof(nd.ifname));
397224144Shrs	error = ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd);
398224144Shrs	if (error) {
399224144Shrs		close(s);
400255156Shrs		if (errno != EPFNOSUPPORT)
401255156Shrs			syslog(LOG_ERR, "<%s> ioctl() failed.", __func__);
402224144Shrs		return (1);
403224144Shrs	}
404224144Shrs	ifi->ifi_nd_flags = nd.ndi.flags;
405224144Shrs	close(s);
406224144Shrs
407224144Shrs	return (0);
40855505Sshin}
40955505Sshin
410224144Shrsstruct ifinfo *
411224144Shrsupdate_ifinfo(struct ifilist_head_t *ifi_head, int ifindex)
41255505Sshin{
413224144Shrs	struct if_msghdr *ifm;
414224144Shrs	struct ifinfo *ifi = NULL;
415224144Shrs	struct sockaddr *sa;
416224144Shrs	struct sockaddr *rti_info[RTAX_MAX];
417224144Shrs	char *msg;
418224144Shrs	size_t len;
419224144Shrs	char *lim;
420224144Shrs	int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_IFLIST, 0 };
421224144Shrs	int error;
42255505Sshin
423224144Shrs	syslog(LOG_DEBUG, "<%s> enter", __func__);
42455505Sshin
425224144Shrs	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), NULL, &len, NULL, 0) <
426224144Shrs	    0) {
427224144Shrs		syslog(LOG_ERR,
428224144Shrs		    "<%s> sysctl: NET_RT_IFLIST size get failed", __func__);
42955505Sshin		exit(1);
43055505Sshin	}
431224144Shrs	if ((msg = malloc(len)) == NULL) {
432118660Sume		syslog(LOG_ERR, "<%s> malloc failed", __func__);
43355505Sshin		exit(1);
43455505Sshin	}
435224144Shrs	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), msg, &len, NULL, 0) <
436224144Shrs	    0) {
437224144Shrs		syslog(LOG_ERR,
438224144Shrs		    "<%s> sysctl: NET_RT_IFLIST get failed", __func__);
43955505Sshin		exit(1);
44055505Sshin	}
44155505Sshin
442224144Shrs	lim = msg + len;
443224144Shrs	for (ifm = (struct if_msghdr *)msg;
444224144Shrs	     ifm != NULL && ifm < (struct if_msghdr *)lim;
445224144Shrs	     ifm = get_next_msghdr(ifm,(struct if_msghdr *)lim)) {
446224144Shrs		int ifi_new;
44755505Sshin
448224144Shrs		syslog(LOG_DEBUG, "<%s> ifm = %p, lim = %p, diff = %zu",
449224144Shrs		    __func__, ifm, lim, (char *)lim - (char *)ifm);
45055505Sshin
451224144Shrs		if (ifm->ifm_version != RTM_VERSION) {
452224144Shrs			syslog(LOG_ERR,
453224144Shrs			    "<%s> ifm_vesrion mismatch", __func__);
454224144Shrs			exit(1);
455224144Shrs		}
45655505Sshin		if (ifm->ifm_msglen == 0) {
457224144Shrs			syslog(LOG_WARNING,
458224144Shrs			    "<%s> ifm_msglen is 0", __func__);
459224144Shrs			free(msg);
460224144Shrs			return (NULL);
46155505Sshin		}
46255505Sshin
463224144Shrs		ifi_new = 0;
46455505Sshin		if (ifm->ifm_type == RTM_IFINFO) {
465224144Shrs			struct ifreq ifr;
466224144Shrs			int s;
467224144Shrs			char ifname[IFNAMSIZ];
468224144Shrs
469224144Shrs			syslog(LOG_DEBUG, "<%s> RTM_IFINFO found. "
470224144Shrs			    "ifm_index = %d, ifindex = %d",
471224144Shrs			    __func__, ifm->ifm_index, ifindex);
472224144Shrs
473224144Shrs			/* when ifindex is specified */
474224144Shrs			if (ifindex != UPDATE_IFINFO_ALL &&
475224144Shrs			    ifindex != ifm->ifm_index)
476224144Shrs				continue;
477224144Shrs
478224144Shrs			/* lookup an entry with the same ifindex */
479224144Shrs			TAILQ_FOREACH(ifi, ifi_head, ifi_next) {
480224144Shrs				if (ifm->ifm_index == ifi->ifi_ifindex)
481224144Shrs					break;
482224144Shrs				if_indextoname(ifm->ifm_index, ifname);
483224144Shrs				if (strncmp(ifname, ifi->ifi_ifname,
484224144Shrs					sizeof(ifname)) == 0)
485224144Shrs					break;
486224144Shrs			}
487224144Shrs			if (ifi == NULL) {
488224144Shrs				syslog(LOG_DEBUG,
489224144Shrs				    "<%s> new entry for idx=%d",
490224144Shrs				    __func__, ifm->ifm_index);
491224144Shrs				ELM_MALLOC(ifi, exit(1));
492224144Shrs				ifi->ifi_rainfo = NULL;
493224144Shrs				ifi->ifi_state = IFI_STATE_UNCONFIGURED;
494224144Shrs				ifi->ifi_persist = 0;
495224144Shrs				ifi_new = 1;
496224144Shrs			}
497224144Shrs			/* ifindex */
498224144Shrs			ifi->ifi_ifindex = ifm->ifm_index;
499224144Shrs
500224144Shrs			/* ifname */
501224144Shrs			if_indextoname(ifm->ifm_index, ifi->ifi_ifname);
502224144Shrs			if (ifi->ifi_ifname == NULL) {
503224144Shrs				syslog(LOG_WARNING,
504224144Shrs				    "<%s> ifname not found (idx=%d)",
505224144Shrs				    __func__, ifm->ifm_index);
506224144Shrs				if (ifi_new)
507224144Shrs					free(ifi);
508224144Shrs				continue;
509224144Shrs			}
510224144Shrs
511224144Shrs			if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
512224144Shrs				syslog(LOG_ERR,
513224144Shrs				    "<%s> socket() failed.", __func__);
514224144Shrs				if (ifi_new)
515224144Shrs					free(ifi);
516224144Shrs				continue;
517224144Shrs			}
518224144Shrs
519224144Shrs			/* MTU  */
520224144Shrs			ifi->ifi_phymtu = ifm->ifm_data.ifi_mtu;
521224144Shrs			if (ifi->ifi_phymtu == 0) {
522224144Shrs				memset(&ifr, 0, sizeof(ifr));
523224144Shrs				ifr.ifr_addr.sa_family = AF_INET6;
524300281Struckman				strlcpy(ifr.ifr_name, ifi->ifi_ifname,
525224144Shrs				    sizeof(ifr.ifr_name));
526224144Shrs				error = ioctl(s, SIOCGIFMTU, (caddr_t)&ifr);
527224144Shrs				if (error) {
528224144Shrs					close(s);
529224144Shrs					syslog(LOG_ERR,
530224144Shrs					    "<%s> ioctl() failed.",
531224144Shrs					    __func__);
532224144Shrs					if (ifi_new)
533224144Shrs						free(ifi);
534224144Shrs					continue;
535224144Shrs				}
536224144Shrs				ifi->ifi_phymtu = ifr.ifr_mtu;
537224144Shrs				if (ifi->ifi_phymtu == 0) {
538224144Shrs					syslog(LOG_WARNING,
539224144Shrs					    "<%s> no interface mtu info"
540224144Shrs					    " on %s.  %d will be used.",
541224144Shrs					    __func__, ifi->ifi_ifname,
542224144Shrs					    IPV6_MMTU);
543224144Shrs					ifi->ifi_phymtu = IPV6_MMTU;
544224144Shrs				}
545224144Shrs			}
546224144Shrs			close(s);
547224144Shrs
548224144Shrs			/* ND flags */
549224144Shrs			error = update_ifinfo_nd_flags(ifi);
550224144Shrs			if (error) {
551224144Shrs				if (ifi_new)
552224144Shrs					free(ifi);
553224144Shrs				continue;
554224144Shrs			}
555224144Shrs
556224144Shrs			/* SDL */
557224144Shrs			sa = (struct sockaddr *)(ifm + 1);
558224144Shrs			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
559224144Shrs			if ((sa = rti_info[RTAX_IFP]) != NULL) {
560224144Shrs				if (sa->sa_family == AF_LINK) {
561224144Shrs					memcpy(&ifi->ifi_sdl,
562224144Shrs					    (struct sockaddr_dl *)sa,
563224144Shrs					    sizeof(ifi->ifi_sdl));
564224144Shrs				}
565224144Shrs			} else
566224144Shrs				memset(&ifi->ifi_sdl, 0,
567224144Shrs				    sizeof(ifi->ifi_sdl));
568224144Shrs
569224144Shrs			/* flags */
570224144Shrs			ifi->ifi_flags = ifm->ifm_flags;
571224144Shrs
572224144Shrs			/* type */
573224144Shrs			ifi->ifi_type = ifm->ifm_type;
57455505Sshin		} else {
575224144Shrs			syslog(LOG_ERR,
576224144Shrs			    "out of sync parsing NET_RT_IFLIST\n"
577224144Shrs			    "expected %d, got %d\n msglen = %d\n",
578224144Shrs			    RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen);
579224144Shrs			exit(1);
58055505Sshin		}
581224144Shrs
582224144Shrs		if (ifi_new) {
583224144Shrs			syslog(LOG_DEBUG,
584224144Shrs			    "<%s> adding %s(idx=%d) to ifilist",
585224144Shrs			    __func__, ifi->ifi_ifname, ifi->ifi_ifindex);
586224144Shrs			TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next);
58755505Sshin		}
58855505Sshin	}
589224144Shrs	free(msg);
590224144Shrs
591224144Shrs	if (mcastif != NULL) {
592224144Shrs		error = sock_mc_rr_update(&sock, mcastif);
593224144Shrs		if (error)
594224144Shrs			exit(1);
595224144Shrs	}
596224144Shrs
597224144Shrs	return (ifi);
59855505Sshin}
59955505Sshin
600224144Shrsstatic struct if_msghdr *
601224144Shrsget_next_msghdr(struct if_msghdr *ifm, struct if_msghdr *lim)
60255505Sshin{
603224144Shrs	struct ifa_msghdr *ifam;
604224144Shrs
605224144Shrs	for (ifam = (struct ifa_msghdr *)((char *)ifm + ifm->ifm_msglen);
606224144Shrs	     ifam < (struct ifa_msghdr *)lim;
607224144Shrs	     ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen)) {
608224144Shrs		if (!ifam->ifam_msglen) {
609224144Shrs			syslog(LOG_WARNING,
610224144Shrs			    "<%s> ifa_msglen is 0", __func__);
611224144Shrs			return (NULL);
612224144Shrs		}
613224144Shrs		if (ifam->ifam_type != RTM_NEWADDR)
614224144Shrs			break;
615224144Shrs	}
616224144Shrs
617224144Shrs	return ((struct if_msghdr *)ifam);
618224144Shrs}
619224144Shrs
620224144Shrsint
621224144Shrsgetinet6sysctl(int code)
622224144Shrs{
623224144Shrs	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
624224144Shrs	int value;
625224144Shrs	size_t size;
626224144Shrs
627224144Shrs	mib[3] = code;
628224144Shrs	size = sizeof(value);
629224144Shrs	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
630224144Shrs	    < 0) {
631224144Shrs		syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
632224144Shrs		    __func__, code,
633224144Shrs		    strerror(errno));
634224144Shrs		return (-1);
635224144Shrs	}
636224144Shrs	else
637224144Shrs		return (value);
638224144Shrs}
639224144Shrs
640224144Shrs
641224144Shrsint
642224144Shrssock_mc_join(struct sockinfo *s, int ifindex)
643224144Shrs{
644224144Shrs	struct ipv6_mreq mreq;
645224144Shrs	char ifname[IFNAMSIZ];
646224144Shrs
647224144Shrs	syslog(LOG_DEBUG, "<%s> enter", __func__);
648224144Shrs
649224144Shrs	if (ifindex == 0)
650224144Shrs		return (1);
651224144Shrs
652224144Shrs	/*
653224144Shrs	 * join all routers multicast address on each advertising
654224144Shrs	 * interface.
655224144Shrs	 */
656224144Shrs	memset(&mreq, 0, sizeof(mreq));
657224144Shrs	/* XXX */
658224144Shrs	memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
659224144Shrs	    &sin6_linklocal_allrouters.sin6_addr,
660224144Shrs	    sizeof(mreq.ipv6mr_multiaddr.s6_addr));
661224144Shrs
662224144Shrs	mreq.ipv6mr_interface = ifindex;
663224144Shrs	if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
664224144Shrs		sizeof(mreq)) < 0) {
665224144Shrs		syslog(LOG_ERR,
666224144Shrs		    "<%s> IPV6_JOIN_GROUP(link) on %s: %s",
667224144Shrs		    __func__, if_indextoname(ifindex, ifname),
668224144Shrs		    strerror(errno));
669224144Shrs		return (1);
670224144Shrs	}
671222732Shrs	syslog(LOG_DEBUG,
672224144Shrs	    "<%s> %s: join link-local all-routers MC group",
673224144Shrs	    __func__, if_indextoname(ifindex, ifname));
674222732Shrs
675224144Shrs	return (0);
676224144Shrs}
677224144Shrs
678224144Shrsint
679224144Shrssock_mc_leave(struct sockinfo *s, int ifindex)
680224144Shrs{
681224144Shrs	struct ipv6_mreq mreq;
682224144Shrs	char ifname[IFNAMSIZ];
683224144Shrs
684224144Shrs	syslog(LOG_DEBUG, "<%s> enter", __func__);
685224144Shrs
686224144Shrs	if (ifindex == 0)
687224144Shrs		return (1);
688224144Shrs
689224144Shrs	/*
690224144Shrs	 * join all routers multicast address on each advertising
691224144Shrs	 * interface.
692224144Shrs	 */
693224144Shrs
694224144Shrs	memset(&mreq, 0, sizeof(mreq));
695224144Shrs	/* XXX */
696224144Shrs	memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
697224144Shrs	    &sin6_linklocal_allrouters.sin6_addr,
698224144Shrs	    sizeof(mreq.ipv6mr_multiaddr.s6_addr));
699224144Shrs
700224144Shrs	mreq.ipv6mr_interface = ifindex;
701224144Shrs	if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq,
702224144Shrs		sizeof(mreq)) < 0) {
703224144Shrs		syslog(LOG_ERR,
704224144Shrs		    "<%s> IPV6_JOIN_LEAVE(link) on %s: %s",
705224144Shrs		    __func__, if_indextoname(ifindex, ifname),
706224144Shrs		    strerror(errno));
707224144Shrs		return (1);
70855505Sshin	}
709224144Shrs	syslog(LOG_DEBUG,
710224144Shrs	    "<%s> %s: leave link-local all-routers MC group",
711224144Shrs	    __func__, if_indextoname(ifindex, ifname));
71255505Sshin
713224144Shrs	return (0);
71455505Sshin}
715224144Shrs
716224144Shrsint
717224144Shrssock_mc_rr_update(struct sockinfo *s, char *mif)
718224144Shrs{
719224144Shrs	struct ipv6_mreq mreq;
720224144Shrs
721224144Shrs	syslog(LOG_DEBUG, "<%s> enter", __func__);
722224144Shrs
723224144Shrs	if (mif == NULL)
724224144Shrs		return (1);
725224144Shrs	/*
726224144Shrs	 * When attending router renumbering, join all-routers site-local
727224144Shrs	 * multicast group.
728224144Shrs	 */
729224144Shrs	/* XXX */
730224144Shrs	memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
731224144Shrs	    &sin6_sitelocal_allrouters.sin6_addr,
732224144Shrs	    sizeof(mreq.ipv6mr_multiaddr.s6_addr));
733224144Shrs	if ((mreq.ipv6mr_interface = if_nametoindex(mif)) == 0) {
734224144Shrs		syslog(LOG_ERR,
735224144Shrs		    "<%s> invalid interface: %s",
736224144Shrs		    __func__, mif);
737224144Shrs		return (1);
738224144Shrs	}
739224144Shrs
740224144Shrs	if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
741224144Shrs		&mreq, sizeof(mreq)) < 0) {
742224144Shrs		syslog(LOG_ERR,
743224144Shrs		    "<%s> IPV6_JOIN_GROUP(site) on %s: %s",
744224144Shrs		    __func__, mif, strerror(errno));
745224144Shrs		return (1);
746224144Shrs	}
747224144Shrs
748224144Shrs	syslog(LOG_DEBUG,
749224144Shrs	    "<%s> %s: join site-local all-routers MC group",
750224144Shrs	    __func__, mif);
751224144Shrs
752224144Shrs	return (0);
753224144Shrs}
754