if.c revision 66776
166776Skris/*	$KAME$	*/
266776Skris
355163Sshin/*
455163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
555163Sshin * All rights reserved.
662632Skris *
755163Sshin * Redistribution and use in source and binary forms, with or without
855163Sshin * modification, are permitted provided that the following conditions
955163Sshin * are met:
1055163Sshin * 1. Redistributions of source code must retain the above copyright
1155163Sshin *    notice, this list of conditions and the following disclaimer.
1255163Sshin * 2. Redistributions in binary form must reproduce the above copyright
1355163Sshin *    notice, this list of conditions and the following disclaimer in the
1455163Sshin *    documentation and/or other materials provided with the distribution.
1555163Sshin * 3. Neither the name of the project nor the names of its contributors
1655163Sshin *    may be used to endorse or promote products derived from this software
1755163Sshin *    without specific prior written permission.
1862632Skris *
1955163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2055163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2155163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2255163Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2355163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2455163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2555163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2655163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2755163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2855163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2955163Sshin * SUCH DAMAGE.
3055163Sshin *
3155163Sshin * $FreeBSD: head/usr.sbin/rtsold/if.c 66776 2000-10-06 23:46:52Z kris $
3255163Sshin */
3355163Sshin
3455163Sshin#include <sys/param.h>
3555163Sshin#include <sys/socket.h>
3655163Sshin#include <sys/sysctl.h>
3755163Sshin#include <sys/ioctl.h>
3866776Skris#include <sys/queue.h>
3955163Sshin
4055163Sshin#include <net/if.h>
4162632Skris#if defined(__FreeBSD__) && __FreeBSD__ >= 3
4255163Sshin#include <net/if_var.h>
4362632Skris#endif /* __FreeBSD__ >= 3 */
4455163Sshin#include <net/if_types.h>
4555163Sshin#include <net/route.h>
4655163Sshin#include <net/if_dl.h>
4755163Sshin#include <net/if_media.h>
4862632Skris#ifdef __FreeBSD__
4962632Skris# include <net/ethernet.h>
5062632Skris#endif
5162632Skris#ifdef __NetBSD__
5262632Skris#include <net/if_ether.h>
5362632Skris#endif
5462632Skris#if defined(__bsdi__) || defined(__OpenBSD__)
5562632Skris# include <netinet/in.h>
5662632Skris# include <netinet/if_ether.h>
5762632Skris#endif
5855163Sshin#include <netinet/in.h>
5955163Sshin#include <netinet/icmp6.h>
6055163Sshin
6155163Sshin#include <netinet6/in6_var.h>
6255163Sshin
6355163Sshin#include <stdio.h>
6455163Sshin#include <unistd.h>
6555163Sshin#include <stdlib.h>
6655163Sshin#include <syslog.h>
6755163Sshin#include <string.h>
6855163Sshin#include <fcntl.h>
6955163Sshin#include <errno.h>
7055163Sshin#include <limits.h>
7162632Skris#ifdef HAVE_GETIFADDRS
7262632Skris#include <ifaddrs.h>
7362632Skris#endif
7455163Sshin
7555163Sshin#include "rtsold.h"
7655163Sshin
7762632Skrisextern int rssock;
7855163Sshinstatic int ifsock;
7955163Sshin
8062632Skrisstatic int get_llflag __P((const char *name));
8162632Skris#ifndef HAVE_GETIFADDRS
8262632Skrisstatic unsigned int if_maxindex __P((void));
8362632Skris#endif
8455163Sshinstatic void get_rtaddrs __P((int addrs, struct sockaddr *sa,
8555163Sshin			     struct sockaddr **rti_info));
8655163Sshin
8755163Sshinint
8855163Sshinifinit()
8955163Sshin{
9062632Skris	ifsock = rssock;
9155163Sshin
9255163Sshin	return(0);
9355163Sshin}
9455163Sshin
9555163Sshinint
9655163Sshininterface_up(char *name)
9755163Sshin{
9855163Sshin	struct ifreq ifr;
9962632Skris	int llflag;
10055163Sshin
10155163Sshin	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
10255163Sshin
10355163Sshin	if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
10455163Sshin		warnmsg(LOG_WARNING, __FUNCTION__, "ioctl(SIOCGIFFLAGS): %s",
10555163Sshin		       strerror(errno));
10655163Sshin		return(-1);
10755163Sshin	}
10855163Sshin	if (!(ifr.ifr_flags & IFF_UP)) {
10955163Sshin		ifr.ifr_flags |= IFF_UP;
11055163Sshin		if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
11155163Sshin			warnmsg(LOG_ERR, __FUNCTION__,
11255163Sshin				"ioctl(SIOCSIFFLAGS): %s", strerror(errno));
11355163Sshin		}
11455163Sshin		return(-1);
11555163Sshin	}
11655163Sshin
11755163Sshin	warnmsg(LOG_DEBUG, __FUNCTION__, "checking if %s is ready...", name);
11855163Sshin
11962632Skris	llflag = get_llflag(name);
12062632Skris	if (llflag < 0) {
12155163Sshin		warnmsg(LOG_WARNING, __FUNCTION__,
12262632Skris			"get_llflag() failed, anyway I'll try");
12355163Sshin		return 0;
12455163Sshin	}
12555163Sshin
12662632Skris	if (!(llflag & IN6_IFF_NOTREADY)) {
12755163Sshin		warnmsg(LOG_DEBUG, __FUNCTION__,
12855163Sshin			"%s is ready", name);
12955163Sshin		return(0);
13062632Skris	} else {
13162632Skris		if (llflag & IN6_IFF_TENTATIVE) {
13255163Sshin			warnmsg(LOG_DEBUG, __FUNCTION__, "%s is tentative",
13355163Sshin			       name);
13455163Sshin			return IFS_TENTATIVE;
13555163Sshin		}
13662632Skris		if (llflag & IN6_IFF_DUPLICATED)
13755163Sshin			warnmsg(LOG_DEBUG, __FUNCTION__, "%s is duplicated",
13855163Sshin			       name);
13955163Sshin		return -1;
14055163Sshin	}
14155163Sshin}
14255163Sshin
14355163Sshinint
14455163Sshininterface_status(struct ifinfo *ifinfo)
14555163Sshin{
14655163Sshin	char *ifname = ifinfo->ifname;
14755163Sshin	struct ifreq ifr;
14855163Sshin	struct ifmediareq ifmr;
14962632Skris
15055163Sshin	/* get interface flags */
15155163Sshin	memset(&ifr, 0, sizeof(ifr));
15255163Sshin	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
15355163Sshin	if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
15455163Sshin		warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGIFFLAGS) on %s: %s",
15555163Sshin		       ifname, strerror(errno));
15655163Sshin		return(-1);
15755163Sshin	}
15855163Sshin	/*
15955163Sshin	 * if one of UP and RUNNING flags is dropped,
16055163Sshin	 * the interface is not active.
16155163Sshin	 */
16255163Sshin	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
16355163Sshin		goto inactive;
16455163Sshin	}
16555163Sshin
16655163Sshin	/* Next, check carrier on the interface, if possible */
16755163Sshin	if (!ifinfo->mediareqok)
16855163Sshin		goto active;
16955163Sshin	memset(&ifmr, 0, sizeof(ifmr));
17055163Sshin	strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
17155163Sshin
17255163Sshin	if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
17355163Sshin		if (errno != EINVAL) {
17455163Sshin			warnmsg(LOG_DEBUG, __FUNCTION__,
17555163Sshin				"ioctl(SIOCGIFMEDIA) on %s: %s",
17655163Sshin			       ifname, strerror(errno));
17755163Sshin			return(-1);
17855163Sshin		}
17955163Sshin		/*
18055163Sshin		 * EINVAL simply means that the interface does not support
18155163Sshin		 * the SIOCGIFMEDIA ioctl. We regard it alive.
18255163Sshin		 */
18355163Sshin		ifinfo->mediareqok = 0;
18455163Sshin		goto active;
18555163Sshin	}
18655163Sshin
18755163Sshin	if (ifmr.ifm_status & IFM_AVALID) {
18855163Sshin		switch(ifmr.ifm_active & IFM_NMASK) {
18955163Sshin		 case IFM_ETHER:
19055163Sshin			 if (ifmr.ifm_status & IFM_ACTIVE)
19155163Sshin				 goto active;
19255163Sshin			 else
19355163Sshin				 goto inactive;
19455163Sshin			 break;
19555163Sshin		 default:
19655163Sshin			 goto inactive;
19755163Sshin		}
19855163Sshin	}
19955163Sshin
20055163Sshin  inactive:
20155163Sshin	return(0);
20255163Sshin
20355163Sshin  active:
20455163Sshin	return(1);
20555163Sshin}
20655163Sshin
20762632Skris#define ROUNDUP(a, size) \
20855163Sshin	(((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
20955163Sshin
21062632Skris#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
21155163Sshin	((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
21255163Sshin						 sizeof(u_long)) :\
21355163Sshin			  			 sizeof(u_long)))
21462632Skris#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
21555163Sshin
21655163Sshinint
21755163Sshinlladdropt_length(struct sockaddr_dl *sdl)
21855163Sshin{
21955163Sshin	switch(sdl->sdl_type) {
22055163Sshin	 case IFT_ETHER:
22155163Sshin		 return(ROUNDUP8(ETHER_ADDR_LEN + 2));
22255163Sshin	 default:
22355163Sshin		 return(0);
22455163Sshin	}
22555163Sshin}
22655163Sshin
22755163Sshinvoid
22855163Sshinlladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
22955163Sshin{
23055163Sshin	char *addr;
23155163Sshin
23255163Sshin	ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
23355163Sshin
23455163Sshin	switch(sdl->sdl_type) {
23555163Sshin	 case IFT_ETHER:
23655163Sshin		 ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
23755163Sshin		 addr = (char *)(ndopt + 1);
23855163Sshin		 memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
23955163Sshin		 break;
24055163Sshin	 default:
24155163Sshin		 warnmsg(LOG_ERR, __FUNCTION__,
24255163Sshin			 "unsupported link type(%d)", sdl->sdl_type);
24355163Sshin		 exit(1);
24455163Sshin	}
24555163Sshin
24655163Sshin	return;
24755163Sshin}
24855163Sshin
24955163Sshinstruct sockaddr_dl *
25055163Sshinif_nametosdl(char *name)
25155163Sshin{
25255163Sshin	int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
25355163Sshin	char *buf, *next, *lim;
25455163Sshin	size_t len;
25555163Sshin	struct if_msghdr *ifm;
25655163Sshin	struct sockaddr *sa, *rti_info[RTAX_MAX];
25755163Sshin	struct sockaddr_dl *sdl = NULL, *ret_sdl;
25855163Sshin
25955163Sshin	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
26055163Sshin		return(NULL);
26155163Sshin	if ((buf = malloc(len)) == NULL)
26255163Sshin		return(NULL);
26355163Sshin	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
26455163Sshin		free(buf);
26555163Sshin		return(NULL);
26655163Sshin	}
26755163Sshin
26855163Sshin	lim = buf + len;
26955163Sshin	for (next = buf; next < lim; next += ifm->ifm_msglen) {
27055163Sshin		ifm = (struct if_msghdr *)next;
27155163Sshin		if (ifm->ifm_type == RTM_IFINFO) {
27255163Sshin			sa = (struct sockaddr *)(ifm + 1);
27355163Sshin			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
27455163Sshin			if ((sa = rti_info[RTAX_IFP]) != NULL) {
27555163Sshin				if (sa->sa_family == AF_LINK) {
27655163Sshin					sdl = (struct sockaddr_dl *)sa;
27762632Skris					if (strlen(name) != sdl->sdl_nlen)
27862632Skris						continue; /* not same len */
27955163Sshin					if (strncmp(&sdl->sdl_data[0],
28055163Sshin						    name,
28155163Sshin						    sdl->sdl_nlen) == 0) {
28255163Sshin						break;
28355163Sshin					}
28455163Sshin				}
28555163Sshin			}
28655163Sshin		}
28755163Sshin	}
28855163Sshin	if (next == lim) {
28955163Sshin		/* search failed */
29055163Sshin		free(buf);
29155163Sshin		return(NULL);
29255163Sshin	}
29355163Sshin
29455163Sshin	if ((ret_sdl = malloc(sdl->sdl_len)) == NULL)
29555163Sshin		return(NULL);
29655163Sshin	memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
29755163Sshin
29855163Sshin	return(ret_sdl);
29955163Sshin}
30055163Sshin
30155163Sshinint
30255163Sshingetinet6sysctl(int code)
30355163Sshin{
30455163Sshin	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
30555163Sshin	int value;
30655163Sshin	size_t size;
30755163Sshin
30855163Sshin	mib[3] = code;
30955163Sshin	size = sizeof(value);
31055163Sshin	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) < 0)
31155163Sshin		return -1;
31255163Sshin	else
31355163Sshin		return value;
31455163Sshin}
31555163Sshin
31655163Sshin/*------------------------------------------------------------*/
31755163Sshin
31862632Skris/* get ia6_flags for link-local addr on if.  returns -1 on error. */
31955163Sshinstatic int
32062632Skrisget_llflag(const char *name)
32155163Sshin{
32262632Skris#ifdef HAVE_GETIFADDRS
32362632Skris	struct ifaddrs *ifap, *ifa;
32462632Skris	struct in6_ifreq ifr6;
32562632Skris	struct sockaddr_in6 *sin6;
32662632Skris	int s;
32755163Sshin
32862632Skris	if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) {
32962632Skris		warnmsg(LOG_ERR, __FUNCTION__, "socket(SOCK_DGRAM): %s",
33062632Skris		    strerror(errno));
33155163Sshin		exit(1);
33255163Sshin	}
33362632Skris	if (getifaddrs(&ifap) != 0) {
33462632Skris		warnmsg(LOG_ERR, __FUNCTION__, "etifaddrs: %s",
33562632Skris		    strerror(errno));
33662632Skris		exit(1);
33755163Sshin	}
33862632Skris
33962632Skris	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
34062632Skris		if (strlen(ifa->ifa_name) != strlen(name)
34162632Skris		 || strncmp(ifa->ifa_name, name, strlen(name)) != 0)
34262632Skris			continue;
34362632Skris		if (ifa->ifa_addr->sa_family != AF_INET6)
34462632Skris			continue;
34562632Skris		sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
34662632Skris		if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
34762632Skris			continue;
34862632Skris
34962632Skris		memset(&ifr6, 0, sizeof(ifr6));
35062632Skris		strcpy(ifr6.ifr_name, name);
35162632Skris		memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len);
35262632Skris		if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
35362632Skris			warnmsg(LOG_ERR, __FUNCTION__,
35462632Skris			    "ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno));
35562632Skris			exit(1);
35662632Skris		}
35762632Skris
35862632Skris		freeifaddrs(ifap);
35962632Skris		close(s);
36062632Skris		return ifr6.ifr_ifru.ifru_flags6;
36155163Sshin	}
36262632Skris
36362632Skris	freeifaddrs(ifap);
36462632Skris	close(s);
36562632Skris	return -1;
36662632Skris#else
36762632Skris	int s;
36862632Skris	unsigned int maxif;
36962632Skris	struct ifreq *iflist;
37062632Skris	struct ifconf ifconf;
37162632Skris	struct ifreq *ifr, *ifr_end;
37262632Skris	struct sockaddr_in6 *sin6;
37362632Skris	struct in6_ifreq ifr6;
37462632Skris
37562632Skris	maxif = if_maxindex() + 1;
37662632Skris	iflist = (struct ifreq *)malloc(maxif * BUFSIZ);	/* XXX */
37762632Skris	if (iflist == NULL) {
37862632Skris		warnmsg(LOG_ERR, __FUNCTION__, "not enough core");
37962632Skris		exit(1);
38055163Sshin	}
38155163Sshin
38262632Skris	if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) {
38362632Skris		warnmsg(LOG_ERR, __FUNCTION__, "socket(SOCK_DGRAM): %s",
38462632Skris		    strerror(errno));
38562632Skris		exit(1);
38655163Sshin	}
38762632Skris	memset(&ifconf, 0, sizeof(ifconf));
38862632Skris	ifconf.ifc_req = iflist;
38962632Skris	ifconf.ifc_len = maxif * BUFSIZ;	/* XXX */
39062632Skris	if (ioctl(s, SIOCGIFCONF, &ifconf) < 0) {
39162632Skris		warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGIFCONF): %s",
39262632Skris		    strerror(errno));
39362632Skris		exit(1);
39455163Sshin	}
39555163Sshin
39662632Skris	/* Look for this interface in the list */
39762632Skris	ifr_end = (struct ifreq *) (ifconf.ifc_buf + ifconf.ifc_len);
39862632Skris	for (ifr = ifconf.ifc_req;
39962632Skris	     ifr < ifr_end;
40062632Skris	     ifr = (struct ifreq *) ((char *) &ifr->ifr_addr
40162632Skris				    + ifr->ifr_addr.sa_len)) {
40262632Skris		if (strlen(ifr->ifr_name) != strlen(name)
40362632Skris		 || strncmp(ifr->ifr_name, name, strlen(name)) != 0)
40462632Skris			continue;
40562632Skris		if (ifr->ifr_addr.sa_family != AF_INET6)
40662632Skris			continue;
40762632Skris		sin6 = (struct sockaddr_in6 *)&ifr->ifr_addr;
40862632Skris		if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
40962632Skris			continue;
41062632Skris
41162632Skris		memset(&ifr6, 0, sizeof(ifr6));
41262632Skris		strcpy(ifr6.ifr_name, name);
41362632Skris		memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len);
41462632Skris		if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
41562632Skris			warnmsg(LOG_ERR, __FUNCTION__,
41662632Skris			    "ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno));
41762632Skris			exit(1);
41855163Sshin		}
41955163Sshin
42062632Skris		free(iflist);
42162632Skris		close(s);
42262632Skris		return ifr6.ifr_ifru.ifru_flags6;
42355163Sshin	}
42455163Sshin
42562632Skris	free(iflist);
42662632Skris	close(s);
42755163Sshin	return -1;
42862632Skris#endif
42955163Sshin}
43055163Sshin
43162632Skris#ifndef HAVE_GETIFADDRS
43262632Skrisstatic unsigned int
43362632Skrisif_maxindex()
43462632Skris{
43562632Skris	struct if_nameindex *p, *p0;
43662632Skris	unsigned int max = 0;
43762632Skris
43862632Skris	p0 = if_nameindex();
43962632Skris	for (p = p0; p && p->if_index && p->if_name; p++) {
44062632Skris		if (max < p->if_index)
44162632Skris			max = p->if_index;
44262632Skris	}
44362632Skris	if_freenameindex(p0);
44462632Skris	return max;
44562632Skris}
44662632Skris#endif
44762632Skris
44855163Sshinstatic void
44955163Sshinget_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
45055163Sshin{
45155163Sshin	int i;
45262632Skris
45355163Sshin	for (i = 0; i < RTAX_MAX; i++) {
45455163Sshin		if (addrs & (1 << i)) {
45555163Sshin			rti_info[i] = sa;
45655163Sshin			NEXT_SA(sa);
45755163Sshin		}
45855163Sshin		else
45955163Sshin			rti_info[i] = NULL;
46055163Sshin	}
46155163Sshin}
462