RFC4137協議的全稱是"State Machine for Extensible Authentication Protocol(EAP)Peer and Authenticator",它描述了Peer端(即Supplicant端)和Authenticator端通過狀態機(State Machine)這種方式來實現EAP處理流程的具體步驟和相關細節。本節將重點介紹Supplicant端SM的設計原理。為了行文方便,本節將使用SUPP代替Supplicant。
#### **一、Supplicant端SM設計原理**
對狀態機來說,最重要的是其狀態切換圖。RFC4137中SUPP SM狀態切換如圖4-21所示。
圖4-21的內容極為豐富,此處先介紹其中三個知識點。
* SUPP SM一共定義了13個狀態,每個狀態用一個框表示。框頂部所示為狀態名,如INTIALZE、IDLE等。
* 每個狀態都可以有自己的Entry Action(以后簡稱EA)。進入這個狀態后,EA將被執行。EA由狀態框中狀態名下邊的偽代碼(采用了類C++語法)表示。以FAILURE狀態為例,當狀態機進入該狀態后,將執行"eapFail=TRUE"偽代碼,eapFail是SUPP SM定義的變量,下文將詳細介紹圖中涉及的變量和相關函數。
* 圖中的UCT代表Unconditional Transition,即無條件狀態轉換。以DISCARD狀態和IDLE狀態為例,由于UCT的存在,當SUPP SM在DISCARD狀態中執行完其EA后,將直接轉換到IDLE狀態。
對一個狀態機而言,其狀態的轉換是因為外界條件發生變化導致。在規范中,這些外界條件由變量來表達。圖4-21中出現了很多變量和EA所包括的一些函數,它們都由RFC4137文檔定義。了解它們的作用對真正理解SUPP SM有直接和重要幫助。接下來的章節就將介紹這些變量和函數。
:-: 
圖4-21 SUPP SM狀態切換
:-: 
圖4-22 RFC4137 SUPP SM模塊劃分
RFC4137將和SUPP SM相關的模塊分為三層,如圖4-22所示。圖中最底層是Lower Layer(LL),這一層的作用是接收和發送EAP包。位于中間的SUPP SM層實現了Supplican狀態機。最上層是EAP Method(EM)層,它實現了具體的EAP方法。
SUPP SM將與EM層和LL層交互。一個最典型的交互例子就是LL收到EAP數據包后,將該數據包交給SUPP SM層去處理。如果該EAP包需要EM層處理(例如具體的驗證算法需要EM完成),則SUPP SM層將該包交給EM。EM處理完的結果將由SUPP SM轉交給LL去發送。
>[info] 提示 RFC4137中,三層之間交互的手段可以是設置變量,或者是調用函數。
先來看SUPP SM與LL交互時所使用的變量。
**1、LL層和SUPP SM層交互變量**
LL層和SUPP SM層的交互比較簡單,主要包括三個步驟。
1. LL層收到EAP數據包后,將其保存在eapReqData變量中,然后設置eapReq變量為TRUE。這個變量的改變對SUPP SM層來說是一個觸發信號(signal)。SUPP SM可能會發生狀態轉換。
2. SUPP SM層從eapReqData中取出數據后進行處理。如果有需要回復的數據,則設置eapResp值為TRUE,否則設置eapNoResp值為TRUE。回復數據存儲在eapRespData中。LL層將發送此回復包。
3. 如果SUPP SM完成身份驗證后,它將設置eapSuccess或eapFailure變量以告知LL層其驗證結果。eapSuccess為TRUE,表明驗證成功。eapFailure為TRUE,則驗證失敗。
上述描述中所涉及的變量及其類型如表4-1所示。注意,此處的數據類型屬于偽代碼。
>[info] 提示 在WPAS中,LL層并非那些直接利用socket進行數據收發的模塊,而是EAPOL模塊。
EAP和EAPOL模塊的關系將留待下一節再介紹。
:-: 
:-: 
表4-11 LL層和SUPP層交互變量
表4-1中altAccept和altReject兩個變量的命名非常晦澀難懂。RFC4137指出這兩個變量的定義在RFC3748中。實際上,RFC3748從頭至尾都沒有出現過這兩個變量。經過仔細研究,筆者發現[這篇文章](http://lists.frascone.com/pipermail/eap/msg02578.html )對此有一個說法,內容如下。
這兩個變量取名為lowerLayerSuccess和lowerLayerFailure更合適,它們用于通知LL層Success或Failure信息。結合上述資料,筆者查閱了RFC3748 7.12節,在802.11網絡中,Indicatioin(通知)Success和Failure的可能場景如下。
* 當supplicant收到Disassociate幀或者Deauthenticate幀時,表示lowerLayerFailure。
* 當收到4-Way Handshake第一個Message時,表示lowerLayerSuccess。
>[info] 規范閱讀提示 RFC4137中,上述變量還可分為兩種類型。
1. 由LL層暴露給SUPP SM層的變量(Variables from Lower Layer to Peer)。它們從表4-1中的eapReq開始,到altReject結束。原則上,LL層和SUPP SM層都可以修改這些變量。
2. 由SUPP SM層暴露給LL層的變量(Variables from Peer to Lower Layer)。它們從表4-1中的eapResp開始,到eapKeyAvailable結束。原則上,LL層和SUPP SM層都可以修改這些變量。另外,這些變量也可由SUPP SM層暴露給EM層來使用。
接著來看SUPP SM層和EM層交互變量。
**2、SUPP SM層和EM層交互變量**
SUPP SM層和EM層的交互也是通過變量來完成的。這些變量如表4-2所示。
:-: 
:-: 
表4-2 SUPP SM和EM層交互變量
注意,methodState和decision的值由具體的認證方法(即Method)來確定。
>[info] 提示 本書不討論所有EAP方法的具體實現。感興趣的讀者可以深入研究EAP模塊。不過對EAP SUPP SM來說,methodState和decision的取值情況才是最重要的,因為它們會直接影響SUPP SM的狀態切換。
**3、SUPP SM其他變量和處理函數**
RFC4137還為SUPP SM還定義了其他一些變量(Peer State Machine Local Variables)及函數。變量定義見表4-3。
:-: 
表4-3 SUPP SM層內部變量定義
:-: 
表4-4 SUPP SM處理函數的定義。
注意,表4-4中用偽代碼展示了這些函數的使用案例,它們并不遵守C++語法。例如:
~~~
(rxReq,rxSuccess,rxFailure,reqId,reqMethod)=parseEapReq(eapReqData)
~~~
等號左邊為parseEapReq函數的返回值,等號右邊括號中的"eapReqData"為parseEapReq的輸入參數。如果使用案例中沒有等號,則表示該函數無返回值(具體實現時,可設置該函數的返回值為void)。
注意 圖4-21中還包含一些其他函數,奇怪的是規范中并沒有列舉它們。不過,相信讀者很容易理解這些數作用,此處不詳述。現在,讀者能看懂圖4-21所示的狀態圖了嗎?
**4、SUPP SM定義的狀態**
SUPP SM定義的13個狀態如表4-5所示。
:-: 
:-: 
表4-5 SUPP SM狀態
前面展示了RFC4137中和SUPP SM相關的內容。筆者覺得這個狀態機定義太過煩瑣。不過,本著簡單、明了并且沒有歧義的原則,這種做法似乎又無可厚非。從筆者經驗來看,讀者只要能看懂圖4-21所示SUPP SM狀態切換圖即算掌握了EAP模塊的精髓了。WPAS中EAP SUPPSM該如何實現呢?請看下節。
#### **二、EAP SUPP SM代碼分析**
上文中曾提到過,RFC4137定義的狀態機非常煩瑣,而具體實現可以根據情況進行裁剪。不過,WPAS中的EAP SUPP SM卻較為嚴格得遵循了RFC4137。先來看其定義的數據類型和數據結構。
**1、相關數據結構與數據類型**
圖4-23所示為EAP SUPP SM定義的枚舉變量類型。
:-: 
圖4-23 EAP SUPP SM枚舉類型定義
圖4-23中,左圖定義了EapDecision、EapMethodState、Boolean、eapol_bool_var和eapol_int_var枚舉類型,它們都和上節介紹的變量及類型有關。右圖定義了EapType枚舉變量,代表不同的EAP Method。
:-: 
圖4-24所示為WPAS中和SUPP SM相關的數據結構。
圖4-24中,eap_sm是RFC4137 EAP SUPP SM的代表。從其成員變量的命名可知,它幾乎完全是按照RFC4137來實現的。eap_sm定義了一個名為EAP_state的枚舉類型成員變量。
eap_sm通過m成員變量指向一個eap_method鏈表。eap_method是一個由next指針鏈接起來的單向鏈表,每一個eap_method對象代表一種具體的EAP Method(EAP Method的注冊請回顧4.3.2節“eap_register_methods函數分析”)。eap_method最重要的是其處理函數,WPAS對其略有修改。例如,process函數實際上完成了表4-4中m.check、m.process和m.buildResp的功能。
eap_method中,process函數第三個參數的類型是`eap_method_ret*`,代表一個`eap_method_ret`對象。由圖4-24可知,它包括了ignore、methodState、decision以及allowNotifications變量。
eap_sm的eapol_cb對象指向一個eapol_callbacks對象,它是LL層的代表。不過,eapol_callbacks的定義看起來和RFC4137關系不大。
>[info] 注意 圖4-24中eap_sm第一個成員變量EAP_State是一個枚舉類型,其枚舉值就是圖4-21中SUPP SM的各個狀態。
EAP SUPP SM初始化時,eap_sm的eapol_callbacks被設置為eapol_cb對象。代碼如下所示。
**eapol_supp_sm.c::eapol_cb定義**
~~~
static struct eapol_callbacks eapol_cb = {
eapol_sm_get_config,eapol_sm_get_bool,eapol_sm_set_bool,eapol_sm_get_int,
eapol_sm_set_int,eapol_sm_get_eapReqData,eapol_sm_set_config_blob,
eapol_sm_get_config_blob,eapol_sm_notify_pending,eapol_sm_eap_param_needed,
eapol_sm_notify_cert
};
~~~
上述函數相關代碼我們留待碰到它們時再介紹。
**2、WPAS狀態機通用宏**
WPAS中有許多狀態機,所以它定義了一些通用宏來幫助實現狀態機相關的代碼。這些宏的定義如下所示。
**state_machine.h**
~~~
/*
定義一個狀態的EA,它是一個函數聲明。
而STATE_MACHINE_DATA也是一個宏。對于EAP SSM來說,其類型是struct eap_sm。
global代表觸發該狀態的原因是否為UCT。
*/
#define SM_STATE(machine, state) \
static void sm_ ## machine ## _ ## state ## _Enter(STATE_MACHINE_DATA *sm,int global)
// 每個狀態進入后執行的一段代碼。一般是打印一些信息,并設置新的狀態
#define SM_ENTRY(machine, state) \
if (!global || sm->machine ## _state != machine ## _ ## state) { \
sm->changed = TRUE; \ // changed變量用于記錄狀態機的狀態是否發生變化
wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " #machine \
" entering state " #state); \
} \
sm->machine ## _state = machine ## _ ## state; // 設置狀態機的狀態
// SM_ENTER宏對應一次函數調用,調用的是SM_STATE宏定義的函數
#define SM_ENTER(machine, state) \
sm_ ## machine ## _ ## state ## _Enter(sm, 0)
// 對應一次函數調用,表示因UCT而直接進入某個狀態
#define SM_ENTER_GLOBAL(machine, state) \
sm_ ## machine ## _ ## state ## _Enter(sm, 1) // 這個函數由SM_STATE宏聲明
// 運行狀態機。該宏定義一個函數
#define SM_STEP(machine) \
static void sm_ ## machine ## _Step(STATE_MACHINE_DATA *sm)
// 該宏對應一次函數調用,即sm_##machine_Step(sm),sm參數由調用函數內聲明
#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
~~~
下面通過SUPP SM的實現代碼來認識下上述通用宏的用法。
**3、EAP SUPP SM的實現**
SUPP SM的狀態比較多,此處僅列舉DISABLED狀態的實現代碼以幫助讀者理解通用宏的作用。對狀態機來說,其狀態對應的EA非常重要。如下代碼所示為DISABLED狀態對應的EA。根據上節對通用宏的介紹,EA由SM_STATE宏來定義。
**eap.c::SM_STATE(EAP,DISABLED)**
~~~
該宏對應的代碼是
static void sm_EAP_DISABLED_Enter(STATE_MACHINE_DATA *sm,int global)
*/
SM_STATE(EAP, DISABLED)
{
SM_ENTRY(EAP, DISABLED); // 每個狀態的EA都會執行SM_ENTRY代碼段
/*
SM_ENTRY宏對應的代碼是:
if (!global || sm->EAP_state != EAP_DISABLED) {
sm->changed = TRUE;
wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " "EAP" \
" entering state " "DISABLED"); // 這段日志對了解SUPP SM當前處于哪個狀態非常重要
}
// EAP_state是eap_sm中成員變量。讀者可參考圖4-24
sm->EAP_state = EAP_DISABLED;
*/
sm->num_rounds = 0;
}
~~~
SM_STATE只是定義了狀態機某個狀態的EA,那么狀態機是如何運作的呢?根據圖4-21以及前文所述,狀態機的狀態切換主要是通過判斷條件是否滿足來完成。SM_STEP定義的函數就是用于檢查狀態機的這些條件變量,然后根據情況進行狀態轉換的。SUPP SM的SM_STEP宏對應的代碼如下所示。
**eap.c::SM_STEP(EAP)**
~~~
/*
SM_STEP宏對應的函數定義為:
static void sm_EAP_Step(STATE_MACHINE_DATA *sm)
*/
SM_STEP(EAP)
{
/*
對應UCT的處理。eapol_get_bool函數將調用eapol_callbacks對象中的eapol_sm_get_
bool函數其內部返回eap_sm中eapRestart成員變量的值。
*/
if (eapol_get_bool(sm, EAPOL_eapRestart) && eapol_get_bool(sm, EAPOL_portEnabled))
/*
調用由SM_STATE(EAP,INITIALZE)定義的函數,以進入EAP_INITIALIZE狀態。
GLOBAL的意思是UCT。請讀者注意此處的判斷條件:如果eap_sm的eapRestart和
portEnabled成員變量都為true,則直接進入INITIALIZE狀態。它完全和圖4-21
SUPP SM狀態圖一樣。
*/
SM_ENTER_GLOBAL(EAP, INITIALIZE);
else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled)
SM_ENTER_GLOBAL(EAP, DISABLED);
else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
......// 有一些EAP方法在認證錯誤時會有很多消息往來,WPAS對此做了一個限制
// 一旦這些錯誤消息往來超過50次(由EAP_MAX_AUTH_ROUDS),則直接進入FAILURE狀態
sm->num_rounds++;
SM_ENTER_GLOBAL(EAP, FAILURE); // GLOBAL代表UCT的情況
}
} else eap_peer_sm_step_local(sm); // 對應其他非UCT的情況
}
~~~
此處簡單看一下eap_peer_sm_step_local的代碼。
**eap.c::eap_peer_sm_step_local**
~~~
static void eap_peer_sm_step_local(struct eap_sm *sm)
{
switch (sm->EAP_state) {
......
case EAP_IDLE:
// 圖4-21中,idle狀態可依據條件不同而跳轉到其他多個狀態
// 下面這個函數用于選擇目標狀態及跳轉到它
eap_peer_sm_step_idle(sm);// 根據圖4-21的idle狀態跳轉,讀者能想象出該函數的代碼實現嗎
break;
case EAP_RECEIVED:
eap_peer_sm_step_received(sm);
break;
case EAP_GET_METHOD:
if (sm->selectedMethod == sm->reqMethod)
SM_ENTER(EAP, METHOD); // 直接進入METHOD狀態
else
SM_ENTER(EAP, SEND_RESPONSE); // 直接進入SEND_RESPONSE狀態
break;
......
}
}
~~~
eap_peer_sm_step_local用于處理那些非UCT導致的狀態切換。
>[info] 提示 EAP SUPP SM的代碼雖不復雜,但由于SUPP SM狀態和觸發條件(即定義的那些變量)太多,想通過看代碼去跟蹤SUPP SM間的狀態跳轉是一件非常困難的事情。相比而言,圖4-21比代碼要直觀,更加容易把注意力集中在目標狀態以及它對應的EA上。另外,SM_STATE代碼段中包含的那段wpa_printf輸出將告知EAP模塊當前的狀態。讀者以后在分析WPAS日志時千萬要注意。
#### **三、EAP SUPP SM總結**
EAP SUPP SM基于RFC4137而實現,其內部變量的定義以及狀態切換邏輯都來源于規范。以筆者的經驗來看,掌握RFC4137是理解WPAS中EAP SUPP SM實現的基石。另外,對具體的處理邏輯而言,SUPP SM最重要的內容還是各個狀態對應的EA。正如前文所述,圖4-21對SUPP SM的運行極為重要,希望讀者認真學習。
另外,對WPAS中狀態機的實現來說,SM_STATE用于定義某個狀態的EA(即一個函數)。每個EA都會執行SM_ENTRY宏定義的一段代碼。SM_ENTER和SM_ENTER_GLOBAL宏用于調用SM_STATE定義的函數。GLOBAL代表UCT的情況。SM_STEP宏用于運行整個狀態機。請讀者注意它和SM_ENTER的區別。SM_ENTER宏將直接調用某個指定狀態(由SM_ENTER宏的參數決定)的EA,而SM_STEP則將根據SM中的變量情況來決定下一個要跳轉的狀態,然后調用它的SM_ENTER。下面來看EAPOL模塊的實現。
- 前言
- 第1章 準備工作
- 1.1 Android系統架構
- 1.2 工具使用
- 1.2.1 Source Insight的使用
- 1.2.2 Eclipse的使用
- 1.2.3 BusyBox的使用
- 1.3 本書資源下載說明
- 第2章 深入理解Netd
- 2.1 概述
- 2.2 Netd工作流程
- 2.2.1 main函數分析
- 2.2.2 NetlinkManager分析
- 2.2.3 CommandListener分析
- 2.2.4 DnsProxyListener分析
- 2.2.5 MDnsSdListener分析
- 2.3 CommandListener中的命令
- 2.3.1 iptables、tc和ip命令
- 2.3.2 CommandListener構造函數和測試工具ndc
- 2.3.3 InterfaceCmd命令
- 2.3.4 IpFwd和FirewallCmd命令
- 2.3.5 ListTtysCmd和PppdCmd命令
- 2.3.6 BandwidthControlCmd和IdletimerControlCmd命令
- 2.3.7 NatCmd命令
- 2.3.8 TetherCmd和SoftapCmd命令
- 2.3.9 ResolverCmd命令
- 2.4 NetworkManagementService介紹
- 2.4.1 create函數詳解
- 2.4.2 systemReady函數詳解
- 2.5 本章總結和參考資料說明
- 2.5.1 本章總結
- 2.5.2 參考資料說明
- 第3章 Wi-Fi基礎知識
- 3.1 概述
- 3.2 無線電頻譜和802.11協議的發展歷程
- 3.2.1 無線電頻譜知識
- 3.2.2 IEEE 802.11發展歷程
- 3.3 802.11無線網絡技術
- 3.3.1 OSI基本參考模型及相關基本概念
- 3.3.2 802.11知識點導讀
- 3.3.3 802.11組件
- 3.3.4 802.11 Service介紹
- 3.3.5 802.11 MAC服務和幀
- 3.3.6 802.11 MAC管理實體
- 3.3.7 無線網絡安全技術知識點
- 3.4 Linux Wi-Fi編程API介紹
- 3.4.1 Linux Wireless Extensions介紹
- 3.4.2 nl80211介紹
- 3.5 本章總結和參考資料說明
- 3.5.1 本章總結
- 3.5.2 參考資料說明
- 第4章 深入理解wpa_supplicant
- 4.1 概述
- 4.2 初識wpa_supplicant
- 4.2.1 wpa_supplicant架構
- 4.2.2 wpa_supplicant編譯配置
- 4.2.3 wpa_supplicant命令和控制API
- 4.2.4 git的使用
- 4.3 wpa_supplicant初始化流程
- 4.3.1 main函數分析
- 4.3.2 wpa_supplicant_init函數分析
- 4.3.3 wpa_supplicant_add_iface函數分析
- 4.3.4 wpa_supplicant_init_iface函數分析
- 4.4 EAP和EAPOL模塊
- 4.4.1 EAP模塊分析
- 4.4.2 EAPOL模塊分析
- 4.5 wpa_supplicant連接無線網絡分析
- 4.5.1 ADD_NETWORK命令處理
- 4.5.2 SET_NETWORK命令處理
- 4.5.3 ENABLE_NETWORK命令處理
- 4.6 本章總結和參考資料說明
- 4.6.1 本章總結
- 4.6.2 參考資料說明
- 第5章 深入理解WifiService
- 5.1 概述
- 5.2 WifiService的創建及初始化
- 5.2.1 HSM和AsyncChannel介紹
- 5.2.2 WifiService構造函數分析
- 5.2.3 WifiStateMachine介紹
- 5.3 加入無線網絡分析
- 5.3.1 Settings操作Wi-Fi分析
- 5.3.2 WifiService操作Wi-Fi分析
- 5.4 WifiWatchdogStateMachine介紹
- 5.5 Captive Portal Check介紹
- 5.6 本章總結和參考資料說明
- 5.6.1 本章總結
- 5.6.2 參考資料說明
- 第6章 深入理解Wi-Fi Simple Configuration
- 6.1 概述
- 6.2 WSC基礎知識
- 6.2.1 WSC應用場景
- 6.2.2 WSC核心組件及接口
- 6.3 Registration Protocol詳解
- 6.3.1 WSC IE和Attribute介紹
- 6.3.2 802.11管理幀WSC IE設置
- 6.3.3 EAP-WSC介紹
- 6.4 WSC代碼分析
- 6.4.1 Settings中的WSC處理
- 6.4.2 WifiStateMachine的處理
- 6.4.3 wpa_supplicant中的WSC處理
- 6.4.4 EAP-WSC處理流程分析
- 6.5 本章總結和參考資料說明
- 6.5.1 本章總結
- 6.5.2 參考資料說明
- 第7章 深入理解Wi-Fi P2P
- 7.1 概述
- 7.2 P2P基礎知識
- 7.2.1 P2P架構
- 7.2.2 P2P Discovery技術
- 7.2.3 P2P工作流程
- 7.3 WifiP2pSettings和WifiP2pService介紹
- 7.3.1 WifiP2pSettings工作流程
- 7.3.2 WifiP2pService工作流程
- 7.4 wpa_supplicant中的P2P
- 7.4.1 P2P模塊初始化
- 7.4.2 P2P Device Discovery流程分析
- 7.4.3 Provision Discovery流程分析
- 7.4.4 GO Negotiation流程分析
- 7.5 本章總結和參考資料說明
- 7.5.1 本章總結
- 7.5.2 參考資料說明
- 第8章 深入理解NFC
- 8.1 概述
- 8.2 NFC基礎知識
- 8.2.1 NFC概述
- 8.2.2 NFC R/W運行模式
- 8.2.3 NFC P2P運行模式
- 8.2.4 NFC CE運行模式
- 8.2.5 NCI原理
- 8.2.6 NFC相關規范
- 8.3 Android中的NFC
- 8.3.1 NFC應用示例
- 8.3.2 NFC系統模塊
- 8.4 NFC HAL層討論
- 8.5 本章總結和參考資料說明
- 8.5.1 本章總結
- 8.5.2 參考資料說明
- 第9章 深入理解GPS
- 9.1 概述
- 9.2 GPS基礎知識
- 9.2.1 衛星導航基本原理
- 9.2.2 GPS系統組成及原理
- 9.2.3 OMA-SUPL協議
- 9.3 Android中的位置管理
- 9.3.1 LocationManager架構
- 9.3.2 LocationManager應用示例
- 9.3.3 LocationManager系統模塊
- 9.4 本章總結和參考資料說明
- 9.4.1 本章總結
- 9.4.2 參考資料說明
- 附錄