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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <arpa/inet.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <inet/ip.h>
31 #include <inetcfg.h>
32 #include <libdladm.h>
33 #include <libdllink.h>
34 #include <libdlwlan.h>
35 #include <netdb.h>
36 #include <netinet/in.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/socket.h>
41 #include <sys/types.h>
42 
43 #include <libnwam.h>
44 #include "conditions.h"
45 #include "ncu.h"
46 #include "objects.h"
47 #include "util.h"
48 
49 /*
50  * conditions.c - contains routines which check state to see if activation
51  * conditions for NWAM objects are satisfied and rates activation conditions to
52  * help determine which is most specific.
53  *
54  * If the activation-mode is CONDITIONAL_ANY or CONDITIONAL_ALL, the conditions
55  * property is set to a string made up of conditional expressions. Each
56  * expression is made up of a condition that can be assigned a boolean value,
57  * e.g. "system-domain is sun.com" or "ncu ip:bge0 is-not active". If the
58  * activation-mode is CONDITIONAL_ANY, the condition will be satisfied if any
59  * one of the conditions is true; if the activation-mode is CONDITIONAL_ALL,
60  * the condition is satisfied only if all of the conditions are true.
61  */
62 
63 uint64_t condition_check_interval = CONDITION_CHECK_INTERVAL_DEFAULT;
64 
65 extern int getdomainname(char *, int);
66 
67 /* NCP, NCU, ENM and location conditions */
68 static boolean_t test_condition_ncp(nwam_condition_t condition,
69     const char *ncp_name);
70 static boolean_t test_condition_ncu(nwam_condition_t condition,
71     const char *ncu_name);
72 static boolean_t test_condition_enm(nwam_condition_t condition,
73     const char *enm_name);
74 static boolean_t test_condition_loc(nwam_condition_t condition,
75     const char *loc_name);
76 
77 /* IP address conditions */
78 static boolean_t test_condition_ip_address(nwam_condition_t condition,
79     const char *ip_address);
80 
81 /* domainname conditions */
82 static boolean_t test_condition_sys_domain(nwam_condition_t condition,
83     const char *domainname);
84 static boolean_t test_condition_adv_domain(nwam_condition_t condition,
85     const char *domainname);
86 
87 /*  WLAN conditions */
88 static boolean_t test_condition_wireless_essid(nwam_condition_t condition,
89     const char *essid);
90 static boolean_t test_condition_wireless_bssid(nwam_condition_t condition,
91     const char *essid);
92 
93 struct nwamd_condition_map {
94 	nwam_condition_object_type_t object_type;
95 	boolean_t (*condition_func)(nwam_condition_t, const char *);
96 } condition_map[] =
97 {
98 	{ NWAM_CONDITION_OBJECT_TYPE_NCP, test_condition_ncp },
99 	{ NWAM_CONDITION_OBJECT_TYPE_NCU, test_condition_ncu },
100 	{ NWAM_CONDITION_OBJECT_TYPE_ENM, test_condition_enm },
101 	{ NWAM_CONDITION_OBJECT_TYPE_LOC, test_condition_loc },
102 	{ NWAM_CONDITION_OBJECT_TYPE_IP_ADDRESS, test_condition_ip_address },
103 	{ NWAM_CONDITION_OBJECT_TYPE_SYS_DOMAIN, test_condition_sys_domain },
104 	{ NWAM_CONDITION_OBJECT_TYPE_ADV_DOMAIN, test_condition_adv_domain },
105 	{ NWAM_CONDITION_OBJECT_TYPE_ESSID, test_condition_wireless_essid },
106 	{ NWAM_CONDITION_OBJECT_TYPE_BSSID, test_condition_wireless_bssid }
107 };
108 
109 /*
110  * This function takes which kind of conditions (is or is not) we are testing
111  * the object against and an object and applies the conditon to the object.
112  */
113 static boolean_t
114 test_condition_object_state(nwam_condition_t condition,
115     nwam_object_type_t object_type, const char *object_name)
116 {
117 	nwamd_object_t object;
118 	nwam_state_t state;
119 
120 	object = nwamd_object_find(object_type, object_name);
121 	if (object == NULL)
122 		return (B_FALSE);
123 
124 	state = object->nwamd_object_state;
125 	nwamd_object_release(object);
126 
127 	switch (condition) {
128 	case NWAM_CONDITION_IS:
129 		return (state == NWAM_STATE_ONLINE);
130 	case NWAM_CONDITION_IS_NOT:
131 		return (state != NWAM_STATE_ONLINE);
132 	default:
133 		return (B_FALSE);
134 	}
135 }
136 
137 static boolean_t
138 test_condition_ncp(nwam_condition_t condition, const char *name)
139 {
140 	boolean_t active;
141 
142 	(void) pthread_mutex_lock(&active_ncp_mutex);
143 	active = (strcasecmp(active_ncp, name) == 0);
144 	(void) pthread_mutex_unlock(&active_ncp_mutex);
145 
146 	switch (condition) {
147 	case NWAM_CONDITION_IS:
148 		return (active);
149 	case NWAM_CONDITION_IS_NOT:
150 		return (active != B_TRUE);
151 	default:
152 		return (B_FALSE);
153 	}
154 }
155 
156 static boolean_t
157 test_condition_ncu(nwam_condition_t condition, const char *name)
158 {
159 	char *real_name, *ncu_name;
160 	nwam_ncu_handle_t ncuh;
161 	nwam_ncu_type_t ncu_type;
162 	boolean_t rv;
163 
164 	/* names are case-insensitive, so get real name from libnwam */
165 	if (nwam_ncu_read(active_ncph, name, NWAM_NCU_TYPE_INTERFACE, 0, &ncuh)
166 	    == NWAM_SUCCESS) {
167 		ncu_type = NWAM_NCU_TYPE_INTERFACE;
168 	} else if (nwam_ncu_read(active_ncph, name, NWAM_NCU_TYPE_LINK, 0,
169 	    &ncuh) == NWAM_SUCCESS) {
170 		ncu_type = NWAM_NCU_TYPE_LINK;
171 	} else {
172 		return (B_FALSE);
173 	}
174 	if (nwam_ncu_get_name(ncuh, &real_name) != NWAM_SUCCESS) {
175 		nwam_ncu_free(ncuh);
176 		return (B_FALSE);
177 	}
178 	nwam_ncu_free(ncuh);
179 
180 	/*
181 	 * Name may be either unqualified or qualified by NCU type
182 	 * (interface:/link:).  Need to translate unqualified names
183 	 * to qualified, specifying interface:name if an interface
184 	 * NCU is present, otherwise link:ncu.
185 	 */
186 	if (nwam_ncu_name_to_typed_name(real_name, ncu_type, &ncu_name)
187 	    != NWAM_SUCCESS) {
188 		free(real_name);
189 		return (B_FALSE);
190 	}
191 	free(real_name);
192 
193 	rv = test_condition_object_state(condition, NWAM_OBJECT_TYPE_NCU,
194 	    ncu_name);
195 	free(ncu_name);
196 	return (rv);
197 }
198 
199 static boolean_t
200 test_condition_enm(nwam_condition_t condition, const char *enm_name)
201 {
202 	nwam_enm_handle_t enmh;
203 	char *real_name;
204 	boolean_t rv;
205 
206 	/* names are case-insensitive, so get real name from libnwam */
207 	if (nwam_enm_read(enm_name, 0, &enmh) != NWAM_SUCCESS)
208 		return (B_FALSE);
209 	if (nwam_enm_get_name(enmh, &real_name) != NWAM_SUCCESS) {
210 		nwam_enm_free(enmh);
211 		return (B_FALSE);
212 	}
213 	nwam_enm_free(enmh);
214 
215 	rv = test_condition_object_state(condition, NWAM_OBJECT_TYPE_ENM,
216 	    real_name);
217 	free(real_name);
218 	return (rv);
219 }
220 
221 static boolean_t
222 test_condition_loc(nwam_condition_t condition, const char *loc_name)
223 {
224 	nwam_loc_handle_t loch;
225 	char *real_name;
226 	boolean_t rv;
227 
228 	/* names are case-insensitive, so get real name from libnwam */
229 	if (nwam_loc_read(loc_name, 0, &loch) != NWAM_SUCCESS)
230 		return (B_FALSE);
231 	if (nwam_loc_get_name(loch, &real_name) != NWAM_SUCCESS) {
232 		nwam_loc_free(loch);
233 		return (B_FALSE);
234 	}
235 	nwam_loc_free(loch);
236 
237 	rv = test_condition_object_state(condition, NWAM_OBJECT_TYPE_LOC,
238 	    real_name);
239 	free(real_name);
240 	return (rv);
241 }
242 
243 static boolean_t
244 test_condition_domain(nwam_condition_t condition, const char *target_domain,
245     const char *found_domain)
246 {
247 	int i, len_t, len_f;
248 	char target[MAXHOSTNAMELEN], found[MAXHOSTNAMELEN];
249 
250 	len_t = target_domain == NULL ? 0 : strlen(target_domain);
251 	len_f = found_domain == NULL ? 0 : strlen(found_domain);
252 
253 	/* convert target_domain and found_domain to lowercase for strstr() */
254 	for (i = 0; i < len_t; i++)
255 		target[i] = tolower(target_domain[i]);
256 	target[len_t] = '\0';
257 
258 	for (i = 0; i < len_f; i++)
259 		found[i] = tolower(found_domain[i]);
260 	found[len_f] = '\0';
261 
262 	switch (condition) {
263 	case NWAM_CONDITION_IS:
264 		return (found_domain != NULL && strcmp(found, target) == 0);
265 	case NWAM_CONDITION_IS_NOT:
266 		return (found_domain == NULL || strcmp(found, target) != 0);
267 	case NWAM_CONDITION_CONTAINS:
268 		return (found_domain != NULL && strstr(found, target) != NULL);
269 	case NWAM_CONDITION_DOES_NOT_CONTAIN:
270 		return (found_domain == NULL || strstr(found, target) == NULL);
271 	default:
272 		return (B_FALSE);
273 	}
274 }
275 
276 struct ncu_adv_domains {
277 	struct ncu_adv_domains *next;
278 	char *dns_domain;
279 	char *nis_domain;
280 };
281 
282 static int
283 get_adv_domains(nwamd_object_t obj, void *arg)
284 {
285 	nwamd_ncu_t *ncu = (nwamd_ncu_t *)obj->nwamd_object_data;
286 	struct ncu_adv_domains **headpp = (struct ncu_adv_domains **)arg;
287 	struct ncu_adv_domains *adp;
288 	char *dns, *nis;
289 
290 	if (ncu->ncu_type != NWAM_NCU_TYPE_INTERFACE)
291 		return (0);
292 
293 	dns = nwamd_get_dhcpinfo_data("DNSdmain", ncu->ncu_name);
294 	nis = nwamd_get_dhcpinfo_data("NISdmain", ncu->ncu_name);
295 
296 	if (dns != NULL || nis != NULL) {
297 		adp = (struct ncu_adv_domains *)malloc(sizeof (*adp));
298 		if (adp == NULL)
299 			return (1);
300 		adp->dns_domain = dns;
301 		adp->nis_domain = nis;
302 		adp->next = *headpp;
303 		*headpp = adp;
304 	}
305 
306 	return (0);
307 }
308 
309 static boolean_t
310 test_condition_sys_domain(nwam_condition_t condition, const char *domainname)
311 {
312 	char cur_domainname[MAXHOSTNAMELEN];
313 
314 	if (getdomainname(cur_domainname, MAXHOSTNAMELEN) != 0)
315 		return (B_FALSE);
316 
317 	return (test_condition_domain(condition, domainname, cur_domainname));
318 }
319 
320 static boolean_t
321 test_condition_adv_domain(nwam_condition_t condition, const char *domainname)
322 {
323 	struct ncu_adv_domains *adv_domains = NULL;
324 	struct ncu_adv_domains *adp, *prev;
325 	boolean_t positive, rtn;
326 
327 	(void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU, get_adv_domains,
328 	    &adv_domains);
329 
330 	positive = (condition == NWAM_CONDITION_IS ||
331 	    condition == NWAM_CONDITION_CONTAINS);
332 
333 	/*
334 	 * Walk the advertised domain list.  Our test function tests one
335 	 * single domain, but we're dealing with a list: if our condition
336 	 * is positive ('is' or 'contains'), the test function for each
337 	 * domain results are or'd together; if our condition is negative
338 	 * ('is-not' or 'does-not-contain'), the test function results must
339 	 * be and'd.  Thus our short-circuit exit value depends on our
340 	 * condition: if the test function returns TRUE it implies immediate
341 	 * success for a positive condition; if it returns FALSE it implies
342 	 * immediate failure for a negative condition.
343 	 */
344 	adp = adv_domains;
345 	while (adp != NULL) {
346 		if ((test_condition_domain(condition, domainname,
347 		    adp->dns_domain) == positive) ||
348 		    (test_condition_domain(condition, domainname,
349 		    adp->nis_domain) == positive)) {
350 			rtn = positive;
351 			break;
352 		}
353 		adp = adp->next;
354 	}
355 	if (adp == NULL) {
356 		/*
357 		 * We did not short-circuit; we therefore failed if our
358 		 * condition was positive, and succeeded if our condition
359 		 * was negative.
360 		 */
361 		rtn = !positive;
362 	}
363 
364 	/* now free the domain list */
365 	adp = adv_domains;
366 	while (adp != NULL) {
367 		prev = adp;
368 		adp = prev->next;
369 		free(prev->dns_domain);
370 		free(prev->nis_domain);
371 		free(prev);
372 	}
373 
374 	return (rtn);
375 }
376 
377 /*
378  * Returns true if prefixlen bits of addr1 match prefixlen bits of addr2.
379  */
380 static boolean_t
381 prefixmatch(uchar_t *addr1, uchar_t *addr2, int prefixlen)
382 {
383 	uchar_t mask[IPV6_ABITS/8];
384 	int i, j = 0;
385 
386 	if (prefixlen == 0)
387 		return (B_TRUE);
388 
389 	while (prefixlen > 0) {
390 		if (prefixlen >= 8) {
391 			mask[j++] = 0xFF;
392 			prefixlen -= 8;
393 		} else {
394 			mask[j] |= 1 << (8 - prefixlen);
395 			prefixlen--;
396 		}
397 	}
398 	/* Ensure at least one byte is tested */
399 	if (j == 0) j++;
400 
401 	for (i = 0; i < j; i++) {
402 		if ((addr1[i] & mask[i]) != (addr2[i] & mask[i]))
403 			return (B_FALSE);
404 	}
405 	return (B_TRUE);
406 }
407 
408 struct nwamd_ipaddr_condition_walk_arg {
409 	nwam_condition_t condition;
410 	struct sockaddr_storage sockaddr;
411 	int prefixlen;
412 	boolean_t res;
413 };
414 
415 static int
416 check_ipaddr(icfg_if_t *intf, void *arg)
417 {
418 	struct nwamd_ipaddr_condition_walk_arg *wa = arg;
419 	struct sockaddr_storage sockaddr;
420 	icfg_handle_t h;
421 	socklen_t addrlen = intf->if_protocol == AF_INET ?
422 	    sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6);
423 	int prefixlen = 0;
424 	boolean_t match = B_FALSE;
425 	uchar_t *addr1, *addr2;
426 
427 	if (icfg_open(&h, intf) != ICFG_SUCCESS)
428 		return (0);
429 
430 	if (icfg_get_addr(h, (struct sockaddr *)&sockaddr, &addrlen,
431 	    &prefixlen, B_TRUE) != ICFG_SUCCESS) {
432 		nlog(LOG_ERR, "check_ipaddr: icfg_get_addr: %s",
433 		    strerror(errno));
434 		return (0);
435 	}
436 
437 	if (intf->if_protocol == AF_INET) {
438 		addr1 = (uchar_t *)&(((struct sockaddr_in *)
439 		    &sockaddr)->sin_addr.s_addr);
440 		addr2 = (uchar_t *)&(((struct sockaddr_in *)
441 		    &(wa->sockaddr))->sin_addr.s_addr);
442 	} else {
443 		addr1 = (uchar_t *)&(((struct sockaddr_in6 *)
444 		    &sockaddr)->sin6_addr.s6_addr);
445 		addr2 = (uchar_t *)&(((struct sockaddr_in6 *)
446 		    &(wa->sockaddr))->sin6_addr.s6_addr);
447 	}
448 
449 	match = prefixmatch(addr1, addr2, wa->prefixlen);
450 	icfg_close(h);
451 
452 	nlog(LOG_DEBUG, "check_ipaddr: match %d\n", match);
453 	switch (wa->condition) {
454 	case NWAM_CONDITION_IS:
455 	case NWAM_CONDITION_IS_IN_RANGE:
456 		wa->res = match;
457 		if (match)
458 			return (1);
459 		return (0);
460 	case NWAM_CONDITION_IS_NOT:
461 	case NWAM_CONDITION_IS_NOT_IN_RANGE:
462 		wa->res = !match;
463 		return (0);
464 	default:
465 		return (0);
466 	}
467 }
468 
469 static boolean_t
470 test_condition_ip_address(nwam_condition_t condition,
471     const char *ip_address_string)
472 {
473 	int proto;
474 	char *copy, *ip_address, *prefixlen_string, *lasts;
475 	socklen_t addrlen = sizeof (struct sockaddr_in);
476 	socklen_t addr6len = sizeof (struct sockaddr_in6);
477 	struct nwamd_ipaddr_condition_walk_arg wa;
478 
479 	if ((copy = strdup(ip_address_string)) == NULL)
480 		return (B_FALSE);
481 
482 	if ((ip_address = strtok_r(copy, " \t/", &lasts)) == NULL) {
483 		free(copy);
484 		return (B_FALSE);
485 	}
486 
487 	prefixlen_string = strtok_r(NULL, " \t", &lasts);
488 
489 	if (icfg_str_to_sockaddr(AF_INET, ip_address,
490 	    (struct sockaddr *)&(wa.sockaddr), &addrlen) == ICFG_SUCCESS) {
491 		proto = AF_INET;
492 		wa.prefixlen = IP_ABITS;
493 	} else if (icfg_str_to_sockaddr(AF_INET6, ip_address,
494 	    (struct sockaddr *)&(wa.sockaddr), &addr6len) == ICFG_SUCCESS) {
495 		proto = AF_INET6;
496 		wa.prefixlen = IPV6_ABITS;
497 	} else {
498 		nlog(LOG_ERR, "test_condition_ip_address: "
499 		    "icfg_str_to_sockaddr: %s", strerror(errno));
500 		free(copy);
501 		return (B_FALSE);
502 	}
503 
504 	if (prefixlen_string != NULL)
505 		wa.prefixlen = atoi(prefixlen_string);
506 
507 	wa.condition = condition;
508 
509 	switch (condition) {
510 	case NWAM_CONDITION_IS:
511 	case NWAM_CONDITION_IS_IN_RANGE:
512 		wa.res = B_FALSE;
513 		break;
514 	case NWAM_CONDITION_IS_NOT:
515 	case NWAM_CONDITION_IS_NOT_IN_RANGE:
516 		wa.res = B_TRUE;
517 		break;
518 	default:
519 		free(copy);
520 		return (B_FALSE);
521 	}
522 
523 	(void) icfg_iterate_if(proto, ICFG_PLUMBED, &wa, check_ipaddr);
524 
525 	free(copy);
526 
527 	return (wa.res);
528 }
529 
530 struct nwamd_wlan_condition_walk_arg {
531 	nwam_condition_t condition;
532 	const char *exp_essid;
533 	const char *exp_bssid;
534 	uint_t num_connected;
535 	boolean_t res;
536 };
537 
538 static int
539 check_wlan(const char *linkname, void *arg)
540 {
541 	struct nwamd_wlan_condition_walk_arg *wa = arg;
542 	datalink_id_t linkid;
543 	dladm_wlan_linkattr_t attr;
544 	dladm_status_t status;
545 	char cur_essid[DLADM_STRSIZE];
546 	char cur_bssid[DLADM_STRSIZE];
547 	char errmsg[DLADM_STRSIZE];
548 
549 	if ((status = dladm_name2info(dld_handle, linkname, &linkid, NULL, NULL,
550 	    NULL)) != DLADM_STATUS_OK) {
551 		nlog(LOG_DEBUG, "check_wlan: dladm_name2info() for %s "
552 		    "failed: %s", linkname,
553 		    dladm_status2str(status, errmsg));
554 		return (DLADM_WALK_CONTINUE);
555 	}
556 
557 	status = dladm_wlan_get_linkattr(dld_handle, linkid, &attr);
558 	if (status != DLADM_STATUS_OK) {
559 		nlog(LOG_DEBUG, "check_wlan: dladm_wlan_get_linkattr() for %s "
560 		    "failed: %s", linkname,
561 		    dladm_status2str(status, errmsg));
562 		return (DLADM_WALK_CONTINUE);
563 	}
564 	if (attr.la_status == DLADM_WLAN_LINK_DISCONNECTED)
565 		return (DLADM_WALK_TERMINATE);
566 
567 	wa->num_connected++;
568 
569 	if (wa->exp_essid != NULL) {
570 		/* Is the NIC associated with the expected access point? */
571 		(void) dladm_wlan_essid2str(&attr.la_wlan_attr.wa_essid,
572 		    cur_essid);
573 		switch (wa->condition) {
574 		case NWAM_CONDITION_IS:
575 			wa->res = strcmp(cur_essid, wa->exp_essid) == 0;
576 			if (wa->res)
577 				return (DLADM_WALK_TERMINATE);
578 			break;
579 		case NWAM_CONDITION_IS_NOT:
580 			wa->res = strcmp(cur_essid, wa->exp_essid) != 0;
581 			if (!wa->res)
582 				return (DLADM_WALK_TERMINATE);
583 			break;
584 		case NWAM_CONDITION_CONTAINS:
585 			wa->res = strstr(cur_essid, wa->exp_essid) != NULL;
586 			if (wa->res)
587 				return (DLADM_WALK_TERMINATE);
588 			break;
589 		case NWAM_CONDITION_DOES_NOT_CONTAIN:
590 			wa->res = strstr(cur_essid, wa->exp_essid) == NULL;
591 			if (!wa->res)
592 				return (DLADM_WALK_TERMINATE);
593 			break;
594 		default:
595 			return (DLADM_WALK_TERMINATE);
596 		}
597 		return (DLADM_WALK_CONTINUE);
598 	}
599 	if (wa->exp_bssid != NULL) {
600 		/* Is the NIC associated with the expected access point? */
601 		(void) dladm_wlan_bssid2str(&attr.la_wlan_attr.wa_bssid,
602 		    cur_bssid);
603 		switch (wa->condition) {
604 		case NWAM_CONDITION_IS:
605 			wa->res = strcmp(cur_bssid, wa->exp_bssid) == 0;
606 			if (wa->res)
607 				return (DLADM_WALK_TERMINATE);
608 			break;
609 		case NWAM_CONDITION_IS_NOT:
610 			wa->res = strcmp(cur_bssid, wa->exp_bssid) != 0;
611 			if (!wa->res)
612 				return (DLADM_WALK_TERMINATE);
613 			break;
614 		default:
615 			return (DLADM_WALK_TERMINATE);
616 		}
617 		return (DLADM_WALK_CONTINUE);
618 	}
619 	/*
620 	 * Neither an ESSID or BSSID match is required - being connected to a
621 	 * WLAN is enough.
622 	 */
623 	switch (wa->condition) {
624 	case NWAM_CONDITION_IS:
625 		wa->res = B_TRUE;
626 		return (DLADM_WALK_TERMINATE);
627 	default:
628 		wa->res = B_FALSE;
629 		return (DLADM_WALK_TERMINATE);
630 	}
631 	/*NOTREACHED*/
632 	return (DLADM_WALK_CONTINUE);
633 }
634 
635 static boolean_t
636 test_condition_wireless_essid(nwam_condition_t condition,
637     const char *essid)
638 {
639 	struct nwamd_wlan_condition_walk_arg wa;
640 
641 	wa.condition = condition;
642 	wa.exp_essid = essid;
643 	wa.exp_bssid = NULL;
644 	wa.num_connected = 0;
645 	wa.res = B_FALSE;
646 
647 	(void) dladm_walk(check_wlan, dld_handle, &wa, DATALINK_CLASS_PHYS,
648 	    DL_WIFI, DLADM_OPT_ACTIVE);
649 
650 	return (wa.num_connected > 0 && wa.res == B_TRUE);
651 }
652 
653 static boolean_t
654 test_condition_wireless_bssid(nwam_condition_t condition,
655     const char *bssid)
656 {
657 	struct nwamd_wlan_condition_walk_arg wa;
658 
659 	wa.condition = condition;
660 	wa.exp_bssid = bssid;
661 	wa.exp_essid = NULL;
662 	wa.num_connected = 0;
663 	wa.res = B_FALSE;
664 
665 	(void) dladm_walk(check_wlan, dld_handle, &wa, DATALINK_CLASS_PHYS,
666 	    DL_WIFI, DLADM_OPT_ACTIVE);
667 
668 	return (wa.num_connected > 0 && wa.res == B_TRUE);
669 }
670 
671 /*
672  * This function takes an activation mode and a string representation of a
673  * condition and evaluates it.
674  */
675 boolean_t
676 nwamd_check_conditions(nwam_activation_mode_t activation_mode,
677     char **condition_strings, uint_t num_conditions)
678 {
679 	boolean_t ret;
680 	nwam_condition_t condition;
681 	nwam_condition_object_type_t object_type;
682 	char *object_name;
683 	int i, j;
684 
685 	for (i = 0; i < num_conditions; i++) {
686 
687 		if (nwam_condition_string_to_condition(condition_strings[i],
688 		    &object_type, &condition, &object_name) != NWAM_SUCCESS) {
689 			nlog(LOG_ERR, "check_conditions: invalid condition %s",
690 			    condition_strings[i]);
691 			return (B_FALSE);
692 		}
693 		ret = B_FALSE;
694 
695 		for (j = 0; j < (sizeof (condition_map) /
696 		    sizeof (struct nwamd_condition_map)); j++) {
697 			if (condition_map[j].object_type == object_type)
698 				ret = condition_map[j].condition_func(condition,
699 				    object_name);
700 		}
701 
702 		free(object_name);
703 
704 		if (activation_mode == NWAM_ACTIVATION_MODE_CONDITIONAL_ANY &&
705 		    ret) {
706 			return (B_TRUE);
707 		}
708 		if (activation_mode == NWAM_ACTIVATION_MODE_CONDITIONAL_ALL &&
709 		    !ret) {
710 			return (B_FALSE);
711 		}
712 	}
713 	if (activation_mode == NWAM_ACTIVATION_MODE_CONDITIONAL_ANY && ret)
714 		return (B_TRUE);
715 	if (activation_mode == NWAM_ACTIVATION_MODE_CONDITIONAL_ALL && ret)
716 		return (B_TRUE);
717 
718 	return (B_FALSE);
719 }
720 
721 /*
722  * In rating activation conditions, we take the best-rated CONDITIONAL_ANY
723  * condition, or sum all the CONDITIONAL_ALL condition ratings. This allows
724  * us to compare between location activation conditions to pick the best.
725  */
726 uint64_t
727 nwamd_rate_conditions(nwam_activation_mode_t activation_mode,
728     char **conditions, uint_t num_conditions)
729 {
730 	nwam_condition_t condition;
731 	nwam_condition_object_type_t object_type;
732 	char *object_name;
733 	int i;
734 	uint64_t rating = 0, total_rating = 0;
735 
736 	for (i = 0; i < num_conditions; i++) {
737 
738 		object_name = NULL;
739 		if (nwam_condition_string_to_condition(conditions[i],
740 		    &object_type, &condition, &object_name) != NWAM_SUCCESS ||
741 		    nwam_condition_rate(object_type, condition, &rating)
742 		    != NWAM_SUCCESS) {
743 			nlog(LOG_ERR, "nwamd_rate_conditions: could not rate "
744 			    "condition");
745 			free(object_name);
746 			return (0);
747 		}
748 		free(object_name);
749 
750 		if (activation_mode == NWAM_ACTIVATION_MODE_CONDITIONAL_ANY) {
751 			if (rating > total_rating)
752 				total_rating = rating;
753 		} else if (activation_mode ==
754 		    NWAM_ACTIVATION_MODE_CONDITIONAL_ALL) {
755 			total_rating += rating;
756 		}
757 	}
758 	return (total_rating);
759 }
760 
761 /*
762  * Different from nwamd_triggered_check_all_conditions() in that this
763  * function enqueues a timed check event.
764  */
765 void
766 nwamd_set_timed_check_all_conditions(void)
767 {
768 	nwamd_event_t check_event = nwamd_event_init
769 	    (NWAM_EVENT_TYPE_TIMED_CHECK_CONDITIONS, NWAM_OBJECT_TYPE_UNKNOWN,
770 	    0, NULL);
771 	if (check_event != NULL) {
772 		/* Add another timed event to recheck conditions */
773 		nwamd_event_enqueue_timed(check_event,
774 		    condition_check_interval > CONDITION_CHECK_INTERVAL_MIN ?
775 		    condition_check_interval : CONDITION_CHECK_INTERVAL_MIN);
776 	}
777 }
778 
779 /*
780  * Does not enqueue another check event.
781  */
782 void
783 nwamd_check_all_conditions(void)
784 {
785 	nwamd_enm_check_conditions();
786 	nwamd_loc_check_conditions();
787 }
788 
789 void
790 nwamd_create_timed_condition_check_event(void)
791 {
792 	nwamd_event_t check_event = nwamd_event_init
793 	    (NWAM_EVENT_TYPE_TIMED_CHECK_CONDITIONS, NWAM_OBJECT_TYPE_UNKNOWN,
794 	    0, NULL);
795 	if (check_event != NULL)
796 		nwamd_event_enqueue(check_event);
797 }
798 
799 void
800 nwamd_create_triggered_condition_check_event(uint32_t when)
801 {
802 	nwamd_event_t check_event;
803 
804 	if (!nwamd_event_enqueued(NWAM_EVENT_TYPE_TRIGGERED_CHECK_CONDITIONS,
805 	    NWAM_OBJECT_TYPE_UNKNOWN, NULL)) {
806 		check_event = nwamd_event_init
807 		    (NWAM_EVENT_TYPE_TRIGGERED_CHECK_CONDITIONS,
808 		    NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL);
809 		if (check_event != NULL)
810 			nwamd_event_enqueue_timed(check_event, when);
811 	}
812 }
813