xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/events.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * This file contains routines to retrieve events from the system and package
29  * them for high level processing.
30  *
31  * struct np_event is the basic event structure.  The np_event structure and
32  * its npe_name member are allocated using malloc(3c).  free_event() frees both
33  * the npe_name member and the associated np_event structure.
34  *
35  * np_queue_add_event() and np_queue_get_event() provide functionality for
36  * adding events to a queue and blocking on that queue for an event.
37  *
38  * Functions of the form addevent_*() provide the mechanism to cook down a
39  * higher level event into an np_event and put it on the queue.
40  *
41  * hotplug_handler() is called for EC_DEV_ADD and EC_DEV_REMOVE hotplug events
42  * of class ESC_NETWORK - i.e. hotplug insertion/removal of network card -
43  * and plumbs/unplumbs the interface, adding/removing it from running
44  * configuration (the interface and llp lists).
45  *
46  * routing_events() reads routing messages off of an IPv4 routing socket and
47  * by calling addevent_*() functions places appropriate events on the queue.
48  *
49  * start_event_collection() creates a thread to run routing_events() and one
50  * to run periodic_wireless_scan() in.  Finally it does an initial collection
51  * of information from each interface currently known.
52  */
53 
54 #include <arpa/inet.h>
55 #include <errno.h>
56 #include <libsysevent.h>
57 #include <sys/sysevent/eventdefs.h>
58 #include <sys/sysevent/dev.h>
59 #include <libnvpair.h>
60 #include <net/if.h>
61 #include <net/route.h>
62 #include <pthread.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <sys/fcntl.h>
66 #include <syslog.h>
67 #include <unistd.h>
68 
69 #include "defines.h"
70 #include "structures.h"
71 #include "functions.h"
72 #include "variables.h"
73 
74 struct np_event *equeue;
75 static struct np_event *equeue_end;
76 
77 pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
78 pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
79 pthread_t routing, scan;
80 
81 static sysevent_handle_t *sysevent_handle;
82 
83 static void hotplug_handler(sysevent_t *ev);
84 static void printaddrs(int mask, void *address);
85 static char *printaddr(void **address);
86 static void *getaddr(int addrid, int mask, void *address);
87 
88 union rtm_buf
89 {
90 	/* Routing information. */
91 	struct
92 	{
93 		struct rt_msghdr rtm;
94 		struct sockaddr_storage addr[RTAX_MAX];
95 	} r;
96 
97 	/* Interface information. */
98 	struct
99 	{
100 		struct if_msghdr ifm;
101 		struct sockaddr_storage addr[RTAX_MAX];
102 	} im;
103 
104 	/* Interface address information. */
105 	struct
106 	{
107 		struct ifa_msghdr ifa;
108 		struct sockaddr_storage addr[RTAX_MAX];
109 	} ia;
110 };
111 
112 void
113 free_event(struct np_event *npe)
114 {
115 	free(npe);
116 }
117 
118 boolean_t
119 np_queue_add_event(enum np_event_type evt, const char *ifname)
120 {
121 	struct np_event *npe;
122 	size_t slen;
123 
124 	slen = ifname == NULL ? 0 : (strlen(ifname) + 1);
125 	if ((npe = calloc(1, sizeof (*npe) + slen)) == NULL) {
126 		syslog(LOG_ERR, "event %s alloc for %s failed",
127 		    npe_type_str(evt), STRING(ifname));
128 		return (B_FALSE);
129 	}
130 	if (ifname != NULL)
131 		npe->npe_name = strcpy((char *)(npe + 1), ifname);
132 	npe->npe_type = evt;
133 
134 	(void) pthread_mutex_lock(&queue_mutex);
135 	dprintf("adding event type %s name %s to queue",
136 	    npe_type_str(evt), STRING(ifname));
137 	if (equeue_end != NULL) {
138 		equeue_end->npe_next = npe;
139 		equeue_end = npe;
140 	} else {
141 		equeue = equeue_end = npe;
142 	}
143 	equeue_end->npe_next = NULL;
144 	(void) pthread_cond_signal(&queue_cond);
145 	(void) pthread_mutex_unlock(&queue_mutex);
146 	return (B_TRUE);
147 }
148 
149 /*
150  * Blocking getevent.  This routine will block until there is an event for
151  * it to return.
152  */
153 struct np_event *
154 np_queue_get_event(void)
155 {
156 	struct np_event *rv = NULL;
157 
158 	(void) pthread_mutex_lock(&queue_mutex);
159 
160 	while (equeue == NULL)
161 		(void) pthread_cond_wait(&queue_cond, &queue_mutex);
162 
163 	rv = equeue;
164 	equeue = equeue->npe_next;
165 	if (equeue == NULL)
166 		equeue_end = NULL;
167 
168 	(void) pthread_mutex_unlock(&queue_mutex);
169 
170 	rv->npe_next = NULL;
171 	return (rv);
172 }
173 
174 const char *
175 npe_type_str(enum np_event_type type)
176 {
177 	switch (type) {
178 	case EV_LINKDROP:
179 		return ("LINKDROP");
180 	case EV_LINKUP:
181 		return ("LINKUP");
182 	case EV_LINKFADE:
183 		return ("LINKFADE");
184 	case EV_LINKDISC:
185 		return ("LINKDISC");
186 	case EV_NEWAP:
187 		return ("NEWAP");
188 	case EV_USER:
189 		return ("USER");
190 	case EV_TIMER:
191 		return ("TIMER");
192 	case EV_SHUTDOWN:
193 		return ("SHUTDOWN");
194 	case EV_NEWADDR:
195 		return ("NEWADDR");
196 	case EV_RESELECT:
197 		return ("RESELECT");
198 	case EV_DOOR_TIME:
199 		return ("DOOR_TIME");
200 	case EV_ADDIF:
201 		return ("ADDIF");
202 	case EV_REMIF:
203 		return ("REMIF");
204 	case EV_TAKEDOWN:
205 		return ("TAKEDOWN");
206 	default:
207 		return ("unknown");
208 	}
209 }
210 
211 static const char *
212 rtmtype_str(int type)
213 {
214 	static char typestr[12]; /* strlen("type ") + enough for an int */
215 
216 	switch (type) {
217 		case RTM_ADD:
218 			return ("ADD");
219 		case RTM_DELETE:
220 			return ("DELETE");
221 		case RTM_NEWADDR:
222 			return ("NEWADDR");
223 		case RTM_DELADDR:
224 			return ("DELADDR");
225 		case RTM_IFINFO:
226 			return ("IFINFO");
227 		default:
228 			(void) snprintf(typestr, sizeof (typestr), "type %d",
229 			    type);
230 			return (typestr);
231 	}
232 }
233 
234 /*
235  * At present, we only handle EC_DEV_ADD/EC_DEV_REMOVE sysevents of
236  * subclass ESC_NETWORK.  These signify hotplug addition/removal.
237  *
238  * The sysevents are converted into NWAM events so that we can process them in
239  * the main loop.  If we didn't do this, we'd either have bad pointer
240  * references or need to have reference counts on everything.  Serializing
241  * through the event mechanism is much simpler.
242  */
243 static void
244 hotplug_handler(sysevent_t *ev)
245 {
246 	int32_t instance;
247 	char *driver;
248 	char ifname[LIFNAMSIZ];
249 	nvlist_t *attr_list;
250 	char *event_class = sysevent_get_class_name(ev);
251 	char *event_subclass = sysevent_get_subclass_name(ev);
252 	int retv;
253 
254 	dprintf("hotplug_handler: event %s/%s", event_class,
255 	    event_subclass);
256 
257 	/* Make sure sysevent is of expected class/subclass */
258 	if ((strcmp(event_class, EC_DEV_ADD) != 0 &&
259 	    strcmp(event_class, EC_DEV_REMOVE) != 0) ||
260 	    strcmp(event_subclass, ESC_NETWORK) != 0) {
261 		syslog(LOG_ERR, "hotplug_handler: unexpected sysevent "
262 		    "class/subclass %s/%s", event_class, event_subclass);
263 		return;
264 	}
265 
266 	/*
267 	 * Retrieve driver name and instance attributes, and combine to
268 	 * get interface name.
269 	 */
270 	if (sysevent_get_attr_list(ev, &attr_list) != 0) {
271 		syslog(LOG_ERR, "hotplug_handler: sysevent_get_attr_list: %m");
272 		return;
273 	}
274 	retv = nvlist_lookup_string(attr_list, DEV_DRIVER_NAME, &driver);
275 	if (retv == 0)
276 		retv = nvlist_lookup_int32(attr_list, DEV_INSTANCE, &instance);
277 	if (retv != 0) {
278 		syslog(LOG_ERR, "handle_hotplug_interface: nvlist_lookup "
279 		    "of attributes failed: %s", strerror(retv));
280 	} else {
281 		(void) snprintf(ifname, LIFNAMSIZ, "%s%d", driver, instance);
282 		(void) np_queue_add_event(strcmp(event_class, EC_DEV_ADD) == 0 ?
283 		    EV_ADDIF : EV_REMIF, ifname);
284 	}
285 	nvlist_free(attr_list);
286 }
287 
288 static void
289 hotplug_events_unregister(void)
290 {
291 	/* Unsubscribe to sysevents */
292 	sysevent_unbind_handle(sysevent_handle);
293 	sysevent_handle = NULL;
294 }
295 
296 static void
297 hotplug_events_register(void)
298 {
299 	const char *subclass = ESC_NETWORK;
300 
301 	sysevent_handle = sysevent_bind_handle(hotplug_handler);
302 	if (sysevent_handle == NULL) {
303 		syslog(LOG_ERR, "sysevent_bind_handle: %s", strerror(errno));
304 		return;
305 	}
306 	/*
307 	 * Subscribe to ESC_NETWORK subclass of EC_DEV_ADD and EC_DEV_REMOVE
308 	 * events.  As a result,  we get sysevent notification of hotplug
309 	 * add/remove events,  which we handle above in hotplug_event_handler().
310 	 */
311 	if (sysevent_subscribe_event(sysevent_handle, EC_DEV_ADD, &subclass, 1)
312 	    != 0 || sysevent_subscribe_event(sysevent_handle, EC_DEV_REMOVE,
313 	    &subclass, 1) != 0) {
314 		syslog(LOG_ERR, "sysevent_subscribe_event: %s",
315 		    strerror(errno));
316 		hotplug_events_unregister();
317 	}
318 }
319 
320 /*
321  * This thread reads routing socket events and sends them to the main state
322  * machine.  We must be careful with access to interface data structures here,
323  * as we're not the main thread, which may delete things.  Holding a pointer is
324  * not allowed.
325  */
326 /* ARGSUSED */
327 static void *
328 routing_events(void *arg)
329 {
330 	int rtsock;
331 	int n;
332 	union rtm_buf buffer;
333 	struct rt_msghdr *rtm;
334 	struct ifa_msghdr *ifa;
335 	struct if_msghdr *ifm;
336 
337 	/*
338 	 * We use v4 interfaces as proxies for links so those are the only
339 	 * routing messages we need to listen to.  Look at the comments in
340 	 * structures.h for more information about the split between the
341 	 * llp and interfaces.
342 	 */
343 	rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET);
344 	if (rtsock == -1) {
345 		syslog(LOG_ERR, "failed to open routing socket: %m");
346 		exit(EXIT_FAILURE);
347 	}
348 
349 	dprintf("routing socket %d", rtsock);
350 
351 	for (;;) {
352 		char *addrs;
353 		struct sockaddr_dl *addr_dl;
354 		struct sockaddr_in *addr_in;
355 
356 		rtm = &buffer.r.rtm;
357 		n = read(rtsock, &buffer, sizeof (buffer));
358 		if (n == -1 && errno == EAGAIN) {
359 			continue;
360 		} else if (n == -1) {
361 			syslog(LOG_ERR, "error reading routing socket "
362 			    "%d: %m", rtsock);
363 			/* Low likelihood.  What's recovery path?  */
364 			continue;
365 		}
366 
367 		if (rtm->rtm_msglen < n) {
368 			syslog(LOG_ERR, "only read %d bytes from "
369 			    "routing socket but message claims to be "
370 			    "of length %d", rtm->rtm_msglen);
371 			continue;
372 		}
373 
374 		if (rtm->rtm_version != RTM_VERSION) {
375 			syslog(LOG_ERR, "tossing routing message of "
376 			    "version %d type %d", rtm->rtm_version,
377 			    rtm->rtm_type);
378 			continue;
379 		}
380 
381 		if (rtm->rtm_msglen != n) {
382 			dprintf("routing message of %d size came from "
383 			    "read of %d on socket %d", rtm->rtm_msglen,
384 			    n, rtsock);
385 		}
386 
387 		switch (rtm->rtm_type) {
388 		case RTM_DELADDR: {
389 			uint64_t ifflags;
390 
391 			/*
392 			 * Check for failure due to CR 6745448: if we get a
393 			 * report that an address has been deleted, then check
394 			 * for interface up, datalink down, and actual address
395 			 * non-zero.  If that combination is seen, then this is
396 			 * a DHCP cached lease, and we need to remove it from
397 			 * the system, or it'll louse up the kernel routes
398 			 * (which aren't smart enough to avoid dead
399 			 * interfaces).
400 			 */
401 			ifa = (void *)rtm;
402 			addrs = (char *)ifa + sizeof (*ifa);
403 
404 			dprintf("routing message DELADDR: index %d flags %x",
405 			    ifa->ifam_index, ifa->ifam_flags);
406 			printaddrs(ifa->ifam_addrs, addrs);
407 
408 			if (ifa->ifam_index == 0) {
409 				/* what is this? */
410 				dprintf("tossing index 0 routing event");
411 				break;
412 			}
413 
414 			addr_in = getaddr(RTA_IFA, ifa->ifam_addrs, addrs);
415 			if (addr_in == NULL) {
416 				dprintf("no RTA_IFA in RTM_DELADDR message");
417 				break;
418 			}
419 
420 			addr_dl = getaddr(RTA_IFP, ifa->ifam_addrs, addrs);
421 			if (addr_dl == NULL) {
422 				dprintf("no RTA_IFP in RTM_DELADDR message");
423 				break;
424 			}
425 
426 			addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
427 
428 			if (addr_in->sin_addr.s_addr == INADDR_ANY) {
429 				ifflags = get_ifflags(addr_dl->sdl_data,
430 				    AF_INET);
431 				if ((ifflags & IFF_UP) &&
432 				    !(ifflags & IFF_RUNNING))
433 					zero_out_v4addr(addr_dl->sdl_data);
434 			}
435 			break;
436 		}
437 
438 		case RTM_NEWADDR:
439 			ifa = (void *)rtm;
440 			addrs = (char *)ifa + sizeof (*ifa);
441 
442 			dprintf("routing message NEWADDR: index %d flags %x",
443 			    ifa->ifam_index, ifa->ifam_flags);
444 			printaddrs(ifa->ifam_addrs, addrs);
445 
446 			if (ifa->ifam_index == 0) {
447 				/* what is this? */
448 				dprintf("tossing index 0 routing event");
449 				break;
450 			}
451 
452 			addr_in = getaddr(RTA_IFA, ifa->ifam_addrs, addrs);
453 			if (addr_in == NULL) {
454 				dprintf("no RTA_IFA in RTM_NEWADDR message");
455 				break;
456 			}
457 
458 			addr_dl = getaddr(RTA_IFP, ifa->ifam_addrs, addrs);
459 			if (addr_dl == NULL) {
460 				dprintf("no RTA_IFP in RTM_NEWADDR message");
461 				break;
462 			}
463 
464 			/*
465 			 * We don't use the lladdr in this structure so we can
466 			 * run over it.
467 			 */
468 			addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
469 
470 			update_interface_v4_address(addr_dl->sdl_data,
471 			    addr_in->sin_addr.s_addr);
472 			break;
473 
474 		case RTM_IFINFO:
475 			ifm = (void *)rtm;
476 			addrs = (char *)ifm + sizeof (*ifm);
477 			dprintf("routing message IFINFO: index %d flags %x",
478 			    ifm->ifm_index, ifm->ifm_flags);
479 			printaddrs(ifm->ifm_addrs, addrs);
480 
481 			if (ifm->ifm_index == 0) {
482 				dprintf("tossing index 0 routing event");
483 				break;
484 			}
485 
486 			addr_dl = getaddr(RTA_IFP, ifm->ifm_addrs, addrs);
487 			if (addr_dl == NULL) {
488 				dprintf("no RTA_IFP in RTM_IFINFO message");
489 				break;
490 			}
491 
492 			/*
493 			 * We don't use the lladdr in this structure so we can
494 			 * run over it.
495 			 */
496 			addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
497 
498 			update_interface_flags(addr_dl->sdl_data,
499 			    ifm->ifm_flags);
500 			break;
501 
502 		default:
503 			dprintf("routing message %s socket %d discarded",
504 			    rtmtype_str(rtm->rtm_type), rtsock);
505 			break;
506 		}
507 	}
508 	/* NOTREACHED */
509 	return (NULL);
510 }
511 
512 static char *
513 printaddr(void **address)
514 {
515 	static char buffer[80];
516 	sa_family_t family = *(sa_family_t *)*address;
517 	struct sockaddr_in *s4 = *address;
518 	struct sockaddr_in6 *s6 = *address;
519 	struct sockaddr_dl *dl = *address;
520 
521 	switch (family) {
522 	case AF_UNSPEC:
523 		(void) inet_ntop(AF_UNSPEC, &s4->sin_addr, buffer,
524 		    sizeof (buffer));
525 		*address = (char *)*address + sizeof (*s4);
526 		break;
527 	case AF_INET:
528 		(void) inet_ntop(AF_INET, &s4->sin_addr, buffer,
529 		    sizeof (buffer));
530 		*address = (char *)*address + sizeof (*s4);
531 		break;
532 	case AF_INET6:
533 		(void) inet_ntop(AF_INET6, &s6->sin6_addr, buffer,
534 		    sizeof (buffer));
535 		*address = (char *)*address + sizeof (*s6);
536 		break;
537 	case AF_LINK:
538 		(void) snprintf(buffer, sizeof (buffer), "link %.*s",
539 		    dl->sdl_nlen, dl->sdl_data);
540 		*address = (char *)*address + sizeof (*dl);
541 		break;
542 	default:
543 		/*
544 		 * We can't reliably update the size of this thing
545 		 * because we don't know what its type is.  So bump
546 		 * it by a sockaddr_in and see what happens.  The
547 		 * caller should really make sure this never happens.
548 		 */
549 		*address = (char *)*address + sizeof (*s4);
550 		(void) snprintf(buffer, sizeof (buffer),
551 		    "unknown address family %d", family);
552 		break;
553 	}
554 	return (buffer);
555 }
556 
557 static void
558 printaddrs(int mask, void *address)
559 {
560 	if (mask == 0)
561 		return;
562 	if (mask & RTA_DST)
563 		dprintf("destination address: %s", printaddr(&address));
564 	if (mask & RTA_GATEWAY)
565 		dprintf("gateway address: %s", printaddr(&address));
566 	if (mask & RTA_NETMASK)
567 		dprintf("netmask: %s", printaddr(&address));
568 	if (mask & RTA_GENMASK)
569 		dprintf("cloning mask: %s", printaddr(&address));
570 	if (mask & RTA_IFP)
571 		dprintf("interface name: %s", printaddr(&address));
572 	if (mask & RTA_IFA)
573 		dprintf("interface address: %s", printaddr(&address));
574 	if (mask & RTA_AUTHOR)
575 		dprintf("author: %s", printaddr(&address));
576 	if (mask & RTA_BRD)
577 		dprintf("broadcast address: %s", printaddr(&address));
578 }
579 
580 static void
581 nextaddr(void **address)
582 {
583 	sa_family_t family = *(sa_family_t *)*address;
584 
585 	switch (family) {
586 	case AF_UNSPEC:
587 	case AF_INET:
588 		*address = (char *)*address + sizeof (struct sockaddr_in);
589 		break;
590 	case AF_INET6:
591 		*address = (char *)*address + sizeof (struct sockaddr_in6);
592 		break;
593 	case AF_LINK:
594 		*address = (char *)*address + sizeof (struct sockaddr_dl);
595 		break;
596 	default:
597 		syslog(LOG_ERR, "unknown af (%d) while parsing rtm", family);
598 		break;
599 	}
600 }
601 
602 static void *
603 getaddr(int addrid, int mask, void *address)
604 {
605 	int i;
606 	void *p = address;
607 
608 	if ((mask & addrid) == 0)
609 		return (NULL);
610 
611 	for (i = 1; i < addrid; i <<= 1) {
612 		if (i & mask)
613 			nextaddr(&p);
614 	}
615 	return (p);
616 }
617 
618 boolean_t
619 start_event_collection(void)
620 {
621 	int err;
622 
623 	/*
624 	 * if these are ever created/destroyed repetitively then we will
625 	 * have to change this.
626 	 */
627 
628 	if (err = pthread_create(&routing, NULL, routing_events, NULL)) {
629 		syslog(LOG_ERR, "pthread_create routing: %s", strerror(err));
630 		exit(EXIT_FAILURE);
631 	} else {
632 		dprintf("routing thread: %d", routing);
633 	}
634 
635 	if (err = pthread_create(&scan, NULL, periodic_wireless_scan, NULL)) {
636 		syslog(LOG_ERR, "pthread_create wireless scan: %s",
637 		    strerror(err));
638 		exit(EXIT_FAILURE);
639 	} else {
640 		dprintf("scan thread: %d", scan);
641 	}
642 
643 	/*
644 	 * This function registers a callback which will get a dedicated thread
645 	 * for handling of hotplug sysevents when they occur.
646 	 */
647 	hotplug_events_register();
648 
649 	dprintf("initial interface scan");
650 	walk_interface(start_if_info_collect, "check");
651 
652 	return (B_TRUE);
653 }
654