1/*	$NetBSD: if.c,v 1.20 2011/12/11 20:44:44 christos Exp $	*/
2/*	$KAME: if.c,v 1.36 2004/11/30 22:32:01 suz Exp $	*/
3
4/*
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/param.h>
34#include <sys/socket.h>
35#include <sys/sysctl.h>
36#include <sys/ioctl.h>
37#include <net/if.h>
38#include <net/if_types.h>
39#include <ifaddrs.h>
40#include <net/if_ether.h>
41#include <net/route.h>
42#include <net/if_dl.h>
43#include <netinet/in.h>
44#include <netinet/icmp6.h>
45#include <unistd.h>
46#include <errno.h>
47#include <stdlib.h>
48#include <string.h>
49#include <syslog.h>
50#include "rtadvd.h"
51#include "if.h"
52
53#ifndef RT_ROUNDUP
54#define RT_ROUNDUP(a)							       \
55	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
56#define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len))
57#endif
58
59struct if_msghdr **iflist;
60int iflist_init_ok;
61size_t ifblock_size;
62char *ifblock;
63
64static void get_iflist(char **buf, size_t *size);
65static void parse_iflist(struct if_msghdr ***ifmlist_p, char *buf,
66		       size_t bufsize);
67
68static void
69get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
70{
71	int i;
72
73	for (i = 0; i < RTAX_MAX; i++) {
74		if (addrs & (1 << i)) {
75			rti_info[i] = sa;
76			RT_ADVANCE(sa, sa);
77		}
78		else
79			rti_info[i] = NULL;
80	}
81}
82
83struct sockaddr_dl *
84if_nametosdl(const char *name)
85{
86	struct ifaddrs *ifap, *ifa;
87	struct sockaddr_dl *sdl;
88
89	if (getifaddrs(&ifap) != 0)
90		return (NULL);
91
92	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
93		if (strcmp(ifa->ifa_name, name) != 0)
94			continue;
95		if (ifa->ifa_addr->sa_family != AF_LINK)
96			continue;
97
98		sdl = malloc(ifa->ifa_addr->sa_len);
99		if (!sdl)
100			continue;	/*XXX*/
101
102		memcpy(sdl, ifa->ifa_addr, ifa->ifa_addr->sa_len);
103		freeifaddrs(ifap);
104		return (sdl);
105	}
106
107	freeifaddrs(ifap);
108	return (NULL);
109}
110
111int
112if_getmtu(const char *name)
113{
114	struct ifaddrs *ifap, *ifa;
115	struct if_data *ifd;
116	u_long mtu = 0;
117
118	if (getifaddrs(&ifap) < 0)
119		return(0);
120	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
121		if (strcmp(ifa->ifa_name, name) == 0) {
122			ifd = ifa->ifa_data;
123			if (ifd)
124				mtu = ifd->ifi_mtu;
125			break;
126		}
127	}
128	freeifaddrs(ifap);
129
130#ifdef SIOCGIFMTU		/* XXX: this ifdef may not be necessary */
131	if (mtu == 0) {
132		struct ifreq ifr;
133		int s;
134
135		if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
136			return(0);
137
138		ifr.ifr_addr.sa_family = AF_INET6;
139		strncpy(ifr.ifr_name, name,
140			sizeof(ifr.ifr_name));
141		if (ioctl(s, SIOCGIFMTU, &ifr) < 0) {
142			close(s);
143			return(0);
144		}
145		close(s);
146
147		mtu = ifr.ifr_mtu;
148	}
149#endif
150
151	return(mtu);
152}
153
154/* give interface index and its old flags, then new flags returned */
155int
156if_getflags(int ifindex, int oifflags)
157{
158	struct ifreq ifr;
159	int s;
160
161	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
162		syslog(LOG_ERR, "<%s> socket: %s", __func__,
163		       strerror(errno));
164		return (oifflags & ~IFF_UP);
165	}
166
167	if_indextoname(ifindex, ifr.ifr_name);
168	if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
169		syslog(LOG_ERR, "<%s> ioctl:SIOCGIFFLAGS: failed for %s",
170		       __func__, ifr.ifr_name);
171		close(s);
172		return (oifflags & ~IFF_UP);
173	}
174	close(s);
175	return (ifr.ifr_flags);
176}
177
178#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
179int
180lladdropt_length(struct sockaddr_dl *sdl)
181{
182	switch (sdl->sdl_type) {
183	case IFT_ETHER:
184	case IFT_FDDI:
185		return(ROUNDUP8(ETHER_ADDR_LEN + 2));
186	default:
187		return(0);
188	}
189}
190
191void
192lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
193{
194	char *addr;
195
196	ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
197
198	switch (sdl->sdl_type) {
199	case IFT_ETHER:
200	case IFT_FDDI:
201		ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
202		addr = (char *)(ndopt + 1);
203		memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
204		break;
205	default:
206		syslog(LOG_ERR, "<%s> unsupported link type(%d)",
207		    __func__, sdl->sdl_type);
208		exit(1);
209	}
210
211	return;
212}
213
214#define FILTER_MATCH(type, filter) ((0x1 << type) & filter)
215#define SIN6(s) ((struct sockaddr_in6 *)(s))
216#define SDL(s) ((struct sockaddr_dl *)(s))
217char *
218get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter)
219{
220	struct rt_msghdr *rtm;
221	struct ifa_msghdr *ifam;
222	struct sockaddr *sa, *dst, *gw, *ifa, *rti_info[RTAX_MAX];
223
224	*lenp = 0;
225	for (rtm = (struct rt_msghdr *)buf;
226	     rtm < (struct rt_msghdr *)lim;
227	     rtm = (struct rt_msghdr *)(((char *)rtm) + rtm->rtm_msglen)) {
228		/* just for safety */
229		if (!rtm->rtm_msglen) {
230			syslog(LOG_WARNING, "<%s> rtm_msglen is 0 "
231				"(buf=%p lim=%p rtm=%p)", __func__,
232				buf, lim, rtm);
233			break;
234		}
235		if (FILTER_MATCH(rtm->rtm_type, filter) == 0) {
236			continue;
237		}
238
239		switch (rtm->rtm_type) {
240		case RTM_GET:
241		case RTM_ADD:
242		case RTM_DELETE:
243			/* address related checks */
244			sa = (struct sockaddr *)(rtm + 1);
245			get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
246			if ((dst = rti_info[RTAX_DST]) == NULL ||
247			    dst->sa_family != AF_INET6)
248				continue;
249
250			if (IN6_IS_ADDR_LINKLOCAL(&SIN6(dst)->sin6_addr) ||
251			    IN6_IS_ADDR_MULTICAST(&SIN6(dst)->sin6_addr))
252				continue;
253
254			if ((gw = rti_info[RTAX_GATEWAY]) == NULL ||
255			    gw->sa_family != AF_LINK)
256				continue;
257			if (ifindex && SDL(gw)->sdl_index != ifindex)
258				continue;
259
260			if (rti_info[RTAX_NETMASK] == NULL)
261				continue;
262
263			/* found */
264			*lenp = rtm->rtm_msglen;
265			return (char *)rtm;
266			/* NOTREACHED */
267		case RTM_NEWADDR:
268		case RTM_DELADDR:
269			ifam = (struct ifa_msghdr *)rtm;
270
271			/* address related checks */
272			sa = (struct sockaddr *)(ifam + 1);
273			get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
274			if ((ifa = rti_info[RTAX_IFA]) == NULL ||
275			    (ifa->sa_family != AF_INET &&
276			     ifa->sa_family != AF_INET6))
277				continue;
278
279			if (ifa->sa_family == AF_INET6 &&
280			    (IN6_IS_ADDR_LINKLOCAL(&SIN6(ifa)->sin6_addr) ||
281			     IN6_IS_ADDR_MULTICAST(&SIN6(ifa)->sin6_addr)))
282				continue;
283
284			if (ifindex && ifam->ifam_index != ifindex)
285				continue;
286
287			/* found */
288			*lenp = ifam->ifam_msglen;
289			return (char *)rtm;
290			/* NOTREACHED */
291		case RTM_IFINFO:
292			/* found */
293			*lenp = rtm->rtm_msglen;
294			return (char *)rtm;
295			/* NOTREACHED */
296		}
297	}
298
299	return (char *)rtm;
300}
301#undef FILTER_MATCH
302
303struct in6_addr *
304get_addr(char *buf)
305{
306	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
307	struct sockaddr *sa, *rti_info[RTAX_MAX];
308
309	sa = (struct sockaddr *)(rtm + 1);
310	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
311
312	return(&SIN6(rti_info[RTAX_DST])->sin6_addr);
313}
314
315int
316get_rtm_ifindex(char *buf)
317{
318	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
319	struct sockaddr *sa, *rti_info[RTAX_MAX];
320
321	sa = (struct sockaddr *)(rtm + 1);
322	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
323
324	return(((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index);
325}
326
327int
328get_ifm_ifindex(char *buf)
329{
330	struct if_msghdr *ifm = (struct if_msghdr *)buf;
331
332	return ((int)ifm->ifm_index);
333}
334
335int
336get_ifam_ifindex(char *buf)
337{
338	struct ifa_msghdr *ifam = (struct ifa_msghdr *)buf;
339
340	return ((int)ifam->ifam_index);
341}
342
343int
344get_ifm_flags(char *buf)
345{
346	struct if_msghdr *ifm = (struct if_msghdr *)buf;
347
348	return (ifm->ifm_flags);
349}
350
351int
352get_prefixlen(char *buf)
353{
354	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
355	struct sockaddr *sa, *rti_info[RTAX_MAX];
356	unsigned char *p, *lim;
357
358	sa = (struct sockaddr *)(rtm + 1);
359	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
360	sa = rti_info[RTAX_NETMASK];
361
362	p = (unsigned char *)(&SIN6(sa)->sin6_addr);
363	lim = (unsigned char *)sa + sa->sa_len;
364	return prefixlen(p, lim);
365}
366
367int
368prefixlen(const unsigned char *p, const unsigned char *lim)
369{
370	int masklen;
371
372	for (masklen = 0; p < lim; p++) {
373		switch (*p) {
374		case 0xff:
375			masklen += 8;
376			break;
377		case 0xfe:
378			masklen += 7;
379			break;
380		case 0xfc:
381			masklen += 6;
382			break;
383		case 0xf8:
384			masklen += 5;
385			break;
386		case 0xf0:
387			masklen += 4;
388			break;
389		case 0xe0:
390			masklen += 3;
391			break;
392		case 0xc0:
393			masklen += 2;
394			break;
395		case 0x80:
396			masklen += 1;
397			break;
398		case 0x00:
399			break;
400		default:
401			return(-1);
402		}
403	}
404
405	return(masklen);
406}
407
408int
409rtmsg_type(char *buf)
410{
411	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
412
413	return(rtm->rtm_type);
414}
415
416int
417rtmsg_len(char *buf)
418{
419	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
420
421	return(rtm->rtm_msglen);
422}
423
424int
425ifmsg_len(char *buf)
426{
427	struct if_msghdr *ifm = (struct if_msghdr *)buf;
428
429	return(ifm->ifm_msglen);
430}
431
432/*
433 * alloc buffer and get if_msghdrs block from kernel,
434 * and put them into the buffer
435 */
436static void
437get_iflist(char **buf, size_t *size)
438{
439	int mib[6];
440
441	mib[0] = CTL_NET;
442	mib[1] = PF_ROUTE;
443	mib[2] = 0;
444	mib[3] = AF_INET6;
445	mib[4] = NET_RT_IFLIST;
446	mib[5] = 0;
447
448	if (sysctl(mib, 6, NULL, size, NULL, 0) < 0) {
449		syslog(LOG_ERR, "<%s> sysctl: iflist size get failed",
450		       __func__);
451		exit(1);
452	}
453	if ((*buf = malloc(*size)) == NULL) {
454		syslog(LOG_ERR, "<%s> malloc failed", __func__);
455		exit(1);
456	}
457	if (sysctl(mib, 6, *buf, size, NULL, 0) < 0) {
458		syslog(LOG_ERR, "<%s> sysctl: iflist get failed",
459		       __func__);
460		exit(1);
461	}
462	return;
463}
464
465/*
466 * alloc buffer and parse if_msghdrs block passed as arg,
467 * and init the buffer as list of pointers ot each of the if_msghdr.
468 */
469static void
470parse_iflist(struct if_msghdr ***ifmlist_p, char *buf, size_t bufsize)
471{
472	int iflentry_size, malloc_size;
473	struct if_msghdr *ifm;
474	struct ifa_msghdr *ifam;
475	char *lim;
476
477	/*
478	 * Estimate least size of an iflist entry, to be obtained from kernel.
479	 * Should add sizeof(sockaddr) ??
480	 */
481	iflentry_size = sizeof(struct if_msghdr);
482	/* roughly estimate max list size of pointers to each if_msghdr */
483	malloc_size = (bufsize/iflentry_size) * sizeof(void *);
484	if ((*ifmlist_p = (struct if_msghdr **)malloc(malloc_size)) == NULL) {
485		syslog(LOG_ERR, "<%s> malloc failed", __func__);
486		exit(1);
487	}
488
489	lim = buf + bufsize;
490	for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) {
491		if (ifm->ifm_msglen == 0) {
492			syslog(LOG_WARNING, "<%s> ifm_msglen is 0 "
493			       "(buf=%p lim=%p ifm=%p)", __func__,
494			       buf, lim, ifm);
495			return;
496		}
497
498		if (ifm->ifm_type == RTM_IFINFO) {
499			(*ifmlist_p)[ifm->ifm_index] = ifm;
500		} else {
501			syslog(LOG_ERR, "out of sync parsing NET_RT_IFLIST\n"
502			       "expected %d, got %d\n msglen = %d\n"
503			       "buf:%p, ifm:%p, lim:%p\n",
504			       RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen,
505			       buf, ifm, lim);
506			exit (1);
507		}
508		for (ifam = (struct ifa_msghdr *)
509			((char *)ifm + ifm->ifm_msglen);
510		     ifam < (struct ifa_msghdr *)lim;
511		     ifam = (struct ifa_msghdr *)
512		     	((char *)ifam + ifam->ifam_msglen)) {
513			/* just for safety */
514			if (!ifam->ifam_msglen) {
515				syslog(LOG_WARNING, "<%s> ifa_msglen is 0 "
516				       "(buf=%p lim=%p ifam=%p)", __func__,
517				       buf, lim, ifam);
518				return;
519			}
520			if (ifam->ifam_type != RTM_NEWADDR)
521				break;
522		}
523		ifm = (struct if_msghdr *)ifam;
524	}
525}
526
527void
528init_iflist()
529{
530	if (ifblock) {
531		free(ifblock);
532		ifblock_size = 0;
533	}
534	if (iflist)
535		free(iflist);
536	/* get iflist block from kernel */
537	get_iflist(&ifblock, &ifblock_size);
538
539	/* make list of pointers to each if_msghdr */
540	parse_iflist(&iflist, ifblock, ifblock_size);
541}
542