wifi连接流程
->wifi连接过程中app可见的操作流程:
列表中选择一个可用AP -> 弹出对话框 -> 输入AP密码点击保存 -> 底层处理连接流程
->底层处理概述
WifiStateMachine处理SAVE_NETWORK消息 -> 状态机发送CMD_START_CONNECT并处理 -> wifinative.connectToNetwork 被调用处理连接消息 -> suplicantstaIfaceHal.connectToNetwork -> addnetwork ->associate -> authentication 4次握手 -> connect
->app
在WifiSettings界面选择了一个AP
->AP是没有加密的,就保存并进行连接
->AP已经保存过,直接连接这个AP
->AP加过密,并且未保存,会显示配置AP参数的对话框。
"accroding the select ap , showDialog or connect ..."
@Override
public boolean onPreferenceTreeClick(Preference preference) {
// If the preference has a fragment set, open that
if (preference.getFragment() != null) {
preference.setOnPreferenceClickListener(null);
return super.onPreferenceTreeClick(preference);
}
if (preference instanceof LongPressAccessPointPreference) {
mSelectedAccessPoint = ((LongPressAccessPointPreference) preference).getAccessPoint();
if (mSelectedAccessPoint == null) {
return false;
}
if (mSelectedAccessPoint.isActive()) {
return super.onPreferenceTreeClick(preference);
}
/**
* Bypass dialog and connect to unsecured networks, or previously connected saved
* networks, or Passpoint provided networks.
*/
WifiConfiguration config = mSelectedAccessPoint.getConfig();
if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE) {
mSelectedAccessPoint.generateOpenNetworkConfig();
connect(mSelectedAccessPoint.getConfig(), mSelectedAccessPoint.isSaved());
} else if (mSelectedAccessPoint.isSaved() && config != null
&& config.getNetworkSelectionStatus() != null
&& config.getNetworkSelectionStatus().getHasEverConnected()) {
connect(config, true /* isSavedNetwork */);
} else if (mSelectedAccessPoint.isPasspoint()) {
// Access point provided by an installed Passpoint provider, connect using
// the associated config.
connect(config, true /* isSavedNetwork */);
} else {
showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT);
}
} else if (preference == mAddPreference) {
onAddNetworkPressed();
} else {
return super.onPreferenceTreeClick(preference);
}
return true;
}
本次分析首次配置AP密码流程,这种情况调用showDialog方法
->showDialog
private void showDialog(AccessPoint accessPoint, int dialogMode) {
if (accessPoint != null) {
WifiConfiguration config = accessPoint.getConfig();
if (WifiUtils.isNetworkLockedDown(getActivity(), config) && accessPoint.isActive()) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(),
RestrictedLockUtils.getDeviceOwner(getActivity()));
return;
}
}
if (mDialog != null) {
removeDialog(WIFI_DIALOG_ID);
mDialog = null;
}
// Save the access point and edit mode
mDlgAccessPoint = accessPoint;
mDialogMode = dialogMode;
showDialog(WIFI_DIALOG_ID);
}
public Dialog onCreateDialog(int dialogId) {
switch (dialogId) {
case WIFI_DIALOG_ID:
if (mDlgAccessPoint == null && mAccessPointSavedState == null) {
// add new network
mDialog = WifiDialog
.createFullscreen(getActivity(), this, mDlgAccessPoint, mDialogMode);
} else {
// modify network
if (mDlgAccessPoint == null) {
// restore AP from save state
mDlgAccessPoint = new AccessPoint(getActivity(), mAccessPointSavedState);
// Reset the saved access point data
mAccessPointSavedState = null;
}
mDialog = WifiDialog
.createModal(getActivity(), this, mDlgAccessPoint, mDialogMode);
}
mSelectedAccessPoint = mDlgAccessPoint;
return mDialog;
case WRITE_NFC_DIALOG_ID:
if (mSelectedAccessPoint != null) {
mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(
getActivity(),
mSelectedAccessPoint.getSecurity());
} else if (mWifiNfcDialogSavedState != null) {
mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(getActivity(),
mWifiNfcDialogSavedState);
}
return mWifiToNfcDialog;
}
return super.onCreateDialog(dialogId);
}
以上是app弹出对话框的代码:获取选择的的AP信息 -> 创建窗口并显示
接下来需要我么手动输入AP的密码,并点击保存按钮,进入正式的链接流程
窗口被创建以后,保存按钮会一直被监听,监听到点击动作,立即调用onSubmit方法进行响应
" 检测到监听动作后的调用 "
public void onSubmit(WifiDialog dialog) {
if (mDialog != null) {
submit(mDialog.getController());
}
}
void submit(WifiConfigController configController) {
final WifiConfiguration config = configController.getConfig();
if (config == null) {
if (mSelectedAccessPoint != null
&& mSelectedAccessPoint.isSaved()) {
connect(mSelectedAccessPoint.getConfig(), true /* isSavedNetwork */);
}
} else if (configController.getMode() == WifiConfigUiBase.MODE_MODIFY) {
mWifiManager.save(config, mSaveListener);
} else {
mWifiManager.save(config, mSaveListener);
if (mSelectedAccessPoint != null) { // Not an "Add network"
connect(config, false /* isSavedNetwork */);
}
}
mWifiTracker.resumeScanning();
}
这里就需要根据AP是否被保存过进行分类处理,如果没有保存就先进行保存,然后在进行连接处理。
接下来根据代码:mwifimanager.save(),接下来的处理会被framework层处理了
->Framework
framework层的处理如下:
wifiMannager异步消息发送SAVE NETWORK
-> wifiServiceImpl 处理消息
->wifiStateMachine
1.保存网络配置信息
2.CMD_START_ CONNECT处理
-> wifiNative 处理连接任务
-> wpa_supplicant
1.connect network
2.addnetworkandsaveconfig
3.addnetwork
->wlan driver
一、关联阶段
1. WifiSettings.submit – > WifiManager
- WifiSettings 干的事情比较简单,当在dialog完成ssid 以及密码填充后,直接call WifiManager save
- WifiManager 收到Save 之后,wifi的链接流程正式开始
->frameworks/base/wifi/java/android/net/wifi/WifiManager.java
public void save(WifiConfiguration config, ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
getChannel().sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
在wifiManager中使用了异步通信给wifiservice发送了消息,将工作传递下去
->frameworks/base/wifi/java/android/net/wifi/WifiServiceImpl.java
"key log SAVE nid= config= "
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
...
case WifiManager.SAVE_NETWORK: {
if (checkChangePermissionAndReplyIfNotAuthorized(
msg, WifiManager.SAVE_NETWORK_FAILED)) {
WifiConfiguration config = (WifiConfiguration) msg.obj;
int networkId = msg.arg1;
Slog.d(TAG, "SAVE"
+ " nid=" + Integer.toString(networkId)
+ " config=" + config
+ " uid=" + msg.sendingUid
+ " name="
+ mContext.getPackageManager().getNameForUid(msg.sendingUid));
if (config != null) {
/* Command is forwarded to state machine */
ClientModeImpl.sendMessage(Message.obtain(msg));
} else {
Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED,
WifiManager.INVALID_ARGS);
}
}
break;
}
...
}
}
...
}
AsyncChannel 用于两个Handler 之间的通信,具体的通信方式为源Handler 通过sendMessage 向目标Handler 发送消息,而目标Handler 通过replyToMessage 回复源Handler 处理结果;这两个Handler 可位于同一个进程,也可分属于两个不同的进程.
此处有关键log打印可供调试:SAVE ......
接下来就开始将任务交给了WifiStateMachine
->frameworks/base/wifi/java/android/net/wifi/ClientModempl.java
"状态机需要触发两个关键工作:->saveNetworkConfig ->startconnectToNetwork"
"在进行连接工作前,keylog: SAVE_NETWORK credential changed for config= ..."
case WifiManager.SAVE_NETWORK:
result = saveNetworkConfigAndSendReply(message);
netId = result.getNetworkId();
if (result.isSuccess() && mWifiInfo.getNetworkId() == netId) {
if (result.hasCredentialChanged()) {
config = (WifiConfiguration) message.obj;
// The network credentials changed and we're connected to this network,
// start a new connection with the updated credentials.
logi("SAVE_NETWORK credential changed for config=" + config.configKey()
+ ", Reconnecting.");
startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY);
} else {
if (result.hasProxyChanged()) {
log("Reconfiguring proxy on connection");
mIpClient.setHttpProxy(
getCurrentWifiConfiguration().getHttpProxy());
}
if (result.hasIpChanged()) {
// The current connection configuration was changed
// We switched from DHCP to static or from static to DHCP, or the
// static IP address has changed.
log("Reconfiguring IP on connection");
// TODO(b/36576642): clear addresses and disable IPv6
// to simplify obtainingIpState.
transitionTo(mObtainingIpState);
}
}
}
break;
状态机当前处于Disconnected状态,当接受到wifiserviceimpl调用状态机的发送的SAVE_NETWORK消息后,当前状态无法处理此消息,会追朔到其父类ConnectModeState来处理,处理方式就是上文的代码所示。
->saveNetworkConfigAndSendReply(message)
状态机主要的工作之一:保存网络配置
"->保存并更新config并广播 <没有成功会打印错误log>"
"->enable network"
private NetworkUpdateResult saveNetworkConfigAndSendReply(Message message) {
WifiConfiguration config = (WifiConfiguration) message.obj;
if (config == null) {
loge("SAVE_NETWORK with null configuration "
+ mSupplicantStateTracker.getSupplicantStateName()
+ " my state " + getCurrentState().getName());
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
NetworkUpdateResult result =
mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
if (!result.isSuccess()) {
loge("SAVE_NETWORK adding/updating config=" + config + " failed");
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
return result;
}
if (!mWifiConfigManager.enableNetwork(
result.getNetworkId(), false, message.sendingUid)) {
loge("SAVE_NETWORK enabling config=" + config + " failed");
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
return result;
}
->startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY);
状态机主要工作之二:开始链接AP
public void startConnectToNetwork(int networkId, int uid, String bssid) {
sendMessage(CMD_START_CONNECT, networkId, uid, bssid);
}
"ConnectModeState handle the message "
"key log CMD_START_CONNECT sup state..."
"key log Connecting with currentMacAddress "
case CMD_START_CONNECT:
/* connect command coming from auto-join */
netId = message.arg1;
int uid = message.arg2;
bssid = (String) message.obj;
synchronized (mWifiReqCountLock) {
if (!hasConnectionRequests()) {
if (mNetworkAgent == null) {
loge("CMD_START_CONNECT but no requests and not connected,"
+ " bailing");
break;
} else if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
loge("CMD_START_CONNECT but no requests and connected, but app "
+ "does not have sufficient permissions, bailing");
break;
}
}
}
config = mWifiConfigManager.getConfiguredNetworkWithoutMasking(netId);
logd("CMD_START_CONNECT sup state "
+ mSupplicantStateTracker.getSupplicantStateName()
+ " my state " + getCurrentState().getName()
+ " nid=" + Integer.toString(netId)
+ " roam=" + Boolean.toString(mIsAutoRoaming));
if (config == null) {
loge("CMD_START_CONNECT and no config, bail out...");
break;
}
mTargetNetworkId = netId;
setTargetBssid(config, bssid);
if (mEnableConnectedMacRandomization.get()) {
configureRandomizedMacAddress(config);
}
String currentMacAddress = mWifiNative.getMacAddress(mInterfaceName);
mWifiInfo.setMacAddress(currentMacAddress);
Log.i(TAG, "Connecting with " + currentMacAddress + " as the mac address");
reportConnectionAttemptStart(config, mTargetRoamBSSID,
WifiMetricsProto.ConnectionEvent.ROAM_UNRELATED);
if (mWifiNative.connectToNetwork(mInterfaceName, config)) {
mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT, config);
lastConnectAttemptTimestamp = mClock.getWallClockMillis();
targetWificonfiguration = config;
mIsAutoRoaming = false;
if (getCurrentState() != mDisconnectedState) {
transitionTo(mDisconnectingState);
}
} else {
loge("CMD_START_CONNECT Failed to start connection to network " + config);
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
WifiMetricsProto.ConnectionEvent.HLF_NONE);
replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
WifiManager.ERROR);
break;
}
break;
startConnectToNetwork() 是给自己发送了CMD_START_CONNECT消息,ConnectModeState状态去出处理这个消息;
之前的链接流程都没有做太多的的处理,到这里开始作对链接初步检测:
->检查链接请求
->检查权限
->随机mac地址检查以及设置
->调用Native继续处理链接流程
->frameworks/base/wifi/java/android/net/wifi/WifiNative.java
public boolean connectToNetwork(@NonNull String ifaceName, WifiConfiguration configuration) {
// Abort ongoing scan before connect() to unblock connection request.
mWificondControl.abortScan(ifaceName);
return mSupplicantStaIfaceHal.connectToNetwork(ifaceName, configuration);
}
开始链接之前wifinative关闭了wifi的扫描 -> 调用supplicatStaIfaceHal处理跟多的链接细节
->supplicentStaIfaceHal
1. 中止任何正在进行的扫描来不阻塞连接请求
2.删除wpa_supplicant中的任何现有网络(这会隐式触发断开连接)
3.在wpa_supplicant中添加一个新的网络
4.在wpa_supplicant中保存提供的configuration
5.在wpa_supplicant中选择新的网络
6.触发wpa_supplicant 的重新连接命令
->frameworks/base/wifi/java/android/net/wifi/SupplicantStaIfaceHal.java
"key log: connectToNetwork AP_name protocol"
public boolean connectToNetwork(@NonNull String ifaceName, @NonNull WifiConfiguration config) {
synchronized (mLock) {
logd("connectToNetwork " + config.configKey());
WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
if (WifiConfigurationUtil.isSameNetwork(config, currentConfig)) {
String networkSelectionBSSID = config.getNetworkSelectionStatus()
.getNetworkSelectionBSSID();
String networkSelectionBSSIDCurrent =
currentConfig.getNetworkSelectionStatus().getNetworkSelectionBSSID();
if (Objects.equals(networkSelectionBSSID, networkSelectionBSSIDCurrent)) {
logd("Network is already saved, will not trigger remove and add operation.");
} else {
logd("Network is already saved, but need to update BSSID.");
if (!setCurrentNetworkBssid(
ifaceName,
config.getNetworkSelectionStatus().getNetworkSelectionBSSID())) {
loge("Failed to set current network BSSID.");
return false;
}
mCurrentNetworkLocalConfigs.put(ifaceName, new WifiConfiguration(config));
}
} else {
mCurrentNetworkRemoteHandles.remove(ifaceName);
mCurrentNetworkLocalConfigs.remove(ifaceName);
if (!removeAllNetworks(ifaceName)) {
loge("Failed to remove existing networks");
return false;
}
Pair<SupplicantStaNetworkHal, WifiConfiguration> pair =
addNetworkAndSaveConfig(ifaceName, config);
if (pair == null) {
loge("Failed to add/save network configuration: " + config.configKey());
return false;
}
mCurrentNetworkRemoteHandles.put(ifaceName, pair.first);
mCurrentNetworkLocalConfigs.put(ifaceName, pair.second);
}
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(ifaceName, "connectToNetwork");
if (networkHandle == null || !networkHandle.select()) {
loge("Failed to select network configuration: " + config.configKey());
return false;
}
return true;
}
}
看下此阶段的相关log
"12-21 10:54:37.899 984 2216 D SupplicantStaIfaceHal: connectToNetwork WINGTECH-GUEST WPA_PSK isAscii=true"
12-21 10:54:37.900 23453 23453 I wpa_supplicant: getCapabilitiesInternal capabilities: NONE IEEE8021X WPA-EAP WPA-PSK WPA-EAP-SUITE-B WPA-EAP-SUITE-B-192 OWE DPP FILS-SHA256 FILS-SHA384 FT-PSK
12-21 10:54:37.902 23453 23453 I wpa_supplicant: getCapabilitiesInternal capabilities: NONE IEEE8021X WPA-EAP WPA-PSK WPA-EAP-SUITE-B WPA-EAP-SUITE-B-192 OWE DPP FILS-SHA256 FILS-SHA384 FT-PSK
12-21 10:54:37.902 984 2216 I SupplicantStaIfaceHal: addSupplicantStaNetwork via HIDL
12-21 10:54:37.909 984 2216 I System.out: stanetwork getId >>0
12-21 10:54:37.909 984 2216 E SupplicantStaNetworkHal: set ISupplicantVendorStaNetwork successfull
12-21 10:54:37.918 24377 24377 I Perf : Connecting to perf service.
12-21 10:54:37.918 984 1103 I system_server: Background concurrent copying GC freed 67248(2899KB) AllocSpace objects, 65(6452KB) LOS objects, 42% free, 11MB/19MB, paused 142us total 135.163ms
12-21 10:54:37.924 984 2216 E SupplicantStaNetworkHal: Invalid VendorKeyMgmtMask bit in keyMgmt: 1
12-21 10:54:37.924 984 2216 E SupplicantStaNetworkHal: Invalid VendorKeyMgmtMask bit in keyMgmt: 11
12-21 10:54:37.924 984 2216 E SupplicantStaNetworkHal: Vendor Key Management 0
12-21 10:54:37.924 23453 23453 I wpa_supplicant: Vendor key_mgmt: 0x142
12-21 10:54:37.925 984 2216 E SupplicantStaNetworkHal: Invalid protoMask bit in wificonfig: 0
12-21 10:54:37.925 984 2216 E SupplicantStaNetworkHal: Invalid protoMask bit in wificonfig: 1
12-21 10:54:37.926 23453 23453 I wpa_supplicant: Vendor Proto: 0x3
12-21 10:54:37.929 23453 23453 I wpa_supplicant: getCapabilitiesInternal capabilities: NONE IEEE8021X WPA-EAP WPA-PSK WPA-EAP-SUITE-B WPA-EAP-SUITE-B-192 OWE DPP FILS-SHA256 FILS-SHA384 FT-PSK
12-21 10:54:37.931 23453 23453 E wpa_supplicant: eap_proxy: eap_proxy_notify_config
12-21 10:54:37.933 23453 23453 I wpa_supplicant: wlan0: Trying to associate with SSID 'WINGTECH-GUEST'
log:SupplicantStaIfaceHal: addSupplicantStaNetwork via HIDL 这是 SaddNetworkAndSaveConfig里面打出来的log
两件任务需要被做:1、保存config到supplicant中 2、调用selcet方法调用,通过HIDL到HAL层
1、保存config到supplicant中
->SaddNetworkAndSaveConfig
"key log: addSupplicantStaNetwork via HIDL"
private Pair<SupplicantStaNetworkHal, WifiConfiguration>
addNetworkAndSaveConfig(WifiConfiguration config) {
synchronized (mLock) {
logi("addSupplicantStaNetwork via HIDL");
if (config == null) {
loge("Cannot add NULL network!");
return null;
}
SupplicantStaNetworkHal network = addNetwork();
if (network == null) {
loge("Failed to add a network!");
return null;
}
network.getId();
network.setVendorStaNetwork(getVendorNetwork (ifaceName, network.getNetworkId()))
"will print set ISupplicantVendorStaNetwork successfull"
boolean saveSuccess = false;
try {
saveSuccess = network.saveWifiConfiguration(config);"在config 中设置各种属性 "
} catch (IllegalArgumentException e) {
Log.e(TAG, "Exception while saving config params: " + config, e);
}
if (!saveSuccess) {
loge("Failed to save variables for: " + config.configKey());
if (!removeAllNetworks()) {
loge("Failed to remove all networks on failure.");
}
return null;
}
return new Pair(network, new WifiConfiguration(config));
}
}
->本地调用addnetwork
private SupplicantStaNetworkHal addNetwork() {
synchronized (mLock) {
final String methodStr = "addNetwork";
if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
Mutable<ISupplicantNetwork> newNetwork = new Mutable<>();
try {
mISupplicantStaIface.addNetwork((SupplicantStatus status,
ISupplicantNetwork network) -> {
if (checkStatusAndLogFailure(status, methodStr)) {
newNetwork.value = network;
}
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
if (newNetwork.value != null) {
return getStaNetworkMockable(
ISupplicantStaNetwork.asInterface(newNetwork.value.asBinder()));
} else {
return null;
}
}
}
mISupplicantStaIface是连接supplicant成功后framework保留下来的服务器对象即ISupplicantStaIface应该是对应sta_iface.cpp的引用。
接下来save config流程通过hidl重java走到了c++的hidl代码。
->HIDL
external/wpa_supplicant_8/wpa_supplicant/hidl/1.0/sta_iface.cpp
Return<void> StaIface::addNetwork(addNetwork_cb _hidl_cb)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&StaIface::addNetworkInternal, _hidl_cb);
}
std::pair<SupplicantStatus, sp<ISupplicantNetwork>>
StaIface::addNetworkInternal()
{
android::sp<ISupplicantStaNetwork> network;
struct wpa_supplicant *wpa_s = retrieveIfacePtr();
struct wpa_ssid *ssid = wpa_supplicant_add_network(wpa_s);
if (!ssid) {
return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, network};
}
HidlManager *hidl_manager = HidlManager::getInstance();
if (!hidl_manager ||
hidl_manager->getStaNetworkHidlObjectByIfnameAndNetworkId(
wpa_s->ifname, ssid->id, &network)) {
return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, network};
}
return {{SupplicantStatusCode::SUCCESS, ""}, network};
}
HIDL通信的C++接口实现处,将保存网络的任务传递给了wpa_supplicat,wpa_supplicant_add_network(wpa_s);
->wpa_supplicant.c
/**
* wpa_supplicant_add_network - Add a new network
* @wpa_s: wpa_supplicant structure for a network interface
* Returns: The new network configuration or %NULL if operation failed
*
* This function performs the following operations:
* 1. Adds a new network.
* 2. Send network addition notification.
* 3. Marks the network disabled.
* 4. Set network default parameters.
*/
struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid;
ssid = wpa_config_add_network(wpa_s->conf);
if (!ssid)
return NULL;
wpas_notify_network_added(wpa_s, ssid);
ssid->disabled = 1;
wpa_config_set_network_defaults(ssid);
return ssid;
}
wpa_supplicant添加网络,做了如下四个工作
1.添加新的网络
2.发出网络添加通知
3.将该网络标记为disable
4.设置网络默认参数
->1.1wpa_config_add_network(conf)
external/wpa_supplicant8/wpa_supplicat/config.c
struct wpa_ssid * wpa_config_add_network(struct wpa_config *config)
{
int id;
struct wpa_ssid *ssid, *last = NULL;
id = -1;
ssid = config->ssid;
while (ssid) {
if (ssid->id > id)
id = ssid->id;
last = ssid;
ssid = ssid->next;
}
id++;
ssid = os_zalloc(sizeof(*ssid));
if (ssid == NULL)
return NULL;
ssid->id = id;
dl_list_init(&ssid->psk_list);
if (last)
last->next = ssid;
else
config->ssid = ssid;
wpa_config_update_prio_list(config);
return ssid;
}
在config.c的处理中,可以看到ssid是存在一个链表之中的。
1、while循环中是为了计算出当前我们需要保存的网络的id号,在最后的id++执行后,就是当前我们的网络的id号
2、last->next = ssid; 此处插入了一个空的ssid,
3、wpa_config_update_prio_list(config)
/**
* wpa_config_update_prio_list - Update network priority list
* @config: Configuration data from wpa_config_read()
* Returns: 0 on success, -1 on failure
*
* This function is called to update the priority list of networks in the
* configuration when a network is being added or removed. This is also called
* if a priority for a network is changed.
*/
int wpa_config_update_prio_list(struct wpa_config *config)
{
struct wpa_ssid *ssid;
int ret = 0;
os_free(config->pssid);
config->pssid = NULL;
config->num_prio = 0;
ssid = config->ssid;
while (ssid) {
ssid->pnext = NULL;
if (wpa_config_add_prio_network(config, ssid) < 0)
ret = -1;
ssid = ssid->next;
}
return ret;
}
/**
* wpa_config_add_prio_network - Add a network to priority lists
* @config: Configuration data from wpa_config_read()
* @ssid: Pointer to the network configuration to be added to the list
* Returns: 0 on success, -1 on failure
*
* This function is used to add a network block to the priority list of
* networks. This must be called for each network when reading in the full
* configuration. In addition, this can be used indirectly when updating
* priorities by calling wpa_config_update_prio_list().
*/
int wpa_config_add_prio_network(struct wpa_config *config,
struct wpa_ssid *ssid)
{
int prio;
struct wpa_ssid *prev, **nlist;
/*
* Add to an existing priority list if one is available for the
* configured priority level for this network.
*/
for (prio = 0; prio < config->num_prio; prio++) {
prev = config->pssid[prio];
if (prev->priority == ssid->priority) {
while (prev->pnext)
prev = prev->pnext;
prev->pnext = ssid;
return 0;
}
}
/* First network for this priority - add a new priority list */
nlist = os_realloc_array(config->pssid, config->num_prio + 1,
sizeof(struct wpa_ssid *));
if (nlist == NULL)
return -1;
for (prio = 0; prio < config->num_prio; prio++) {
if (nlist[prio]->priority < ssid->priority) {
os_memmove(&nlist[prio + 1], &nlist[prio],
(config->num_prio - prio) *
sizeof(struct wpa_ssid *));
break;
}
}
nlist[prio] = ssid;
config->num_prio++;
config->pssid = nlist;
return 0;
}
每添加一个新的网络,都会重新初始化一下pssid队列,相同priority的网络会接续在之前队列后面,否则在nlist里新建一个优先级元素
->1.1添加新的网络总结:添加新的网络最后在config.c中完成,主要有两件事情:1、ssid链表中加入新的节点 2、更新优先级链表
->1.2发出网络添加通知
->notify.c
void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
if (wpa_s->p2p_mgmt)
return;
/*
* Networks objects created during any P2P activities should not be
* exposed out. They might/will confuse certain non-P2P aware
* applications since these network objects won't behave like
* regular ones.
*/
if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) {
wpas_dbus_register_network(wpa_s, ssid);
wpas_hidl_register_network(wpa_s, ssid);
}
}
->hidl.cpp
int wpas_hidl_register_network(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
{
if (!wpa_s || !wpa_s->global->hidl || !ssid)
return 1;
wpa_printf(
MSG_DEBUG, "Registering network to hidl control: %d", ssid->id);
HidlManager *hidl_manager = HidlManager::getInstance();
if (!hidl_manager)
return 1;
return hidl_manager->registerNetwork(wpa_s, ssid);
}
->hidl_manager.cpp
/**
* Register a network to hidl manager.
*
* @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
* the network is added.
* @param ssid |wpa_ssid| struct corresponding to the network being added.
*
* @return 0 on success, 1 on failure.
*/
int HidlManager::registerNetwork(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
{
if (!wpa_s || !ssid)
return 1;
// Generate the key to be used to lookup the network.
const std::string network_key =
getNetworkObjectMapKey(wpa_s->ifname, ssid->id);
if (isP2pIface(wpa_s)) {
if (addHidlObjectToMap<P2pNetwork>(
network_key,
new P2pNetwork(wpa_s->global, wpa_s->ifname, ssid->id),
p2p_network_object_map_)) {
wpa_printf(
MSG_ERROR,
"Failed to register P2P network with HIDL "
"control: %d",
ssid->id);
return 1;
}
p2p_network_callbacks_map_[network_key] =
std::vector<android::sp<ISupplicantP2pNetworkCallback>>();
// Invoke the |onNetworkAdded| method on all registered
// callbacks.
callWithEachP2pIfaceCallback(
wpa_s->ifname,
std::bind(
&ISupplicantP2pIfaceCallback::onNetworkAdded,
std::placeholders::_1, ssid->id));
} else {
if (addHidlObjectToMap<StaNetwork>(
network_key,
new StaNetwork(wpa_s->global, wpa_s->ifname, ssid->id),
sta_network_object_map_)) {
wpa_printf(
MSG_ERROR,
"Failed to register STA network with HIDL "
"control: %d",
ssid->id);
return 1;
}
sta_network_callbacks_map_[network_key] =
std::vector<android::sp<ISupplicantStaNetworkCallback>>();
// Invoke the |onNetworkAdded| method on all registered
// callbacks.
callWithEachStaIfaceCallback(
wpa_s->ifname,
std::bind(
&ISupplicantStaIfaceCallback::onNetworkAdded,
std::placeholders::_1, ssid->id));
}
return 0;
}
/**
* Creates a unique key for the network using the provided |ifname| and
* |network_id| to be used in the internal map of |ISupplicantNetwork| objects.
* This is of the form |ifname|_|network_id|. For ex: "wlan0_1".
*
* @param ifname Name of the corresponding interface.
* @param network_id ID of the corresponding network.
*/
const std::string getNetworkObjectMapKey(
const std::string &ifname, int network_id)
{
return ifname + "_" + std::to_string(network_id);
}
// Map of all the STA network specific hidl objects controlled by
// wpa_supplicant. This map is keyed in by the corresponding
// |ifname| & |network_id|.
std::map<const std::string, android::sp<StaNetwork>>
sta_network_object_map_;
对应getStaNetworkHidlObjectByIfnameAndNetworkId获得的network就是
new StaNetwork(wpa_s->global, wpa_s->ifname, ssid->id)
->2、选择网络
/**
* Trigger a connection to this network.
*
* @return true if it succeeds, false otherwise.
*/
public boolean select() {
synchronized (mLock) {
final String methodStr = "select";
if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
try {
SupplicantStatus status = mISupplicantStaNetwork.select();
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
}
wpa_suoppliant处理完保存往网络的任务后,又回到发framework层,还是继续通过HIDL通信将任务下发到C++
->sta_network.cpp HIDL接口实现文件
Return<void> StaNetwork::select(select_cb _hidl_cb)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
&StaNetwork::selectInternal, _hidl_cb);
}
SupplicantStatus StaNetwork::selectInternal()
{
struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
if (wpa_ssid->disabled == 2) {
return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
}
struct wpa_supplicant *wpa_s = retrieveIfacePtr();
wpa_s->scan_min_time.sec = 0;
wpa_s->scan_min_time.usec = 0;
wpa_supplicant_select_network(wpa_s, wpa_ssid);
return {SupplicantStatusCode::SUCCESS, ""};
}
/**
* wpa_supplicant_select_network - Attempt association with a network
* @wpa_s: wpa_supplicant structure for a network interface
* @ssid: wpa_ssid structure for a configured network or %NULL for any network
*/
void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
struct wpa_ssid *other_ssid;
int disconnected = 0;
if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
disconnected = 1;
}
//若当前已连接或正处于连接状态,则取消当前连接,方便进行最新下发的连接请求
if (ssid)
wpas_clear_temp_disabled(wpa_s, ssid, 1);
/*
* Mark all other networks disabled or mark all networks enabled if no
* network specified.
*/
for (other_ssid = wpa_s->conf->ssid; other_ssid;
other_ssid = other_ssid->next) {
int was_disabled = other_ssid->disabled;
if (was_disabled == 2)
continue; /* do not change persistent P2P group data */
other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0;
if (was_disabled && !other_ssid->disabled)
wpas_clear_temp_disabled(wpa_s, other_ssid, 0);
if (was_disabled != other_ssid->disabled)
wpas_notify_network_enabled_changed(wpa_s, other_ssid);
}
//禁用其他所有网络或者当ssid为空时启用所有网络
if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid &&
wpa_s->wpa_state >= WPA_AUTHENTICATING) {
/* We are already associated with the selected network */
wpa_printf(MSG_DEBUG, "Already associated with the "
"selected network - do nothing");
return;
}
//若当前要连接的网络已经开始连接,则直接返回
if (ssid) {
wpa_s->current_ssid = ssid;
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
wpa_s->connect_without_scan =
(ssid->mode == WPAS_MODE_MESH) ? ssid : NULL;
/*
* Don't optimize next scan freqs since a new ESS has been
* selected.
*/
os_free(wpa_s->next_scan_freqs);
wpa_s->next_scan_freqs = NULL;
} else {
wpa_s->connect_without_scan = NULL;
}
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
if (wpa_s->connect_without_scan ||
wpa_supplicant_fast_associate(wpa_s) != 1) {
wpa_s->scan_req = NORMAL_SCAN_REQ;
wpas_scan_reset_sched_scan(wpa_s);
wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
}
if (ssid)
wpas_notify_network_selected(wpa_s, ssid);
}
1、wpa_s->connect_without_scan =(ssid->mode == WPAS_MODE_MESH) ? ssid : NULL
这里的ssid->mode 指的是mode - IEEE 802.11 operation mode (Infrastucture/IBSS),代表网络模式。
/**
* mode - IEEE 802.11 operation mode (Infrastucture/IBSS)
*
* 0 = infrastructure (Managed) mode, i.e., associate with an AP.
//https://baike.baidu.com/item/infrastructure/7975967?fr=aladdin
//基础结构
基础设施;基础建设;也是是无线站点STA的一种工作模式;另外一种工作模式是Ad-hoc 模式。
Infrastructure模式无线局域网是指通过AP互连工作模式,把AP看做传统局域网中集线器功能。
类似传统有线星型拓扑方案,此种模式需要有一台符合IEEE 802.11b/g模式的AP或无线路由器存在,所有通信通过AP或无线路由器作连接,就如同有线网络下利用集线器来作连接,该模式下的无线网可以通过AP或无线路由器的以太网口与有线网相连。此种方式是大家最为经常用到的方式。
区别:
1:Infrastructure基础架构是需要固定的中心控制的,但AD-HOC不需要;
2:自组织能力差,AD-HOC可实现自我接入网络的能力;
3:在实现多条路由的功能时,AD-HOC只需要普通的路由节点即可实现,而Infrastructure需要专用的路由器完成;
4:拓扑结构:Infrastructure一般是传统的拓扑形式,如 星型,环形等,但是静态设置的;而AD-HOC是动态实现的。
*
* 1 = IBSS (ad-hoc, peer-to-peer)
https://baike.baidu.com/item/IBSS/2634980?fr=aladdin
"IBSS(Independent Basic Service Set)独立基本服务集,IBSS是一种无线拓扑结构,IEEE802.11标准的模式·IBSS模式,又称作独立广播卫星服务,也称为特设模式,是专为点对点连接。
IBSS模式没有无线基础设施骨干,但至少需要2台wireless station。
特设模式,让用户自发地形成一个无线局域网。为了分享文件,如演示图表和电子表格,我们可以轻易把手上网卡,以特设方式,在会议室迅速建立一个小型无线局域网。"
*
* 2 = AP (access point)
*
* 3 = P2P Group Owner (can be set in the configuration file)
*
* 4 = P2P Group Formation (used internally; not in configuration
* files)
*
* 5 = Mesh
https://baike.baidu.com/item/mesh
"Mesh网络即”无线网格网络”,它是“多跳(multi-hop)”网络,是由ad hoc网络发展而来,是解决“最后一公里”问题的关键技术之一。在向下一代网络演进的过程中,无线是一个不可缺的技术。无线mesh可以与其它网络协同通信。是一个动态的可以不断扩展的网络架构,任意的两个设备均可以保持无线互联。"
A mesh network (or simply meshnet) is a local network topology in which the infrastructure nodes (i.e. bridges, switches and other infrastructure devices) connect directly, dynamically and non-hierarchically to as many other nodes as possible and cooperate with one another to efficiently route data from/to clients. This lack of dependency on one node allows for every node to participate in the relay of information. Mesh networks dynamically self-organize and self-configure, which can reduce installation overhead. The ability to self-configure enables dynamic distribution of workloads, particularly in the event that a few nodes should fail. This in turn contributes to fault-tolerance and reduced maintenance costs.
*
* Note: IBSS can only be used with key_mgmt NONE (plaintext and static
* WEP) and WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE
* (fixed group key TKIP/CCMP) is available for backwards compatibility,
* but its use is deprecated. WPA-None requires following network block
* options: proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or
* CCMP, but not both), and psk must also be set (either directly or
* using ASCII passphrase).
*/
enum wpas_mode {
WPAS_MODE_INFRA = 0,
WPAS_MODE_IBSS = 1,
WPAS_MODE_AP = 2,
WPAS_MODE_P2P_GO = 3,
WPAS_MODE_P2P_GROUP_FORMATION = 4,
WPAS_MODE_MESH = 5,
} mode
网络初始化wpa_config_set_network_defaults(config.c)的时候未对mode进行特殊赋值,所以网络默认mode为WPAS_MODE_INFRA,这意味着wpa_s->connect_without_scan将被赋值为NULL,
if (wpa_s->connect_without_scan ||
wpa_supplicant_fast_associate(wpa_s) != 1) {
wpa_s->scan_req = NORMAL_SCAN_REQ;
wpas_scan_reset_sched_scan(wpa_s);
wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
}
从这段逻辑来看mesh模式是不需要基于搜索结果的,其他模式需要基于搜索结果,即普通连接wpa_supplicant_fast_associate是会被执行的,当associate失败,会再次扫描
->/external/wpa_supplicant8/wpa_supplicat/events.c
int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s)
{
#ifdef CONFIG_NO_SCAN_PROCESSING
return -1;
#else /* CONFIG_NO_SCAN_PROCESSING */
struct os_reltime now;
wpa_s->ignore_post_flush_scan_res = 0;
if (wpa_s->last_scan_res_used == 0)
return -1;
os_get_reltime(&now);
if (os_reltime_expired(&now, &wpa_s->last_scan, 5)) {
wpa_printf(MSG_DEBUG, "Fast associate: Old scan results");
return -1;
}
return wpas_select_network_from_last_scan(wpa_s, 0, 1);
#endif /* CONFIG_NO_SCAN_PROCESSING */
}
看下什么情况下会打印" Fast associate: Old scan results " 从log看应该是对扫描结果是否有效的判断。
static inline int os_reltime_expired(struct os_reltime *now,
struct os_reltime *ts,
os_time_t timeout_secs)
{
struct os_reltime age;
os_reltime_sub(now, ts, &age);
return (age.sec > timeout_secs) ||
(age.sec == timeout_secs && age.usec > 0);
}
static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b,
struct os_reltime *res)
{
res->sec = a->sec - b->sec;
res->usec = a->usec - b->usec;
if (res->usec < 0) {
res->sec--;
res->usec += 1000000;
}
sec是指秒,usec是指μs,这段逻辑简单来说就是连接时机如果是在最新的一次扫描结果5s之后下发,就被认为是旧的扫描结果,不继续下发连接指令,也就是说扫描结果的有效性是5s。
接着继续看选择网络的流程
->wpas_select_network_from_last_scan(wpas,0,1)
static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
int new_scan, int own_request)
{
struct wpa_bss *selected;
struct wpa_ssid *ssid = NULL;
int time_to_reenable = wpas_reenabled_network_time(wpa_s);
if (time_to_reenable > 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Postpone network selection by %d seconds since all networks are disabled",
time_to_reenable);
eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
eloop_register_timeout(time_to_reenable, 0,
wpas_network_reenabled, wpa_s, NULL);
return 0;
}
if (wpa_s->p2p_mgmt)
return 0; /* no normal connection on p2p_mgmt interface */
selected = wpa_supplicant_pick_network(wpa_s, &ssid);
#ifdef CONFIG_MESH
if (wpa_s->ifmsh) {
wpa_msg(wpa_s, MSG_INFO,
"Avoiding join because we already joined a mesh group");
return 0;
}
#endif /* CONFIG_MESH */
if (selected) {
int skip;
skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
if (skip) {
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
return 0;
}
if (ssid != wpa_s->current_ssid &&
wpa_s->wpa_state >= WPA_AUTHENTICATING) {
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
}
if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
return -1;
}
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
/*
* Do not allow other virtual radios to trigger operations based
* on these scan results since we do not want them to start
* other associations at the same time.
*/
return 1;
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
ssid = wpa_supplicant_pick_new_network(wpa_s);
if (ssid) {
wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network");
wpa_supplicant_associate(wpa_s, NULL, ssid);
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
} else if (own_request) {
/*
* No SSID found. If SCAN results are as a result of
* own scan request and not due to a scan request on
* another shared interface, try another scan.
*/
int timeout_sec = wpa_s->scan_interval;
int timeout_usec = 0;
#ifdef CONFIG_P2P
int res;
res = wpas_p2p_scan_no_go_seen(wpa_s);
if (res == 2)
return 2;
if (res == 1)
return 0;
if (wpa_s->p2p_in_provisioning ||
wpa_s->show_group_started ||
wpa_s->p2p_in_invitation) {
/*
* Use shorter wait during P2P Provisioning
* state and during P2P join-a-group operation
* to speed up group formation.
*/
timeout_sec = 0;
timeout_usec = 250000;
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
return 0;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_INTERWORKING
if (wpa_s->conf->auto_interworking &&
wpa_s->conf->interworking &&
wpa_s->conf->cred) {
wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: "
"start ANQP fetch since no matching "
"networks found");
wpa_s->network_select = 1;
wpa_s->auto_network_select = 1;
interworking_start_fetch_anqp(wpa_s);
return 1;
}
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_WPS
if (wpa_s->after_wps > 0 || wpas_wps_searching(wpa_s)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Use shorter wait during WPS processing");
timeout_sec = 0;
timeout_usec = 500000;
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
return 0;
}
#endif /* CONFIG_WPS */
if (wpa_supplicant_req_sched_scan(wpa_s))
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
wpa_msg_ctrl(wpa_s, MSG_INFO,
WPA_EVENT_NETWORK_NOT_FOUND);
}
}
return 0;
}
->wpa_supplicant_connect
int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
struct wpa_bss *selected,
struct wpa_ssid *ssid)
{
if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
"PBC session overlap");
wpas_notify_wps_event_pbc_overlap(wpa_s);
#ifdef CONFIG_P2P
if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
wpa_s->p2p_in_provisioning) {
eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb,
wpa_s, NULL);
return -1;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_WPS
wpas_wps_pbc_overlap(wpa_s);
wpas_wps_cancel(wpa_s);
#endif /* CONFIG_WPS */
return -1;
}
wpa_msg(wpa_s, MSG_DEBUG,
"Considering connect request: reassociate: %d selected: "
MACSTR " bssid: " MACSTR " pending: " MACSTR
" wpa_state: %s ssid=%p current_ssid=%p",
wpa_s->reassociate, MAC2STR(selected->bssid),
MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
wpa_supplicant_state_txt(wpa_s->wpa_state),
ssid, wpa_s->current_ssid);
/*
* Do not trigger new association unless the BSSID has changed or if
* reassociation is requested. If we are in process of associating with
* the selected BSSID, do not trigger new attempt.
*/
if (wpa_s->reassociate ||
(os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
((wpa_s->wpa_state != WPA_ASSOCIATING &&
wpa_s->wpa_state != WPA_AUTHENTICATING) ||
(!is_zero_ether_addr(wpa_s->pending_bssid) &&
os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) !=
0) ||
(is_zero_ether_addr(wpa_s->pending_bssid) &&
ssid != wpa_s->current_ssid)))) {
if (wpa_supplicant_scard_init(wpa_s, ssid)) {
wpa_supplicant_req_new_scan(wpa_s, 10, 0);
return 0;
}
wpa_msg(wpa_s, MSG_DEBUG, "Request association with " MACSTR,
MAC2STR(selected->bssid));
wpa_supplicant_associate(wpa_s, selected, ssid);
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "Already associated or trying to "
"connect with the selected AP");
}
return 0;
}
-> wpa_supplicant_associate
/**
* wpa_supplicant_associate - Request association
* @wpa_s: Pointer to wpa_supplicant data
* @bss: Scan results for the selected BSS, or %NULL if not available
* @ssid: Configuration data for the selected network
*
* This function is used to request %wpa_supplicant to associate with a BSS.
*/
void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid)
{
struct wpa_connect_work *cwork;
int rand_style;
wpa_s->own_disconnect_req = 0;
/*
* If we are starting a new connection, any previously pending EAPOL
* RX cannot be valid anymore.
*/
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = NULL;
if (ssid->mac_addr == -1)
rand_style = wpa_s->conf->mac_addr;
else
rand_style = ssid->mac_addr;
wmm_ac_clear_saved_tspecs(wpa_s);
wpa_s->reassoc_same_bss = 0;
wpa_s->reassoc_same_ess = 0;
#ifdef CONFIG_TESTING_OPTIONS
wpa_s->testing_resend_assoc = 0;
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_s->last_ssid == ssid) {
wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
wpa_s->reassoc_same_ess = 1;
if (wpa_s->current_bss && wpa_s->current_bss == bss) {
wmm_ac_save_tspecs(wpa_s);
wpa_s->reassoc_same_bss = 1;
}
}
if (rand_style > 0 && !wpa_s->reassoc_same_ess) {
if (wpas_update_random_addr(wpa_s, rand_style) < 0)
return;
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
} else if (rand_style == 0 && wpa_s->mac_addr_changed) {
if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Could not restore permanent MAC address");
return;
}
wpa_s->mac_addr_changed = 0;
if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Could not update MAC address information");
return;
}
wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address");
}
wpa_s->last_ssid = ssid;
#ifdef CONFIG_IBSS_RSN
ibss_rsn_deinit(wpa_s->ibss_rsn);
wpa_s->ibss_rsn = NULL;
#else /* CONFIG_IBSS_RSN */
if (ssid->mode == WPAS_MODE_IBSS &&
!(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) {
wpa_msg(wpa_s, MSG_INFO,
"IBSS RSN not supported in the build");
return;
}
#endif /* CONFIG_IBSS_RSN */
if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
#ifdef CONFIG_AP
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP)) {
wpa_msg(wpa_s, MSG_INFO, "Driver does not support AP "
"mode");
return;
}
if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) {
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
wpas_p2p_ap_setup_failed(wpa_s);
return;
}
wpa_s->current_bss = bss;
#else /* CONFIG_AP */
wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in "
"the build");
#endif /* CONFIG_AP */
return;
}
if (ssid->mode == WPAS_MODE_MESH) {
#ifdef CONFIG_MESH
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MESH)) {
wpa_msg(wpa_s, MSG_INFO,
"Driver does not support mesh mode");
return;
}
if (bss)
ssid->frequency = bss->freq;
if (wpa_supplicant_join_mesh(wpa_s, ssid) < 0) {
wpa_msg(wpa_s, MSG_ERROR, "Could not join mesh");
return;
}
wpa_s->current_bss = bss;
wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
ssid->id);
wpas_notify_mesh_group_started(wpa_s, ssid);
#else /* CONFIG_MESH */
wpa_msg(wpa_s, MSG_ERROR,
"mesh mode support not included in the build");
#endif /* CONFIG_MESH */
return;
}
/*
* Set WPA state machine configuration to match the selected network now
* so that the information is available before wpas_start_assoc_cb()
* gets called. This is needed at least for RSN pre-authentication where
* candidate APs are added to a list based on scan result processing
* before completion of the first association.
*/
wpa_supplicant_rsn_supp_set_config(wpa_s, ssid);
#ifdef CONFIG_DPP
if (wpas_dpp_check_connect(wpa_s, ssid, bss) != 0)
return;
#endif /* CONFIG_DPP */
#ifdef CONFIG_TDLS
if (bss)
wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1),
bss->ie_len);
#endif /* CONFIG_TDLS */
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
ssid->mode == IEEE80211_MODE_INFRA) {
sme_authenticate(wpa_s, bss, ssid);
return;
}
if (wpa_s->connect_work) {
wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since connect_work exist");
return;
}
if (radio_work_pending(wpa_s, "connect")) {
wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since pending work exist");
return;
}
#ifdef CONFIG_SME
if (ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) {
/* Clear possibly set auth_alg, if any, from last attempt. */
wpa_s->sme.auth_alg = WPA_AUTH_ALG_OPEN;
}
#endif /* CONFIG_SME */
wpas_abort_ongoing_scan(wpa_s);
cwork = os_zalloc(sizeof(*cwork));
if (cwork == NULL)
return;
cwork->bss = bss;
cwork->ssid = ssid;
if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1,
wpas_start_assoc_cb, cwork) < 0) {
os_free(cwork);
}
}
->radio_add_work 里面带了一个wpa_start_assoc_cb的参数
先看radio_add_work
/**
* radio_add_work - Add a radio work item
* @wpa_s: Pointer to wpa_supplicant data
* @freq: Frequency of the offchannel operation in MHz or 0
* @type: Unique identifier for each type of work
* @next: Force as the next work to be executed
* @cb: Callback function for indicating when radio is available
* @ctx: Context pointer for the work (work->ctx in cb())
* Returns: 0 on success, -1 on failure
*
* This function is used to request time for an operation that requires
* exclusive radio control. Once the radio is available, the registered callback
* function will be called. radio_work_done() must be called once the exclusive
* radio operation has been completed, so that the radio is freed for other
* operations. The special case of deinit=1 is used to free the context data
* during interface removal. That does not allow the callback function to start
* the radio operation, i.e., it must free any resources allocated for the radio
* work and return.
*
* The @freq parameter can be used to indicate a single channel on which the
* offchannel operation will occur. This may allow multiple radio work
* operations to be performed in parallel if they apply for the same channel.
* Setting this to 0 indicates that the work item may use multiple channels or
* requires exclusive control of the radio.
*/
int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
const char *type, int next,
void (*cb)(struct wpa_radio_work *work, int deinit),
void *ctx)
{
struct wpa_radio *radio = wpa_s->radio;
struct wpa_radio_work *work;
int was_empty;
work = os_zalloc(sizeof(*work));
if (work == NULL)
return -1;
wpa_dbg(wpa_s, MSG_DEBUG, "Add radio work '%s'@%p", type, work);
os_get_reltime(&work->time);
work->freq = freq;
work->type = type;
work->wpa_s = wpa_s;
work->cb = cb;
work->ctx = ctx;
if (freq)
work->bands = wpas_freq_to_band(freq);
else if (os_strcmp(type, "scan") == 0 ||
os_strcmp(type, "p2p-scan") == 0)
work->bands = wpas_get_bands(wpa_s,
((struct wpa_driver_scan_params *)
ctx)->freqs);
else
work->bands = wpas_get_bands(wpa_s, NULL);
was_empty = dl_list_empty(&wpa_s->radio->work);
if (next)
dl_list_add(&wpa_s->radio->work, &work->list);
else
dl_list_add_tail(&wpa_s->radio->work, &work->list);
if (was_empty) {
wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately");
radio_work_check_next(wpa_s);
} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)
&& radio->num_active_works < MAX_ACTIVE_WORKS) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Try to schedule a radio work (num_active_works=%u)",
radio->num_active_works);
radio_work_check_next(wpa_s);
}
return 0;
}
->wpa_start_assoc_cb
static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_connect_work *cwork = work->ctx;
struct wpa_bss *bss = cwork->bss;
struct wpa_ssid *ssid = cwork->ssid;
struct wpa_supplicant *wpa_s = work->wpa_s;
u8 *wpa_ie;
int use_crypt, ret, i, bssid_changed;
unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt;
struct wpa_driver_associate_params params;
int wep_keys_set = 0;
int assoc_failed = 0;
struct wpa_ssid *old_ssid;
u8 prev_bssid[ETH_ALEN];
#ifdef CONFIG_HT_OVERRIDES
struct ieee80211_ht_capabilities htcaps;
struct ieee80211_ht_capabilities htcaps_mask;
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_VHT_OVERRIDES
struct ieee80211_vht_capabilities vhtcaps;
struct ieee80211_vht_capabilities vhtcaps_mask;
#endif /* CONFIG_VHT_OVERRIDES */
if (deinit) {
if (work->started) {
wpa_s->connect_work = NULL;
/* cancel possible auth. timeout */
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s,
NULL);
}
wpas_connect_work_free(cwork);
return;
}
wpa_s->connect_work = work;
if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) ||
wpas_network_disabled(wpa_s, ssid)) {
wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt");
wpas_connect_work_done(wpa_s);
return;
}
os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN);
os_memset(¶ms, 0, sizeof(params));
wpa_s->reassociate = 0;
wpa_s->eap_expected_failure = 0;
if (bss &&
(!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) {
#ifdef CONFIG_IEEE80211R
const u8 *ie, *md = NULL;
#endif /* CONFIG_IEEE80211R */
wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
" (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq);
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
os_memset(wpa_s->bssid, 0, ETH_ALEN);
os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
if (bssid_changed)
wpas_notify_bssid_changed(wpa_s);
#ifdef CONFIG_IEEE80211R
ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
md = ie + 2;
wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
if (md) {
/* Prepare for the next transition */
wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_WPS
} else if ((ssid->ssid == NULL || ssid->ssid_len == 0) &&
wpa_s->conf->ap_scan == 2 &&
(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
/* Use ap_scan==1 style network selection to find the network
*/
wpas_connect_work_done(wpa_s);
wpa_s->scan_req = MANUAL_SCAN_REQ;
wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0);
return;
#endif /* CONFIG_WPS */
} else {
wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
if (bss)
os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
else
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
}
if (!wpa_s->pno)
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
/* Starting new association, so clear the possibly used WPA IE from the
* previous association. */
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, ¶ms, NULL);
if (!wpa_ie) {
wpas_connect_work_done(wpa_s);
return;
}
wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
use_crypt = 1;
cipher_pairwise = wpa_s->pairwise_cipher;
cipher_group = wpa_s->group_cipher;
cipher_group_mgmt = wpa_s->mgmt_group_cipher;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
use_crypt = 0;
if (wpa_set_wep_keys(wpa_s, ssid)) {
use_crypt = 1;
wep_keys_set = 1;
}
}
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS)
use_crypt = 0;
#ifdef IEEE8021X_EAPOL
if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
if ((ssid->eapol_flags &
(EAPOL_FLAG_REQUIRE_KEY_UNICAST |
EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) == 0 &&
!wep_keys_set) {
use_crypt = 0;
} else {
/* Assume that dynamic WEP-104 keys will be used and
* set cipher suites in order for drivers to expect
* encryption. */
cipher_pairwise = cipher_group = WPA_CIPHER_WEP104;
}
}
#endif /* IEEE8021X_EAPOL */
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
/* Set the key before (and later after) association */
wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
}
wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
if (bss) {
params.ssid = bss->ssid;
params.ssid_len = bss->ssid_len;
if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set ||
wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
wpa_printf(MSG_DEBUG, "Limit connection to BSSID "
MACSTR " freq=%u MHz based on scan results "
"(bssid_set=%d wps=%d)",
MAC2STR(bss->bssid), bss->freq,
ssid->bssid_set,
wpa_s->key_mgmt == WPA_KEY_MGMT_WPS);
params.bssid = bss->bssid;
params.freq.freq = bss->freq;
}
params.bssid_hint = bss->bssid;
params.freq_hint = bss->freq;
params.pbss = bss_is_pbss(bss);
} else {
if (ssid->bssid_hint_set)
params.bssid_hint = ssid->bssid_hint;
params.ssid = ssid->ssid;
params.ssid_len = ssid->ssid_len;
params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0;
}
if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set &&
wpa_s->conf->ap_scan == 2) {
params.bssid = ssid->bssid;
params.fixed_bssid = 1;
}
/* Initial frequency for IBSS/mesh */
if ((ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) &&
ssid->frequency > 0 && params.freq.freq == 0)
ibss_mesh_setup_freq(wpa_s, ssid, ¶ms.freq);
if (ssid->mode == WPAS_MODE_IBSS) {
params.fixed_freq = ssid->fixed_freq;
if (ssid->beacon_int)
params.beacon_int = ssid->beacon_int;
else
params.beacon_int = wpa_s->conf->beacon_int;
}
params.pairwise_suite = cipher_pairwise;
params.group_suite = cipher_group;
params.mgmt_group_suite = cipher_group_mgmt;
params.key_mgmt_suite = wpa_s->key_mgmt;
params.wpa_proto = wpa_s->wpa_proto;
wpa_s->auth_alg = params.auth_alg;
params.mode = ssid->mode;
params.bg_scan_period = ssid->bg_scan_period;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (ssid->wep_key_len[i])
params.wep_key[i] = ssid->wep_key[i];
params.wep_key_len[i] = ssid->wep_key_len[i];
}
params.wep_tx_keyidx = ssid->wep_tx_keyidx;
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
(params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
params.passphrase = ssid->passphrase;
if (ssid->psk_set)
params.psk = ssid->psk;
}
if (wpa_s->conf->key_mgmt_offload) {
if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
params.req_key_mgmt_offload =
ssid->proactive_key_caching < 0 ?
wpa_s->conf->okc : ssid->proactive_key_caching;
else
params.req_key_mgmt_offload = 1;
if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) &&
ssid->psk_set)
params.psk = ssid->psk;
}
params.drop_unencrypted = use_crypt;
#ifdef CONFIG_IEEE80211W
params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid);
if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) {
const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
struct wpa_ie_data ie;
if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 &&
ie.capabilities &
(WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected AP supports "
"MFP: require MFP");
params.mgmt_frame_protection =
MGMT_FRAME_PROTECTION_REQUIRED;
}
}
#endif /* CONFIG_IEEE80211W */
params.p2p = ssid->p2p_group;
if (wpa_s->p2pdev->set_sta_uapsd)
params.uapsd = wpa_s->p2pdev->sta_uapsd;
else
params.uapsd = -1;
#ifdef CONFIG_HT_OVERRIDES
os_memset(&htcaps, 0, sizeof(htcaps));
os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
params.htcaps = (u8 *) &htcaps;
params.htcaps_mask = (u8 *) &htcaps_mask;
wpa_supplicant_apply_ht_overrides(wpa_s, ssid, ¶ms);
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_VHT_OVERRIDES
os_memset(&vhtcaps, 0, sizeof(vhtcaps));
os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask));
params.vhtcaps = &vhtcaps;
params.vhtcaps_mask = &vhtcaps_mask;
wpa_supplicant_apply_vht_overrides(wpa_s, ssid, ¶ms);
#endif /* CONFIG_VHT_OVERRIDES */
#ifdef CONFIG_P2P
/*
* If multi-channel concurrency is not supported, check for any
* frequency conflict. In case of any frequency conflict, remove the
* least prioritized connection.
*/
if (wpa_s->num_multichan_concurrent < 2) {
int freq, num;
num = get_shared_radio_freqs(wpa_s, &freq, 1);
if (num > 0 && freq > 0 && freq != params.freq.freq) {
wpa_printf(MSG_DEBUG,
"Assoc conflicting freq found (%d != %d)",
freq, params.freq.freq);
if (wpas_p2p_handle_frequency_conflicts(
wpa_s, params.freq.freq, ssid) < 0) {
wpas_connect_work_done(wpa_s);
os_free(wpa_ie);
return;
}
}
}
#endif /* CONFIG_P2P */
if (wpa_s->reassoc_same_ess && !is_zero_ether_addr(prev_bssid) &&
wpa_s->current_ssid)
params.prev_bssid = prev_bssid;
ret = wpa_drv_associate(wpa_s, ¶ms);
os_free(wpa_ie);
if (ret < 0) {
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
"failed");
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) {
/*
* The driver is known to mean what is saying, so we
* can stop right here; the association will not
* succeed.
*/
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
return;
}
/* try to continue anyway; new association will be tried again
* after timeout */
assoc_failed = 1;
}
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
/* Set the key after the association just in case association
* cleared the previously configured key. */
wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
/* No need to timeout authentication since there is no key
* management. */
wpa_supplicant_cancel_auth_timeout(wpa_s);
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
#ifdef CONFIG_IBSS_RSN
} else if (ssid->mode == WPAS_MODE_IBSS &&
wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
/*
* RSN IBSS authentication is per-STA and we can disable the
* per-BSSID authentication.
*/
wpa_supplicant_cancel_auth_timeout(wpa_s);
#endif /* CONFIG_IBSS_RSN */
} else {
/* Timeout for IEEE 802.11 authentication and association */
int timeout = 60;
if (assoc_failed) {
/* give IBSS a bit more time */
timeout = ssid->mode == WPAS_MODE_IBSS ? 10 : 5;
} else if (wpa_s->conf->ap_scan == 1) {
/* give IBSS a bit more time */
timeout = ssid->mode == WPAS_MODE_IBSS ? 20 : 10;
}
wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
}
if (wep_keys_set &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC)) {
/* Set static WEP keys again */
wpa_set_wep_keys(wpa_s, ssid);
}
if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) {
/*
* Do not allow EAP session resumption between different
* network configurations.
*/
eapol_sm_invalidate_cached_session(wpa_s->eapol);
}
old_ssid = wpa_s->current_ssid;
wpa_s->current_ssid = ssid;
if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
wpa_s->current_bss = bss;
#ifdef CONFIG_HS20
hs20_configure_frame_filters(wpa_s);
#endif /* CONFIG_HS20 */
}
wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
wpa_supplicant_initiate_eapol(wpa_s);
if (old_ssid != wpa_s->current_ssid)
wpas_notify_network_changed(wpa_s);
}