dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / lib / nwamd / known_wlans.c
blob2d68e1704cddc028ae466aac1c1e4b95681dba9c
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <ctype.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <libdladm.h>
33 #include <libdllink.h>
34 #include <libdlwlan.h>
35 #include <libgen.h>
36 #include <libnwam.h>
38 #include "events.h"
39 #include "known_wlans.h"
40 #include "ncu.h"
41 #include "objects.h"
42 #include "util.h"
45 * known_wlans.c - contains routines which handle the known WLAN abstraction.
48 #define KNOWN_WIFI_NETS_FILE "/etc/nwam/known_wifi_nets"
50 /* enum for parsing each line of /etc/nwam/known_wifi_nets */
51 typedef enum {
52 ESSID = 0,
53 BSSID,
54 MAX_FIELDS
55 } known_wifi_nets_fields_t;
57 /* Structure for one BSSID */
58 typedef struct bssid {
59 struct qelem bssid_links;
60 char *bssid;
61 } bssid_t;
63 /* Structure for an ESSID and its BSSIDs */
64 typedef struct kw {
65 struct qelem kw_links;
66 char kw_essid[NWAM_MAX_NAME_LEN];
67 uint32_t kw_num_bssids;
68 struct qelem kw_bssids;
69 } kw_t;
71 /* Holds the linked-list of ESSIDs to make Known WLANs out of */
72 static struct qelem kw_list;
74 /* Used in walking secobjs looking for an ESSID prefix match. */
75 struct nwamd_secobj_arg {
76 char nsa_essid_prefix[DLADM_WLAN_MAX_KEYNAME_LEN];
77 char nsa_keyname[DLADM_WLAN_MAX_KEYNAME_LEN];
78 dladm_wlan_key_t *nsa_key;
79 uint64_t nsa_secmode;
82 static void
83 kw_list_init(void)
85 kw_list.q_forw = kw_list.q_back = &kw_list;
88 static void
89 kw_list_free(void)
91 kw_t *kw;
92 bssid_t *b;
94 while (kw_list.q_forw != &kw_list) {
95 kw = (kw_t *)kw_list.q_forw;
97 /* free kw_bssids */
98 while (kw->kw_bssids.q_forw != &kw->kw_bssids) {
99 b = (bssid_t *)kw->kw_bssids.q_forw;
100 remque(&b->bssid_links);
101 free(b->bssid);
102 free(b);
104 remque(&kw->kw_links);
105 free(kw);
109 /* Returns the entry in kw_list for the given ESSID. NULL if non-existent */
110 static kw_t *
111 kw_lookup(const char *essid)
113 kw_t *kw;
115 if (essid == NULL)
116 return (NULL);
118 for (kw = (kw_t *)kw_list.q_forw;
119 kw != (kw_t *)&kw_list;
120 kw = (kw_t *)kw->kw_links.q_forw) {
121 if (strcmp(essid, kw->kw_essid) == 0)
122 return (kw);
124 return (NULL);
127 /* Adds an ESSID/BSSID combination to kw_list. Returns B_TRUE on success. */
128 static boolean_t
129 kw_add(const char *essid, const char *bssid)
131 kw_t *kw;
132 bssid_t *b;
134 if ((b = calloc(1, sizeof (bssid_t))) == NULL) {
135 nlog(LOG_ERR, "kw_add: cannot allocate for bssid_t: %m");
136 return (B_FALSE);
138 if ((kw = calloc(1, sizeof (kw_t))) == NULL) {
139 nlog(LOG_ERR, "kw_add: cannot allocate for kw_t: %m");
140 free(b);
141 return (B_FALSE);
143 kw->kw_bssids.q_forw = kw->kw_bssids.q_back = &kw->kw_bssids;
145 b->bssid = strdup(bssid);
146 (void) strlcpy(kw->kw_essid, essid, sizeof (kw->kw_essid));
147 kw->kw_num_bssids = 1;
149 insque(&b->bssid_links, kw->kw_bssids.q_back);
150 insque(&kw->kw_links, kw_list.q_back);
152 nlog(LOG_DEBUG, "kw_add: added Known WLAN %s, BSSID %s", essid, bssid);
153 return (B_TRUE);
157 * Add the BSSID to the given kw. Since /etc/nwam/known_wifi_nets is
158 * populated such that the wifi networks visited later are towards the end
159 * of the file, remove the give kw from its current position and append it
160 * to the end of kw_list. This ensures that kw_list is in the reverse
161 * order of visited wifi networks. Returns B_TRUE on success.
163 static boolean_t
164 kw_update(kw_t *kw, const char *bssid)
166 bssid_t *b;
168 if ((b = calloc(1, sizeof (bssid_t))) == NULL) {
169 nlog(LOG_ERR, "kw_update: cannot allocate for bssid_t: %m");
170 return (B_FALSE);
173 b->bssid = strdup(bssid);
174 insque(&b->bssid_links, kw->kw_bssids.q_back);
175 kw->kw_num_bssids++;
177 /* remove kw from current position */
178 remque(&kw->kw_links);
179 /* and insert at end */
180 insque(&kw->kw_links, kw_list.q_back);
182 nlog(LOG_DEBUG, "kw_update: appended BSSID %s to Known WLAN %s",
183 bssid, kw->kw_essid);
184 return (B_TRUE);
188 * Parses /etc/nwam/known_wifi_nets and populates kw_list, with the oldest
189 * wifi networks first in the list. Returns the number of unique entries
190 * in kw_list (to use for priority values).
192 static int
193 parse_known_wifi_nets(void)
195 FILE *fp;
196 char line[LINE_MAX];
197 char *cp, *tok[MAX_FIELDS];
198 int lnum, num_kw = 0;
199 kw_t *kw;
201 kw_list_init();
204 * The file format is:
205 * essid\tbssid (essid followed by tab followed by bssid)
207 fp = fopen(KNOWN_WIFI_NETS_FILE, "r");
208 if (fp == NULL)
209 return (0);
210 for (lnum = 1; fgets(line, sizeof (line), fp) != NULL; lnum++) {
212 cp = line;
213 while (isspace(*cp))
214 cp++;
215 if (*cp == '#' || *cp == '\0')
216 continue;
218 if (bufsplit(cp, MAX_FIELDS, tok) != MAX_FIELDS) {
219 syslog(LOG_ERR, "%s:%d: wrong number of tokens; "
220 "ignoring entry", KNOWN_WIFI_NETS_FILE, lnum);
221 continue;
224 if ((kw = kw_lookup(tok[ESSID])) == NULL) {
225 if (!kw_add(tok[ESSID], tok[BSSID])) {
226 nlog(LOG_ERR,
227 "%s:%d: cannot add entry (%s,%s) to list",
228 KNOWN_WIFI_NETS_FILE, lnum,
229 tok[ESSID], tok[BSSID]);
230 } else {
231 num_kw++;
233 } else {
234 if (!kw_update(kw, tok[BSSID])) {
235 nlog(LOG_ERR,
236 "%s:%d:cannot update entry (%s,%s) to list",
237 KNOWN_WIFI_NETS_FILE, lnum,
238 tok[ESSID], tok[BSSID]);
241 /* next line ... */
244 (void) fclose(fp);
245 return (num_kw);
249 * Walk security objects looking for one that matches the essid prefix.
250 * Store the key and keyname if a match is found - we use the last match
251 * as the key for the known WLAN, since it is the most recently updated.
253 /* ARGSUSED0 */
254 static boolean_t
255 find_secobj_matching_prefix(dladm_handle_t dh, void *arg,
256 const char *secobjname)
258 struct nwamd_secobj_arg *nsa = arg;
260 if (strncmp(nsa->nsa_essid_prefix, secobjname,
261 strlen(nsa->nsa_essid_prefix)) == 0) {
262 nlog(LOG_DEBUG, "find_secobj_matching_prefix: "
263 "found secobj with prefix %s : %s\n",
264 nsa->nsa_essid_prefix, secobjname);
265 /* Free last key found (if any) */
266 free(nsa->nsa_key);
267 /* Retrive key so we can get security mode */
268 nsa->nsa_key = nwamd_wlan_get_key_named(secobjname, 0);
269 (void) strlcpy(nsa->nsa_keyname, secobjname,
270 sizeof (nsa->nsa_keyname));
271 switch (nsa->nsa_key->wk_class) {
272 case DLADM_SECOBJ_CLASS_WEP:
273 nsa->nsa_secmode = DLADM_WLAN_SECMODE_WEP;
274 nlog(LOG_DEBUG, "find_secobj_matching_prefix: "
275 "got WEP key %s", nsa->nsa_keyname);
276 break;
277 case DLADM_SECOBJ_CLASS_WPA:
278 nsa->nsa_secmode = DLADM_WLAN_SECMODE_WPA;
279 nlog(LOG_DEBUG, "find_secobj_matching_prefix: "
280 "got WPA key %s", nsa->nsa_keyname);
281 break;
282 default:
283 /* shouldn't happen */
284 nsa->nsa_secmode = DLADM_WLAN_SECMODE_NONE;
285 nlog(LOG_ERR, "find_secobj_matching_prefix: "
286 "key class for key %s was invalid",
287 nsa->nsa_keyname);
288 break;
291 return (B_TRUE);
295 /* Upgrade /etc/nwam/known_wifi_nets file to new libnwam-based config model */
296 void
297 upgrade_known_wifi_nets_config(void)
299 kw_t *kw;
300 bssid_t *b;
301 nwam_known_wlan_handle_t kwh;
302 char **bssids;
303 nwam_error_t err;
304 uint64_t priority;
305 int i, num_kw;
306 struct nwamd_secobj_arg nsa;
308 nlog(LOG_INFO, "Upgrading %s to Known WLANs", KNOWN_WIFI_NETS_FILE);
310 /* Parse /etc/nwam/known_wifi_nets */
311 num_kw = parse_known_wifi_nets();
313 /* Create Known WLANs for each unique ESSID */
314 for (kw = (kw_t *)kw_list.q_forw, priority = num_kw-1;
315 kw != (kw_t *)&kw_list;
316 kw = (kw_t *)kw->kw_links.q_forw, priority--) {
317 nwam_value_t priorityval = NULL;
318 nwam_value_t bssidsval = NULL;
319 nwam_value_t secmodeval = NULL;
320 nwam_value_t keynameval = NULL;
322 nlog(LOG_DEBUG, "Creating Known WLAN %s", kw->kw_essid);
324 if ((err = nwam_known_wlan_create(kw->kw_essid, &kwh))
325 != NWAM_SUCCESS) {
326 nlog(LOG_ERR, "upgrade wlan %s: "
327 "could not create known wlan: %s", kw->kw_essid,
328 nwam_strerror(err));
329 continue;
332 /* priority of this ESSID */
333 if ((err = nwam_value_create_uint64(priority, &priorityval))
334 != NWAM_SUCCESS) {
335 nlog(LOG_ERR, "upgrade wlan %s: "
336 "could not create priority value: %s", kw->kw_essid,
337 nwam_strerror(err));
338 nwam_known_wlan_free(kwh);
339 continue;
341 err = nwam_known_wlan_set_prop_value(kwh,
342 NWAM_KNOWN_WLAN_PROP_PRIORITY, priorityval);
343 nwam_value_free(priorityval);
344 if (err != NWAM_SUCCESS) {
345 nlog(LOG_ERR, "upgrade wlan %s: "
346 "could not set priority value: %s", kw->kw_essid,
347 nwam_strerror(err));
348 nwam_known_wlan_free(kwh);
349 continue;
352 /* loop through kw->kw_bssids and create an array of bssids */
353 bssids = calloc(kw->kw_num_bssids, sizeof (char *));
354 if (bssids == NULL) {
355 nwam_known_wlan_free(kwh);
356 nlog(LOG_ERR, "upgrade wlan %s: "
357 "could not calloc for bssids: %m", kw->kw_essid);
358 continue;
360 for (b = (bssid_t *)kw->kw_bssids.q_forw, i = 0;
361 b != (bssid_t *)&kw->kw_bssids;
362 b = (bssid_t *)b->bssid_links.q_forw, i++) {
363 bssids[i] = strdup(b->bssid);
366 /* BSSIDs for this ESSID */
367 if ((err = nwam_value_create_string_array(bssids,
368 kw->kw_num_bssids, &bssidsval)) != NWAM_SUCCESS) {
369 nlog(LOG_ERR, "upgrade wlan %s: "
370 "could not create bssids value: %s", kw->kw_essid,
371 nwam_strerror(err));
372 for (i = 0; i < kw->kw_num_bssids; i++)
373 free(bssids[i]);
374 free(bssids);
375 nwam_known_wlan_free(kwh);
376 continue;
378 err = nwam_known_wlan_set_prop_value(kwh,
379 NWAM_KNOWN_WLAN_PROP_BSSIDS, bssidsval);
380 nwam_value_free(bssidsval);
381 for (i = 0; i < kw->kw_num_bssids; i++)
382 free(bssids[i]);
383 free(bssids);
384 if (err != NWAM_SUCCESS) {
385 nlog(LOG_ERR, "upgrade wlan %s: "
386 "could not set bssids: %s", kw->kw_essid,
387 nwam_strerror(err));
388 nwam_known_wlan_free(kwh);
389 continue;
393 * Retrieve last key matching ESSID prefix if any, and set
394 * the retrieved key name and security mode.
396 nwamd_set_key_name(kw->kw_essid, NULL, nsa.nsa_essid_prefix,
397 sizeof (nsa.nsa_essid_prefix));
398 nsa.nsa_key = NULL;
399 nsa.nsa_secmode = DLADM_WLAN_SECMODE_NONE;
400 (void) dladm_walk_secobj(dld_handle, &nsa,
401 find_secobj_matching_prefix, DLADM_OPT_PERSIST);
402 if (nsa.nsa_key != NULL) {
403 if ((err = nwam_value_create_string(nsa.nsa_keyname,
404 &keynameval)) == NWAM_SUCCESS) {
405 (void) nwam_known_wlan_set_prop_value(kwh,
406 NWAM_KNOWN_WLAN_PROP_KEYNAME, keynameval);
408 free(nsa.nsa_key);
409 nwam_value_free(keynameval);
412 if ((err = nwam_value_create_uint64(nsa.nsa_secmode,
413 &secmodeval)) != NWAM_SUCCESS ||
414 (err = nwam_known_wlan_set_prop_value(kwh,
415 NWAM_KNOWN_WLAN_PROP_SECURITY_MODE, secmodeval))
416 != NWAM_SUCCESS) {
417 nlog(LOG_ERR, "upgrade wlan %s: "
418 "could not set security mode: %s",
419 kw->kw_essid, nwam_strerror(err));
420 nwam_value_free(secmodeval);
421 nwam_known_wlan_free(kwh);
422 continue;
425 /* commit, no collision checking by libnwam */
426 err = nwam_known_wlan_commit(kwh,
427 NWAM_FLAG_KNOWN_WLAN_NO_COLLISION_CHECK);
428 nwam_known_wlan_free(kwh);
429 if (err != NWAM_SUCCESS) {
430 nlog(LOG_ERR, "upgrade wlan %s: "
431 "could not commit wlan: %s", kw->kw_essid,
432 nwam_strerror(err));
434 /* next ... */
437 kw_list_free();
440 nwam_error_t
441 known_wlan_get_keyname(const char *essid, char *name)
443 nwam_known_wlan_handle_t kwh = NULL;
444 nwam_value_t keynameval = NULL;
445 char *keyname;
446 nwam_error_t err;
448 if ((err = nwam_known_wlan_read(essid, 0, &kwh)) != NWAM_SUCCESS)
449 return (err);
450 if ((err = nwam_known_wlan_get_prop_value(kwh,
451 NWAM_KNOWN_WLAN_PROP_KEYNAME, &keynameval)) == NWAM_SUCCESS &&
452 (err = nwam_value_get_string(keynameval, &keyname))
453 == NWAM_SUCCESS) {
454 (void) strlcpy(name, keyname, NWAM_MAX_VALUE_LEN);
456 if (keynameval != NULL)
457 nwam_value_free(keynameval);
459 if (kwh != NULL)
460 nwam_known_wlan_free(kwh);
462 return (err);
465 /* Performs a scan on a wifi link NCU */
466 /* ARGSUSED */
467 static int
468 nwamd_ncu_known_wlan_committed(nwamd_object_t object, void *data)
470 nwamd_ncu_t *ncu_data = object->nwamd_object_data;
472 if (ncu_data->ncu_type != NWAM_NCU_TYPE_LINK)
473 return (0);
475 /* network selection will be done only if possible */
476 if (ncu_data->ncu_link.nwamd_link_media == DL_WIFI)
477 (void) nwamd_wlan_scan(ncu_data->ncu_name);
478 return (0);
481 /* Handle known WLAN initialization/refresh event */
482 /* ARGSUSED */
483 void
484 nwamd_known_wlan_handle_init_event(nwamd_event_t known_wlan_event)
487 * Since the Known WLAN list has changed, do a rescan so that the
488 * best network is selected.
490 (void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
491 nwamd_ncu_known_wlan_committed, NULL);
494 void
495 nwamd_known_wlan_handle_action_event(nwamd_event_t known_wlan_event)
497 switch (known_wlan_event->event_msg->nwe_data.nwe_object_action.
498 nwe_action) {
499 case NWAM_ACTION_ADD:
500 case NWAM_ACTION_REFRESH:
501 nwamd_known_wlan_handle_init_event(known_wlan_event);
502 break;
503 case NWAM_ACTION_DESTROY:
504 /* Nothing needs to be done for destroy */
505 break;
506 /* all other events are invalid for known WLANs */
507 case NWAM_ACTION_ENABLE:
508 case NWAM_ACTION_DISABLE:
509 default:
510 nlog(LOG_INFO, "nwam_known_wlan_handle_action_event: "
511 "unexpected action");
512 break;
517 nwamd_known_wlan_action(const char *known_wlan, nwam_action_t action)
519 nwamd_event_t known_wlan_event = nwamd_event_init_object_action
520 (NWAM_OBJECT_TYPE_KNOWN_WLAN, known_wlan, NULL, action);
521 if (known_wlan_event == NULL)
522 return (1);
523 nwamd_event_enqueue(known_wlan_event);
524 return (0);