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) */
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
);
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
);
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",
295 /* Upgrade /etc/nwam/known_wifi_nets file to new libnwam-based config model */
297 upgrade_known_wifi_nets_config(void)
301 nwam_known_wlan_handle_t kwh
;
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
))
326 nlog(LOG_ERR
, "upgrade wlan %s: "
327 "could not create known wlan: %s", kw
->kw_essid
,
332 /* priority of this ESSID */
333 if ((err
= nwam_value_create_uint64(priority
, &priorityval
))
335 nlog(LOG_ERR
, "upgrade wlan %s: "
336 "could not create priority value: %s", kw
->kw_essid
,
338 nwam_known_wlan_free(kwh
);
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
,
348 nwam_known_wlan_free(kwh
);
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
);
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
,
372 for (i
= 0; i
< kw
->kw_num_bssids
; i
++)
375 nwam_known_wlan_free(kwh
);
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
++)
384 if (err
!= NWAM_SUCCESS
) {
385 nlog(LOG_ERR
, "upgrade wlan %s: "
386 "could not set bssids: %s", kw
->kw_essid
,
388 nwam_known_wlan_free(kwh
);
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
));
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
);
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
))
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
);
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
,
441 known_wlan_get_keyname(const char *essid
, char *name
)
443 nwam_known_wlan_handle_t kwh
= NULL
;
444 nwam_value_t keynameval
= NULL
;
448 if ((err
= nwam_known_wlan_read(essid
, 0, &kwh
)) != NWAM_SUCCESS
)
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
))
454 (void) strlcpy(name
, keyname
, NWAM_MAX_VALUE_LEN
);
456 if (keynameval
!= NULL
)
457 nwam_value_free(keynameval
);
460 nwam_known_wlan_free(kwh
);
465 /* Performs a scan on a wifi link NCU */
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
)
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
);
481 /* Handle known WLAN initialization/refresh event */
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
);
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
.
499 case NWAM_ACTION_ADD
:
500 case NWAM_ACTION_REFRESH
:
501 nwamd_known_wlan_handle_init_event(known_wlan_event
);
503 case NWAM_ACTION_DESTROY
:
504 /* Nothing needs to be done for destroy */
506 /* all other events are invalid for known WLANs */
507 case NWAM_ACTION_ENABLE
:
508 case NWAM_ACTION_DISABLE
:
510 nlog(LOG_INFO
, "nwam_known_wlan_handle_action_event: "
511 "unexpected action");
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
)
523 nwamd_event_enqueue(known_wlan_event
);