/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <execinfo.h>
#include <kstat.h>
#include <libdladm.h>
#include <libdllink.h>
#include <libdlstat.h>
#include <libdlwlan.h>
#include <libnwam.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <libdlpi.h>
#include <ucontext.h>
#include "events.h"
#include "llp.h"
#include "objects.h"
#include "ncp.h"
#include "ncu.h"
#include "known_wlans.h"
#include "util.h"
/*
* ncu_phys.c - contains routines that are physical-link specific.
* Mostly WiFi code.
*/
/*
* Get link state from kstats. Used to determine initial link state for
* cases where drivers do not support DL_NOTE_LINK_UP/DOWN. If link
* state is LINK_STATE_UNKNOWN, we assume the link is up and the IP NCU
* timeout will cause us to move on to other links.
*/
{
return (link_state);
goto out;
/*
* The kstat query could fail if the underlying MAC
* driver was already detached.
*/
goto out;
}
goto out;
&link_state);
out:
(void) kstat_close(kcp);
return (link_state);
}
/*
* autopush modules. We set MAC address last as setting it may cause a chip
* reset which can prevent other device property setting succeeding.
*/
void
{
int retval;
char *cp;
/*
* Set MTU here - either default value (if mtu == 0 indicating it has
* not been set) or specified value.
*/
if (mtu == 0) {
if (status != DLADM_STATUS_OK) {
"dladm_get_linkprop failed: %s",
return;
}
} else {
}
if (status != DLADM_STATUS_OK) {
"dladm_set_linkprop failed: %s",
}
if (status != DLADM_STATUS_OK) {
"dladm_set_linkprop failed for autopush property: %s",
}
/*
* Set physical address - either factory (if link_mac_addr is NULL
* or we are unsetting properties) or specified MAC address string.
*/
"nwamd_set_unset_link_properties: malloc() failed");
return;
}
"could not get physical address for %s: %s",
return;
}
} else {
if (addrlen == -1) {
"nwamd_set_unset_link_properties: "
"%s: bad address for %s",
return;
} else {
" malloc() failed");
return;
}
}
}
/*
* Only set physical address if desired address differs from current -
* this avoids unnecessary chip resets for some drivers.
*/
&hwaddrlen);
if (retval != DLPI_SUCCESS) {
"failed setting mac address on %s: %s",
}
}
}
/*
* The variable wireless_scan_level specifies the signal level
* that we will initiate connections to previously-visited APs
* at when we are in the connected state.
*/
/*
* The variable wireless_scan_interval specifies how often the periodic
* scan occurs.
*/
/*
* The variable wireless_autoconf specifies if we use dladm_wlan_autoconf()
* to connect.
*/
/*
* The variable wireless_strict_bssid specifies if we only connect
* to WLANs with BSSIDs that we previously connected to.
*/
/*
* We need to ensure scan or connect threads do not run concurrently
* on any links - otherwise we get radio interference. Acquire this
*/
static void
scanconnect_entry(void)
{
(void) pthread_mutex_lock(&wireless_mutex);
}
static void
scanconnect_exit(void)
{
(void) pthread_mutex_unlock(&wireless_mutex);
}
/*
* and dladm_get_secobj().
*/
/*
* Convert key hexascii string to raw secobj value. This
* code is very similar to convert_secobj() in dladm.c, it would
* be good to have a libdladm function to convert values.
*/
static int
{
buf_len);
if (buf_len == 0) {
/* length zero means "delete" */
return (0);
}
buf_len);
if (class == DLADM_SECOBJ_CLASS_WPA) {
/*
* Per IEEE802.11i spec, the Pre-shared key (PSK) length should
* be between 8 and 63.
*/
"key_string_to_secobj_value:"
" invalid WPA key length: buf_len = %d", buf_len);
return (-1);
}
return (0);
}
switch (buf_len) {
case 5: /* ASCII key sizes */
case 13:
break;
case 10:
case 26: /* Hex key sizes, not preceded by 0x */
!= 0) {
"key_string_to_secobj_value: invalid WEP key");
return (-1);
}
break;
case 12:
case 28: /* Hex key sizes, preceded by 0x */
obj_lenp) != 0) {
"key_string_to_secobj_value: invalid WEP key");
return (-1);
}
break;
default:
"key_string_to_secobj_value: invalid WEP key length");
return (-1);
}
return (0);
}
/*
* Callback used on each known WLAN:
* return 1 if a secobj, linked with an existing kwown wlan, has the same name
* of the secobj that is being created.
*/
static int
{
char **old_keyname;
nwam_strerror(err));
return (0);
}
!= NWAM_SUCCESS) {
nwam_strerror(err));
return (0);
}
for (i = 0; i < num_old_keyname; i++) {
/* Found matching keyname so terminate walk */
return (1);
}
return (0);
}
/*
* Print the key name format into the appropriate field, then convert any ":"
* characters to ".", as ":[1-4]" is the slot indicator, which otherwise
* would trip us up. Invalid characters for secobj names are ignored.
* The fourth parameter is expected to be of size DLADM_SECOBJ_NAME_MAX.
*
* (Note that much of the system uses DLADM_WLAN_MAX_KEYNAME_LEN, which is 64
* rather than 32, but that dladm_get_secobj will fail if a length greater than
* DLD_SECOBJ_NAME_MAX is seen, and that's 32. This is all horribly broken.)
*/
void
{
int i, j;
/* create a concatenated string with essid and bssid */
essid);
} else {
}
/* copy only valid chars to the return string, terminating with \0 */
i = 0; /* index into secobj_name */
j = 0; /* index into name */
while (secobj_name[i] != '\0') {
if (j == nsz - 1)
break;
if (secobj_name[i] == ':') {
name[j] = '.';
j++;
} else if (isalnum(secobj_name[i]) ||
secobj_name[i] == '_') {
name[j] = secobj_name[i];
j++;
}
i++;
}
name[j] = '\0';
}
{
int ret = 0;
== NULL) {
"for link %s", linkname);
return (NWAM_ENTITY_NOT_FOUND);
}
class) != 0) {
/* above function logs internally on failure */
return (NWAM_ERROR_INTERNAL);
}
/*
* Name key object for this WLAN so it can be later retrieved.
* (bssid is appended if an object, with the same keyname,
* already exists and is associated to a known wlan)
*/
/*
* We also check if the keyval is the same. The user might want
* to use the same key for more APs with the same ESSID.
* This can result in a known wlan with multiple BSSIDs
*/
if (ret == 1) {
sizeof (obj_name));
} else {
sizeof (obj_name));
}
/*
* We have validated the new key, so remove the old one.
* This will actually delete the keyobj only if the user had set
* a wrong key and is replacing it with a new one for the same AP.
*/
"'%s' for key: %s", obj_name,
return (NWAM_ERROR_INTERNAL);
}
/* if we're just deleting the key, then we're done */
if (raw_key[0] == '\0') {
return (NWAM_SUCCESS);
}
if (status != DLADM_STATUS_OK) {
"'%s' for key: %s", obj_name,
return (NWAM_ERROR_INTERNAL);
}
sizeof (link->nwamd_link_wifi_keyname));
if (security_mode == DLADM_WLAN_SECMODE_WEP) {
}
/* If link NCU is offline* or online, (re)connect. */
switch (ncu_obj->nwamd_object_state) {
case NWAM_STATE_ONLINE:
/* if changing the key of the connected WLAN, reconnect */
break;
/* if we are waiting for the key, connect */
if (ncu_obj->nwamd_object_aux_state ==
break;
default:
break;
}
return (NWAM_SUCCESS);
}
/*
* returns NULL if no key was recovered from libdladm. Passing in
* security mode of 0 means we don't care what key type it is.
*/
{
if (security_mode == DLADM_WLAN_SECMODE_NONE)
return (NULL);
/*
* Newly-allocated key must be freed by caller, or by
* subsequent call to nwamd_wlan_get_key_named().
*/
return (NULL);
}
/*
* Set name appropriately to retrieve key for this WLAN. Note that we
* cannot use the actual wk_name buffer size, as it's two times too
* large for dladm_get_secobj.
*/
/* Try the kernel first, then fall back to persistent storage. */
if (status != DLADM_STATUS_OK) {
"dladm_get_secobj(TEMP) failed: %s",
}
switch (status) {
case DLADM_STATUS_OK:
break;
case DLADM_STATUS_NOTFOUND:
/*
* We do not want an error in the case that the secobj
* is not found, since we then prompt for it.
*/
return (NULL);
default:
return (NULL);
}
if (security_mode != 0) {
switch (class) {
case DLADM_SECOBJ_CLASS_WEP:
if (security_mode == DLADM_WLAN_SECMODE_WEP)
return (cooked_key);
break;
case DLADM_SECOBJ_CLASS_WPA:
if (security_mode == DLADM_WLAN_SECMODE_WPA)
return (cooked_key);
break;
default:
/* shouldn't happen */
class);
break;
}
/* key type mismatch */
return (NULL);
}
return (cooked_key);
}
static dladm_wlan_key_t *
{
}
/*
* Checks if a wireless network can be selected or not. A wireless network
* CANNOT be selected if the NCU is DISABLED, or the NCU is OFFLINE or
* ONLINE* and has lower priority than the currently active priority-group.
* Called with object lock held.
*/
static boolean_t
{
return (B_FALSE);
(void) pthread_mutex_lock(&active_ncp_mutex);
(void) pthread_mutex_unlock(&active_ncp_mutex);
return (B_FALSE);
}
(void) pthread_mutex_unlock(&active_ncp_mutex);
return (B_TRUE);
}
/*
* scan data. If these change, we need to trigger a scan
* event since the updated values need to be communicated
* to the GUI.
*/
void
{
int i;
for (i = 0; i < s->nwamd_wifi_scan_curr_num; i++) {
link->nwamd_link_wifi_essid) != 0 ||
link->nwamd_link_wifi_bssid) != 0))
continue;
if (selected) {
if (!s->nwamd_wifi_scan_curr[i].nww_selected)
} else {
if (s->nwamd_wifi_scan_curr[i].nww_selected)
}
if (connected) {
if (!s->nwamd_wifi_scan_curr[i].nww_connected)
} else {
if (s->nwamd_wifi_scan_curr[i].nww_connected)
}
}
if (trigger_scan_event || s->nwamd_wifi_scan_changed) {
if (scan_event != NULL) {
/* Avoid sending same scan data multiple times */
}
}
}
/*
* Callback used on each known WLAN - if the BSSID is matched, set
* the ESSID of the hidden WLAN to the known WLAN name.
*/
static int
{
nwam_strerror(err));
return (0);
}
!= NWAM_SUCCESS) {
nwam_strerror(err));
return (0);
}
for (i = 0; i < num_bssids; i++) {
!= NWAM_SUCCESS) {
"nwam_known_wlan_get_name: %s",
nwam_strerror(err));
continue;
}
sizeof (link->nwamd_link_wifi_essid));
/* Found ESSID for BSSID so terminate walk */
return (1);
}
}
return (0);
}
/*
* We may have encountered a BSSID for a hidden WLAN before and as a result
* may have a known WLAN entry with this BSSID. Walk known WLANs, searching
* for a BSSID match. Called with object lock held.
*/
static void
{
}
{
== NULL) {
"for link %s", linkname);
return (NWAM_ENTITY_NOT_FOUND);
}
/*
* If wireless selection is not possible because of the current
* state or priority-group, then stop.
*/
if (!wireless_selection_possible(ncu_obj)) {
return (NWAM_ENTITY_INVALID_STATE);
}
/* unset selected, connected flag for previously connected wlan */
/* Disconnect to allow new selection to go ahead */
sizeof (link->nwamd_link_wifi_essid));
sizeof (link->nwamd_link_wifi_bssid));
/* If this is a hidden wlan, then essid is empty */
/* set selected flag for newly-selected WLAN */
/* does this WLAN require a key? If so go to NEED_KEY */
/*
* nwam secobjs can have two formats: nwam-ESSID-BSSID and
* nwam-ESSID. There is no reason for searching through known
* wlan keynames since this is only the selection process.
*/
/*
* Found old key format,
* known wlans with similar names might exist
*/
"WLAN key %s",
} else {
"find key for WLAN '%s'",
}
} else {
}
} else {
}
return (NWAM_SUCCESS);
}
/*
* See if BSSID is in visited list of BSSIDs for known WLAN. Used for
* strict BSSID matching (depends on wireless_strict_bssid property value).
*/
static int
{
char **bssids;
int found = 0;
return (0);
}
!= NWAM_SUCCESS) {
return (0);
}
for (i = 0; i < nelem; i++) {
found = 1;
break;
}
}
return (found);
}
/* Find most prioritized AP with strongest signal in scan data. */
static int
{
int i;
nwam_strerror(err));
return (0);
}
if (link->nwamd_link_wifi_connected) {
(void) dladm_wlan_str2strength
}
/*
* If we're >= scan level, don't pick another Known WLAN if still
* connected (even if a Known WLAN with higher priority is available).
* If the user wants to connect to a different Known WLAN, it can be
* done from the GUI or select-wifi subcommand of nwamadm(1M).
*/
if (curr_strength >= wireless_scan_level &&
return (1);
}
for (i = 0; i < s->nwamd_wifi_scan_curr_num; i++) {
/*
* We need to either match the scanned essid, or in the case
* where the essid was not broadcast, match the scanned bssid.
*/
continue;
/*
* If wireless_strict_bssid is specified, need to match
* BSSID too.
*/
if (wireless_strict_bssid && !b_match)
continue;
/*
* Found a match. Since we walk known WLANs in
* priority order, it's guaranteed to be the
* most prioritized. It may not be the strongest though -
* we continue the walk and record the strength along
* with the ESSID and BSSID, so that if we encounter
* another AP with the same ESSID but a higher signal strength,
* we will choose it - but only if the currently-connected
* WLAN is at or below wireless_scan_level.
*/
(void) dladm_wlan_str2strength
if (curr_strength > max_strength) {
sizeof (link->nwamd_link_wifi_essid));
/*
* Set BSSID if wireless_strict_bssid is specified or
* if this is a hidden WLAN. Store the BSSID here and
* then later determine the hidden WLAN's name in the
* connect thread.
*/
if (wireless_strict_bssid ||
sizeof (link->nwamd_link_wifi_bssid));
}
sizeof (link->nwamd_link_wifi_signal_strength));
}
(void) dladm_wlan_str2strength
}
return (found ? 1 : 0);
}
static boolean_t
{
int ret;
/*
* Walk known WLANs, finding lowest priority (preferred) WLAN
* in our scan results.
*/
return (ret == 1);
}
/*
* WLAN scan code for WIFI link NCUs.
*/
/* Create periodic scan event for object. Called with object lock held. */
void
{
if (wireless_scan_interval == 0) {
"wireless_scan_interval set to 0 so no periodic scanning");
return;
}
if (scan_event != NULL) {
}
}
/* Handle periodic scan event (which puts link into WIFI_INIT state */
void
{
return;
}
/* Only rescan if state is offline* or online */
/* rescan, then create periodic scan event */
}
}
static boolean_t
{
nwamd_wifi_scan_t *s = arg;
if (index == NWAMD_MAX_NUM_WLANS) {
return (B_TRUE);
}
/*
* reacquire the object lock.
*/
s->nwamd_wifi_scan_curr_num++;
/* Check if this AP was in previous scan results */
for (i = 0; i < s->nwamd_wifi_scan_last_num; i++) {
essid_name) == 0 &&
bssid_name) == 0);
if (found)
break;
}
if (!found)
return (B_TRUE);
}
/*
* Check if we're connected to the expected WLAN, or in the case of autoconf
* record the WLAN we're connected to.
*/
{
int retries = 0;
/*
* This is awful, but some wireless drivers
* (particularly 'ath') will erroneously report
* "disconnected" if queried right after a scan. If we
* see 'down' reported here, we retry a few times to
* make sure it's really down.
*/
while (retries++ < 4) {
&attr) != DLADM_STATUS_OK) {
break;
}
}
} else {
return (B_FALSE);
}
/*
* If we're using autoconf, we have no control over what we connect to,
*/
if (link->nwamd_link_wifi_autoconf) {
sizeof (link->nwamd_link_wifi_essid));
sizeof (link->nwamd_link_wifi_bssid));
}
/*
* Are we connected to expected WLAN? Note:
* we'd like to verify BSSID, but we cannot due to CR 6772510.
*/
/* Update connected signal strength */
/* Store current BSSID */
sizeof (link->nwamd_link_wifi_bssid));
/*
* We're connected, but we've dropped below
* scan threshold. Initiate a scan.
*/
"connected but signal under threshold...");
}
return (connected);
/*
* For hidden WLANs, no ESSID is specified, so we cannot verify
* WLAN name.
*/
"nwamd_wlan_connected: connected to hidden WLAN, cannot "
"verify connection details");
return (connected);
} else {
"nwamd_wlan_connected: wrong AP on %s; expected %s %s",
return (B_FALSE);
}
}
/*
* WLAN scan thread. Called with the per-link WiFi mutex held.
*/
static void *
{
int i;
== NULL) {
"for link %s", linkname);
return (NULL);
}
/*
* It is possible multiple scan threads have queued up waiting for the
* object lock. We try to prevent excessive scanning by limiting the
* interval between scans to WIRELESS_SCAN_REQUESTED_INTERVAL_MIN sec.
*/
"was < %d sec ago, ignoring scan request",
return (NULL);
}
/*
* Prepare scan data - copy link name and copy previous "current"
* scan results from the nwamd_link_t to the last scan results for
* the next scan so that we can compare results to find if things
* have changed since last time.
*/
(void) bzero(&s, sizeof (nwamd_wifi_scan_t));
sizeof (s.nwamd_wifi_scan_link));
if (s.nwamd_wifi_scan_last_num > 0) {
(void) memcpy(s.nwamd_wifi_scan_last,
s.nwamd_wifi_scan_last_num * sizeof (nwam_wlan_t));
}
if (!s.nwamd_wifi_scan_changed) {
/* Scan may have lost WLANs, if so this qualifies as change */
}
if (status != DLADM_STATUS_OK) {
return (NULL);
}
== NULL) {
"for link %s after doing scan", linkname);
return (NULL);
}
/* For new scan data, add key info from known WLANs */
for (i = 0; i < s.nwamd_wifi_scan_curr_num; i++) {
/*
* If strict_bssid is true, we start checking for
* known wlans with the same BSSID.
* This would prevent the selection of secobjs
* that actually are referenced by different kwl
* with the same ESSID.
*/
if (wireless_strict_bssid) {
int b_match = 0;
(void) nwam_walk_known_wlans(bssid_match,
s.nwamd_wifi_scan_curr[i].nww_bssid, 0,
&b_match);
if (b_match == 0)
continue;
}
== NWAM_SUCCESS &&
!= NULL) {
s.nwamd_wifi_scan_curr[i].nww_have_key =
s.nwamd_wifi_scan_curr[i].nww_keyindex =
s.nwamd_wifi_scan_curr[i].
}
}
}
/* Copy scan data into nwamd_link_t */
link->nwamd_link_wifi_scan = s;
/* Set selected, connected and send scan event if we've got new data */
/*
* If wireless selection is not possible because of the current
* state or priority-group, then this was just a scan request.
* Nothing else to do.
*/
if (!wireless_selection_possible(ncu_obj)) {
return (NULL);
}
/*
* Check if WLAN is on our known WLAN list. If no
* previously-visited WLANs are found in scan data, set
* new state to NEED_SELECTION (provided we're not currently
* connected, as can be the case during a periodic scan or
* monitor-triggered scan where the signal strength recovers.
*/
if (!nwamd_find_known_wlan(ncu_obj)) {
if (!nwamd_wlan_connected(ncu_obj)) {
if (link->nwamd_link_wifi_connected) {
"unexpected disconnect after scan");
} else {
"no known WLANs - ask user");
}
} else {
/* still connected. if not online, change to online */
}
}
} else {
if (!nwamd_wlan_connected(ncu_obj)) {
sizeof (essid));
sizeof (bssid));
} else {
/* still connected. if not online, change to online */
}
}
}
return (NULL);
}
{
return (NWAM_NO_MEMORY);
}
link);
link) != 0) {
return (NWAM_ERROR_INTERNAL);
}
/* detach thread so that it doesn't become a zombie */
(void) pthread_detach(wifi_thread);
return (NWAM_SUCCESS);
}
/*
* WLAN connection code.
*/
static dladm_status_t
{
return (status);
}
static void *
{
== NULL) {
"for link %s", linkname);
return (NULL);
}
if (!wireless_selection_possible(ncu_obj)) {
goto done;
}
/* If it is already connected to the required AP, just return. */
if (nwamd_wlan_connected(ncu_obj)) {
goto done;
}
!= DLADM_STATUS_OK) {
goto done;
}
/* note: bssid logic here is non-functional */
} else {
}
}
/* First check for the key */
goto done;
}
/* Make a copy of the key as we need to unlock the object */
goto done;
}
sizeof (dladm_wlan_key_t));
keycount = 1;
} else {
keycount = 0;
}
/*
* Connect; only scan if a bssid was not specified. If it times out,
* try a second time using autoconf. Drop the object lock during the
* connect attempt since connecting may take some time, and access to
* the link object during that period would be impossible if we held the
* lock.
*/
if (status != DLADM_STATUS_OK) {
/* Connect failed, try autoconf */
NULL, 0, 0)) != DLADM_STATUS_OK) {
"%s", linkname);
goto done_unlocked;
}
if (status == DLADM_STATUS_OK)
}
/* Connect succeeded, reacquire object */
== NULL) {
"for link %s", linkname);
goto done_unlocked;
}
if (autoconf)
/*
* may be wrong. It is difficult to find a reliable test that works
* across APs however. Do nothing for now.
*/
if (link->nwamd_link_wifi_connected) {
/* add to known WLANs */
"add '%s' to known WLANs",
!= NWAM_SUCCESS) {
"could not add to known WLANs: %s",
nwam_strerror(err));
}
}
"succeeded, setting state online");
}
done:
return (NULL);
}
void
{
return;
}
link);
/* detach thread so that it doesn't become a zombie */
(void) pthread_detach(wifi_thread);
}
/*
* Launch signal strength-monitoring thread which periodically
* checks connection and signal strength. If we become disconnected
* or signal drops below threshold specified by wireless_scan_level,
* initiate a scan. The scan initiation is taken care of by
* the call to nwamd_wlan_connected().
*/
static void *
{
for (;;) {
"not find object for link %s", linkname);
break;
}
"%s is %s, stopping thread", linkname,
break;
}
/*
* First time thru loop, we check if there is another
* link monitoring thread in operation - if so exit this
* thread.
*/
if (first_time) {
if (link->nwamd_link_wifi_monitor_thread != 0) {
/* Already have a monitor thread for link? */
break;
} else {
pthread_self();
}
}
if (!nwamd_wlan_connected(ncu_obj)) {
"disconnect occured for WLAN on link %s", linkname);
break;
}
(void) sleep(WIRELESS_MONITOR_SIGNAL_INTERVAL);
}
return (NULL);
}
void
{
return;
}
link);
link) != 0) {
"link %s", link);
return;
}
/* detach thread so that it doesn't become a zombie */
(void) pthread_detach(wifi_thread);
}
void
{
return;
}
/*
* We ignore link state events for WiFi because it is very flaky.
* Instead we use the monitor thread and drive WiFi state changes from
* there.
*/
return;
}
/*
* If it's a link up event and we're not disabled, go online.
*/
if (link->nwamd_link_activation_mode ==
(void) pthread_mutex_lock(&active_ncp_mutex);
(void) pthread_mutex_unlock(&active_ncp_mutex);
/* compare priority groups */
"nwamd_ncu_handle_link_state_event: "
"got LINK UP event for priority group "
"%lld, less preferred than current %lld, "
"ignoring",
} else if (link->nwamd_link_priority_group ==
"nwamd_ncu_handle_link_state_event: "
"got LINK UP event for priority group "
"%lld, same as current %lld",
/*
* Change link state to UP. It will be
* propagated to IP state machine. Only do
* the NCU check if and when the interface
* NCU is online.
*/
} else {
"nwamd_ncu_handle_link_state_event: "
"got LINK UP event for priority group "
"%lld, more preferred than current %lld",
/*
* We need to mark the link as up so that when
* it is activated we will bring the interface
* up.
*/
return;
}
} else if (link->nwamd_link_activation_mode ==
"got LINK UP event for manual NCU %s",
}
}
/*
* If the link is down then start or continue transition down.
*/
if (link->nwamd_link_activation_mode ==
"nwamd_ncu_handle_link_state_event: "
"got LINK DOWN for priority group %lld",
/* Moving to offline checks priority group */
} else {
"got LINK DOWN event for manual NCU %s",
}
}
}