events.c revision 3938:670947f6c3f6
1251881Speter/*
2251881Speter * CDDL HEADER START
3251881Speter *
4251881Speter * The contents of this file are subject to the terms of the
5251881Speter * Common Development and Distribution License (the "License").
6251881Speter * You may not use this file except in compliance with the License.
7251881Speter *
8251881Speter * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9251881Speter * or http://www.opensolaris.org/os/licensing.
10251881Speter * See the License for the specific language governing permissions
11251881Speter * and limitations under the License.
12251881Speter *
13251881Speter * When distributing Covered Code, include this CDDL HEADER in each
14251881Speter * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15251881Speter * If applicable, add the following below this CDDL HEADER, with the
16251881Speter * fields enclosed by brackets "[]" replaced with your own identifying
17251881Speter * information: Portions Copyright [yyyy] [name of copyright owner]
18251881Speter *
19251881Speter * CDDL HEADER END
20251881Speter */
21251881Speter
22251881Speter/*
23251881Speter * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24251881Speter * Use is subject to license terms.
25251881Speter */
26251881Speter
27251881Speter#pragma ident	"%Z%%M%	%I%	%E% SMI"
28251881Speter
29251881Speter/*
30251881Speter * This file contains routines to retrieve events from the system and package
31251881Speter * them for high level processing.
32251881Speter *
33251881Speter * struct np_event is the basic event structure.  The np_event structure and
34251881Speter * its npe_name member are allocated using malloc(3c).  free_event() frees both
35251881Speter * the npe_name member and the associated np_event structure.
36251881Speter *
37251881Speter * np_queue_add_event() and np_queue_get_event() provide functionality for
38251881Speter * adding events to a queue and blocking on that queue for an event.
39251881Speter *
40251881Speter * Functions of the form addevent_*() provide the mechanism to cook down a
41251881Speter * higher level event into an np_event and put it on the queue.
42251881Speter *
43251881Speter * routing_events() reads routing messages off of an IPv4 routing socket and
44251881Speter * 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
71struct np_event *equeue = NULL;
72static struct np_event *equeue_end = NULL;
73
74pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
75pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
76pthread_t routing, scan;
77
78static void printaddrs(int mask, void *address);
79static char *printaddr(void **address);
80static struct sockaddr *dupsockaddr(struct sockaddr *);
81static boolean_t cmpsockaddr(struct sockaddr *, struct sockaddr *);
82static void *getaddr(int addrid, int mask, void *address);
83
84union 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
108void
109free_event(struct np_event *e)
110{
111	free(e->npe_name);
112	free(e);
113}
114
115void
116np_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 */
134struct np_event *
135np_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
155const char *
156npe_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
174static void
175addevent_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
214static void
215addevent_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
255static const char *
256rtmtype_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 */
279static void *
280routing_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 */
454static boolean_t
455cmpsockaddr(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 */
488static struct sockaddr *
489dupsockaddr(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
522static char *
523printaddr(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
567static void
568printaddrs(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
590static void
591nextaddr(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
612static void *
613getaddr(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
628boolean_t
629start_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