1/*	$KAME: if.c,v 1.27 2003/10/05 00:09:36 itojun 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$
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#include <net/if_var.h>
42#include <net/if_types.h>
43#include <net/route.h>
44#include <net/if_dl.h>
45#include <net/if_media.h>
46#include <net/ethernet.h>
47#include <netinet/in.h>
48#include <netinet/icmp6.h>
49
50#include <netinet6/in6_var.h>
51#include <netinet6/nd6.h>
52
53#include <stdio.h>
54#include <unistd.h>
55#include <stdlib.h>
56#include <syslog.h>
57#include <string.h>
58#include <fcntl.h>
59#include <errno.h>
60#include <limits.h>
61#include <ifaddrs.h>
62#include "rtsold.h"
63
64extern int rssock;
65static int ifsock;
66
67static int get_llflag(const char *);
68static void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
69
70int
71ifinit(void)
72{
73	ifsock = rssock;
74
75	return(0);
76}
77
78int
79interface_up(char *name)
80{
81	struct ifreq ifr;
82	struct in6_ndireq nd;
83	int llflag;
84	int s;
85
86	memset(&ifr, 0, sizeof(ifr));
87	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
88	memset(&nd, 0, sizeof(nd));
89	strlcpy(nd.ifname, name, sizeof(nd.ifname));
90
91	if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
92		warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFFLAGS): %s",
93		    strerror(errno));
94		return (-1);
95	}
96	if (!(ifr.ifr_flags & IFF_UP)) {
97		ifr.ifr_flags |= IFF_UP;
98		if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0)
99			warnmsg(LOG_ERR, __func__,
100			    "ioctl(SIOCSIFFLAGS): %s", strerror(errno));
101		return (-1);
102	}
103	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
104		warnmsg(LOG_WARNING, __func__, "socket(AF_INET6, SOCK_DGRAM): %s",
105		    strerror(errno));
106		return (-1);
107	}
108	if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
109		warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFINFO_IN6): %s",
110		    strerror(errno));
111		close(s);
112		return (-1);
113	}
114
115	warnmsg(LOG_DEBUG, __func__, "checking if %s is ready...", name);
116
117	if (nd.ndi.flags & ND6_IFF_IFDISABLED) {
118		if (Fflag) {
119			nd.ndi.flags &= ~ND6_IFF_IFDISABLED;
120			if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd)) {
121				warnmsg(LOG_WARNING, __func__,
122				    "ioctl(SIOCSIFINFO_IN6): %s",
123		    		    strerror(errno));
124				close(s);
125				return (-1);
126			}
127		} else {
128			warnmsg(LOG_WARNING, __func__,
129			    "%s is disabled.", name);
130			close(s);
131			return (-1);
132		}
133	}
134	if (!(nd.ndi.flags & ND6_IFF_ACCEPT_RTADV)) {
135		if (Fflag) {
136			nd.ndi.flags |= ND6_IFF_ACCEPT_RTADV;
137			if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd)) {
138				warnmsg(LOG_WARNING, __func__,
139				    "ioctl(SIOCSIFINFO_IN6): %s",
140		    		    strerror(errno));
141				close(s);
142				return (-1);
143			}
144		} else {
145			warnmsg(LOG_WARNING, __func__,
146			    "%s does not accept Router Advertisement.", name);
147			close(s);
148			return (-1);
149		}
150	}
151	close(s);
152
153	llflag = get_llflag(name);
154	if (llflag < 0) {
155		warnmsg(LOG_WARNING, __func__,
156		    "get_llflag() failed, anyway I'll try");
157		return (0);
158	}
159
160	if (!(llflag & IN6_IFF_NOTREADY)) {
161		warnmsg(LOG_DEBUG, __func__, "%s is ready", name);
162		return (0);
163	} else {
164		if (llflag & IN6_IFF_TENTATIVE) {
165			warnmsg(LOG_DEBUG, __func__, "%s is tentative",
166			    name);
167			return (IFS_TENTATIVE);
168		}
169		if (llflag & IN6_IFF_DUPLICATED)
170			warnmsg(LOG_DEBUG, __func__, "%s is duplicated",
171			    name);
172		return (-1);
173	}
174}
175
176int
177interface_status(struct ifinfo *ifinfo)
178{
179	char *ifname = ifinfo->ifname;
180	struct ifreq ifr;
181	struct ifmediareq ifmr;
182
183	/* get interface flags */
184	memset(&ifr, 0, sizeof(ifr));
185	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
186	if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
187		warnmsg(LOG_ERR, __func__, "ioctl(SIOCGIFFLAGS) on %s: %s",
188		    ifname, strerror(errno));
189		return (-1);
190	}
191	/*
192	 * if one of UP and RUNNING flags is dropped,
193	 * the interface is not active.
194	 */
195	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
196		goto inactive;
197	/* Next, check carrier on the interface, if possible */
198	if (!ifinfo->mediareqok)
199		goto active;
200	memset(&ifmr, 0, sizeof(ifmr));
201	strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
202
203	if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
204		if (errno != EINVAL) {
205			warnmsg(LOG_DEBUG, __func__,
206			    "ioctl(SIOCGIFMEDIA) on %s: %s",
207			    ifname, strerror(errno));
208			return(-1);
209		}
210		/*
211		 * EINVAL simply means that the interface does not support
212		 * the SIOCGIFMEDIA ioctl. We regard it alive.
213		 */
214		ifinfo->mediareqok = 0;
215		goto active;
216	}
217
218	if (ifmr.ifm_status & IFM_AVALID) {
219		switch (ifmr.ifm_active & IFM_NMASK) {
220		case IFM_ETHER:
221		case IFM_IEEE80211:
222			if (ifmr.ifm_status & IFM_ACTIVE)
223				goto active;
224			else
225				goto inactive;
226			break;
227		default:
228			goto inactive;
229		}
230	}
231
232  inactive:
233	return (0);
234
235  active:
236	return (1);
237}
238
239#define ROUNDUP(a, size) \
240	(((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
241
242#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
243	((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
244	sizeof(u_long)) : sizeof(u_long)))
245#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
246
247int
248lladdropt_length(struct sockaddr_dl *sdl)
249{
250	switch (sdl->sdl_type) {
251	case IFT_ETHER:
252#ifdef IFT_IEEE80211
253	case IFT_IEEE80211:
254#endif
255		return (ROUNDUP8(ETHER_ADDR_LEN + 2));
256	default:
257		return (0);
258	}
259}
260
261void
262lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
263{
264	char *addr;
265
266	ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
267
268	switch (sdl->sdl_type) {
269	case IFT_ETHER:
270#ifdef IFT_IEEE80211
271	case IFT_IEEE80211:
272#endif
273		ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
274		addr = (char *)(ndopt + 1);
275		memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
276		break;
277	default:
278		warnmsg(LOG_ERR, __func__,
279		    "unsupported link type(%d)", sdl->sdl_type);
280		exit(1);
281	}
282
283	return;
284}
285
286struct sockaddr_dl *
287if_nametosdl(char *name)
288{
289	int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
290	char *buf, *next, *lim;
291	size_t len;
292	struct if_msghdr *ifm;
293	struct sockaddr *sa, *rti_info[RTAX_MAX];
294	struct sockaddr_dl *sdl = NULL, *ret_sdl;
295
296	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
297		return(NULL);
298	if ((buf = malloc(len)) == NULL)
299		return(NULL);
300	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
301		free(buf);
302		return (NULL);
303	}
304
305	lim = buf + len;
306	for (next = buf; next < lim; next += ifm->ifm_msglen) {
307		ifm = (struct if_msghdr *)next;
308		if (ifm->ifm_type == RTM_IFINFO) {
309			sa = (struct sockaddr *)(ifm + 1);
310			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
311			if ((sa = rti_info[RTAX_IFP]) != NULL) {
312				if (sa->sa_family == AF_LINK) {
313					sdl = (struct sockaddr_dl *)sa;
314					if (strlen(name) != sdl->sdl_nlen)
315						continue; /* not same len */
316					if (strncmp(&sdl->sdl_data[0],
317						    name,
318						    sdl->sdl_nlen) == 0) {
319						break;
320					}
321				}
322			}
323		}
324	}
325	if (next == lim) {
326		/* search failed */
327		free(buf);
328		return (NULL);
329	}
330
331	if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) {
332		free(buf);
333		return (NULL);
334	}
335	memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
336
337	free(buf);
338	return (ret_sdl);
339}
340
341int
342getinet6sysctl(int code)
343{
344	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
345	int value;
346	size_t size;
347
348	mib[3] = code;
349	size = sizeof(value);
350	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) < 0)
351		return (-1);
352	else
353		return (value);
354}
355
356int
357setinet6sysctl(int code, int newval)
358{
359	int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
360	int value;
361	size_t size;
362
363	mib[3] = code;
364	size = sizeof(value);
365	if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size,
366	    &newval, sizeof(newval)) < 0)
367		return (-1);
368	else
369		return (value);
370}
371
372/*------------------------------------------------------------*/
373
374/* get ia6_flags for link-local addr on if.  returns -1 on error. */
375static int
376get_llflag(const char *name)
377{
378	struct ifaddrs *ifap, *ifa;
379	struct in6_ifreq ifr6;
380	struct sockaddr_in6 *sin6;
381	int s;
382
383	if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) {
384		warnmsg(LOG_ERR, __func__, "socket(SOCK_DGRAM): %s",
385		    strerror(errno));
386		exit(1);
387	}
388	if (getifaddrs(&ifap) != 0) {
389		warnmsg(LOG_ERR, __func__, "getifaddrs: %s",
390		    strerror(errno));
391		exit(1);
392	}
393
394	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
395		if (strlen(ifa->ifa_name) != strlen(name) ||
396		    strncmp(ifa->ifa_name, name, strlen(name)) != 0)
397			continue;
398		if (ifa->ifa_addr->sa_family != AF_INET6)
399			continue;
400		sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
401		if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
402			continue;
403
404		memset(&ifr6, 0, sizeof(ifr6));
405		strncpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name));
406		memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len);
407		if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
408			warnmsg(LOG_ERR, __func__,
409			    "ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno));
410			exit(1);
411		}
412
413		freeifaddrs(ifap);
414		close(s);
415		return (ifr6.ifr_ifru.ifru_flags6);
416	}
417
418	freeifaddrs(ifap);
419	close(s);
420	return (-1);
421}
422
423
424static void
425get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
426{
427	int i;
428
429	for (i = 0; i < RTAX_MAX; i++) {
430		if (addrs & (1 << i)) {
431			rti_info[i] = sa;
432			NEXT_SA(sa);
433		} else
434			rti_info[i] = NULL;
435	}
436}
437