xref: /titanic_41/usr/src/cmd/cmd-inet/lib/nwamd/events.c (revision b02e9a2d4d2071d770e5aa9ae8f83f2bbe1f2ced)
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 			/*
357 			 * We don't use the lladdr in this structure so we can
358 			 * run over it.
359 			 */
360 			addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
361 			if_name = addr_dl->sdl_data;
362 			ifp = get_interface(if_name);
363 			if (ifp == NULL) {
364 				dprintf("no interface struct for %s; ignoring "
365 				    "message", STRING(if_name));
366 				break;
367 			}
368 
369 			/* if no cached address, cache it */
370 			if (ifp->if_ipaddr == NULL) {
371 				ifp->if_ipaddr = dupsockaddr(addr);
372 				dprintf("cached address %s for link %s",
373 				    printaddr((void **)&addr), if_name);
374 				addevent_routing_ifa(ifa, if_name);
375 			} else if (!cmpsockaddr(addr, ifp->if_ipaddr)) {
376 				free(ifp->if_ipaddr);
377 				ifp->if_ipaddr = dupsockaddr(addr);
378 				addevent_routing_ifa(ifa, if_name);
379 			}
380 			break;
381 		case RTM_IFINFO:
382 		{
383 			boolean_t plugged_in;
384 
385 			ifm = (void *)rtm;
386 			addrs = (char *)ifm + sizeof (*ifm);
387 			dprintf("routing message IFINFO: index %d flags %x",
388 			    ifm->ifm_index, ifm->ifm_flags);
389 			printaddrs(ifm->ifm_addrs, addrs);
390 
391 			if ((addr_dl = (struct sockaddr_dl *)getaddr(RTA_IFP,
392 			    ifm->ifm_addrs, addrs)) == NULL)
393 				break;
394 			/*
395 			 * We don't use the lladdr in this structure so we can
396 			 * run over it.
397 			 */
398 			addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
399 			if_name = addr_dl->sdl_data;
400 			ifp = get_interface(if_name);
401 			if (ifp == NULL) {
402 				dprintf("no interface struct for %s; ignoring "
403 				    "message", STRING(if_name));
404 				break;
405 			}
406 
407 			/*
408 			 * Check for toggling of the IFF_RUNNING
409 			 * flag.
410 			 *
411 			 * On any change in the flag value, we
412 			 * turn off the DHCPFAILED and DHCPSTARTED
413 			 * flags; the change in the RUNNING state
414 			 * indicates a "fresh start" for the
415 			 * interface, so we should try dhcp again.
416 			 *
417 			 * Ignore specific IFF_RUNNING changes for
418 			 * wireless interfaces; their semantics are
419 			 * a bit different (either the flag is always
420 			 * on, or, with newer drivers, it indicates
421 			 * whether or not they are connected to an AP).
422 			 *
423 			 * For wired interfaces, if the interface was
424 			 * not plugged in and now it is, start info
425 			 * collection.
426 			 *
427 			 * If it was plugged in and now it is
428 			 * unplugged, generate an event.
429 			 *
430 			 * XXX We probably need a lock to protect
431 			 * if_flags setting and getting.
432 			 */
433 			if ((ifp->if_flags & IFF_RUNNING) !=
434 			    (ifm->ifm_flags & IFF_RUNNING)) {
435 				ifp->if_lflags &= ~IF_DHCPFAILED;
436 				ifp->if_lflags &= ~IF_DHCPSTARTED;
437 			}
438 			if (ifp->if_type == IF_WIRELESS)
439 				break;
440 			plugged_in =
441 			    ((ifp->if_flags & IFF_RUNNING) != 0);
442 			ifp->if_flags = ifm->ifm_flags;
443 			if (!plugged_in &&
444 			    (ifm->ifm_flags & IFF_RUNNING)) {
445 				start_if_info_collect(ifp, NULL);
446 			} else if (plugged_in &&
447 			    !(ifm->ifm_flags & IFF_RUNNING)) {
448 				check_drop_dhcp(ifp);
449 				addevent_routing_msghdr(ifm, if_name);
450 			}
451 			break;
452 		}
453 		default:
454 			dprintf("routing message %s socket %d discarded",
455 			    rtmtype_str(rtm->rtm_type), rtsock);
456 			break;
457 		}
458 	}
459 	/* NOTREACHED */
460 	return (NULL);
461 }
462 
463 /* return B_TRUE if sin_family and sin_addr are the same, B_FALSE if not */
464 static boolean_t
465 cmpsockaddr(struct sockaddr *addr1, struct sockaddr *addr2)
466 {
467 	struct sockaddr_in *sina, *sinb;
468 	struct sockaddr_in6 *sin6a, *sin6b;
469 
470 	if (addr1->sa_family != addr2->sa_family)
471 		return (B_FALSE);
472 
473 	switch (addr1->sa_family) {
474 	case AF_INET:
475 		/* LINTED E_BAD_PTR_CAST_ALIGN */
476 		sina = (struct sockaddr_in *)addr1;
477 		/* LINTED E_BAD_PTR_CAST_ALIGN */
478 		sinb = (struct sockaddr_in *)addr2;
479 		return (sina->sin_addr.s_addr == sinb->sin_addr.s_addr);
480 	case AF_INET6:
481 		/* LINTED E_BAD_PTR_CAST_ALIGN */
482 		sin6a = (struct sockaddr_in6 *)addr1;
483 		/* LINTED E_BAD_PTR_CAST_ALIGN */
484 		sin6b = (struct sockaddr_in6 *)addr2;
485 		return
486 		    (IN6_ARE_ADDR_EQUAL(&sin6a->sin6_addr, &sin6b->sin6_addr));
487 	default:
488 		dprintf("cmpsockaddr: unsupported af (%d)", addr1->sa_family);
489 		return (B_FALSE);
490 	}
491 }
492 
493 /*
494  * Duplicate a sockaddr. Caller will be responsible for freeing memory when it
495  * is no longer needed. Currently only supports AF_INET and AF_INET6
496  * (returns NULL otherwise).
497  */
498 static struct sockaddr *
499 dupsockaddr(struct sockaddr *addr)
500 {
501 	struct sockaddr_in *t1, *ret1;
502 	struct sockaddr_in6 *t2, *ret2;
503 
504 	switch (addr->sa_family) {
505 	case AF_INET:
506 		if ((ret1 = calloc(1, sizeof (struct sockaddr_in))) == NULL) {
507 			syslog(LOG_ERR, "dupsockaddr: calloc failed");
508 			return (NULL);
509 		}
510 		/* LINTED E_BAD_PTR_CAST_ALIGN */
511 		t1 = (struct sockaddr_in *)addr;
512 		ret1->sin_family = t1->sin_family;
513 		ret1->sin_addr.s_addr = t1->sin_addr.s_addr;
514 		return ((struct sockaddr *)ret1);
515 	case AF_INET6:
516 		if ((ret2 = calloc(1, sizeof (struct sockaddr_in6))) == NULL) {
517 			syslog(LOG_ERR, "dupsockaddr: calloc failed");
518 			return (NULL);
519 		}
520 		/* LINTED E_BAD_PTR_CAST_ALIGN */
521 		t2 = (struct sockaddr_in6 *)addr;
522 		ret2->sin6_family = t2->sin6_family;
523 		(void) memcpy((void *)&ret2->sin6_addr,
524 		    (const void *)&t2->sin6_addr, sizeof (struct in6_addr));
525 		return ((struct sockaddr *)ret2);
526 	default:
527 		dprintf("dupsockaddr: unsupported af (%d)", addr->sa_family);
528 		return (NULL);
529 	}
530 }
531 
532 static char *
533 printaddr(void **address)
534 {
535 	static char buffer[80];
536 	sa_family_t family = *(sa_family_t *)*address;
537 	struct sockaddr_in *s4 = *address;
538 	struct sockaddr_in6 *s6 = *address;
539 	struct sockaddr_dl *dl = *address;
540 
541 	switch (family) {
542 		case AF_UNSPEC:
543 			(void) inet_ntop(AF_UNSPEC, &s4->sin_addr, buffer,
544 			    sizeof (buffer));
545 			*address = (char *)*address + sizeof (*s4);
546 			break;
547 		case AF_INET:
548 			(void) inet_ntop(AF_INET, &s4->sin_addr, buffer,
549 			    sizeof (buffer));
550 			*address = (char *)*address + sizeof (*s4);
551 			break;
552 		case AF_INET6:
553 			(void) inet_ntop(AF_INET6, &s6->sin6_addr, buffer,
554 			    sizeof (buffer));
555 			*address = (char *)*address + sizeof (*s6);
556 			break;
557 		case AF_LINK:
558 			(void) snprintf(buffer, sizeof (buffer), "link %.*s",
559 			    dl->sdl_nlen, dl->sdl_data);
560 			*address = (char *)*address + sizeof (*dl);
561 			break;
562 		default:
563 			/*
564 			 * We can't reliably update the size of this thing
565 			 * because we don't know what its type is.  So bump
566 			 * it by a sockaddr_in and see what happens.  The
567 			 * caller should really make sure this never happens.
568 			 */
569 			*address = (char *)*address + sizeof (*s4);
570 			(void) snprintf(buffer, sizeof (buffer),
571 			    "unknown address family %d", family);
572 			break;
573 	}
574 	return (buffer);
575 }
576 
577 static void
578 printaddrs(int mask, void *address)
579 {
580 	if (mask == 0)
581 		return;
582 	if (mask & RTA_DST)
583 		dprintf("destination address: %s", printaddr(&address));
584 	if (mask & RTA_GATEWAY)
585 		dprintf("gateway address: %s", printaddr(&address));
586 	if (mask & RTA_NETMASK)
587 		dprintf("netmask: %s", printaddr(&address));
588 	if (mask & RTA_GENMASK)
589 		dprintf("cloning mask: %s", printaddr(&address));
590 	if (mask & RTA_IFP)
591 		dprintf("interface name: %s", printaddr(&address));
592 	if (mask & RTA_IFA)
593 		dprintf("interface address: %s", printaddr(&address));
594 	if (mask & RTA_AUTHOR)
595 		dprintf("author: %s", printaddr(&address));
596 	if (mask & RTA_BRD)
597 		dprintf("broadcast address: %s", printaddr(&address));
598 }
599 
600 static void
601 nextaddr(void **address)
602 {
603 	sa_family_t family = *(sa_family_t *)*address;
604 
605 	switch (family) {
606 	case AF_UNSPEC:
607 	case AF_INET:
608 		*address = (char *)*address + sizeof (struct sockaddr_in);
609 		break;
610 	case AF_INET6:
611 		*address = (char *)*address + sizeof (struct sockaddr_in6);
612 		break;
613 	case AF_LINK:
614 		*address = (char *)*address + sizeof (struct sockaddr_dl);
615 		break;
616 	default:
617 		syslog(LOG_ERR, "unknown af (%d) while parsing rtm", family);
618 		break;
619 	}
620 }
621 
622 static void *
623 getaddr(int addrid, int mask, void *address)
624 {
625 	int i;
626 	void *p = address;
627 
628 	if ((mask & addrid) == 0)
629 		return (NULL);
630 
631 	for (i = 1; i < addrid; i <<= 1) {
632 		if (i & mask)
633 			nextaddr(&p);
634 	}
635 	return (p);
636 }
637 
638 boolean_t
639 start_event_collection(void)
640 {
641 	int err;
642 	boolean_t check_cache = B_TRUE;
643 
644 	/*
645 	 * if these are ever created/destroyed repetitively then we will
646 	 * have to change this.
647 	 */
648 
649 	if (err = pthread_create(&routing, NULL, routing_events, NULL)) {
650 		syslog(LOG_ERR, "pthread_create routing: %s", strerror(err));
651 		exit(EXIT_FAILURE);
652 	} else {
653 		dprintf("routing thread: %d", routing);
654 	}
655 
656 	if (err = pthread_create(&scan, NULL, periodic_wireless_scan, NULL)) {
657 		syslog(LOG_ERR, "pthread_create wireless scan: %s",
658 		    strerror(err));
659 		exit(EXIT_FAILURE);
660 	} else {
661 		dprintf("scan thread: %d", scan);
662 	}
663 
664 	walk_interface(start_if_info_collect, &check_cache);
665 
666 	return (B_TRUE);
667 }
668