if.c revision 118664
1/*	$KAME: if.c,v 1.15 2001/05/22 06:04:17 jinmei Exp $	*/
2
3/*
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * $FreeBSD: head/usr.sbin/rtsold/if.c 118664 2003-08-08 16:56:01Z ume $
32 */
33
34#include <sys/param.h>
35#include <sys/socket.h>
36#include <sys/sysctl.h>
37#include <sys/ioctl.h>
38#include <sys/queue.h>
39
40#include <net/if.h>
41#if defined(__FreeBSD__) && __FreeBSD__ >= 3
42#include <net/if_var.h>
43#endif /* __FreeBSD__ >= 3 */
44#include <net/if_types.h>
45#include <net/route.h>
46#include <net/if_dl.h>
47#include <net/if_media.h>
48#ifdef __FreeBSD__
49# include <net/ethernet.h>
50#endif
51#ifdef __NetBSD__
52#include <net/if_ether.h>
53#endif
54#if defined(__bsdi__) || defined(__OpenBSD__)
55# include <netinet/in.h>
56# include <netinet/if_ether.h>
57#endif
58#include <netinet/in.h>
59#include <netinet/icmp6.h>
60
61#include <netinet6/in6_var.h>
62
63#include <stdio.h>
64#include <unistd.h>
65#include <stdlib.h>
66#include <syslog.h>
67#include <string.h>
68#include <fcntl.h>
69#include <errno.h>
70#include <limits.h>
71#include <ifaddrs.h>
72#include "rtsold.h"
73
74extern int rssock;
75static int ifsock;
76
77static int get_llflag __P((const char *name));
78static void get_rtaddrs __P((int addrs, struct sockaddr *sa,
79			     struct sockaddr **rti_info));
80
81int
82ifinit()
83{
84	ifsock = rssock;
85
86	return(0);
87}
88
89int
90interface_up(char *name)
91{
92	struct ifreq ifr;
93	int llflag;
94
95	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
96
97	if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
98		warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFFLAGS): %s",
99		    strerror(errno));
100		return(-1);
101	}
102	if (!(ifr.ifr_flags & IFF_UP)) {
103		ifr.ifr_flags |= IFF_UP;
104		if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0)
105			warnmsg(LOG_ERR, __func__,
106			    "ioctl(SIOCSIFFLAGS): %s", strerror(errno));
107		return(-1);
108	}
109
110	warnmsg(LOG_DEBUG, __func__, "checking if %s is ready...", name);
111
112	llflag = get_llflag(name);
113	if (llflag < 0) {
114		warnmsg(LOG_WARNING, __func__,
115		    "get_llflag() failed, anyway I'll try");
116		return 0;
117	}
118
119	if (!(llflag & IN6_IFF_NOTREADY)) {
120		warnmsg(LOG_DEBUG, __func__, "%s is ready", name);
121		return(0);
122	} else {
123		if (llflag & IN6_IFF_TENTATIVE) {
124			warnmsg(LOG_DEBUG, __func__, "%s is tentative",
125			    name);
126			return IFS_TENTATIVE;
127		}
128		if (llflag & IN6_IFF_DUPLICATED)
129			warnmsg(LOG_DEBUG, __func__, "%s is duplicated",
130			    name);
131		return -1;
132	}
133}
134
135int
136interface_status(struct ifinfo *ifinfo)
137{
138	char *ifname = ifinfo->ifname;
139	struct ifreq ifr;
140	struct ifmediareq ifmr;
141
142	/* get interface flags */
143	memset(&ifr, 0, sizeof(ifr));
144	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
145	if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
146		warnmsg(LOG_ERR, __func__, "ioctl(SIOCGIFFLAGS) on %s: %s",
147		    ifname, strerror(errno));
148		return(-1);
149	}
150	/*
151	 * if one of UP and RUNNING flags is dropped,
152	 * the interface is not active.
153	 */
154	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
155		goto inactive;
156	}
157
158	/* Next, check carrier on the interface, if possible */
159	if (!ifinfo->mediareqok)
160		goto active;
161	memset(&ifmr, 0, sizeof(ifmr));
162	strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
163
164	if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
165		if (errno != EINVAL) {
166			warnmsg(LOG_DEBUG, __func__,
167			    "ioctl(SIOCGIFMEDIA) on %s: %s",
168			    ifname, strerror(errno));
169			return(-1);
170		}
171		/*
172		 * EINVAL simply means that the interface does not support
173		 * the SIOCGIFMEDIA ioctl. We regard it alive.
174		 */
175		ifinfo->mediareqok = 0;
176		goto active;
177	}
178
179	if (ifmr.ifm_status & IFM_AVALID) {
180		switch (ifmr.ifm_active & IFM_NMASK) {
181		case IFM_ETHER:
182			if (ifmr.ifm_status & IFM_ACTIVE)
183				goto active;
184			else
185				goto inactive;
186			break;
187		default:
188			goto inactive;
189		}
190	}
191
192  inactive:
193	return(0);
194
195  active:
196	return(1);
197}
198
199#define ROUNDUP(a, size) \
200	(((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
201
202#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
203	((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
204	sizeof(u_long)) : sizeof(u_long)))
205#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
206
207int
208lladdropt_length(struct sockaddr_dl *sdl)
209{
210	switch (sdl->sdl_type) {
211	case IFT_ETHER:
212#ifdef IFT_IEEE80211
213	case IFT_IEEE80211:
214#endif
215		return(ROUNDUP8(ETHER_ADDR_LEN + 2));
216	default:
217		return(0);
218	}
219}
220
221void
222lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
223{
224	char *addr;
225
226	ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
227
228	switch (sdl->sdl_type) {
229	case IFT_ETHER:
230#ifdef IFT_IEEE80211
231	case IFT_IEEE80211:
232#endif
233		ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
234		addr = (char *)(ndopt + 1);
235		memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
236		break;
237	default:
238		warnmsg(LOG_ERR, __func__,
239		    "unsupported link type(%d)", sdl->sdl_type);
240		exit(1);
241	}
242
243	return;
244}
245
246struct sockaddr_dl *
247if_nametosdl(char *name)
248{
249	int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
250	char *buf, *next, *lim;
251	size_t len;
252	struct if_msghdr *ifm;
253	struct sockaddr *sa, *rti_info[RTAX_MAX];
254	struct sockaddr_dl *sdl = NULL, *ret_sdl;
255
256	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
257		return(NULL);
258	if ((buf = malloc(len)) == NULL)
259		return(NULL);
260	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
261		free(buf);
262		return(NULL);
263	}
264
265	lim = buf + len;
266	for (next = buf; next < lim; next += ifm->ifm_msglen) {
267		ifm = (struct if_msghdr *)next;
268		if (ifm->ifm_type == RTM_IFINFO) {
269			sa = (struct sockaddr *)(ifm + 1);
270			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
271			if ((sa = rti_info[RTAX_IFP]) != NULL) {
272				if (sa->sa_family == AF_LINK) {
273					sdl = (struct sockaddr_dl *)sa;
274					if (strlen(name) != sdl->sdl_nlen)
275						continue; /* not same len */
276					if (strncmp(&sdl->sdl_data[0],
277						    name,
278						    sdl->sdl_nlen) == 0) {
279						break;
280					}
281				}
282			}
283		}
284	}
285	if (next == lim) {
286		/* search failed */
287		free(buf);
288		return(NULL);
289	}
290
291	if ((ret_sdl = malloc(sdl->sdl_len)) == NULL)
292		return(NULL);
293	memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
294
295	free(buf);
296	return(ret_sdl);
297}
298
299int
300getinet6sysctl(int code)
301{
302	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
303	int value;
304	size_t size;
305
306	mib[3] = code;
307	size = sizeof(value);
308	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) < 0)
309		return -1;
310	else
311		return value;
312}
313
314/*------------------------------------------------------------*/
315
316/* get ia6_flags for link-local addr on if.  returns -1 on error. */
317static int
318get_llflag(const char *name)
319{
320	struct ifaddrs *ifap, *ifa;
321	struct in6_ifreq ifr6;
322	struct sockaddr_in6 *sin6;
323	int s;
324
325	if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) {
326		warnmsg(LOG_ERR, __func__, "socket(SOCK_DGRAM): %s",
327		    strerror(errno));
328		exit(1);
329	}
330	if (getifaddrs(&ifap) != 0) {
331		warnmsg(LOG_ERR, __func__, "getifaddrs: %s",
332		    strerror(errno));
333		exit(1);
334	}
335
336	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
337		if (strlen(ifa->ifa_name) != strlen(name) ||
338		    strncmp(ifa->ifa_name, name, strlen(name)) != 0)
339			continue;
340		if (ifa->ifa_addr->sa_family != AF_INET6)
341			continue;
342		sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
343		if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
344			continue;
345
346		memset(&ifr6, 0, sizeof(ifr6));
347		strcpy(ifr6.ifr_name, name);
348		memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len);
349		if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
350			warnmsg(LOG_ERR, __func__,
351			    "ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno));
352			exit(1);
353		}
354
355		freeifaddrs(ifap);
356		close(s);
357		return ifr6.ifr_ifru.ifru_flags6;
358	}
359
360	freeifaddrs(ifap);
361	close(s);
362	return -1;
363}
364
365
366static void
367get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
368{
369	int i;
370
371	for (i = 0; i < RTAX_MAX; i++) {
372		if (addrs & (1 << i)) {
373			rti_info[i] = sa;
374			NEXT_SA(sa);
375		} else
376			rti_info[i] = NULL;
377	}
378}
379