xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/events.c (revision 924db11bd45e384ed350190cc9fc0f9ea85e21dd)
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 void *getaddr(int addrid, int mask, void *address);
81 
82 union rtm_buf
83 {
84 	/* Routing information. */
85 	struct
86 	{
87 		struct rt_msghdr rtm;
88 		struct sockaddr_storage addr[RTAX_MAX];
89 	} r;
90 
91 	/* Interface information. */
92 	struct
93 	{
94 		struct if_msghdr ifm;
95 		struct sockaddr_storage addr[RTAX_MAX];
96 	} im;
97 
98 	/* Interface address information. */
99 	struct
100 	{
101 		struct ifa_msghdr ifa;
102 		struct sockaddr_storage addr[RTAX_MAX];
103 	} ia;
104 };
105 
106 void
107 free_event(struct np_event *e)
108 {
109 	free(e->npe_name);
110 	free(e);
111 }
112 
113 void
114 np_queue_add_event(struct np_event *e)
115 {
116 	(void) pthread_mutex_lock(&queue_mutex);
117 	if (equeue_end != NULL) {
118 		equeue_end->npe_next = e;
119 		equeue_end = e;
120 	} else {
121 		equeue = equeue_end = e;
122 	}
123 	equeue_end->npe_next = NULL;
124 	(void) pthread_cond_signal(&queue_cond);
125 	(void) pthread_mutex_unlock(&queue_mutex);
126 }
127 
128 /*
129  * Blocking getevent.  This routine will block until there is an event for
130  * it to return.
131  */
132 struct np_event *
133 np_queue_get_event(void)
134 {
135 	struct np_event *rv = NULL;
136 
137 	(void) pthread_mutex_lock(&queue_mutex);
138 
139 	while (equeue == NULL)
140 		(void) pthread_cond_wait(&queue_cond, &queue_mutex);
141 
142 	rv = equeue;
143 	equeue = equeue->npe_next;
144 	if (equeue == NULL)
145 		equeue_end = NULL;
146 
147 	(void) pthread_mutex_unlock(&queue_mutex);
148 
149 	rv->npe_next = NULL;
150 	return (rv);
151 }
152 
153 const char *
154 npe_type_str(enum np_event_type type)
155 {
156 	switch (type) {
157 		case EV_ROUTING:
158 			return ("ROUTING");
159 		case EV_SYS:
160 			return ("SYS");
161 		case EV_TIMER:
162 			return ("TIMER");
163 		case EV_SHUTDOWN:
164 			return ("SHUTDOWN");
165 		case EV_NEWADDR:
166 			return ("NEWADDR");
167 		default:
168 			return ("unknown");
169 	}
170 }
171 
172 static void
173 addevent_routing_ifa(struct ifa_msghdr *ifa, const char *name)
174 {
175 	struct np_event *e;
176 
177 	dprintf("addevent_routing_ifa");
178 	if (ifa->ifam_index == 0) {
179 		/* what is this? */
180 		dprintf("tossing index 0 routing event");
181 		return;
182 	}
183 
184 	e = calloc(1, sizeof (*e));
185 	if (e == NULL) {
186 		syslog(LOG_ERR, "calloc failed");
187 		return;
188 	}
189 
190 	switch (ifa->ifam_type) {
191 	case RTM_NEWADDR:
192 		assert(name != NULL);
193 		e->npe_type = EV_NEWADDR;
194 		if ((e->npe_name = strdup(name)) == NULL) {
195 			syslog(LOG_ERR, "strdup failed");
196 			free(e);
197 			return;
198 		}
199 		dprintf("adding event type %s name %s to queue",
200 		    npe_type_str(e->npe_type), STRING(e->npe_name));
201 		np_queue_add_event(e);
202 		break;
203 
204 	default:
205 		free(e);
206 		dprintf("unhandled type in addevent_routing_ifa %d",
207 		    ifa->ifam_type);
208 		break;
209 	}
210 }
211 
212 static void
213 addevent_routing_msghdr(struct if_msghdr *ifm, const char *name)
214 {
215 	struct np_event *e;
216 
217 	dprintf("addevent_routing_msghdr");
218 	if (ifm->ifm_index == 0) {
219 		/* what is this? */
220 		dprintf("tossing index 0 routing event");
221 		return;
222 	}
223 
224 	switch (ifm->ifm_type) {
225 	case RTM_IFINFO:
226 		assert(name != NULL);
227 		e = calloc(1, sizeof (*e));
228 		if (e == NULL) {
229 			syslog(LOG_ERR, "calloc failed");
230 			return;
231 		}
232 
233 		e->npe_type = EV_ROUTING;
234 		if ((e->npe_name = strdup(name)) == NULL) {
235 			syslog(LOG_ERR, "strdup failed");
236 			free(e);
237 			return;
238 		}
239 		dprintf("flags = %x, IFF_RUNNING = %x", ifm->ifm_flags,
240 		    IFF_RUNNING);
241 		dprintf("adding event type %s name %s to queue",
242 		    npe_type_str(e->npe_type), STRING(e->npe_name));
243 		np_queue_add_event(e);
244 		break;
245 
246 	default:
247 		dprintf("unhandled type in addevent_routing_msghdr %d",
248 		    ifm->ifm_type);
249 		break;
250 	}
251 }
252 
253 static const char *
254 rtmtype_str(int type)
255 {
256 	static char typestr[12]; /* strlen("type ") + enough for an int */
257 
258 	switch (type) {
259 		case RTM_ADD:
260 			return ("ADD");
261 		case RTM_DELETE:
262 			return ("DELETE");
263 		case RTM_NEWADDR:
264 			return ("NEWADDR");
265 		case RTM_DELADDR:
266 			return ("DELADDR");
267 		case RTM_IFINFO:
268 			return ("IFINFO");
269 		default:
270 			(void) snprintf(typestr, sizeof (typestr), "type %d",
271 			    type);
272 			return (typestr);
273 	}
274 }
275 
276 /* ARGSUSED */
277 static void *
278 routing_events(void *arg)
279 {
280 	int rtsock;
281 	int n;
282 	union rtm_buf buffer;
283 	struct rt_msghdr *rtm;
284 	struct ifa_msghdr *ifa;
285 	struct if_msghdr *ifm;
286 
287 	/*
288 	 * We use v4 interfaces as proxies for links so those are the only
289 	 * routing messages we need to listen to.  Look at the comments in
290 	 * structures.h for more information about the split between the
291 	 * llp and interfaces.
292 	 */
293 	rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET);
294 	if (rtsock == -1) {
295 		syslog(LOG_ERR, "failed to open routing socket: %m");
296 		exit(EXIT_FAILURE);
297 	}
298 
299 	dprintf("routing socket %d", rtsock);
300 
301 	for (;;) {
302 		struct interface *ifp;
303 		char *addrs, *if_name;
304 		struct sockaddr_dl *addr_dl;
305 		struct sockaddr *addr;
306 
307 		rtm = &buffer.r.rtm;
308 		n = read(rtsock, &buffer, sizeof (buffer));
309 		if (n == -1 && errno == EAGAIN) {
310 			continue;
311 		} else if (n == -1) {
312 			syslog(LOG_ERR, "error reading routing socket "
313 			    "%d: %m", rtsock);
314 			/* Low likelihood.  What's recovery path?  */
315 			continue;
316 		}
317 
318 		if (rtm->rtm_msglen < n) {
319 			syslog(LOG_ERR, "only read %d bytes from "
320 			    "routing socket but message claims to be "
321 			    "of length %d", rtm->rtm_msglen);
322 			continue;
323 		}
324 
325 		if (rtm->rtm_version != RTM_VERSION) {
326 			syslog(LOG_ERR, "tossing routing message of "
327 			    "version %d type %d", rtm->rtm_version,
328 			    rtm->rtm_type);
329 			continue;
330 		}
331 
332 		if (rtm->rtm_msglen != n) {
333 			dprintf("routing message of %d size came from "
334 			    "read of %d on socket %d", rtm->rtm_msglen,
335 			    n, rtsock);
336 		}
337 
338 		switch (rtm->rtm_type) {
339 		case RTM_NEWADDR:
340 			ifa = (void *)rtm;
341 			addrs = (char *)ifa + sizeof (*ifa);
342 
343 			dprintf("routing message NEWADDR: index %d flags %x",
344 			    ifa->ifam_index, ifa->ifam_flags);
345 			printaddrs(ifa->ifam_addrs, addrs);
346 
347 			if ((addr = (struct sockaddr *)getaddr(RTA_IFA,
348 			    ifa->ifam_addrs, addrs)) == NULL)
349 				break;
350 
351 			if ((addr_dl = (struct sockaddr_dl *)getaddr
352 			    (RTA_IFP, ifa->ifam_addrs, addrs)) == NULL)
353 				break;
354 			/*
355 			 * We don't use the lladdr in this structure so we can
356 			 * run over it.
357 			 */
358 			addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
359 			if_name = addr_dl->sdl_data;
360 			ifp = get_interface(if_name);
361 			if (ifp == NULL) {
362 				dprintf("no interface struct for %s; ignoring "
363 				    "message", STRING(if_name));
364 				break;
365 			}
366 
367 			/* if no cached address, cache it */
368 			if (ifp->if_ipaddr == NULL) {
369 				ifp->if_ipaddr = dupsockaddr(addr);
370 				dprintf("cached address %s for link %s",
371 				    printaddr((void **)&addr), if_name);
372 				addevent_routing_ifa(ifa, if_name);
373 			} else if (!cmpsockaddr(addr, ifp->if_ipaddr)) {
374 				free(ifp->if_ipaddr);
375 				ifp->if_ipaddr = dupsockaddr(addr);
376 				addevent_routing_ifa(ifa, if_name);
377 			}
378 			break;
379 		case RTM_IFINFO:
380 		{
381 			boolean_t plugged_in;
382 
383 			ifm = (void *)rtm;
384 			addrs = (char *)ifm + sizeof (*ifm);
385 			dprintf("routing message IFINFO: index %d flags %x",
386 			    ifm->ifm_index, ifm->ifm_flags);
387 			printaddrs(ifm->ifm_addrs, addrs);
388 
389 			if ((addr_dl = (struct sockaddr_dl *)getaddr(RTA_IFP,
390 			    ifm->ifm_addrs, addrs)) == NULL)
391 				break;
392 			/*
393 			 * We don't use the lladdr in this structure so we can
394 			 * run over it.
395 			 */
396 			addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
397 			if_name = addr_dl->sdl_data;
398 			ifp = get_interface(if_name);
399 			if (ifp == NULL) {
400 				dprintf("no interface struct for %s; ignoring "
401 				    "message", STRING(if_name));
402 				break;
403 			}
404 
405 			/*
406 			 * Check for toggling of the IFF_RUNNING flag.
407 			 *
408 			 * On any change in the flag value, we turn off the
409 			 * DHCP flags; the change in the RUNNING state
410 			 * indicates a "fresh start" for the interface, so we
411 			 * should try dhcp again.
412 			 *
413 			 * Ignore specific IFF_RUNNING changes for
414 			 * wireless interfaces; their semantics are
415 			 * a bit different (either the flag is always
416 			 * on, or, with newer drivers, it indicates
417 			 * whether or not they are connected to an AP).
418 			 *
419 			 * For wired interfaces, if the interface was
420 			 * not plugged in and now it is, start info
421 			 * collection.
422 			 *
423 			 * If it was plugged in and now it is
424 			 * unplugged, generate an event.
425 			 *
426 			 * XXX We probably need a lock to protect
427 			 * if_flags setting and getting.
428 			 */
429 			if ((ifp->if_flags & IFF_RUNNING) !=
430 			    (ifm->ifm_flags & IFF_RUNNING)) {
431 				ifp->if_lflags &= ~IF_DHCPFLAGS;
432 			}
433 			if (ifp->if_type == IF_WIRELESS)
434 				break;
435 			plugged_in = ((ifp->if_flags & IFF_RUNNING) != 0);
436 			ifp->if_flags = ifm->ifm_flags;
437 			if (!plugged_in &&
438 			    (ifm->ifm_flags & IFF_RUNNING)) {
439 				start_if_info_collect(ifp, NULL);
440 			} else if (plugged_in &&
441 			    !(ifm->ifm_flags & IFF_RUNNING)) {
442 				check_drop_dhcp(ifp);
443 				addevent_routing_msghdr(ifm, if_name);
444 			}
445 			break;
446 		}
447 		default:
448 			dprintf("routing message %s socket %d discarded",
449 			    rtmtype_str(rtm->rtm_type), rtsock);
450 			break;
451 		}
452 	}
453 	/* NOTREACHED */
454 	return (NULL);
455 }
456 
457 static char *
458 printaddr(void **address)
459 {
460 	static char buffer[80];
461 	sa_family_t family = *(sa_family_t *)*address;
462 	struct sockaddr_in *s4 = *address;
463 	struct sockaddr_in6 *s6 = *address;
464 	struct sockaddr_dl *dl = *address;
465 
466 	switch (family) {
467 	case AF_UNSPEC:
468 		(void) inet_ntop(AF_UNSPEC, &s4->sin_addr, buffer,
469 		    sizeof (buffer));
470 		*address = (char *)*address + sizeof (*s4);
471 		break;
472 	case AF_INET:
473 		(void) inet_ntop(AF_INET, &s4->sin_addr, buffer,
474 		    sizeof (buffer));
475 		*address = (char *)*address + sizeof (*s4);
476 		break;
477 	case AF_INET6:
478 		(void) inet_ntop(AF_INET6, &s6->sin6_addr, buffer,
479 		    sizeof (buffer));
480 		*address = (char *)*address + sizeof (*s6);
481 		break;
482 	case AF_LINK:
483 		(void) snprintf(buffer, sizeof (buffer), "link %.*s",
484 		    dl->sdl_nlen, dl->sdl_data);
485 		*address = (char *)*address + sizeof (*dl);
486 		break;
487 	default:
488 		/*
489 		 * We can't reliably update the size of this thing
490 		 * because we don't know what its type is.  So bump
491 		 * it by a sockaddr_in and see what happens.  The
492 		 * caller should really make sure this never happens.
493 		 */
494 		*address = (char *)*address + sizeof (*s4);
495 		(void) snprintf(buffer, sizeof (buffer),
496 		    "unknown address family %d", family);
497 		break;
498 	}
499 	return (buffer);
500 }
501 
502 static void
503 printaddrs(int mask, void *address)
504 {
505 	if (mask == 0)
506 		return;
507 	if (mask & RTA_DST)
508 		dprintf("destination address: %s", printaddr(&address));
509 	if (mask & RTA_GATEWAY)
510 		dprintf("gateway address: %s", printaddr(&address));
511 	if (mask & RTA_NETMASK)
512 		dprintf("netmask: %s", printaddr(&address));
513 	if (mask & RTA_GENMASK)
514 		dprintf("cloning mask: %s", printaddr(&address));
515 	if (mask & RTA_IFP)
516 		dprintf("interface name: %s", printaddr(&address));
517 	if (mask & RTA_IFA)
518 		dprintf("interface address: %s", printaddr(&address));
519 	if (mask & RTA_AUTHOR)
520 		dprintf("author: %s", printaddr(&address));
521 	if (mask & RTA_BRD)
522 		dprintf("broadcast address: %s", printaddr(&address));
523 }
524 
525 static void
526 nextaddr(void **address)
527 {
528 	sa_family_t family = *(sa_family_t *)*address;
529 
530 	switch (family) {
531 	case AF_UNSPEC:
532 	case AF_INET:
533 		*address = (char *)*address + sizeof (struct sockaddr_in);
534 		break;
535 	case AF_INET6:
536 		*address = (char *)*address + sizeof (struct sockaddr_in6);
537 		break;
538 	case AF_LINK:
539 		*address = (char *)*address + sizeof (struct sockaddr_dl);
540 		break;
541 	default:
542 		syslog(LOG_ERR, "unknown af (%d) while parsing rtm", family);
543 		break;
544 	}
545 }
546 
547 static void *
548 getaddr(int addrid, int mask, void *address)
549 {
550 	int i;
551 	void *p = address;
552 
553 	if ((mask & addrid) == 0)
554 		return (NULL);
555 
556 	for (i = 1; i < addrid; i <<= 1) {
557 		if (i & mask)
558 			nextaddr(&p);
559 	}
560 	return (p);
561 }
562 
563 boolean_t
564 start_event_collection(void)
565 {
566 	int err;
567 	boolean_t check_cache = B_TRUE;
568 
569 	/*
570 	 * if these are ever created/destroyed repetitively then we will
571 	 * have to change this.
572 	 */
573 
574 	if (err = pthread_create(&routing, NULL, routing_events, NULL)) {
575 		syslog(LOG_ERR, "pthread_create routing: %s", strerror(err));
576 		exit(EXIT_FAILURE);
577 	} else {
578 		dprintf("routing thread: %d", routing);
579 	}
580 
581 	if (err = pthread_create(&scan, NULL, periodic_wireless_scan, NULL)) {
582 		syslog(LOG_ERR, "pthread_create wireless scan: %s",
583 		    strerror(err));
584 		exit(EXIT_FAILURE);
585 	} else {
586 		dprintf("scan thread: %d", scan);
587 	}
588 
589 	walk_interface(start_if_info_collect, &check_cache);
590 
591 	return (B_TRUE);
592 }
593