xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/routing_events.c (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <arpa/inet.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <net/if.h>
31 #include <net/route.h>
32 #include <pthread.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <sys/fcntl.h>
38 #include <unistd.h>
39 
40 #include <libnwam.h>
41 #include "events.h"
42 #include "ncp.h"
43 #include "ncu.h"
44 #include "util.h"
45 
46 /*
47  * routing_events.c - this file contains routines to retrieve routing socket
48  * events and package them for high level processing.
49  */
50 
51 #define	RTMBUFSZ	sizeof (struct rt_msghdr) + \
52 			(RTAX_MAX * sizeof (struct sockaddr_storage))
53 
54 static void printaddrs(int, void *);
55 static char *printaddr(void **);
56 static void *getaddr(int, int, void *);
57 static void setaddr(int, int *, void *, struct sockaddr *);
58 
59 union rtm_buf
60 {
61 	/* Routing information. */
62 	struct
63 	{
64 		struct rt_msghdr rtm;
65 		struct sockaddr_storage addr[RTAX_MAX];
66 	} r;
67 
68 	/* Interface information. */
69 	struct
70 	{
71 		struct if_msghdr ifm;
72 		struct sockaddr_storage addr[RTAX_MAX];
73 	} im;
74 
75 	/* Interface address information. */
76 	struct
77 	{
78 		struct ifa_msghdr ifa;
79 		struct sockaddr_storage addr[RTAX_MAX];
80 	} ia;
81 };
82 
83 static int v4_sock = -1;
84 static int v6_sock = -1;
85 static pthread_t v4_routing, v6_routing;
86 static int seq = 0;
87 
88 static const char *
89 rtmtype_str(int type)
90 {
91 	static char typestr[12]; /* strlen("type ") + enough for an int */
92 
93 	switch (type) {
94 	case RTM_NEWADDR:
95 		return ("NEWADDR");
96 	case RTM_DELADDR:
97 		return ("DELADDR");
98 	case RTM_CHGADDR:
99 		return ("CHGADDR");
100 	case RTM_FREEADDR:
101 		return ("FREEADDR");
102 	default:
103 		(void) snprintf(typestr, sizeof (typestr), "type %d", type);
104 		return (typestr);
105 	}
106 }
107 
108 /* ARGSUSED0 */
109 static void *
110 routing_events_v4(void *arg)
111 {
112 	int n;
113 	union rtm_buf buffer;
114 	struct rt_msghdr *rtm;
115 	struct ifa_msghdr *ifa;
116 	char *addrs, *if_name;
117 	struct sockaddr_dl *addr_dl;
118 	struct sockaddr *addr, *netmask;
119 	nwamd_event_t ip_event;
120 
121 	nlog(LOG_DEBUG, "v4 routing socket %d", v4_sock);
122 
123 	for (;;) {
124 		rtm = &buffer.r.rtm;
125 		n = read(v4_sock, &buffer, sizeof (buffer));
126 		if (n == -1 && errno == EAGAIN) {
127 			continue;
128 		} else if (n == -1) {
129 			nlog(LOG_ERR, "error reading routing socket "
130 			    "%d: %m", v4_sock);
131 			/* Low likelihood.  What's recovery path?  */
132 			continue;
133 		}
134 
135 		if (rtm->rtm_msglen < n) {
136 			nlog(LOG_ERR, "only read %d bytes from "
137 			    "routing socket but message claims to be "
138 			    "of length %d", rtm->rtm_msglen);
139 			continue;
140 		}
141 
142 		if (rtm->rtm_version != RTM_VERSION) {
143 			nlog(LOG_ERR, "tossing routing message of "
144 			    "version %d type %d", rtm->rtm_version,
145 			    rtm->rtm_type);
146 			continue;
147 		}
148 
149 		if (rtm->rtm_msglen != n) {
150 			nlog(LOG_DEBUG, "routing message of %d size came from "
151 			    "read of %d on socket %d", rtm->rtm_msglen,
152 			    n, v4_sock);
153 		}
154 
155 		switch (rtm->rtm_type) {
156 		case RTM_NEWADDR:
157 		case RTM_DELADDR:
158 		case RTM_CHGADDR:
159 		case RTM_FREEADDR:
160 
161 			ifa = (void *)rtm;
162 			addrs = (char *)ifa + sizeof (*ifa);
163 
164 			nlog(LOG_DEBUG, "v4 routing message %s: "
165 			    "index %d flags %x", rtmtype_str(rtm->rtm_type),
166 			    ifa->ifam_index, ifa->ifam_flags);
167 
168 			if ((addr = (struct sockaddr *)getaddr(RTA_IFA,
169 			    ifa->ifam_addrs, addrs)) == NULL)
170 				break;
171 
172 			/* Ignore routing socket messages for 0.0.0.0 */
173 			/*LINTED*/
174 			if (((struct sockaddr_in *)addr)->sin_addr.s_addr
175 			    == INADDR_ANY) {
176 				nlog(LOG_DEBUG, "routing_events_v4: "
177 				    "tossing message for 0.0.0.0");
178 				break;
179 			}
180 
181 			if ((netmask = (struct sockaddr *)getaddr(RTA_NETMASK,
182 			    ifa->ifam_addrs, addrs)) == NULL)
183 				break;
184 
185 			if ((addr_dl = (struct sockaddr_dl *)getaddr
186 			    (RTA_IFP, ifa->ifam_addrs, addrs)) == NULL)
187 				break;
188 			/*
189 			 * We don't use the lladdr in this structure so we can
190 			 * run over it.
191 			 */
192 			addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
193 			if_name = addr_dl->sdl_data; /* no lifnum */
194 
195 			if (ifa->ifam_index == 0) {
196 				nlog(LOG_DEBUG, "tossing index 0 message");
197 				break;
198 			}
199 			if (ifa->ifam_type != rtm->rtm_type) {
200 				nlog(LOG_INFO,
201 				    "routing_events_v4: unhandled type %d",
202 				    ifa->ifam_type);
203 				break;
204 			}
205 
206 			printaddrs(ifa->ifam_addrs, addrs);
207 
208 			/* Create and enqueue IF_STATE event */
209 			ip_event = nwamd_event_init_if_state(if_name,
210 			    ifa->ifam_flags,
211 			    (rtm->rtm_type == RTM_NEWADDR ||
212 			    rtm->rtm_type == RTM_CHGADDR ? B_TRUE : B_FALSE),
213 			    addr, netmask);
214 			if (ip_event != NULL)
215 				nwamd_event_enqueue(ip_event);
216 			break;
217 		}
218 	}
219 	/* NOTREACHED */
220 	return (NULL);
221 }
222 
223 /* ARGSUSED0 */
224 static void *
225 routing_events_v6(void *arg)
226 {
227 	int n;
228 	union rtm_buf buffer;
229 	struct rt_msghdr *rtm;
230 	struct ifa_msghdr *ifa;
231 	char *addrs, *if_name;
232 	struct sockaddr_dl *addr_dl;
233 	struct sockaddr *addr, *netmask;
234 	nwamd_event_t ip_event;
235 
236 	nlog(LOG_DEBUG, "v6 routing socket %d", v6_sock);
237 
238 	for (;;) {
239 
240 		rtm = &buffer.r.rtm;
241 		n = read(v6_sock, &buffer, sizeof (buffer));
242 		if (n == -1 && errno == EAGAIN) {
243 			continue;
244 		} else if (n == -1) {
245 			nlog(LOG_ERR, "error reading routing socket "
246 			    "%d: %m", v6_sock);
247 			/* Low likelihood.  What's recovery path?  */
248 			continue;
249 		}
250 
251 		if (rtm->rtm_msglen < n) {
252 			nlog(LOG_ERR, "only read %d bytes from "
253 			    "routing socket but message claims to be "
254 			    "of length %d", rtm->rtm_msglen);
255 			continue;
256 		}
257 
258 		if (rtm->rtm_version != RTM_VERSION) {
259 			nlog(LOG_ERR, "tossing routing message of "
260 			    "version %d type %d", rtm->rtm_version,
261 			    rtm->rtm_type);
262 			continue;
263 		}
264 
265 		if (rtm->rtm_msglen != n) {
266 			nlog(LOG_DEBUG, "routing message of %d size came from "
267 			    "read of %d on socket %d", rtm->rtm_msglen,
268 			    n, v6_sock);
269 		}
270 
271 		switch (rtm->rtm_type) {
272 		case RTM_NEWADDR:
273 		case RTM_DELADDR:
274 		case RTM_CHGADDR:
275 		case RTM_FREEADDR:
276 
277 			ifa = (void *)rtm;
278 			addrs = (char *)ifa + sizeof (*ifa);
279 
280 			nlog(LOG_DEBUG, "v6 routing message %s: "
281 			    "index %d flags %x", rtmtype_str(rtm->rtm_type),
282 			    ifa->ifam_index, ifa->ifam_flags);
283 
284 			if ((addr = (struct sockaddr *)getaddr(RTA_IFA,
285 			    ifa->ifam_addrs, addrs)) == NULL)
286 				break;
287 
288 			/* Ignore routing socket messages for :: & linklocal */
289 			/*LINTED*/
290 			if (IN6_IS_ADDR_UNSPECIFIED(
291 			    &((struct sockaddr_in6 *)addr)->sin6_addr)) {
292 				nlog(LOG_INFO, "routing_events_v6: "
293 				    "tossing message for ::");
294 				break;
295 			}
296 			/*LINTED*/
297 			if (IN6_IS_ADDR_LINKLOCAL(
298 			    &((struct sockaddr_in6 *)addr)->sin6_addr)) {
299 				nlog(LOG_INFO, "routing_events_v6: "
300 				    "tossing message for link local address");
301 				break;
302 			}
303 
304 			if ((netmask =
305 			    (struct sockaddr *)getaddr(RTA_NETMASK,
306 			    ifa->ifam_addrs, addrs)) == NULL)
307 				break;
308 
309 			if ((addr_dl = (struct sockaddr_dl *)getaddr
310 			    (RTA_IFP, ifa->ifam_addrs, addrs)) == NULL)
311 				break;
312 			/*
313 			 * We don't use the lladdr in this structure so we can
314 			 * run over it.
315 			 */
316 			addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
317 			if_name = addr_dl->sdl_data; /* no lifnum */
318 
319 			if (ifa->ifam_index == 0) {
320 				nlog(LOG_DEBUG, "tossing index 0 message");
321 				break;
322 			}
323 			if (ifa->ifam_type != rtm->rtm_type) {
324 				nlog(LOG_DEBUG,
325 				    "routing_events_v6: unhandled type %d",
326 				    ifa->ifam_type);
327 				break;
328 			}
329 
330 			printaddrs(ifa->ifam_addrs, addrs);
331 
332 			/* Create and enqueue IF_STATE event */
333 			ip_event = nwamd_event_init_if_state(if_name,
334 			    ifa->ifam_flags,
335 			    (rtm->rtm_type == RTM_NEWADDR ||
336 			    rtm->rtm_type == RTM_CHGADDR ? B_TRUE : B_FALSE),
337 			    addr, netmask);
338 			if (ip_event != NULL)
339 				nwamd_event_enqueue(ip_event);
340 			break;
341 
342 		}
343 	}
344 	/* NOTREACHED */
345 	return (NULL);
346 }
347 
348 void
349 nwamd_routing_events_init(void)
350 {
351 	pthread_attr_t attr;
352 
353 	/*
354 	 * Initialize routing sockets here so that we know the routing threads
355 	 * (and any requests to add a route) will be working with a valid socket
356 	 * by the time we start handling events.
357 	 */
358 	v4_sock = socket(AF_ROUTE, SOCK_RAW, AF_INET);
359 	if (v4_sock == -1)
360 		pfail("failed to open v4 routing socket: %m");
361 
362 	v6_sock = socket(AF_ROUTE, SOCK_RAW, AF_INET6);
363 	if (v6_sock == -1)
364 		pfail("failed to open v6 routing socket: %m");
365 
366 	(void) pthread_attr_init(&attr);
367 	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
368 	if (pthread_create(&v4_routing, &attr, routing_events_v4, NULL) != 0 ||
369 	    pthread_create(&v6_routing, &attr, routing_events_v6, NULL) != 0)
370 		pfail("routing thread creation failed");
371 	(void) pthread_attr_destroy(&attr);
372 }
373 
374 void
375 nwamd_routing_events_fini(void)
376 {
377 	(void) pthread_cancel(v4_routing);
378 	(void) pthread_cancel(v6_routing);
379 }
380 
381 void
382 nwamd_add_route(struct sockaddr *dest, struct sockaddr *mask,
383     struct sockaddr *gateway, const char *ifname)
384 {
385 	char rtbuf[RTMBUFSZ];
386 	/* LINTED E_BAD_PTR_CAST_ALIGN */
387 	struct rt_msghdr *rtm = (struct rt_msghdr *)rtbuf;
388 	void *addrs = rtbuf + sizeof (struct rt_msghdr);
389 	struct sockaddr_dl sdl;
390 	int rlen, index;
391 	int af;
392 
393 	af = gateway->sa_family;
394 
395 	/* retrieve the index value for the interface */
396 	if ((index = if_nametoindex(ifname)) == 0) {
397 		nlog(LOG_ERR, "nwamd_add_route: if_nametoindex failed on %s",
398 		    ifname);
399 		return;
400 	}
401 
402 	(void) bzero(&sdl, sizeof (struct sockaddr_dl));
403 	sdl.sdl_family = AF_LINK;
404 	sdl.sdl_index = index;
405 
406 	(void) bzero(rtm, RTMBUFSZ);
407 	rtm->rtm_pid = getpid();
408 	rtm->rtm_type = RTM_ADD;
409 	rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_GATEWAY;
410 	rtm->rtm_version = RTM_VERSION;
411 	rtm->rtm_seq = ++seq;
412 	rtm->rtm_msglen = sizeof (rtbuf);
413 	setaddr(RTA_DST, &rtm->rtm_addrs, &addrs, dest);
414 	setaddr(RTA_GATEWAY, &rtm->rtm_addrs, &addrs, gateway);
415 	setaddr(RTA_NETMASK, &rtm->rtm_addrs, &addrs, mask);
416 	setaddr(RTA_IFP, &rtm->rtm_addrs, &addrs, (struct sockaddr *)&sdl);
417 
418 	if ((rlen = write(af == AF_INET ? v4_sock : v6_sock,
419 	    rtbuf, rtm->rtm_msglen)) < 0) {
420 		nlog(LOG_ERR, "nwamd_add_route: "
421 		    "got error %s writing to routing socket", strerror(errno));
422 	} else if (rlen < rtm->rtm_msglen) {
423 		nlog(LOG_ERR, "nwamd_add_route: "
424 		    "only wrote %d bytes of %d to routing socket\n",
425 		    rlen, rtm->rtm_msglen);
426 	}
427 }
428 
429 static char *
430 printaddr(void **address)
431 {
432 	static char buffer[80];
433 	sa_family_t family = *(sa_family_t *)*address;
434 	struct sockaddr_in *s4 = *address;
435 	struct sockaddr_in6 *s6 = *address;
436 	struct sockaddr_dl *dl = *address;
437 
438 	switch (family) {
439 	case AF_UNSPEC:
440 		(void) inet_ntop(AF_UNSPEC, &s4->sin_addr, buffer,
441 		    sizeof (buffer));
442 		*address = (char *)*address + sizeof (*s4);
443 		break;
444 	case AF_INET:
445 		(void) inet_ntop(AF_INET, &s4->sin_addr, buffer,
446 		    sizeof (buffer));
447 		*address = (char *)*address + sizeof (*s4);
448 		break;
449 	case AF_INET6:
450 		(void) inet_ntop(AF_INET6, &s6->sin6_addr, buffer,
451 		    sizeof (buffer));
452 		*address = (char *)*address + sizeof (*s6);
453 		break;
454 	case AF_LINK:
455 		(void) snprintf(buffer, sizeof (buffer), "link %.*s",
456 		    dl->sdl_nlen, dl->sdl_data);
457 		*address = (char *)*address + sizeof (*dl);
458 		break;
459 	default:
460 		/*
461 		 * We can't reliably update the size of this thing
462 		 * because we don't know what its type is.  So bump
463 		 * it by a sockaddr_in and see what happens.  The
464 		 * caller should really make sure this never happens.
465 		 */
466 		*address = (char *)*address + sizeof (*s4);
467 		(void) snprintf(buffer, sizeof (buffer),
468 		    "unknown address family %d", family);
469 		break;
470 	}
471 	return (buffer);
472 }
473 
474 static void
475 printaddrs(int mask, void *address)
476 {
477 	if (mask == 0)
478 		return;
479 	if (mask & RTA_DST)
480 		nlog(LOG_DEBUG, "destination address: %s", printaddr(&address));
481 	if (mask & RTA_GATEWAY)
482 		nlog(LOG_DEBUG, "gateway address: %s", printaddr(&address));
483 	if (mask & RTA_NETMASK)
484 		nlog(LOG_DEBUG, "netmask: %s", printaddr(&address));
485 	if (mask & RTA_GENMASK)
486 		nlog(LOG_DEBUG, "cloning mask: %s", printaddr(&address));
487 	if (mask & RTA_IFP)
488 		nlog(LOG_DEBUG, "interface name: %s", printaddr(&address));
489 	if (mask & RTA_IFA)
490 		nlog(LOG_DEBUG, "interface address: %s", printaddr(&address));
491 	if (mask & RTA_AUTHOR)
492 		nlog(LOG_DEBUG, "author: %s", printaddr(&address));
493 	if (mask & RTA_BRD)
494 		nlog(LOG_DEBUG, "broadcast address: %s", printaddr(&address));
495 }
496 
497 static void
498 nextaddr(void **address)
499 {
500 	sa_family_t family = *(sa_family_t *)*address;
501 
502 	switch (family) {
503 	case AF_UNSPEC:
504 	case AF_INET:
505 		*address = (char *)*address + sizeof (struct sockaddr_in);
506 		break;
507 	case AF_INET6:
508 		*address = (char *)*address + sizeof (struct sockaddr_in6);
509 		break;
510 	case AF_LINK:
511 		*address = (char *)*address + sizeof (struct sockaddr_dl);
512 		break;
513 	default:
514 		nlog(LOG_ERR, "unknown af (%d) while parsing rtm", family);
515 		break;
516 	}
517 }
518 
519 static void *
520 getaddr(int addrid, int mask, void *addresses)
521 {
522 	int i;
523 	void *p = addresses;
524 
525 	if ((mask & addrid) == 0)
526 		return (NULL);
527 
528 	for (i = 1; i < addrid; i <<= 1) {
529 		if (i & mask)
530 			nextaddr(&p);
531 	}
532 	return (p);
533 }
534 
535 static void
536 setaddr(int addrid, int *maskp, void *addressesp, struct sockaddr *address)
537 {
538 	struct sockaddr *p = *((struct sockaddr **)addressesp);
539 
540 	*maskp |= addrid;
541 
542 	switch (address->sa_family) {
543 	case AF_INET:
544 		(void) memcpy(p, address, sizeof (struct sockaddr_in));
545 		break;
546 	case AF_INET6:
547 		(void) memcpy(p, address, sizeof (struct sockaddr_in6));
548 		break;
549 	case AF_LINK:
550 		(void) memcpy(p, address, sizeof (struct sockaddr_dl));
551 		break;
552 	default:
553 		nlog(LOG_ERR, "setaddr: unknown af (%d) while setting addr",
554 		    address->sa_family);
555 		break;
556 	}
557 	nextaddr(addressesp);
558 }
559