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]
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
33 #include <libdllink.h>
34 #include <libdlwlan.h>
39 #include "known_wlans.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 */
55 } known_wifi_nets_fields_t
;
57 /* Structure for one BSSID */
58 typedef struct bssid
{
59 struct qelem bssid_links
;
63 /* Structure for an ESSID and its BSSIDs */
65 struct qelem kw_links
;
66 char kw_essid
[NWAM_MAX_NAME_LEN
];
67 uint32_t kw_num_bssids
;
68 struct qelem kw_bssids
;
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
;
85 kw_list
.q_forw
= kw_list
.q_back
= &kw_list
;
94 while (kw_list
.q_forw
!= &kw_list
) {
95 kw
= (kw_t
*)kw_list
.q_forw
;
98 while (kw
->kw_bssids
.q_forw
!= &kw
->kw_bssids
) {
99 b
= (bssid_t
*)kw
->kw_bssids
.q_forw
;
100 remque(&b
->bssid_links
);
104 remque(&kw
->kw_links
);
109 /* Returns the entry in kw_list for the given ESSID. NULL if non-existent */
111 kw_lookup(const char *essid
)
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)
127 /* Adds an ESSID/BSSID combination to kw_list. Returns B_TRUE on success. */
129 kw_add(const char *essid
, const char *bssid
)
134 if ((b
= calloc(1, sizeof (bssid_t
))) == NULL
) {
135 nlog(LOG_ERR
, "kw_add: cannot allocate for bssid_t: %m");
138 if ((kw
= calloc(1, sizeof (kw_t
))) == NULL
) {
139 nlog(LOG_ERR
, "kw_add: cannot allocate for kw_t: %m");
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
);
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.
164 kw_update(kw_t
*kw
, const char *bssid
)
168 if ((b
= calloc(1, sizeof (bssid_t
))) == NULL
) {
169 nlog(LOG_ERR
, "kw_update: cannot allocate for bssid_t: %m");
173 b
->bssid
= strdup(bssid
);
174 insque(&b
->bssid_links
, kw
->kw_bssids
.q_back
);
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
);
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).
193 parse_known_wifi_nets(void)
197 char *cp
, *tok
[MAX_FIELDS
];
198 int lnum
, num_kw
= 0;
204 * The file format is:
205 * essid\tbssid (essid followed by tab followed by bssid)
207 fp
= fopen(KNOWN_WIFI_NETS_FILE
, "r");
210 for (lnum
= 1; fgets(line
, sizeof (line
), fp
) != NULL
; lnum
++) {
215 if (*cp
== '#' || *cp
== '\0')
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
);
224 if ((kw
= kw_lookup(tok
[ESSID
])) == NULL
) {
225 if (!kw_add(tok
[ESSID
], tok
[BSSID
])) {
227 "%s:%d: cannot add entry (%s,%s) to list",
228 KNOWN_WIFI_NETS_FILE
, lnum
,
229 tok
[ESSID
], tok
[BSSID
]);
234 if (!kw_update(kw
, tok
[BSSID
])) {
236 "%s:%d:cannot update entry (%s,%s) to list",
237 KNOWN_WIFI_NETS_FILE
, lnum
,
238 tok
[ESSID
], tok
[BSSID
]);
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.
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 if (nsa
->nsa_key
!= NULL
)
268 /* Retrive key so we can get security mode */
269 nsa
->nsa_key
= nwamd_wlan_get_key_named(secobjname
, 0);
270 (void) strlcpy(nsa
->nsa_keyname
, secobjname
,
271 sizeof (nsa
->nsa_keyname
));
272 switch (nsa
->nsa_key
->wk_class
) {
273 case DLADM_SECOBJ_CLASS_WEP
:
274 nsa
->nsa_secmode
= DLADM_WLAN_SECMODE_WEP
;
275 nlog(LOG_DEBUG
, "find_secobj_matching_prefix: "
276 "got WEP key %s", nsa
->nsa_keyname
);
278 case DLADM_SECOBJ_CLASS_WPA
:
279 nsa
->nsa_secmode
= DLADM_WLAN_SECMODE_WPA
;
280 nlog(LOG_DEBUG
, "find_secobj_matching_prefix: "
281 "got WPA key %s", nsa
->nsa_keyname
);
284 /* shouldn't happen */
285 nsa
->nsa_secmode
= DLADM_WLAN_SECMODE_NONE
;
286 nlog(LOG_ERR
, "find_secobj_matching_prefix: "
287 "key class for key %s was invalid",
296 /* Upgrade /etc/nwam/known_wifi_nets file to new libnwam-based config model */
298 upgrade_known_wifi_nets_config(void)
302 nwam_known_wlan_handle_t kwh
;
307 struct nwamd_secobj_arg nsa
;
309 nlog(LOG_INFO
, "Upgrading %s to Known WLANs", KNOWN_WIFI_NETS_FILE
);
311 /* Parse /etc/nwam/known_wifi_nets */
312 num_kw
= parse_known_wifi_nets();
314 /* Create Known WLANs for each unique ESSID */
315 for (kw
= (kw_t
*)kw_list
.q_forw
, priority
= num_kw
-1;
316 kw
!= (kw_t
*)&kw_list
;
317 kw
= (kw_t
*)kw
->kw_links
.q_forw
, priority
--) {
318 nwam_value_t priorityval
= NULL
;
319 nwam_value_t bssidsval
= NULL
;
320 nwam_value_t secmodeval
= NULL
;
321 nwam_value_t keynameval
= NULL
;
323 nlog(LOG_DEBUG
, "Creating Known WLAN %s", kw
->kw_essid
);
325 if ((err
= nwam_known_wlan_create(kw
->kw_essid
, &kwh
))
327 nlog(LOG_ERR
, "upgrade wlan %s: "
328 "could not create known wlan: %s", kw
->kw_essid
,
333 /* priority of this ESSID */
334 if ((err
= nwam_value_create_uint64(priority
, &priorityval
))
336 nlog(LOG_ERR
, "upgrade wlan %s: "
337 "could not create priority value: %s", kw
->kw_essid
,
339 nwam_known_wlan_free(kwh
);
342 err
= nwam_known_wlan_set_prop_value(kwh
,
343 NWAM_KNOWN_WLAN_PROP_PRIORITY
, priorityval
);
344 nwam_value_free(priorityval
);
345 if (err
!= NWAM_SUCCESS
) {
346 nlog(LOG_ERR
, "upgrade wlan %s: "
347 "could not set priority value: %s", kw
->kw_essid
,
349 nwam_known_wlan_free(kwh
);
353 /* loop through kw->kw_bssids and create an array of bssids */
354 bssids
= calloc(kw
->kw_num_bssids
, sizeof (char *));
355 if (bssids
== NULL
) {
356 nwam_known_wlan_free(kwh
);
357 nlog(LOG_ERR
, "upgrade wlan %s: "
358 "could not calloc for bssids: %m", kw
->kw_essid
);
361 for (b
= (bssid_t
*)kw
->kw_bssids
.q_forw
, i
= 0;
362 b
!= (bssid_t
*)&kw
->kw_bssids
;
363 b
= (bssid_t
*)b
->bssid_links
.q_forw
, i
++) {
364 bssids
[i
] = strdup(b
->bssid
);
367 /* BSSIDs for this ESSID */
368 if ((err
= nwam_value_create_string_array(bssids
,
369 kw
->kw_num_bssids
, &bssidsval
)) != NWAM_SUCCESS
) {
370 nlog(LOG_ERR
, "upgrade wlan %s: "
371 "could not create bssids value: %s", kw
->kw_essid
,
373 for (i
= 0; i
< kw
->kw_num_bssids
; i
++)
376 nwam_known_wlan_free(kwh
);
379 err
= nwam_known_wlan_set_prop_value(kwh
,
380 NWAM_KNOWN_WLAN_PROP_BSSIDS
, bssidsval
);
381 nwam_value_free(bssidsval
);
382 for (i
= 0; i
< kw
->kw_num_bssids
; i
++)
385 if (err
!= NWAM_SUCCESS
) {
386 nlog(LOG_ERR
, "upgrade wlan %s: "
387 "could not set bssids: %s", kw
->kw_essid
,
389 nwam_known_wlan_free(kwh
);
394 * Retrieve last key matching ESSID prefix if any, and set
395 * the retrieved key name and security mode.
397 nwamd_set_key_name(kw
->kw_essid
, NULL
, nsa
.nsa_essid_prefix
,
398 sizeof (nsa
.nsa_essid_prefix
));
400 nsa
.nsa_secmode
= DLADM_WLAN_SECMODE_NONE
;
401 (void) dladm_walk_secobj(dld_handle
, &nsa
,
402 find_secobj_matching_prefix
, DLADM_OPT_PERSIST
);
403 if (nsa
.nsa_key
!= NULL
) {
404 if ((err
= nwam_value_create_string(nsa
.nsa_keyname
,
405 &keynameval
)) == NWAM_SUCCESS
) {
406 (void) nwam_known_wlan_set_prop_value(kwh
,
407 NWAM_KNOWN_WLAN_PROP_KEYNAME
, keynameval
);
410 nwam_value_free(keynameval
);
413 if ((err
= nwam_value_create_uint64(nsa
.nsa_secmode
,
414 &secmodeval
)) != NWAM_SUCCESS
||
415 (err
= nwam_known_wlan_set_prop_value(kwh
,
416 NWAM_KNOWN_WLAN_PROP_SECURITY_MODE
, secmodeval
))
418 nlog(LOG_ERR
, "upgrade wlan %s: "
419 "could not set security mode: %s",
420 kw
->kw_essid
, nwam_strerror(err
));
421 nwam_value_free(secmodeval
);
422 nwam_known_wlan_free(kwh
);
426 /* commit, no collision checking by libnwam */
427 err
= nwam_known_wlan_commit(kwh
,
428 NWAM_FLAG_KNOWN_WLAN_NO_COLLISION_CHECK
);
429 nwam_known_wlan_free(kwh
);
430 if (err
!= NWAM_SUCCESS
) {
431 nlog(LOG_ERR
, "upgrade wlan %s: "
432 "could not commit wlan: %s", kw
->kw_essid
,
442 known_wlan_get_keyname(const char *essid
, char *name
)
444 nwam_known_wlan_handle_t kwh
= NULL
;
445 nwam_value_t keynameval
= NULL
;
449 if ((err
= nwam_known_wlan_read(essid
, 0, &kwh
)) != NWAM_SUCCESS
)
451 if ((err
= nwam_known_wlan_get_prop_value(kwh
,
452 NWAM_KNOWN_WLAN_PROP_KEYNAME
, &keynameval
)) == NWAM_SUCCESS
&&
453 (err
= nwam_value_get_string(keynameval
, &keyname
))
455 (void) strlcpy(name
, keyname
, NWAM_MAX_VALUE_LEN
);
457 if (keynameval
!= NULL
)
458 nwam_value_free(keynameval
);
461 nwam_known_wlan_free(kwh
);
466 /* Performs a scan on a wifi link NCU */
469 nwamd_ncu_known_wlan_committed(nwamd_object_t object
, void *data
)
471 nwamd_ncu_t
*ncu_data
= object
->nwamd_object_data
;
473 if (ncu_data
->ncu_type
!= NWAM_NCU_TYPE_LINK
)
476 /* network selection will be done only if possible */
477 if (ncu_data
->ncu_link
.nwamd_link_media
== DL_WIFI
)
478 (void) nwamd_wlan_scan(ncu_data
->ncu_name
);
482 /* Handle known WLAN initialization/refresh event */
485 nwamd_known_wlan_handle_init_event(nwamd_event_t known_wlan_event
)
488 * Since the Known WLAN list has changed, do a rescan so that the
489 * best network is selected.
491 (void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU
,
492 nwamd_ncu_known_wlan_committed
, NULL
);
496 nwamd_known_wlan_handle_action_event(nwamd_event_t known_wlan_event
)
498 switch (known_wlan_event
->event_msg
->nwe_data
.nwe_object_action
.
500 case NWAM_ACTION_ADD
:
501 case NWAM_ACTION_REFRESH
:
502 nwamd_known_wlan_handle_init_event(known_wlan_event
);
504 case NWAM_ACTION_DESTROY
:
505 /* Nothing needs to be done for destroy */
507 /* all other events are invalid for known WLANs */
508 case NWAM_ACTION_ENABLE
:
509 case NWAM_ACTION_DISABLE
:
511 nlog(LOG_INFO
, "nwam_known_wlan_handle_action_event: "
512 "unexpected action");
518 nwamd_known_wlan_action(const char *known_wlan
, nwam_action_t action
)
520 nwamd_event_t known_wlan_event
= nwamd_event_init_object_action
521 (NWAM_OBJECT_TYPE_KNOWN_WLAN
, known_wlan
, NULL
, action
);
522 if (known_wlan_event
== NULL
)
524 nwamd_event_enqueue(known_wlan_event
);