if.c revision 55163
155163Sshin/*
255163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
355163Sshin * All rights reserved.
455163Sshin *
555163Sshin * Redistribution and use in source and binary forms, with or without
655163Sshin * modification, are permitted provided that the following conditions
755163Sshin * are met:
855163Sshin * 1. Redistributions of source code must retain the above copyright
955163Sshin *    notice, this list of conditions and the following disclaimer.
1055163Sshin * 2. Redistributions in binary form must reproduce the above copyright
1155163Sshin *    notice, this list of conditions and the following disclaimer in the
1255163Sshin *    documentation and/or other materials provided with the distribution.
1355163Sshin * 3. Neither the name of the project nor the names of its contributors
1455163Sshin *    may be used to endorse or promote products derived from this software
1555163Sshin *    without specific prior written permission.
1655163Sshin *
1755163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1855163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1955163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2055163Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2155163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2255163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2355163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2455163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2555163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2655163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2755163Sshin * SUCH DAMAGE.
2855163Sshin *
2955163Sshin * $FreeBSD: head/usr.sbin/rtsold/if.c 55163 1999-12-28 02:37:14Z shin $
3055163Sshin */
3155163Sshin
3255163Sshin#include <sys/param.h>
3355163Sshin#include <sys/socket.h>
3455163Sshin#include <sys/sysctl.h>
3555163Sshin#include <sys/ioctl.h>
3655163Sshin
3755163Sshin#include <net/if.h>
3855163Sshin#if defined(__FreeBSD__) && __FreeBSD__ >= 3
3955163Sshin#include <net/if_var.h>
4055163Sshin#endif /* __FreeBSD__ >= 3 */
4155163Sshin#include <net/if_types.h>
4255163Sshin#include <net/route.h>
4355163Sshin#include <net/if_dl.h>
4455163Sshin#include <net/if_media.h>
4555163Sshin#ifdef __FreeBSD__
4655163Sshin# include <net/ethernet.h>
4755163Sshin#endif
4855163Sshin#ifdef __NetBSD__
4955163Sshin#include <net/if_ether.h>
5055163Sshin#endif
5155163Sshin#if defined(__bsdi__) || defined(__OpenBSD__)
5255163Sshin# include <netinet/in.h>
5355163Sshin# include <netinet/if_ether.h>
5455163Sshin#endif
5555163Sshin#include <netinet/in.h>
5655163Sshin#include <netinet/icmp6.h>
5755163Sshin
5855163Sshin#include <netinet6/in6_var.h>
5955163Sshin
6055163Sshin#include <stdio.h>
6155163Sshin#include <unistd.h>
6255163Sshin#include <stdlib.h>
6355163Sshin#include <syslog.h>
6455163Sshin#include <string.h>
6555163Sshin#include <fcntl.h>
6655163Sshin#include <errno.h>
6755163Sshin#include <kvm.h>
6855163Sshin#include <nlist.h>
6955163Sshin#include <limits.h>
7055163Sshin
7155163Sshin#include "rtsold.h"
7255163Sshin
7355163Sshinstatic int ifsock;
7455163Sshin
7555163Sshinstatic int getifa __P((char *name, struct in6_ifaddr *ifap));
7655163Sshinstatic void get_rtaddrs __P((int addrs, struct sockaddr *sa,
7755163Sshin			     struct sockaddr **rti_info));
7855163Sshin
7955163Sshinint
8055163Sshinifinit()
8155163Sshin{
8255163Sshin	if ((ifsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
8355163Sshin		warnmsg(LOG_ERR, __FUNCTION__, "socket: %s", strerror(errno));
8455163Sshin		return(-1);
8555163Sshin	}
8655163Sshin
8755163Sshin	return(0);
8855163Sshin}
8955163Sshin
9055163Sshinint
9155163Sshininterface_up(char *name)
9255163Sshin{
9355163Sshin	struct ifreq ifr;
9455163Sshin	struct in6_ifaddr ifa;
9555163Sshin
9655163Sshin	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
9755163Sshin
9855163Sshin	if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
9955163Sshin		warnmsg(LOG_WARNING, __FUNCTION__, "ioctl(SIOCGIFFLAGS): %s",
10055163Sshin		       strerror(errno));
10155163Sshin		return(-1);
10255163Sshin	}
10355163Sshin	if (!(ifr.ifr_flags & IFF_UP)) {
10455163Sshin		ifr.ifr_flags |= IFF_UP;
10555163Sshin		if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
10655163Sshin			warnmsg(LOG_ERR, __FUNCTION__,
10755163Sshin				"ioctl(SIOCSIFFLAGS): %s", strerror(errno));
10855163Sshin		}
10955163Sshin		return(-1);
11055163Sshin	}
11155163Sshin
11255163Sshin	warnmsg(LOG_DEBUG, __FUNCTION__, "checking if %s is ready...", name);
11355163Sshin
11455163Sshin	if (getifa(name, &ifa) < 0) {
11555163Sshin		warnmsg(LOG_WARNING, __FUNCTION__,
11655163Sshin			"getifa() failed, anyway I'll try");
11755163Sshin		return 0;
11855163Sshin	}
11955163Sshin
12055163Sshin	if (!(ifa.ia6_flags & IN6_IFF_NOTREADY)) {
12155163Sshin		warnmsg(LOG_DEBUG, __FUNCTION__,
12255163Sshin			"%s is ready", name);
12355163Sshin		return(0);
12455163Sshin	}
12555163Sshin	else {
12655163Sshin		if (ifa.ia6_flags & IN6_IFF_TENTATIVE) {
12755163Sshin			warnmsg(LOG_DEBUG, __FUNCTION__, "%s is tentative",
12855163Sshin			       name);
12955163Sshin			return IFS_TENTATIVE;
13055163Sshin		}
13155163Sshin		if (ifa.ia6_flags & IN6_IFF_DUPLICATED)
13255163Sshin			warnmsg(LOG_DEBUG, __FUNCTION__, "%s is duplicated",
13355163Sshin			       name);
13455163Sshin		return -1;
13555163Sshin	}
13655163Sshin}
13755163Sshin
13855163Sshinint
13955163Sshininterface_status(struct ifinfo *ifinfo)
14055163Sshin{
14155163Sshin	char *ifname = ifinfo->ifname;
14255163Sshin	struct ifreq ifr;
14355163Sshin	struct ifmediareq ifmr;
14455163Sshin
14555163Sshin	/* get interface flags */
14655163Sshin	memset(&ifr, 0, sizeof(ifr));
14755163Sshin	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
14855163Sshin	if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
14955163Sshin		warnmsg(LOG_ERR, __FUNCTION__, "ioctl(SIOCGIFFLAGS) on %s: %s",
15055163Sshin		       ifname, strerror(errno));
15155163Sshin		return(-1);
15255163Sshin	}
15355163Sshin	/*
15455163Sshin	 * if one of UP and RUNNING flags is dropped,
15555163Sshin	 * the interface is not active.
15655163Sshin	 */
15755163Sshin	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
15855163Sshin		goto inactive;
15955163Sshin	}
16055163Sshin
16155163Sshin	/* Next, check carrier on the interface, if possible */
16255163Sshin	if (!ifinfo->mediareqok)
16355163Sshin		goto active;
16455163Sshin	memset(&ifmr, 0, sizeof(ifmr));
16555163Sshin	strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
16655163Sshin
16755163Sshin	if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
16855163Sshin		if (errno != EINVAL) {
16955163Sshin			warnmsg(LOG_DEBUG, __FUNCTION__,
17055163Sshin				"ioctl(SIOCGIFMEDIA) on %s: %s",
17155163Sshin			       ifname, strerror(errno));
17255163Sshin			return(-1);
17355163Sshin		}
17455163Sshin		/*
17555163Sshin		 * EINVAL simply means that the interface does not support
17655163Sshin		 * the SIOCGIFMEDIA ioctl. We regard it alive.
17755163Sshin		 */
17855163Sshin		ifinfo->mediareqok = 0;
17955163Sshin		goto active;
18055163Sshin	}
18155163Sshin
18255163Sshin	if (ifmr.ifm_status & IFM_AVALID) {
18355163Sshin		switch(ifmr.ifm_active & IFM_NMASK) {
18455163Sshin		 case IFM_ETHER:
18555163Sshin			 if (ifmr.ifm_status & IFM_ACTIVE)
18655163Sshin				 goto active;
18755163Sshin			 else
18855163Sshin				 goto inactive;
18955163Sshin			 break;
19055163Sshin		 default:
19155163Sshin			 goto inactive;
19255163Sshin		}
19355163Sshin	}
19455163Sshin
19555163Sshin  inactive:
19655163Sshin	return(0);
19755163Sshin
19855163Sshin  active:
19955163Sshin	return(1);
20055163Sshin}
20155163Sshin
20255163Sshin#define	ROUNDUP(a, size) \
20355163Sshin	(((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
20455163Sshin
20555163Sshin#define	NEXT_SA(ap) (ap) = (struct sockaddr *) \
20655163Sshin	((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
20755163Sshin						 sizeof(u_long)) :\
20855163Sshin			  			 sizeof(u_long)))
20955163Sshin#define	ROUNDUP8(a) (1 + (((a) - 1) | 7))
21055163Sshin
21155163Sshinint
21255163Sshinlladdropt_length(struct sockaddr_dl *sdl)
21355163Sshin{
21455163Sshin	switch(sdl->sdl_type) {
21555163Sshin	 case IFT_ETHER:
21655163Sshin		 return(ROUNDUP8(ETHER_ADDR_LEN + 2));
21755163Sshin	 default:
21855163Sshin		 return(0);
21955163Sshin	}
22055163Sshin}
22155163Sshin
22255163Sshinvoid
22355163Sshinlladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
22455163Sshin{
22555163Sshin	char *addr;
22655163Sshin
22755163Sshin	ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
22855163Sshin
22955163Sshin	switch(sdl->sdl_type) {
23055163Sshin	 case IFT_ETHER:
23155163Sshin		 ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
23255163Sshin		 addr = (char *)(ndopt + 1);
23355163Sshin		 memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
23455163Sshin		 break;
23555163Sshin	 default:
23655163Sshin		 warnmsg(LOG_ERR, __FUNCTION__,
23755163Sshin			 "unsupported link type(%d)", sdl->sdl_type);
23855163Sshin		 exit(1);
23955163Sshin	}
24055163Sshin
24155163Sshin	return;
24255163Sshin}
24355163Sshin
24455163Sshinstruct sockaddr_dl *
24555163Sshinif_nametosdl(char *name)
24655163Sshin{
24755163Sshin	int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
24855163Sshin	char *buf, *next, *lim;
24955163Sshin	size_t len;
25055163Sshin	struct if_msghdr *ifm;
25155163Sshin	struct sockaddr *sa, *rti_info[RTAX_MAX];
25255163Sshin	struct sockaddr_dl *sdl = NULL, *ret_sdl;
25355163Sshin
25455163Sshin	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
25555163Sshin		return(NULL);
25655163Sshin	if ((buf = malloc(len)) == NULL)
25755163Sshin		return(NULL);
25855163Sshin	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
25955163Sshin		free(buf);
26055163Sshin		return(NULL);
26155163Sshin	}
26255163Sshin
26355163Sshin	lim = buf + len;
26455163Sshin	for (next = buf; next < lim; next += ifm->ifm_msglen) {
26555163Sshin		ifm = (struct if_msghdr *)next;
26655163Sshin		if (ifm->ifm_type == RTM_IFINFO) {
26755163Sshin			sa = (struct sockaddr *)(ifm + 1);
26855163Sshin			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
26955163Sshin			if ((sa = rti_info[RTAX_IFP]) != NULL) {
27055163Sshin				if (sa->sa_family == AF_LINK) {
27155163Sshin					sdl = (struct sockaddr_dl *)sa;
27255163Sshin					if (strncmp(&sdl->sdl_data[0],
27355163Sshin						    name,
27455163Sshin						    sdl->sdl_nlen) == 0) {
27555163Sshin						break;
27655163Sshin					}
27755163Sshin				}
27855163Sshin			}
27955163Sshin		}
28055163Sshin	}
28155163Sshin	if (next == lim) {
28255163Sshin		/* search failed */
28355163Sshin		free(buf);
28455163Sshin		return(NULL);
28555163Sshin	}
28655163Sshin
28755163Sshin	if ((ret_sdl = malloc(sdl->sdl_len)) == NULL)
28855163Sshin		return(NULL);
28955163Sshin	memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
29055163Sshin
29155163Sshin	return(ret_sdl);
29255163Sshin}
29355163Sshin
29455163Sshinint
29555163Sshingetinet6sysctl(int code)
29655163Sshin{
29755163Sshin	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
29855163Sshin	int value;
29955163Sshin	size_t size;
30055163Sshin
30155163Sshin	mib[3] = code;
30255163Sshin	size = sizeof(value);
30355163Sshin	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) < 0)
30455163Sshin		return -1;
30555163Sshin	else
30655163Sshin		return value;
30755163Sshin}
30855163Sshin
30955163Sshin/*------------------------------------------------------------*/
31055163Sshin
31155163Sshinstatic struct nlist nl[] = {
31255163Sshin#define	N_IFNET	0
31355163Sshin	{ "_ifnet" },
31455163Sshin	{ "" },
31555163Sshin};
31655163Sshin
31755163Sshin#define	KREAD(x, y, z) { \
31855163Sshin	if (kvm_read(kvmd, (u_long)x, (void *)y, sizeof(z)) != sizeof(z)) { \
31955163Sshin		warnmsg(LOG_ERR, __FUNCTION__, "kvm_read failed");	\
32055163Sshin		goto bad;						\
32155163Sshin	}								\
32255163Sshin   }
32355163Sshin
32455163Sshinstatic int
32555163Sshingetifa(char *name, struct in6_ifaddr *ifap)
32655163Sshin{
32755163Sshin	u_short index;
32855163Sshin	kvm_t *kvmd = NULL;
32955163Sshin	char buf[_POSIX2_LINE_MAX];
33055163Sshin	struct ifnet *ifp;
33155163Sshin	struct ifnet ifnet;
33255163Sshin	struct in6_ifaddr *ifa;
33355163Sshin
33455163Sshin	if (!ifap)
33555163Sshin		exit(1);
33655163Sshin
33755163Sshin	index = (u_short)if_nametoindex(name);
33855163Sshin	if (index == 0) {
33955163Sshin		warnmsg(LOG_ERR, __FUNCTION__, "if_nametoindex failed for %s",
34055163Sshin		       name);
34155163Sshin		goto bad;
34255163Sshin	}
34355163Sshin	if ((kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, buf)) == NULL) {
34455163Sshin		warnmsg(LOG_ERR, __FUNCTION__, "kvm_openfiles failed");
34555163Sshin		goto bad;
34655163Sshin	}
34755163Sshin	if (kvm_nlist(kvmd, nl) < 0) {
34855163Sshin		warnmsg(LOG_ERR, __FUNCTION__, "kvm_nlist failed");
34955163Sshin		goto bad;
35055163Sshin	}
35155163Sshin	if (nl[N_IFNET].n_value == 0) {
35255163Sshin		warnmsg(LOG_ERR, __FUNCTION__, "symbol \"%s\" not found",
35355163Sshin		       nl[N_IFNET].n_name);
35455163Sshin		goto bad;
35555163Sshin	}
35655163Sshin
35755163Sshin	KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *);
35855163Sshin	while (ifp) {
35955163Sshin		KREAD(ifp, &ifnet, struct ifnet);
36055163Sshin		if (ifnet.if_index == index)
36155163Sshin			break;
36255163Sshin#if defined(__NetBSD__) || defined(__OpenBSD__)
36355163Sshin		ifp = TAILQ_NEXT(&ifnet, if_list);
36455163Sshin#elif defined(__FreeBSD__) && __FreeBSD__ >= 3
36555163Sshin		ifp = TAILQ_NEXT(&ifnet, if_link);
36655163Sshin#else
36755163Sshin		ifp = ifnet.if_next;
36855163Sshin#endif
36955163Sshin	}
37055163Sshin	if (!ifp) {
37155163Sshin		warnmsg(LOG_ERR, __FUNCTION__, "interface \"%s\" not found",
37255163Sshin		       name);
37355163Sshin		goto bad;
37455163Sshin	}
37555163Sshin
37655163Sshin#if defined(__NetBSD__) || defined(__OpenBSD__)
37755163Sshin	ifa = (struct in6_ifaddr *)TAILQ_FIRST(&ifnet.if_addrlist);
37855163Sshin#elif defined(__FreeBSD__) && __FreeBSD__ >= 3
37955163Sshin	ifa = (struct in6_ifaddr *)TAILQ_FIRST(&ifnet.if_addrhead);
38055163Sshin#else
38155163Sshin	ifa = (struct in6_ifaddr *)ifnet.if_addrlist;
38255163Sshin#endif
38355163Sshin	while (ifa) {
38455163Sshin		KREAD(ifa, ifap, *ifap);
38555163Sshin		if (ifap->ia_addr.sin6_family == AF_INET6
38655163Sshin		 && IN6_IS_ADDR_LINKLOCAL(&ifap->ia_addr.sin6_addr)) {
38755163Sshin			kvm_close(kvmd);
38855163Sshin			return 0;
38955163Sshin		}
39055163Sshin
39155163Sshin#if defined(__NetBSD__) || defined(__OpenBSD__)
39255163Sshin		ifa = (struct in6_ifaddr *)
39355163Sshin			TAILQ_NEXT((struct ifaddr *)ifap, ifa_list);
39455163Sshin#elif defined(__FreeBSD__) && __FreeBSD__ >= 3
39555163Sshin		ifa = (struct in6_ifaddr *)
39655163Sshin			TAILQ_NEXT((struct ifaddr *)ifap, ifa_link);
39755163Sshin#else
39855163Sshin		ifa = (struct in6_ifaddr *)(((struct ifaddr *)ifap)->ifa_next);
39955163Sshin#endif
40055163Sshin	}
40155163Sshin	warnmsg(LOG_ERR, __FUNCTION__, "no IPv6 link-local address for %s",
40255163Sshin	       name);
40355163Sshin
40455163Sshin  bad:
40555163Sshin	if (kvmd)
40655163Sshin		kvm_close(kvmd);
40755163Sshin	return -1;
40855163Sshin}
40955163Sshin
41055163Sshinstatic void
41155163Sshinget_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
41255163Sshin{
41355163Sshin	int i;
41455163Sshin
41555163Sshin	for (i = 0; i < RTAX_MAX; i++) {
41655163Sshin		if (addrs & (1 << i)) {
41755163Sshin			rti_info[i] = sa;
41855163Sshin			NEXT_SA(sa);
41955163Sshin		}
42055163Sshin		else
42155163Sshin			rti_info[i] = NULL;
42255163Sshin	}
42355163Sshin}
424