if.c revision 157108
1124524Sume/*	$KAME: if.c,v 1.27 2003/10/05 00:09:36 itojun Exp $	*/
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 157108 2006-03-24 23:59:51Z suz $
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>
4155163Sshin#include <net/if_var.h>
4255163Sshin#include <net/if_types.h>
4355163Sshin#include <net/route.h>
4455163Sshin#include <net/if_dl.h>
4555163Sshin#include <net/if_media.h>
46118787Sume#include <net/ethernet.h>
4755163Sshin#include <netinet/in.h>
4855163Sshin#include <netinet/icmp6.h>
4955163Sshin
5055163Sshin#include <netinet6/in6_var.h>
5155163Sshin
5255163Sshin#include <stdio.h>
5355163Sshin#include <unistd.h>
5455163Sshin#include <stdlib.h>
5555163Sshin#include <syslog.h>
5655163Sshin#include <string.h>
5755163Sshin#include <fcntl.h>
5855163Sshin#include <errno.h>
5955163Sshin#include <limits.h>
6062632Skris#include <ifaddrs.h>
6155163Sshin#include "rtsold.h"
6255163Sshin
6362632Skrisextern int rssock;
6455163Sshinstatic int ifsock;
6555163Sshin
66118998Sumestatic int get_llflag __P((const char *));
67118998Sumestatic void get_rtaddrs __P((int, struct sockaddr *, struct sockaddr **));
6855163Sshin
6955163Sshinint
70124524Sumeifinit(void)
7155163Sshin{
7262632Skris	ifsock = rssock;
7355163Sshin
7455163Sshin	return(0);
7555163Sshin}
7655163Sshin
7755163Sshinint
7855163Sshininterface_up(char *name)
7955163Sshin{
8055163Sshin	struct ifreq ifr;
8162632Skris	int llflag;
8255163Sshin
83119027Sume	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
8455163Sshin
8555163Sshin	if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
86118660Sume		warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFFLAGS): %s",
87118664Sume		    strerror(errno));
8855163Sshin		return(-1);
8955163Sshin	}
9055163Sshin	if (!(ifr.ifr_flags & IFF_UP)) {
9155163Sshin		ifr.ifr_flags |= IFF_UP;
92118664Sume		if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0)
93118660Sume			warnmsg(LOG_ERR, __func__,
94118664Sume			    "ioctl(SIOCSIFFLAGS): %s", strerror(errno));
9555163Sshin		return(-1);
9655163Sshin	}
9755163Sshin
98118660Sume	warnmsg(LOG_DEBUG, __func__, "checking if %s is ready...", name);
9955163Sshin
10062632Skris	llflag = get_llflag(name);
10162632Skris	if (llflag < 0) {
102118660Sume		warnmsg(LOG_WARNING, __func__,
103118664Sume		    "get_llflag() failed, anyway I'll try");
10455163Sshin		return 0;
10555163Sshin	}
10655163Sshin
10762632Skris	if (!(llflag & IN6_IFF_NOTREADY)) {
108118664Sume		warnmsg(LOG_DEBUG, __func__, "%s is ready", name);
10955163Sshin		return(0);
11062632Skris	} else {
11162632Skris		if (llflag & IN6_IFF_TENTATIVE) {
112118660Sume			warnmsg(LOG_DEBUG, __func__, "%s is tentative",
113118664Sume			    name);
11455163Sshin			return IFS_TENTATIVE;
11555163Sshin		}
11662632Skris		if (llflag & IN6_IFF_DUPLICATED)
117118660Sume			warnmsg(LOG_DEBUG, __func__, "%s is duplicated",
118118664Sume			    name);
11955163Sshin		return -1;
12055163Sshin	}
12155163Sshin}
12255163Sshin
12355163Sshinint
12455163Sshininterface_status(struct ifinfo *ifinfo)
12555163Sshin{
12655163Sshin	char *ifname = ifinfo->ifname;
12755163Sshin	struct ifreq ifr;
12855163Sshin	struct ifmediareq ifmr;
129118664Sume
13055163Sshin	/* get interface flags */
13155163Sshin	memset(&ifr, 0, sizeof(ifr));
132119027Sume	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
13355163Sshin	if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
134118660Sume		warnmsg(LOG_ERR, __func__, "ioctl(SIOCGIFFLAGS) on %s: %s",
135118664Sume		    ifname, strerror(errno));
13655163Sshin		return(-1);
13755163Sshin	}
13855163Sshin	/*
13955163Sshin	 * if one of UP and RUNNING flags is dropped,
14055163Sshin	 * the interface is not active.
14155163Sshin	 */
14255163Sshin	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
14355163Sshin		goto inactive;
14455163Sshin	}
14555163Sshin
14655163Sshin	/* Next, check carrier on the interface, if possible */
14755163Sshin	if (!ifinfo->mediareqok)
14855163Sshin		goto active;
14955163Sshin	memset(&ifmr, 0, sizeof(ifmr));
150119027Sume	strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
15155163Sshin
15255163Sshin	if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
15355163Sshin		if (errno != EINVAL) {
154118660Sume			warnmsg(LOG_DEBUG, __func__,
155118664Sume			    "ioctl(SIOCGIFMEDIA) on %s: %s",
156118664Sume			    ifname, strerror(errno));
15755163Sshin			return(-1);
15855163Sshin		}
15955163Sshin		/*
16055163Sshin		 * EINVAL simply means that the interface does not support
16155163Sshin		 * the SIOCGIFMEDIA ioctl. We regard it alive.
16255163Sshin		 */
16355163Sshin		ifinfo->mediareqok = 0;
16455163Sshin		goto active;
16555163Sshin	}
16655163Sshin
16755163Sshin	if (ifmr.ifm_status & IFM_AVALID) {
168118664Sume		switch (ifmr.ifm_active & IFM_NMASK) {
169118664Sume		case IFM_ETHER:
170118998Sume		case IFM_IEEE80211:
171118664Sume			if (ifmr.ifm_status & IFM_ACTIVE)
172118664Sume				goto active;
173118664Sume			else
174118664Sume				goto inactive;
175118664Sume			break;
176118664Sume		default:
177118664Sume			goto inactive;
17855163Sshin		}
17955163Sshin	}
18055163Sshin
18155163Sshin  inactive:
18255163Sshin	return(0);
18355163Sshin
18455163Sshin  active:
18555163Sshin	return(1);
18655163Sshin}
18755163Sshin
18862632Skris#define ROUNDUP(a, size) \
18955163Sshin	(((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
19055163Sshin
19162632Skris#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
19255163Sshin	((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
193118664Sume	sizeof(u_long)) : sizeof(u_long)))
19462632Skris#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
19555163Sshin
19655163Sshinint
19755163Sshinlladdropt_length(struct sockaddr_dl *sdl)
19855163Sshin{
199118664Sume	switch (sdl->sdl_type) {
200118664Sume	case IFT_ETHER:
20178064Sume#ifdef IFT_IEEE80211
20278064Sume	case IFT_IEEE80211:
20378064Sume#endif
204118664Sume		return(ROUNDUP8(ETHER_ADDR_LEN + 2));
205118664Sume	default:
206118664Sume		return(0);
20755163Sshin	}
20855163Sshin}
20955163Sshin
21055163Sshinvoid
21155163Sshinlladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
21255163Sshin{
21355163Sshin	char *addr;
21455163Sshin
21555163Sshin	ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
21655163Sshin
217118664Sume	switch (sdl->sdl_type) {
218118664Sume	case IFT_ETHER:
21978064Sume#ifdef IFT_IEEE80211
22078064Sume	case IFT_IEEE80211:
22178064Sume#endif
222118664Sume		ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
223118664Sume		addr = (char *)(ndopt + 1);
224118664Sume		memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
225118664Sume		break;
226118664Sume	default:
227118664Sume		warnmsg(LOG_ERR, __func__,
228118664Sume		    "unsupported link type(%d)", sdl->sdl_type);
229118664Sume		exit(1);
23055163Sshin	}
23155163Sshin
23255163Sshin	return;
23355163Sshin}
23455163Sshin
23555163Sshinstruct sockaddr_dl *
23655163Sshinif_nametosdl(char *name)
23755163Sshin{
23855163Sshin	int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
23955163Sshin	char *buf, *next, *lim;
24055163Sshin	size_t len;
24155163Sshin	struct if_msghdr *ifm;
24255163Sshin	struct sockaddr *sa, *rti_info[RTAX_MAX];
24355163Sshin	struct sockaddr_dl *sdl = NULL, *ret_sdl;
24455163Sshin
24555163Sshin	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
24655163Sshin		return(NULL);
24755163Sshin	if ((buf = malloc(len)) == NULL)
24855163Sshin		return(NULL);
24955163Sshin	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
25055163Sshin		free(buf);
25155163Sshin		return(NULL);
25255163Sshin	}
25355163Sshin
25455163Sshin	lim = buf + len;
25555163Sshin	for (next = buf; next < lim; next += ifm->ifm_msglen) {
25655163Sshin		ifm = (struct if_msghdr *)next;
25755163Sshin		if (ifm->ifm_type == RTM_IFINFO) {
25855163Sshin			sa = (struct sockaddr *)(ifm + 1);
25955163Sshin			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
26055163Sshin			if ((sa = rti_info[RTAX_IFP]) != NULL) {
26155163Sshin				if (sa->sa_family == AF_LINK) {
26255163Sshin					sdl = (struct sockaddr_dl *)sa;
26362632Skris					if (strlen(name) != sdl->sdl_nlen)
26462632Skris						continue; /* not same len */
26555163Sshin					if (strncmp(&sdl->sdl_data[0],
26655163Sshin						    name,
26755163Sshin						    sdl->sdl_nlen) == 0) {
26855163Sshin						break;
26955163Sshin					}
27055163Sshin				}
27155163Sshin			}
27255163Sshin		}
27355163Sshin	}
27455163Sshin	if (next == lim) {
27555163Sshin		/* search failed */
27655163Sshin		free(buf);
27755163Sshin		return(NULL);
27855163Sshin	}
27955163Sshin
280157108Ssuz	if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) {
281157108Ssuz		free(buf);
28255163Sshin		return(NULL);
283157108Ssuz	}
28455163Sshin	memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
28555163Sshin
28678064Sume	free(buf);
28755163Sshin	return(ret_sdl);
28855163Sshin}
28955163Sshin
29055163Sshinint
29155163Sshingetinet6sysctl(int code)
29255163Sshin{
29355163Sshin	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
29455163Sshin	int value;
29555163Sshin	size_t size;
29655163Sshin
29755163Sshin	mib[3] = code;
29855163Sshin	size = sizeof(value);
29955163Sshin	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) < 0)
30055163Sshin		return -1;
30155163Sshin	else
30255163Sshin		return value;
30355163Sshin}
30455163Sshin
305124525Sumeint
306124525Sumesetinet6sysctl(int code, int newval)
307124525Sume{
308124525Sume	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
309124525Sume	int value;
310124525Sume	size_t size;
311124525Sume
312124525Sume	mib[3] = code;
313124525Sume	size = sizeof(value);
314124525Sume	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size,
315124525Sume	    &newval, sizeof(newval)) < 0)
316124525Sume		return -1;
317124525Sume	else
318124525Sume		return value;
319124525Sume}
320124525Sume
32155163Sshin/*------------------------------------------------------------*/
32255163Sshin
32362632Skris/* get ia6_flags for link-local addr on if.  returns -1 on error. */
32455163Sshinstatic int
32562632Skrisget_llflag(const char *name)
32655163Sshin{
32762632Skris	struct ifaddrs *ifap, *ifa;
32862632Skris	struct in6_ifreq ifr6;
32962632Skris	struct sockaddr_in6 *sin6;
33062632Skris	int s;
33155163Sshin
33262632Skris	if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) {
333118660Sume		warnmsg(LOG_ERR, __func__, "socket(SOCK_DGRAM): %s",
33462632Skris		    strerror(errno));
33555163Sshin		exit(1);
33655163Sshin	}
33762632Skris	if (getifaddrs(&ifap) != 0) {
338118664Sume		warnmsg(LOG_ERR, __func__, "getifaddrs: %s",
33962632Skris		    strerror(errno));
34062632Skris		exit(1);
34155163Sshin	}
34262632Skris
34362632Skris	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
344118664Sume		if (strlen(ifa->ifa_name) != strlen(name) ||
345118664Sume		    strncmp(ifa->ifa_name, name, strlen(name)) != 0)
34662632Skris			continue;
34762632Skris		if (ifa->ifa_addr->sa_family != AF_INET6)
34862632Skris			continue;
34962632Skris		sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
35062632Skris		if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
35162632Skris			continue;
35262632Skris
35362632Skris		memset(&ifr6, 0, sizeof(ifr6));
354119027Sume		strncpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name));
35562632Skris		memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len);
35662632Skris		if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
357118660Sume			warnmsg(LOG_ERR, __func__,
35862632Skris			    "ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno));
35962632Skris			exit(1);
36062632Skris		}
36162632Skris
36262632Skris		freeifaddrs(ifap);
36362632Skris		close(s);
36462632Skris		return ifr6.ifr_ifru.ifru_flags6;
36555163Sshin	}
36662632Skris
36762632Skris	freeifaddrs(ifap);
36862632Skris	close(s);
36962632Skris	return -1;
37055163Sshin}
37155163Sshin
372118664Sume
37355163Sshinstatic void
37455163Sshinget_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
37555163Sshin{
37655163Sshin	int i;
377118664Sume
37855163Sshin	for (i = 0; i < RTAX_MAX; i++) {
37955163Sshin		if (addrs & (1 << i)) {
38055163Sshin			rti_info[i] = sa;
38155163Sshin			NEXT_SA(sa);
382118664Sume		} else
38355163Sshin			rti_info[i] = NULL;
38455163Sshin	}
38555163Sshin}
386