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