xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/events.c (revision 34a0f871d192b33b865455a8812a3d34c1866315)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains routines to retrieve events from the system and package
31  * them for high level processing.
32  *
33  * struct np_event is the basic event structure.  The np_event structure and
34  * its npe_name member are allocated using malloc(3c).  free_event() frees both
35  * the npe_name member and the associated np_event structure.
36  *
37  * np_queue_add_event() and np_queue_get_event() provide functionality for
38  * adding events to a queue and blocking on that queue for an event.
39  *
40  * Functions of the form addevent_*() provide the mechanism to cook down a
41  * higher level event into an np_event and put it on the queue.
42  *
43  * routing_events() reads routing messages off of an IPv4 routing socket and
44  * by calling addevent_*() functions places appropriate events on the queue.
45  *
46  * start_event_collection() creates a thread to run routing_events() and one
47  * to run periodic_wireless_scan() in.  Finally it does an initial collection
48  * of information from each interface currently known.
49  */
50 
51 #include <arpa/inet.h>
52 #include <assert.h>
53 #include <errno.h>
54 #include <libsysevent.h>
55 #include <net/if.h>
56 #include <net/route.h>
57 #include <pthread.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <sys/fcntl.h>
61 #include <sys/sysevent/eventdefs.h>
62 #include <syslog.h>
63 #include <unistd.h>
64 #include <fcntl.h>
65 
66 #include "defines.h"
67 #include "structures.h"
68 #include "functions.h"
69 #include "variables.h"
70 
71 struct np_event *equeue = NULL;
72 static struct np_event *equeue_end = NULL;
73 
74 pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
75 pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
76 pthread_t routing, scan;
77 
78 static void printaddrs(int mask, void *address);
79 static char *printaddr(void **address);
80 static struct sockaddr *dupsockaddr(struct sockaddr *);
81 static boolean_t cmpsockaddr(struct sockaddr *, struct sockaddr *);
82 static void *getaddr(int addrid, int mask, void *address);
83 
84 union rtm_buf
85 {
86 	/* Routing information. */
87 	struct
88 	{
89 		struct rt_msghdr rtm;
90 		struct sockaddr_storage addr[RTAX_MAX];
91 	} r;
92 
93 	/* Interface information. */
94 	struct
95 	{
96 		struct if_msghdr ifm;
97 		struct sockaddr_storage addr[RTAX_MAX];
98 	} im;
99 
100 	/* Interface address information. */
101 	struct
102 	{
103 		struct ifa_msghdr ifa;
104 		struct sockaddr_storage addr[RTAX_MAX];
105 	} ia;
106 };
107 
108 void
109 free_event(struct np_event *e)
110 {
111 	free(e->npe_name);
112 	free(e);
113 }
114 
115 void
116 np_queue_add_event(struct np_event *e)
117 {
118 	(void) pthread_mutex_lock(&queue_mutex);
119 	if (equeue_end != NULL) {
120 		equeue_end->npe_next = e;
121 		equeue_end = e;
122 	} else {
123 		equeue = equeue_end = e;
124 	}
125 	equeue_end->npe_next = NULL;
126 	(void) pthread_cond_signal(&queue_cond);
127 	(void) pthread_mutex_unlock(&queue_mutex);
128 }
129 
130 /*
131  * Blocking getevent.  This routine will block until there is an event for
132  * it to return.
133  */
134 struct np_event *
135 np_queue_get_event(void)
136 {
137 	struct np_event *rv = NULL;
138 
139 	(void) pthread_mutex_lock(&queue_mutex);
140 
141 	while (equeue == NULL)
142 		(void) pthread_cond_wait(&queue_cond, &queue_mutex);
143 
144 	rv = equeue;
145 	equeue = equeue->npe_next;
146 	if (equeue == NULL)
147 		equeue_end = NULL;
148 
149 	(void) pthread_mutex_unlock(&queue_mutex);
150 
151 	rv->npe_next = NULL;
152 	return (rv);
153 }
154 
155 const char *
156 npe_type_str(enum np_event_type type)
157 {
158 	switch (type) {
159 		case EV_ROUTING:
160 			return ("ROUTING");
161 		case EV_SYS:
162 			return ("SYS");
163 		case EV_TIMER:
164 			return ("TIMER");
165 		case EV_SHUTDOWN:
166 			return ("SHUTDOWN");
167 		case EV_NEWADDR:
168 			return ("NEWADDR");
169 		default:
170 			return ("unknown");
171 	}
172 }
173 
174 static void
175 addevent_routing_ifa(struct ifa_msghdr *ifa, const char *name)
176 {
177 	struct np_event *e;
178 
179 	dprintf("addevent_routing_ifa");
180 	if (ifa->ifam_index == 0) {
181 		/* what is this? */
182 		dprintf("tossing index 0 routing event");
183 		return;
184 	}
185 
186 	e = calloc(1, sizeof (*e));
187 	if (e == NULL) {
188 		syslog(LOG_ERR, "calloc failed");
189 		return;
190 	}
191 
192 	switch (ifa->ifam_type) {
193 	case RTM_NEWADDR:
194 		assert(name != NULL);
195 		e->npe_type = EV_NEWADDR;
196 		if ((e->npe_name = strdup(name)) == NULL) {
197 			syslog(LOG_ERR, "strdup failed");
198 			free(e);
199 			return;
200 		}
201 		dprintf("adding event type %s name %s to queue",
202 		    npe_type_str(e->npe_type), STRING(e->npe_name));
203 		np_queue_add_event(e);
204 		break;
205 
206 	default:
207 		free(e);
208 		dprintf("unhandled type in addevent_routing_ifa %d",
209 		    ifa->ifam_type);
210 		break;
211 	}
212 }
213 
214 static void
215 addevent_routing_msghdr(struct if_msghdr *ifm, const char *name)
216 {
217 	struct np_event *e;
218 
219 	dprintf("addevent_routing_msghdr");
220 	if (ifm->ifm_index == 0) {
221 		/* what is this? */
222 		dprintf("tossing index 0 routing event");
223 		return;
224 	}
225 
226 	switch (ifm->ifm_type) {
227 	case RTM_IFINFO:
228 		assert(name != NULL);
229 		e = calloc(1, sizeof (*e));
230 		if (e == NULL) {
231 			syslog(LOG_ERR, "calloc failed");
232 			return;
233 		}
234 
235 		e->npe_type = EV_ROUTING;
236 		if ((e->npe_name = strdup(name)) == NULL) {
237 			syslog(LOG_ERR, "strdup failed");
238 			free(e);
239 			return;
240 		}
241 		dprintf("flags = %x, IFF_RUNNING = %x", ifm->ifm_flags,
242 		    IFF_RUNNING);
243 		dprintf("adding event type %s name %s to queue",
244 		    npe_type_str(e->npe_type), STRING(e->npe_name));
245 		np_queue_add_event(e);
246 		break;
247 
248 	default:
249 		dprintf("unhandled type in addevent_routing_msghdr %d",
250 		    ifm->ifm_type);
251 		break;
252 	}
253 }
254 
255 static const char *
256 rtmtype_str(int type)
257 {
258 	static char typestr[12]; /* strlen("type ") + enough for an int */
259 
260 	switch (type) {
261 		case RTM_ADD:
262 			return ("ADD");
263 		case RTM_DELETE:
264 			return ("DELETE");
265 		case RTM_NEWADDR:
266 			return ("NEWADDR");
267 		case RTM_DELADDR:
268 			return ("DELADDR");
269 		case RTM_IFINFO:
270 			return ("IFINFO");
271 		default:
272 			(void) snprintf(typestr, sizeof (typestr), "type %d",
273 			    type);
274 			return (typestr);
275 	}
276 }
277 
278 /* ARGSUSED */
279 static void *
280 routing_events(void *arg)
281 {
282 	int rtsock;
283 	int n;
284 	union rtm_buf buffer;
285 	struct rt_msghdr *rtm;
286 	struct ifa_msghdr *ifa;
287 	struct if_msghdr *ifm;
288 
289 	/*
290 	 * We use v4 interfaces as proxies for links so those are the only
291 	 * routing messages we need to listen to.  Look at the comments in
292 	 * structures.h for more information about the split between the
293 	 * llp and interfaces.
294 	 */
295 	rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET);
296 	if (rtsock == -1) {
297 		syslog(LOG_ERR, "failed to open routing socket: %m");
298 		exit(EXIT_FAILURE);
299 	}
300 
301 	dprintf("routing socket %d", rtsock);
302 
303 	for (;;) {
304 		struct interface *ifp;
305 		char *addrs, *if_name;
306 		struct sockaddr_dl *addr_dl;
307 		struct sockaddr *addr;
308 
309 		rtm = &buffer.r.rtm;
310 		n = read(rtsock, &buffer, sizeof (buffer));
311 		if (n == -1 && errno == EAGAIN) {
312 			continue;
313 		} else if (n == -1) {
314 			syslog(LOG_ERR, "error reading routing socket "
315 			    "%d: %m", rtsock);
316 			/* Low likelihood.  What's recovery path?  */
317 			continue;
318 		}
319 
320 		if (rtm->rtm_msglen < n) {
321 			syslog(LOG_ERR, "only read %d bytes from "
322 			    "routing socket but message claims to be "
323 			    "of length %d", rtm->rtm_msglen);
324 			continue;
325 		}
326 
327 		if (rtm->rtm_version != RTM_VERSION) {
328 			syslog(LOG_ERR, "tossing routing message of "
329 			    "version %d type %d", rtm->rtm_version,
330 			    rtm->rtm_type);
331 			continue;
332 		}
333 
334 		if (rtm->rtm_msglen != n) {
335 			dprintf("routing message of %d size came from "
336 			    "read of %d on socket %d", rtm->rtm_msglen,
337 			    n, rtsock);
338 		}
339 
340 		switch (rtm->rtm_type) {
341 		case RTM_NEWADDR:
342 			ifa = (void *)rtm;
343 			addrs = (char *)ifa + sizeof (*ifa);
344 
345 			dprintf("routing message NEWADDR: index %d flags %x",
346 			    ifa->ifam_index, ifa->ifam_flags);
347 			printaddrs(ifa->ifam_addrs, addrs);
348 
349 			if ((addr = (struct sockaddr *)getaddr(RTA_IFA,
350 			    ifa->ifam_addrs, addrs)) == NULL)
351 				break;
352 
353 			if ((addr_dl = (struct sockaddr_dl *)getaddr
354 			    (RTA_IFP, ifa->ifam_addrs, addrs)) == NULL)
355 				break;
356 			if_name = addr_dl->sdl_data;
357 			ifp = get_interface(if_name);
358 			if (ifp == NULL) {
359 				dprintf("no interface struct for %s; ignoring "
360 				    "message", STRING(if_name));
361 				break;
362 			}
363 
364 			/* if no cached address, cache it */
365 			if (ifp->if_ipaddr == NULL) {
366 				ifp->if_ipaddr = dupsockaddr(addr);
367 				dprintf("cached address %s for link %s",
368 				    printaddr((void **)&addr), if_name);
369 				addevent_routing_ifa(ifa, if_name);
370 			} else if (!cmpsockaddr(addr, ifp->if_ipaddr)) {
371 				free(ifp->if_ipaddr);
372 				ifp->if_ipaddr = dupsockaddr(addr);
373 				addevent_routing_ifa(ifa, if_name);
374 			}
375 			break;
376 		case RTM_IFINFO:
377 		{
378 			boolean_t plugged_in;
379 
380 			ifm = (void *)rtm;
381 			addrs = (char *)ifm + sizeof (*ifm);
382 			dprintf("routing message IFINFO: index %d flags %x",
383 			    ifm->ifm_index, ifm->ifm_flags);
384 			printaddrs(ifm->ifm_addrs, addrs);
385 
386 			if ((addr_dl = (struct sockaddr_dl *)getaddr(RTA_IFP,
387 			    ifm->ifm_addrs, addrs)) == NULL)
388 				break;
389 			if_name = addr_dl->sdl_data;
390 			ifp = get_interface(if_name);
391 			if (ifp == NULL) {
392 				dprintf("no interface struct for %s; ignoring "
393 				    "message", STRING(if_name));
394 				break;
395 			}
396 
397 			/*
398 			 * Check for toggling of the IFF_RUNNING
399 			 * flag.
400 			 *
401 			 * On any change in the flag value, we
402 			 * turn off the DHCPFAILED and DHCPSTARTED
403 			 * flags; the change in the RUNNING state
404 			 * indicates a "fresh start" for the
405 			 * interface, so we should try dhcp again.
406 			 *
407 			 * Ignore specific IFF_RUNNING changes for
408 			 * wireless interfaces; their semantics are
409 			 * a bit different (either the flag is always
410 			 * on, or, with newer drivers, it indicates
411 			 * whether or not they are connected to an AP).
412 			 *
413 			 * For wired interfaces, if the interface was
414 			 * not plugged in and now it is, start info
415 			 * collection.
416 			 *
417 			 * If it was plugged in and now it is
418 			 * unplugged, generate an event.
419 			 *
420 			 * XXX We probably need a lock to protect
421 			 * if_flags setting and getting.
422 			 */
423 			if ((ifp->if_flags & IFF_RUNNING) !=
424 			    (ifm->ifm_flags & IFF_RUNNING)) {
425 				ifp->if_lflags &= ~IF_DHCPFAILED;
426 				ifp->if_lflags &= ~IF_DHCPSTARTED;
427 			}
428 			if (ifp->if_type == IF_WIRELESS)
429 				break;
430 			plugged_in =
431 			    ((ifp->if_flags & IFF_RUNNING) != 0);
432 			ifp->if_flags = ifm->ifm_flags;
433 			if (!plugged_in &&
434 			    (ifm->ifm_flags & IFF_RUNNING)) {
435 				start_if_info_collect(ifp, NULL);
436 			} else if (plugged_in &&
437 			    !(ifm->ifm_flags & IFF_RUNNING)) {
438 				check_drop_dhcp(ifp);
439 				addevent_routing_msghdr(ifm, if_name);
440 			}
441 			break;
442 		}
443 		default:
444 			dprintf("routing message %s socket %d discarded",
445 			    rtmtype_str(rtm->rtm_type), rtsock);
446 			break;
447 		}
448 	}
449 	/* NOTREACHED */
450 	return (NULL);
451 }
452 
453 /* return B_TRUE if sin_family and sin_addr are the same, B_FALSE if not */
454 static boolean_t
455 cmpsockaddr(struct sockaddr *addr1, struct sockaddr *addr2)
456 {
457 	struct sockaddr_in *sina, *sinb;
458 	struct sockaddr_in6 *sin6a, *sin6b;
459 
460 	if (addr1->sa_family != addr2->sa_family)
461 		return (B_FALSE);
462 
463 	switch (addr1->sa_family) {
464 	case AF_INET:
465 		/* LINTED E_BAD_PTR_CAST_ALIGN */
466 		sina = (struct sockaddr_in *)addr1;
467 		/* LINTED E_BAD_PTR_CAST_ALIGN */
468 		sinb = (struct sockaddr_in *)addr2;
469 		return (sina->sin_addr.s_addr == sinb->sin_addr.s_addr);
470 	case AF_INET6:
471 		/* LINTED E_BAD_PTR_CAST_ALIGN */
472 		sin6a = (struct sockaddr_in6 *)addr1;
473 		/* LINTED E_BAD_PTR_CAST_ALIGN */
474 		sin6b = (struct sockaddr_in6 *)addr2;
475 		return
476 		    (IN6_ARE_ADDR_EQUAL(&sin6a->sin6_addr, &sin6b->sin6_addr));
477 	default:
478 		dprintf("cmpsockaddr: unsupported af (%d)", addr1->sa_family);
479 		return (B_FALSE);
480 	}
481 }
482 
483 /*
484  * Duplicate a sockaddr. Caller will be responsible for freeing memory when it
485  * is no longer needed. Currently only supports AF_INET and AF_INET6
486  * (returns NULL otherwise).
487  */
488 static struct sockaddr *
489 dupsockaddr(struct sockaddr *addr)
490 {
491 	struct sockaddr_in *t1, *ret1;
492 	struct sockaddr_in6 *t2, *ret2;
493 
494 	switch (addr->sa_family) {
495 	case AF_INET:
496 		if ((ret1 = calloc(1, sizeof (struct sockaddr_in))) == NULL) {
497 			syslog(LOG_ERR, "dupsockaddr: calloc failed");
498 			return (NULL);
499 		}
500 		/* LINTED E_BAD_PTR_CAST_ALIGN */
501 		t1 = (struct sockaddr_in *)addr;
502 		ret1->sin_family = t1->sin_family;
503 		ret1->sin_addr.s_addr = t1->sin_addr.s_addr;
504 		return ((struct sockaddr *)ret1);
505 	case AF_INET6:
506 		if ((ret2 = calloc(1, sizeof (struct sockaddr_in6))) == NULL) {
507 			syslog(LOG_ERR, "dupsockaddr: calloc failed");
508 			return (NULL);
509 		}
510 		/* LINTED E_BAD_PTR_CAST_ALIGN */
511 		t2 = (struct sockaddr_in6 *)addr;
512 		ret2->sin6_family = t2->sin6_family;
513 		(void) memcpy((void *)&ret2->sin6_addr,
514 		    (const void *)&t2->sin6_addr, sizeof (struct in6_addr));
515 		return ((struct sockaddr *)ret2);
516 	default:
517 		dprintf("dupsockaddr: unsupported af (%d)", addr->sa_family);
518 		return (NULL);
519 	}
520 }
521 
522 static char *
523 printaddr(void **address)
524 {
525 	static char buffer[80];
526 	sa_family_t family = *(sa_family_t *)*address;
527 	struct sockaddr_in *s4 = *address;
528 	struct sockaddr_in6 *s6 = *address;
529 	struct sockaddr_dl *dl = *address;
530 
531 	switch (family) {
532 		case AF_UNSPEC:
533 			(void) inet_ntop(AF_UNSPEC, &s4->sin_addr, buffer,
534 			    sizeof (buffer));
535 			*address = (char *)*address + sizeof (*s4);
536 			break;
537 		case AF_INET:
538 			(void) inet_ntop(AF_INET, &s4->sin_addr, buffer,
539 			    sizeof (buffer));
540 			*address = (char *)*address + sizeof (*s4);
541 			break;
542 		case AF_INET6:
543 			(void) inet_ntop(AF_INET6, &s6->sin6_addr, buffer,
544 			    sizeof (buffer));
545 			*address = (char *)*address + sizeof (*s6);
546 			break;
547 		case AF_LINK:
548 			(void) snprintf(buffer, sizeof (buffer), "link %s",
549 			    dl->sdl_data);
550 			*address = (char *)*address + sizeof (*dl);
551 			break;
552 		default:
553 			/*
554 			 * We can't reliably update the size of this thing
555 			 * because we don't know what its type is.  So bump
556 			 * it by a sockaddr_in and see what happens.  The
557 			 * caller should really make sure this never happens.
558 			 */
559 			*address = (char *)*address + sizeof (*s4);
560 			(void) snprintf(buffer, sizeof (buffer),
561 			    "unknown address family %d", family);
562 			break;
563 	}
564 	return (buffer);
565 }
566 
567 static void
568 printaddrs(int mask, void *address)
569 {
570 	if (mask == 0)
571 		return;
572 	if (mask & RTA_DST)
573 		dprintf("destination address: %s", printaddr(&address));
574 	if (mask & RTA_GATEWAY)
575 		dprintf("gateway address: %s", printaddr(&address));
576 	if (mask & RTA_NETMASK)
577 		dprintf("netmask: %s", printaddr(&address));
578 	if (mask & RTA_GENMASK)
579 		dprintf("cloning mask: %s", printaddr(&address));
580 	if (mask & RTA_IFP)
581 		dprintf("interface name: %s", printaddr(&address));
582 	if (mask & RTA_IFA)
583 		dprintf("interface address: %s", printaddr(&address));
584 	if (mask & RTA_AUTHOR)
585 		dprintf("author: %s", printaddr(&address));
586 	if (mask & RTA_BRD)
587 		dprintf("broadcast address: %s", printaddr(&address));
588 }
589 
590 static void
591 nextaddr(void **address)
592 {
593 	sa_family_t family = *(sa_family_t *)*address;
594 
595 	switch (family) {
596 	case AF_UNSPEC:
597 	case AF_INET:
598 		*address = (char *)*address + sizeof (struct sockaddr_in);
599 		break;
600 	case AF_INET6:
601 		*address = (char *)*address + sizeof (struct sockaddr_in6);
602 		break;
603 	case AF_LINK:
604 		*address = (char *)*address + sizeof (struct sockaddr_dl);
605 		break;
606 	default:
607 		syslog(LOG_ERR, "unknown af (%d) while parsing rtm", family);
608 		break;
609 	}
610 }
611 
612 static void *
613 getaddr(int addrid, int mask, void *address)
614 {
615 	int i;
616 	void *p = address;
617 
618 	if ((mask & addrid) == 0)
619 		return (NULL);
620 
621 	for (i = 1; i < addrid; i <<= 1) {
622 		if (i & mask)
623 			nextaddr(&p);
624 	}
625 	return (p);
626 }
627 
628 boolean_t
629 start_event_collection(void)
630 {
631 	int err;
632 
633 	/*
634 	 * if these are ever created/destroyed repetitively then we will
635 	 * have to change this.
636 	 */
637 
638 	if (err = pthread_create(&routing, NULL, routing_events, NULL)) {
639 		syslog(LOG_ERR, "pthread_create routing: %s", strerror(err));
640 		exit(EXIT_FAILURE);
641 	} else {
642 		dprintf("routing thread: %d", routing);
643 	}
644 
645 	if (err = pthread_create(&scan, NULL, periodic_wireless_scan, NULL)) {
646 		syslog(LOG_ERR, "pthread_create wireless scan: %s",
647 		    strerror(err));
648 		exit(EXIT_FAILURE);
649 	} else {
650 		dprintf("scan thread: %d", scan);
651 	}
652 
653 	walk_interface(start_if_info_collect, NULL);
654 
655 	return (B_TRUE);
656 }
657