xref: /freebsd/usr.sbin/rtadvd/if.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
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$
30  */
31 
32 #include <sys/param.h>
33 #include <sys/socket.h>
34 #include <sys/sysctl.h>
35 #include <sys/ioctl.h>
36 #include <net/if.h>
37 #include <net/if_types.h>
38 #include <net/ethernet.h>
39 #include <net/route.h>
40 #include <net/if_dl.h>
41 #include <netinet/in.h>
42 #include <netinet/icmp6.h>
43 #include <unistd.h>
44 #include <errno.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <syslog.h>
48 #include "rtadvd.h"
49 #include "if.h"
50 
51 #define	ROUNDUP(a, size) \
52 	(((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
53 
54 #define	NEXT_SA(ap) (ap) = (struct sockaddr *) \
55 	((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
56 						 sizeof(u_long)) :\
57 			  			 sizeof(u_long)))
58 
59 struct	if_msghdr **iflist;
60 int	iflist_init_ok;
61 size_t	ifblock_size;
62 char	*ifblock;
63 
64 static void	get_iflist __P((char **buf, size_t *size));
65 static void	parse_iflist __P((struct if_msghdr ***ifmlist_p, char *buf,
66 		       size_t bufsize));
67 
68 static void
69 get_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 			NEXT_SA(sa);
77 		}
78 		else
79 			rti_info[i] = NULL;
80 	}
81 }
82 
83 struct sockaddr_dl *
84 if_nametosdl(char *name)
85 {
86 	int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
87 	char *buf, *next, *lim;
88 	size_t len;
89 	struct if_msghdr *ifm;
90 	struct sockaddr *sa, *rti_info[RTAX_MAX];
91 	struct sockaddr_dl *sdl = NULL, *ret_sdl;
92 
93 	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
94 		return(NULL);
95 	if ((buf = malloc(len)) == NULL)
96 		return(NULL);
97 	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
98 		free(buf);
99 		return(NULL);
100 	}
101 
102 	lim = buf + len;
103 	for (next = buf; next < lim; next += ifm->ifm_msglen) {
104 		ifm = (struct if_msghdr *)next;
105 		if (ifm->ifm_type == RTM_IFINFO) {
106 			sa = (struct sockaddr *)(ifm + 1);
107 			get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
108 			if ((sa = rti_info[RTAX_IFP]) != NULL) {
109 				if (sa->sa_family == AF_LINK) {
110 					sdl = (struct sockaddr_dl *)sa;
111 					if (strncmp(&sdl->sdl_data[0],
112 						    name,
113 						    sdl->sdl_nlen) == 0) {
114 						break;
115 					}
116 				}
117 			}
118 		}
119 	}
120 	if (next == lim) {
121 		/* search failed */
122 		free(buf);
123 		return(NULL);
124 	}
125 
126 	if ((ret_sdl = malloc(sdl->sdl_len)) == NULL)
127 		return(NULL);
128 	memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
129 	return(ret_sdl);
130 }
131 
132 int
133 if_getmtu(char *name)
134 {
135 	struct ifreq ifr;
136 	int s;
137 
138 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
139 		return(0);
140 
141 	ifr.ifr_addr.sa_family = AF_INET6;
142 	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
143 	if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0) {
144 		close(s);
145 		return(0);
146 	}
147 
148 	close(s);
149 
150 	return(ifr.ifr_mtu);
151 }
152 
153 /* give interface index and its old flags, then new flags returned */
154 int
155 if_getflags(int ifindex, int oifflags)
156 {
157 	struct ifreq ifr;
158 	int s;
159 
160 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
161 		syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__,
162 		       strerror(errno));
163 		return (oifflags & ~IFF_UP);
164 	}
165 
166 	if_indextoname(ifindex, ifr.ifr_name);
167 	if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
168 		syslog(LOG_ERR, "<%s> ioctl:SIOCGIFFLAGS: failed for %s",
169 		       __FUNCTION__, ifr.ifr_name);
170 		close(s);
171 		return (oifflags & ~IFF_UP);
172 	}
173 	return (ifr.ifr_flags);
174 }
175 
176 #define	ROUNDUP8(a) (1 + (((a) - 1) | 7))
177 int
178 lladdropt_length(struct sockaddr_dl *sdl)
179 {
180 	switch(sdl->sdl_type) {
181 	 case IFT_ETHER:
182 		 return(ROUNDUP8(ETHER_ADDR_LEN + 2));
183 	 default:
184 		 return(0);
185 	}
186 }
187 
188 void
189 lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
190 {
191 	char *addr;
192 
193 	ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
194 
195 	switch(sdl->sdl_type) {
196 	 case IFT_ETHER:
197 		 ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
198 		 addr = (char *)(ndopt + 1);
199 		 memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
200 		 break;
201 	 default:
202 		 syslog(LOG_ERR,
203 			"<%s> unsupported link type(%d)",
204 			__FUNCTION__, sdl->sdl_type);
205 		 exit(1);
206 	}
207 
208 	return;
209 }
210 
211 int
212 rtbuf_len()
213 {
214 	size_t len;
215 
216 	int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0};
217 
218 	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
219 		return(-1);
220 
221 	return(len);
222 }
223 
224 int
225 get_rtinfo(char *buf, size_t *len)
226 {
227 	int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0};
228 
229 	if (sysctl(mib, 6, buf, len, NULL, 0) < 0)
230 		return(-1);
231 
232 	return(0);
233 }
234 
235 #define	FILTER_MATCH(type, filter) ((0x1 << type) & filter)
236 #define	SIN6(s) ((struct sockaddr_in6 *)(s))
237 #define	SDL(s) ((struct sockaddr_dl *)(s))
238 char *
239 get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter)
240 {
241 	struct rt_msghdr *rtm;
242 	struct ifa_msghdr *ifam;
243 	struct sockaddr *sa, *dst, *gw, *ifa, *rti_info[RTAX_MAX];
244 
245 	*lenp = 0;
246 	for (rtm = (struct rt_msghdr *)buf;
247 	     rtm < (struct rt_msghdr *)lim;
248 	     rtm = (struct rt_msghdr *)(((char *)rtm) + rtm->rtm_msglen)) {
249 		/* just for safety */
250 		if (!rtm->rtm_msglen) {
251 			syslog(LOG_WARNING, "<%s> rtm_msglen is 0 "
252 				"(buf=%p lim=%p rtm=%p)", __FUNCTION__,
253 				buf, lim, rtm);
254 			break;
255 		}
256 		if (FILTER_MATCH(rtm->rtm_type, filter) == 0) {
257 			continue;
258 		}
259 
260 		switch (rtm->rtm_type) {
261 		case RTM_GET:
262 		case RTM_ADD:
263 		case RTM_DELETE:
264 			/* address related checks */
265 			sa = (struct sockaddr *)(rtm + 1);
266 			get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
267 			if ((dst = rti_info[RTAX_DST]) == NULL ||
268 			    dst->sa_family != AF_INET6)
269 				continue;
270 
271 			if (IN6_IS_ADDR_LINKLOCAL(&SIN6(dst)->sin6_addr) ||
272 			    IN6_IS_ADDR_MULTICAST(&SIN6(dst)->sin6_addr))
273 				continue;
274 
275 			if ((gw = rti_info[RTAX_GATEWAY]) == NULL ||
276 			    gw->sa_family != AF_LINK)
277 				continue;
278 			if (ifindex && SDL(gw)->sdl_index != ifindex)
279 				continue;
280 
281 			if (rti_info[RTAX_NETMASK] == NULL)
282 				continue;
283 
284 			/* found */
285 			*lenp = rtm->rtm_msglen;
286 			return (char *)rtm;
287 			/* NOTREACHED */
288 		case RTM_NEWADDR:
289 		case RTM_DELADDR:
290 			ifam = (struct ifa_msghdr *)rtm;
291 
292 			/* address related checks */
293 			sa = (struct sockaddr *)(ifam + 1);
294 			get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
295 			if ((ifa = rti_info[RTAX_IFA]) == NULL ||
296 			    (ifa->sa_family != AF_INET &&
297 			     ifa->sa_family != AF_INET6))
298 				continue;
299 
300 			if (ifa->sa_family == AF_INET6 &&
301 			    (IN6_IS_ADDR_LINKLOCAL(&SIN6(ifa)->sin6_addr) ||
302 			     IN6_IS_ADDR_MULTICAST(&SIN6(ifa)->sin6_addr)))
303 				continue;
304 
305 			if (ifindex && ifam->ifam_index != ifindex)
306 				continue;
307 
308 			/* found */
309 			*lenp = ifam->ifam_msglen;
310 			return (char *)rtm;
311 			/* NOTREACHED */
312 		case RTM_IFINFO:
313 			/* found */
314 			*lenp = rtm->rtm_msglen;
315 			return (char *)rtm;
316 			/* NOTREACHED */
317 		}
318 	}
319 
320 	return (char *)rtm;
321 }
322 #undef FILTER_MATCH(type, filter)
323 
324 struct in6_addr *
325 get_addr(char *buf)
326 {
327 	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
328 	struct sockaddr *sa, *rti_info[RTAX_MAX];
329 
330 	sa = (struct sockaddr *)(rtm + 1);
331 	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
332 
333 	return(&SIN6(rti_info[RTAX_DST])->sin6_addr);
334 }
335 
336 int
337 get_rtm_ifindex(char *buf)
338 {
339 	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
340 	struct sockaddr *sa, *rti_info[RTAX_MAX];
341 
342 	sa = (struct sockaddr *)(rtm + 1);
343 	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
344 
345 	return(((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index);
346 }
347 
348 int
349 get_ifm_ifindex(char *buf)
350 {
351 	struct if_msghdr *ifm = (struct if_msghdr *)buf;
352 
353 	return ((int)ifm->ifm_index);
354 }
355 
356 int
357 get_ifam_ifindex(char *buf)
358 {
359 	struct ifa_msghdr *ifam = (struct ifa_msghdr *)buf;
360 
361 	return ((int)ifam->ifam_index);
362 }
363 
364 int
365 get_ifm_flags(char *buf)
366 {
367 	struct if_msghdr *ifm = (struct if_msghdr *)buf;
368 
369 	return (ifm->ifm_flags);
370 }
371 
372 int
373 get_prefixlen(char *buf)
374 {
375 	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
376 	struct sockaddr *sa, *rti_info[RTAX_MAX];
377 	int masklen;
378 	u_char *p, *lim;
379 
380 	sa = (struct sockaddr *)(rtm + 1);
381 	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
382 	sa = rti_info[RTAX_NETMASK];
383 
384 	p = (u_char *)(&SIN6(sa)->sin6_addr);
385 	lim = (u_char *)sa + sa->sa_len;
386 	for (masklen = 0; p < lim; p++) {
387 		switch (*p) {
388 		case 0xff:
389 			masklen += 8;
390 			break;
391 		case 0xfe:
392 			masklen += 7;
393 			break;
394 		case 0xfc:
395 			masklen += 6;
396 			break;
397 		case 0xf8:
398 			masklen += 5;
399 			break;
400 		case 0xf0:
401 			masklen += 4;
402 			break;
403 		case 0xe0:
404 			masklen += 3;
405 			break;
406 		case 0xc0:
407 			masklen += 2;
408 			break;
409 		case 0x80:
410 			masklen += 1;
411 			break;
412 		case 0x00:
413 			break;
414 		default:
415 			return(-1);
416 		}
417 	}
418 
419 	return(masklen);
420 }
421 
422 int
423 rtmsg_type(char *buf)
424 {
425 	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
426 
427 	return(rtm->rtm_type);
428 }
429 
430 int
431 rtmsg_len(char *buf)
432 {
433 	struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
434 
435 	return(rtm->rtm_msglen);
436 }
437 
438 int
439 ifmsg_len(char *buf)
440 {
441 	struct if_msghdr *ifm = (struct if_msghdr *)buf;
442 
443 	return(ifm->ifm_msglen);
444 }
445 
446 /*
447  * alloc buffer and get if_msghdrs block from kernel,
448  * and put them into the buffer
449  */
450 static void
451 get_iflist(char **buf, size_t *size)
452 {
453 	int mib[6];
454 
455 	mib[0] = CTL_NET;
456 	mib[1] = PF_ROUTE;
457 	mib[2] = 0;
458 	mib[3] = AF_INET6;
459 	mib[4] = NET_RT_IFLIST;
460 	mib[5] = 0;
461 
462 	if (sysctl(mib, 6, NULL, size, NULL, 0) < 0) {
463 		syslog(LOG_ERR, "<%s> sysctl: iflist size get failed",
464 		       __FUNCTION__);
465 		exit(1);
466 	}
467 	if ((*buf = malloc(*size)) == NULL) {
468 		syslog(LOG_ERR, "<%s> malloc failed", __FUNCTION__);
469 		exit(1);
470 	}
471 	if (sysctl(mib, 6, *buf, size, NULL, 0) < 0) {
472 		syslog(LOG_ERR, "<%s> sysctl: iflist get failed",
473 		       __FUNCTION__);
474 		exit(1);
475 	}
476 	return;
477 }
478 
479 /*
480  * alloc buffer and parse if_msghdrs block passed as arg,
481  * and init the buffer as list of pointers ot each of the if_msghdr.
482  */
483 static void
484 parse_iflist(struct if_msghdr ***ifmlist_p, char *buf, size_t bufsize)
485 {
486 	int iflentry_size, malloc_size;
487 	struct if_msghdr *ifm;
488 	struct ifa_msghdr *ifam;
489 	char *lim;
490 
491 	/*
492 	 * Estimate least size of an iflist entry, to be obtained from kernel.
493 	 * Should add sizeof(sockaddr) ??
494 	 */
495 	iflentry_size = sizeof(struct if_msghdr);
496 	/* roughly estimate max list size of pointers to each if_msghdr */
497 	malloc_size = (bufsize/iflentry_size) * sizeof(size_t);
498 	if ((*ifmlist_p = (struct if_msghdr **)malloc(malloc_size)) == NULL) {
499 		syslog(LOG_ERR, "<%s> malloc failed", __FUNCTION__);
500 		exit(1);
501 	}
502 
503 	lim = buf + bufsize;
504 	for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) {
505 		if (ifm->ifm_msglen == 0) {
506 			syslog(LOG_WARNING, "<%s> ifm_msglen is 0 "
507 			       "(buf=%p lim=%p ifm=%p)", __FUNCTION__,
508 			       buf, lim, ifm);
509 			return;
510 		}
511 
512 		if (ifm->ifm_type == RTM_IFINFO) {
513 			(*ifmlist_p)[ifm->ifm_index] = ifm;
514 		} else {
515 			syslog(LOG_ERR, "out of sync parsing NET_RT_IFLIST\n"
516 			       "expected %d, got %d\n msglen = %d\n"
517 			       "buf:%p, ifm:%p, lim:%p\n",
518 			       RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen,
519 			       buf, ifm, lim);
520 			exit (1);
521 		}
522 		for (ifam = (struct ifa_msghdr *)
523 			((char *)ifm + ifm->ifm_msglen);
524 		     ifam < (struct ifa_msghdr *)lim;
525 		     ifam = (struct ifa_msghdr *)
526 		     	((char *)ifam + ifam->ifam_msglen)) {
527 			/* just for safety */
528 			if (!ifam->ifam_msglen) {
529 				syslog(LOG_WARNING, "<%s> ifa_msglen is 0 "
530 				       "(buf=%p lim=%p ifam=%p)", __FUNCTION__,
531 				       buf, lim, ifam);
532 				return;
533 			}
534 			if (ifam->ifam_type != RTM_NEWADDR)
535 				break;
536 		}
537 		ifm = (struct if_msghdr *)ifam;
538 	}
539 }
540 
541 void
542 init_iflist()
543 {
544 	if (ifblock) {
545 		free(ifblock);
546 		ifblock_size = 0;
547 	}
548 	if (iflist)
549 		free(iflist);
550 	/* get iflist block from kernel */
551 	get_iflist(&ifblock, &ifblock_size);
552 
553 	/* make list of pointers to each if_msghdr */
554 	parse_iflist(&iflist, ifblock, ifblock_size);
555 
556 }
557