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