Home | History | Annotate | Download | only in nwamd
      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 (wlan_scan_interval != 0) {
    636 		err = pthread_create(&scan, NULL, periodic_wireless_scan, NULL);
    637 		if (err != 0) {
    638 			syslog(LOG_ERR, "pthread_create wireless scan: %s",
    639 			    strerror(err));
    640 			exit(EXIT_FAILURE);
    641 		} else {
    642 			dprintf("wireless scan thread: %d", scan);
    643 		}
    644 	} else {
    645 		dprintf("periodic wireless scan disabled");
    646 	}
    647 
    648 	/*
    649 	 * This function registers a callback which will get a dedicated thread
    650 	 * for handling of hotplug sysevents when they occur.
    651 	 */
    652 	hotplug_events_register();
    653 
    654 	dprintf("initial interface scan");
    655 	walk_interface(start_if_info_collect, "check");
    656 
    657 	return (B_TRUE);
    658 }
    659