171333Sitojun/*	$FreeBSD: releng/10.3/usr.sbin/rtadvd/if.c 290853 2015-11-15 07:10:02Z delphij $	*/
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:
118222732Shrs		return (ROUNDUP8(ETHER_ADDR_LEN + 2));
119118664Sume	default:
120222732Shrs		return (0);
12155505Sshin	}
12255505Sshin}
12355505Sshin
12455505Sshinvoid
12555505Sshinlladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
12655505Sshin{
12755505Sshin	char *addr;
12855505Sshin
12955505Sshin	ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
13055505Sshin
131118664Sume	switch (sdl->sdl_type) {
132118664Sume	case IFT_ETHER:
133118664Sume		ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
134118664Sume		addr = (char *)(ndopt + 1);
135118664Sume		memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
136118664Sume		break;
137118664Sume	default:
138118664Sume		syslog(LOG_ERR, "<%s> unsupported link type(%d)",
139118664Sume		    __func__, sdl->sdl_type);
140118664Sume		exit(1);
14155505Sshin	}
14255505Sshin
14355505Sshin	return;
14455505Sshin}
14555505Sshin
14655505Sshinint
147222732Shrsrtbuf_len(void)
14855505Sshin{
14955505Sshin	size_t len;
15055505Sshin	int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0};
15155505Sshin
15255505Sshin	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
153222732Shrs		return (-1);
15455505Sshin
155222732Shrs	return (len);
15655505Sshin}
15755505Sshin
15862656Skris#define FILTER_MATCH(type, filter) ((0x1 << type) & filter)
15962656Skris#define SIN6(s) ((struct sockaddr_in6 *)(s))
16062656Skris#define SDL(s) ((struct sockaddr_dl *)(s))
16155505Sshinchar *
16255505Sshinget_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter)
16355505Sshin{
16455505Sshin	struct rt_msghdr *rtm;
16555505Sshin	struct ifa_msghdr *ifam;
16655505Sshin	struct sockaddr *sa, *dst, *gw, *ifa, *rti_info[RTAX_MAX];
16755505Sshin
16855505Sshin	*lenp = 0;
16955505Sshin	for (rtm = (struct rt_msghdr *)buf;
17055505Sshin	     rtm < (struct rt_msghdr *)lim;
17155505Sshin	     rtm = (struct rt_msghdr *)(((char *)rtm) + rtm->rtm_msglen)) {
17255505Sshin		/* just for safety */
17355505Sshin		if (!rtm->rtm_msglen) {
17455505Sshin			syslog(LOG_WARNING, "<%s> rtm_msglen is 0 "
175222732Shrs			    "(buf=%p lim=%p rtm=%p)", __func__,
176222732Shrs			    buf, lim, rtm);
17755505Sshin			break;
17855505Sshin		}
179222732Shrs		if (((struct rt_msghdr *)buf)->rtm_version != RTM_VERSION) {
180222732Shrs			syslog(LOG_WARNING,
181222732Shrs			    "<%s> routing message version mismatch "
182222732Shrs			    "(buf=%p lim=%p rtm=%p)", __func__,
183222732Shrs			    buf, lim, rtm);
18455505Sshin			continue;
18555505Sshin		}
18655505Sshin
187222732Shrs		if (FILTER_MATCH(rtm->rtm_type, filter) == 0)
188222732Shrs			continue;
189222732Shrs
19055505Sshin		switch (rtm->rtm_type) {
19155505Sshin		case RTM_GET:
19255505Sshin		case RTM_ADD:
19355505Sshin		case RTM_DELETE:
19455505Sshin			/* address related checks */
19555505Sshin			sa = (struct sockaddr *)(rtm + 1);
19655505Sshin			get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
19755505Sshin			if ((dst = rti_info[RTAX_DST]) == NULL ||
19855505Sshin			    dst->sa_family != AF_INET6)
19955505Sshin				continue;
20055505Sshin
20155505Sshin			if (IN6_IS_ADDR_LINKLOCAL(&SIN6(dst)->sin6_addr) ||
20255505Sshin			    IN6_IS_ADDR_MULTICAST(&SIN6(dst)->sin6_addr))
20355505Sshin				continue;
20455505Sshin
20555505Sshin			if ((gw = rti_info[RTAX_GATEWAY]) == NULL ||
20655505Sshin			    gw->sa_family != AF_LINK)
20755505Sshin				continue;
20855505Sshin			if (ifindex && SDL(gw)->sdl_index != ifindex)
20955505Sshin				continue;
21055505Sshin
21155505Sshin			if (rti_info[RTAX_NETMASK] == NULL)
21255505Sshin				continue;
21355505Sshin
21455505Sshin			/* found */
21555505Sshin			*lenp = rtm->rtm_msglen;
21655505Sshin			return (char *)rtm;
21755505Sshin			/* NOTREACHED */
21855505Sshin		case RTM_NEWADDR:
21955505Sshin		case RTM_DELADDR:
22055505Sshin			ifam = (struct ifa_msghdr *)rtm;
22155505Sshin
22255505Sshin			/* address related checks */
22355505Sshin			sa = (struct sockaddr *)(ifam + 1);
22455505Sshin			get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
22555505Sshin			if ((ifa = rti_info[RTAX_IFA]) == NULL ||
22655505Sshin			    (ifa->sa_family != AF_INET &&
22755505Sshin			     ifa->sa_family != AF_INET6))
22855505Sshin				continue;
22955505Sshin
23055505Sshin			if (ifa->sa_family == AF_INET6 &&
23155505Sshin			    (IN6_IS_ADDR_LINKLOCAL(&SIN6(ifa)->sin6_addr) ||
23255505Sshin			     IN6_IS_ADDR_MULTICAST(&SIN6(ifa)->sin6_addr)))
23355505Sshin				continue;
23455505Sshin
23555505Sshin			if (ifindex && ifam->ifam_index != ifindex)
23655505Sshin				continue;
23755505Sshin
23855505Sshin			/* found */
23955505Sshin			*lenp = ifam->ifam_msglen;
24055505Sshin			return (char *)rtm;
24155505Sshin			/* NOTREACHED */
24255505Sshin		case RTM_IFINFO:
243222732Shrs		case RTM_IFANNOUNCE:
24455505Sshin			/* found */
24555505Sshin			*lenp = rtm->rtm_msglen;
24655505Sshin			return (char *)rtm;
24755505Sshin			/* NOTREACHED */
24855505Sshin		}
24955505Sshin	}
25055505Sshin
251222732Shrs	return ((char *)rtm);
25255505Sshin}
25378064Sume#undef FILTER_MATCH
25455505Sshin
25555505Sshinstruct in6_addr *
25655505Sshinget_addr(char *buf)
25755505Sshin{
25855505Sshin	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
25955505Sshin	struct sockaddr *sa, *rti_info[RTAX_MAX];
26055505Sshin
26155505Sshin	sa = (struct sockaddr *)(rtm + 1);
26255505Sshin	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
26355505Sshin
264222732Shrs	return (&SIN6(rti_info[RTAX_DST])->sin6_addr);
26555505Sshin}
26655505Sshin
26755505Sshinint
26855505Sshinget_rtm_ifindex(char *buf)
26955505Sshin{
27055505Sshin	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
27155505Sshin	struct sockaddr *sa, *rti_info[RTAX_MAX];
27255505Sshin
27355505Sshin	sa = (struct sockaddr *)(rtm + 1);
27455505Sshin	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
27555505Sshin
276222732Shrs	return (((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index);
27755505Sshin}
27855505Sshin
27955505Sshinint
28055505Sshinget_prefixlen(char *buf)
28155505Sshin{
28255505Sshin	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
28355505Sshin	struct sockaddr *sa, *rti_info[RTAX_MAX];
284224144Shrs	char *p, *lim;
285222732Shrs
28655505Sshin	sa = (struct sockaddr *)(rtm + 1);
28755505Sshin	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
28855505Sshin	sa = rti_info[RTAX_NETMASK];
28955505Sshin
290224144Shrs	p = (char *)(&SIN6(sa)->sin6_addr);
291224144Shrs	lim = (char *)sa + sa->sa_len;
29267801Sume	return prefixlen(p, lim);
29367801Sume}
29467801Sume
29567801Sumeint
296224144Shrsprefixlen(unsigned char *p, unsigned char *lim)
29767801Sume{
29867801Sume	int masklen;
29967801Sume
30055505Sshin	for (masklen = 0; p < lim; p++) {
30155505Sshin		switch (*p) {
30255505Sshin		case 0xff:
30355505Sshin			masklen += 8;
30455505Sshin			break;
30555505Sshin		case 0xfe:
30655505Sshin			masklen += 7;
30755505Sshin			break;
30855505Sshin		case 0xfc:
30955505Sshin			masklen += 6;
31055505Sshin			break;
31155505Sshin		case 0xf8:
31255505Sshin			masklen += 5;
31355505Sshin			break;
31455505Sshin		case 0xf0:
31555505Sshin			masklen += 4;
31655505Sshin			break;
31755505Sshin		case 0xe0:
31855505Sshin			masklen += 3;
31955505Sshin			break;
32055505Sshin		case 0xc0:
32155505Sshin			masklen += 2;
32255505Sshin			break;
32355505Sshin		case 0x80:
32455505Sshin			masklen += 1;
32555505Sshin			break;
32655505Sshin		case 0x00:
32755505Sshin			break;
32855505Sshin		default:
329222732Shrs			return (-1);
33055505Sshin		}
33155505Sshin	}
33255505Sshin
333222732Shrs	return (masklen);
33455505Sshin}
33555505Sshin
336224144Shrsstruct ifinfo *
337224144Shrsupdate_persist_ifinfo(struct ifilist_head_t *ifi_head, const char *ifname)
33855505Sshin{
339224144Shrs	struct ifinfo *ifi;
340224144Shrs	int ifindex;
34155505Sshin
342224144Shrs	ifi = NULL;
343224144Shrs	ifindex = if_nametoindex(ifname);
344224144Shrs	TAILQ_FOREACH(ifi, ifi_head, ifi_next) {
345224144Shrs		if (ifindex != 0) {
346224144Shrs			if (ifindex == ifi->ifi_ifindex)
347224144Shrs				break;
348224144Shrs		} else {
349224144Shrs			if (strncmp(ifname, ifi->ifi_ifname,
350224144Shrs				sizeof(ifi->ifi_ifname)) == 0)
351224144Shrs				break;
352224144Shrs		}
353224144Shrs	}
35455505Sshin
355224144Shrs	if (ifi == NULL) {
356224144Shrs		/* A new ifinfo element is needed. */
357224144Shrs		syslog(LOG_DEBUG, "<%s> new entry: %s", __func__,
358224144Shrs		    ifname);
35955505Sshin
360224144Shrs		ELM_MALLOC(ifi, exit(1));
361224144Shrs		ifi->ifi_ifindex = 0;
362290853Sdelphij		strlcpy(ifi->ifi_ifname, ifname, sizeof(ifi->ifi_ifname));
363224144Shrs		ifi->ifi_rainfo = NULL;
364224144Shrs		ifi->ifi_state = IFI_STATE_UNCONFIGURED;
365224144Shrs		TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next);
366224144Shrs	}
367224144Shrs
368224144Shrs	ifi->ifi_persist = 1;
369224144Shrs
370224144Shrs	syslog(LOG_DEBUG, "<%s> %s is marked PERSIST", __func__,
371224144Shrs	    ifi->ifi_ifname);
372224144Shrs	syslog(LOG_DEBUG, "<%s> %s is state = %d", __func__,
373224144Shrs	    ifi->ifi_ifname, ifi->ifi_state);
374224144Shrs	return (ifi);
37555505Sshin}
37655505Sshin
37755505Sshinint
378224144Shrsupdate_ifinfo_nd_flags(struct ifinfo *ifi)
37955505Sshin{
380224144Shrs	struct in6_ndireq nd;
381224144Shrs	int s;
382224144Shrs	int error;
38355505Sshin
384224144Shrs	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
385224144Shrs		syslog(LOG_ERR,
386224144Shrs		    "<%s> socket() failed.", __func__);
387224144Shrs		return (1);
388224144Shrs	}
389224144Shrs	/* ND flags */
390224144Shrs	memset(&nd, 0, sizeof(nd));
391224144Shrs	strncpy(nd.ifname, ifi->ifi_ifname,
392224144Shrs	    sizeof(nd.ifname));
393224144Shrs	error = ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd);
394224144Shrs	if (error) {
395224144Shrs		close(s);
396255156Shrs		if (errno != EPFNOSUPPORT)
397255156Shrs			syslog(LOG_ERR, "<%s> ioctl() failed.", __func__);
398224144Shrs		return (1);
399224144Shrs	}
400224144Shrs	ifi->ifi_nd_flags = nd.ndi.flags;
401224144Shrs	close(s);
402224144Shrs
403224144Shrs	return (0);
40455505Sshin}
40555505Sshin
406224144Shrsstruct ifinfo *
407224144Shrsupdate_ifinfo(struct ifilist_head_t *ifi_head, int ifindex)
40855505Sshin{
409224144Shrs	struct if_msghdr *ifm;
410224144Shrs	struct ifinfo *ifi = NULL;
411224144Shrs	struct sockaddr *sa;
412224144Shrs	struct sockaddr *rti_info[RTAX_MAX];
413224144Shrs	char *msg;
414224144Shrs	size_t len;
415224144Shrs	char *lim;
416224144Shrs	int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_IFLIST, 0 };
417224144Shrs	int error;
41855505Sshin
419224144Shrs	syslog(LOG_DEBUG, "<%s> enter", __func__);
42055505Sshin
421224144Shrs	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), NULL, &len, NULL, 0) <
422224144Shrs	    0) {
423224144Shrs		syslog(LOG_ERR,
424224144Shrs		    "<%s> sysctl: NET_RT_IFLIST size get failed", __func__);
42555505Sshin		exit(1);
42655505Sshin	}
427224144Shrs	if ((msg = malloc(len)) == NULL) {
428118660Sume		syslog(LOG_ERR, "<%s> malloc failed", __func__);
42955505Sshin		exit(1);
43055505Sshin	}
431224144Shrs	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), msg, &len, NULL, 0) <
432224144Shrs	    0) {
433224144Shrs		syslog(LOG_ERR,
434224144Shrs		    "<%s> sysctl: NET_RT_IFLIST get failed", __func__);
43555505Sshin		exit(1);
43655505Sshin	}
43755505Sshin
438224144Shrs	lim = msg + len;
439224144Shrs	for (ifm = (struct if_msghdr *)msg;
440224144Shrs	     ifm != NULL && ifm < (struct if_msghdr *)lim;
441224144Shrs	     ifm = get_next_msghdr(ifm,(struct if_msghdr *)lim)) {
442224144Shrs		int ifi_new;
44355505Sshin
444224144Shrs		syslog(LOG_DEBUG, "<%s> ifm = %p, lim = %p, diff = %zu",
445224144Shrs		    __func__, ifm, lim, (char *)lim - (char *)ifm);
44655505Sshin
447224144Shrs		if (ifm->ifm_version != RTM_VERSION) {
448224144Shrs			syslog(LOG_ERR,
449224144Shrs			    "<%s> ifm_vesrion mismatch", __func__);
450224144Shrs			exit(1);
451224144Shrs		}
45255505Sshin		if (ifm->ifm_msglen == 0) {
453224144Shrs			syslog(LOG_WARNING,
454224144Shrs			    "<%s> ifm_msglen is 0", __func__);
455224144Shrs			free(msg);
456224144Shrs			return (NULL);
45755505Sshin		}
45855505Sshin
459224144Shrs		ifi_new = 0;
46055505Sshin		if (ifm->ifm_type == RTM_IFINFO) {
461224144Shrs			struct ifreq ifr;
462224144Shrs			int s;
463224144Shrs			char ifname[IFNAMSIZ];
464224144Shrs
465224144Shrs			syslog(LOG_DEBUG, "<%s> RTM_IFINFO found. "
466224144Shrs			    "ifm_index = %d, ifindex = %d",
467224144Shrs			    __func__, ifm->ifm_index, ifindex);
468224144Shrs
469224144Shrs			/* when ifindex is specified */
470224144Shrs			if (ifindex != UPDATE_IFINFO_ALL &&
471224144Shrs			    ifindex != ifm->ifm_index)
472224144Shrs				continue;
473224144Shrs
474224144Shrs			/* lookup an entry with the same ifindex */
475224144Shrs			TAILQ_FOREACH(ifi, ifi_head, ifi_next) {
476224144Shrs				if (ifm->ifm_index == ifi->ifi_ifindex)
477224144Shrs					break;
478224144Shrs				if_indextoname(ifm->ifm_index, ifname);
479224144Shrs				if (strncmp(ifname, ifi->ifi_ifname,
480224144Shrs					sizeof(ifname)) == 0)
481224144Shrs					break;
482224144Shrs			}
483224144Shrs			if (ifi == NULL) {
484224144Shrs				syslog(LOG_DEBUG,
485224144Shrs				    "<%s> new entry for idx=%d",
486224144Shrs				    __func__, ifm->ifm_index);
487224144Shrs				ELM_MALLOC(ifi, exit(1));
488224144Shrs				ifi->ifi_rainfo = NULL;
489224144Shrs				ifi->ifi_state = IFI_STATE_UNCONFIGURED;
490224144Shrs				ifi->ifi_persist = 0;
491224144Shrs				ifi_new = 1;
492224144Shrs			}
493224144Shrs			/* ifindex */
494224144Shrs			ifi->ifi_ifindex = ifm->ifm_index;
495224144Shrs
496224144Shrs			/* ifname */
497224144Shrs			if_indextoname(ifm->ifm_index, ifi->ifi_ifname);
498224144Shrs			if (ifi->ifi_ifname == NULL) {
499224144Shrs				syslog(LOG_WARNING,
500224144Shrs				    "<%s> ifname not found (idx=%d)",
501224144Shrs				    __func__, ifm->ifm_index);
502224144Shrs				if (ifi_new)
503224144Shrs					free(ifi);
504224144Shrs				continue;
505224144Shrs			}
506224144Shrs
507224144Shrs			if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
508224144Shrs				syslog(LOG_ERR,
509224144Shrs				    "<%s> socket() failed.", __func__);
510224144Shrs				if (ifi_new)
511224144Shrs					free(ifi);
512224144Shrs				continue;
513224144Shrs			}
514224144Shrs
515224144Shrs			/* MTU  */
516224144Shrs			ifi->ifi_phymtu = ifm->ifm_data.ifi_mtu;
517224144Shrs			if (ifi->ifi_phymtu == 0) {
518224144Shrs				memset(&ifr, 0, sizeof(ifr));
519224144Shrs				ifr.ifr_addr.sa_family = AF_INET6;
520224144Shrs				strncpy(ifr.ifr_name, ifi->ifi_ifname,
521224144Shrs				    sizeof(ifr.ifr_name));
522224144Shrs				error = ioctl(s, SIOCGIFMTU, (caddr_t)&ifr);
523224144Shrs				if (error) {
524224144Shrs					close(s);
525224144Shrs					syslog(LOG_ERR,
526224144Shrs					    "<%s> ioctl() failed.",
527224144Shrs					    __func__);
528224144Shrs					if (ifi_new)
529224144Shrs						free(ifi);
530224144Shrs					continue;
531224144Shrs				}
532224144Shrs				ifi->ifi_phymtu = ifr.ifr_mtu;
533224144Shrs				if (ifi->ifi_phymtu == 0) {
534224144Shrs					syslog(LOG_WARNING,
535224144Shrs					    "<%s> no interface mtu info"
536224144Shrs					    " on %s.  %d will be used.",
537224144Shrs					    __func__, ifi->ifi_ifname,
538224144Shrs					    IPV6_MMTU);
539224144Shrs					ifi->ifi_phymtu = IPV6_MMTU;
540224144Shrs				}
541224144Shrs			}
542224144Shrs			close(s);
543224144Shrs
544224144Shrs			/* ND flags */
545224144Shrs			error = update_ifinfo_nd_flags(ifi);
546224144Shrs			if (error) {
547224144Shrs				if (ifi_new)
548224144Shrs					free(ifi);
549224144Shrs				continue;
550224144Shrs			}
551224144Shrs
552224144Shrs			/* SDL */
553224144Shrs			sa = (struct sockaddr *)(ifm + 1);
554224144Shrs			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
555224144Shrs			if ((sa = rti_info[RTAX_IFP]) != NULL) {
556224144Shrs				if (sa->sa_family == AF_LINK) {
557224144Shrs					memcpy(&ifi->ifi_sdl,
558224144Shrs					    (struct sockaddr_dl *)sa,
559224144Shrs					    sizeof(ifi->ifi_sdl));
560224144Shrs				}
561224144Shrs			} else
562224144Shrs				memset(&ifi->ifi_sdl, 0,
563224144Shrs				    sizeof(ifi->ifi_sdl));
564224144Shrs
565224144Shrs			/* flags */
566224144Shrs			ifi->ifi_flags = ifm->ifm_flags;
567224144Shrs
568224144Shrs			/* type */
569224144Shrs			ifi->ifi_type = ifm->ifm_type;
57055505Sshin		} else {
571224144Shrs			syslog(LOG_ERR,
572224144Shrs			    "out of sync parsing NET_RT_IFLIST\n"
573224144Shrs			    "expected %d, got %d\n msglen = %d\n",
574224144Shrs			    RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen);
575224144Shrs			exit(1);
57655505Sshin		}
577224144Shrs
578224144Shrs		if (ifi_new) {
579224144Shrs			syslog(LOG_DEBUG,
580224144Shrs			    "<%s> adding %s(idx=%d) to ifilist",
581224144Shrs			    __func__, ifi->ifi_ifname, ifi->ifi_ifindex);
582224144Shrs			TAILQ_INSERT_TAIL(ifi_head, ifi, ifi_next);
58355505Sshin		}
58455505Sshin	}
585224144Shrs	free(msg);
586224144Shrs
587224144Shrs	if (mcastif != NULL) {
588224144Shrs		error = sock_mc_rr_update(&sock, mcastif);
589224144Shrs		if (error)
590224144Shrs			exit(1);
591224144Shrs	}
592224144Shrs
593224144Shrs	return (ifi);
59455505Sshin}
59555505Sshin
596224144Shrsstatic struct if_msghdr *
597224144Shrsget_next_msghdr(struct if_msghdr *ifm, struct if_msghdr *lim)
59855505Sshin{
599224144Shrs	struct ifa_msghdr *ifam;
600224144Shrs
601224144Shrs	for (ifam = (struct ifa_msghdr *)((char *)ifm + ifm->ifm_msglen);
602224144Shrs	     ifam < (struct ifa_msghdr *)lim;
603224144Shrs	     ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen)) {
604224144Shrs		if (!ifam->ifam_msglen) {
605224144Shrs			syslog(LOG_WARNING,
606224144Shrs			    "<%s> ifa_msglen is 0", __func__);
607224144Shrs			return (NULL);
608224144Shrs		}
609224144Shrs		if (ifam->ifam_type != RTM_NEWADDR)
610224144Shrs			break;
611224144Shrs	}
612224144Shrs
613224144Shrs	return ((struct if_msghdr *)ifam);
614224144Shrs}
615224144Shrs
616224144Shrsint
617224144Shrsgetinet6sysctl(int code)
618224144Shrs{
619224144Shrs	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
620224144Shrs	int value;
621224144Shrs	size_t size;
622224144Shrs
623224144Shrs	mib[3] = code;
624224144Shrs	size = sizeof(value);
625224144Shrs	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
626224144Shrs	    < 0) {
627224144Shrs		syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
628224144Shrs		    __func__, code,
629224144Shrs		    strerror(errno));
630224144Shrs		return (-1);
631224144Shrs	}
632224144Shrs	else
633224144Shrs		return (value);
634224144Shrs}
635224144Shrs
636224144Shrs
637224144Shrsint
638224144Shrssock_mc_join(struct sockinfo *s, int ifindex)
639224144Shrs{
640224144Shrs	struct ipv6_mreq mreq;
641224144Shrs	char ifname[IFNAMSIZ];
642224144Shrs
643224144Shrs	syslog(LOG_DEBUG, "<%s> enter", __func__);
644224144Shrs
645224144Shrs	if (ifindex == 0)
646224144Shrs		return (1);
647224144Shrs
648224144Shrs	/*
649224144Shrs	 * join all routers multicast address on each advertising
650224144Shrs	 * interface.
651224144Shrs	 */
652224144Shrs	memset(&mreq, 0, sizeof(mreq));
653224144Shrs	/* XXX */
654224144Shrs	memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
655224144Shrs	    &sin6_linklocal_allrouters.sin6_addr,
656224144Shrs	    sizeof(mreq.ipv6mr_multiaddr.s6_addr));
657224144Shrs
658224144Shrs	mreq.ipv6mr_interface = ifindex;
659224144Shrs	if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
660224144Shrs		sizeof(mreq)) < 0) {
661224144Shrs		syslog(LOG_ERR,
662224144Shrs		    "<%s> IPV6_JOIN_GROUP(link) on %s: %s",
663224144Shrs		    __func__, if_indextoname(ifindex, ifname),
664224144Shrs		    strerror(errno));
665224144Shrs		return (1);
666224144Shrs	}
667222732Shrs	syslog(LOG_DEBUG,
668224144Shrs	    "<%s> %s: join link-local all-routers MC group",
669224144Shrs	    __func__, if_indextoname(ifindex, ifname));
670222732Shrs
671224144Shrs	return (0);
672224144Shrs}
673224144Shrs
674224144Shrsint
675224144Shrssock_mc_leave(struct sockinfo *s, int ifindex)
676224144Shrs{
677224144Shrs	struct ipv6_mreq mreq;
678224144Shrs	char ifname[IFNAMSIZ];
679224144Shrs
680224144Shrs	syslog(LOG_DEBUG, "<%s> enter", __func__);
681224144Shrs
682224144Shrs	if (ifindex == 0)
683224144Shrs		return (1);
684224144Shrs
685224144Shrs	/*
686224144Shrs	 * join all routers multicast address on each advertising
687224144Shrs	 * interface.
688224144Shrs	 */
689224144Shrs
690224144Shrs	memset(&mreq, 0, sizeof(mreq));
691224144Shrs	/* XXX */
692224144Shrs	memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
693224144Shrs	    &sin6_linklocal_allrouters.sin6_addr,
694224144Shrs	    sizeof(mreq.ipv6mr_multiaddr.s6_addr));
695224144Shrs
696224144Shrs	mreq.ipv6mr_interface = ifindex;
697224144Shrs	if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq,
698224144Shrs		sizeof(mreq)) < 0) {
699224144Shrs		syslog(LOG_ERR,
700224144Shrs		    "<%s> IPV6_JOIN_LEAVE(link) on %s: %s",
701224144Shrs		    __func__, if_indextoname(ifindex, ifname),
702224144Shrs		    strerror(errno));
703224144Shrs		return (1);
70455505Sshin	}
705224144Shrs	syslog(LOG_DEBUG,
706224144Shrs	    "<%s> %s: leave link-local all-routers MC group",
707224144Shrs	    __func__, if_indextoname(ifindex, ifname));
70855505Sshin
709224144Shrs	return (0);
71055505Sshin}
711224144Shrs
712224144Shrsint
713224144Shrssock_mc_rr_update(struct sockinfo *s, char *mif)
714224144Shrs{
715224144Shrs	struct ipv6_mreq mreq;
716224144Shrs
717224144Shrs	syslog(LOG_DEBUG, "<%s> enter", __func__);
718224144Shrs
719224144Shrs	if (mif == NULL)
720224144Shrs		return (1);
721224144Shrs	/*
722224144Shrs	 * When attending router renumbering, join all-routers site-local
723224144Shrs	 * multicast group.
724224144Shrs	 */
725224144Shrs	/* XXX */
726224144Shrs	memcpy(&mreq.ipv6mr_multiaddr.s6_addr,
727224144Shrs	    &sin6_sitelocal_allrouters.sin6_addr,
728224144Shrs	    sizeof(mreq.ipv6mr_multiaddr.s6_addr));
729224144Shrs	if ((mreq.ipv6mr_interface = if_nametoindex(mif)) == 0) {
730224144Shrs		syslog(LOG_ERR,
731224144Shrs		    "<%s> invalid interface: %s",
732224144Shrs		    __func__, mif);
733224144Shrs		return (1);
734224144Shrs	}
735224144Shrs
736224144Shrs	if (setsockopt(s->si_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
737224144Shrs		&mreq, sizeof(mreq)) < 0) {
738224144Shrs		syslog(LOG_ERR,
739224144Shrs		    "<%s> IPV6_JOIN_GROUP(site) on %s: %s",
740224144Shrs		    __func__, mif, strerror(errno));
741224144Shrs		return (1);
742224144Shrs	}
743224144Shrs
744224144Shrs	syslog(LOG_DEBUG,
745224144Shrs	    "<%s> %s: join site-local all-routers MC group",
746224144Shrs	    __func__, mif);
747224144Shrs
748224144Shrs	return (0);
749224144Shrs}
750