端源碼實(shí)戰(zhàn):從EAPOL幀到狀態(tài)機(jī)設(shè)計(jì))
簡(jiǎn)介開(kāi)源的802.1X客戶(hù)端項(xiàng)目源代碼面向網(wǎng)絡(luò)協(xié)議開(kāi)發(fā)者和安全運(yùn)維人員可用于實(shí)現(xiàn)基于端口的接入認(rèn)證與網(wǎng)絡(luò)準(zhǔn)入控制NAC并支持在Windows、Linux、Android等平臺(tái)定制移植。壓縮包共771個(gè)文件約3.95MB核心為275個(gè).h頭文件、227個(gè).c與47個(gè).cpp源碼文件輔以35個(gè).ui界面、50個(gè).png圖標(biāo)以及PDF/ODT說(shuō)明文檔和工程配置文件覆蓋從底層協(xié)議到界面交互的完整實(shí)現(xiàn)。已有751人學(xué)習(xí)下載。通過(guò)分析這份代碼可以系統(tǒng)梳理802.1X認(rèn)證流程、EAP-TLS/EAP-PEAP等可擴(kuò)展認(rèn)證協(xié)議、RADIUS交互以及NAP安全策略集成方式結(jié)合XSupplicant-2.2.0-src的工程結(jié)構(gòu)還能了解跨平臺(tái)構(gòu)建思路、模塊劃分與版本迭代細(xì)節(jié)為開(kāi)發(fā)自主網(wǎng)絡(luò)準(zhǔn)入方案提供直接參考。 這是我最常被問(wèn)到的網(wǎng)絡(luò)協(xié)議方向之一802.1x客戶(hù)端源碼到底怎么讀、怎么寫(xiě)、怎么調(diào)試。很多人一上來(lái)就翻wpa_supplicant翻了大半天也沒(méi)找到“主函數(shù)”在哪最后只能對(duì)著滿(mǎn)屏源碼嘆氣。其實(shí)不是源碼難而是沒(méi)有從協(xié)議邊界入手。與其直接問(wèn)“代碼怎么寫(xiě)”不如先問(wèn)一句“一個(gè)802.1x客戶(hù)端到底要替用戶(hù)干哪幾件事”。這事想明白了源碼里每個(gè)文件對(duì)應(yīng)什么功能基本一眼就能對(duì)上。1. 客戶(hù)端源碼的第一步把“誰(shuí)認(rèn)證誰(shuí)”徹底搞清楚1.1 三個(gè)角色不是可有可無(wú)的概念I(lǐng)EEE 802.1X整個(gè)體系里有三個(gè)角色請(qǐng)求者Supplicant也就是客戶(hù)端本身、認(rèn)證者Authenticator一般是交換機(jī)或AP、認(rèn)證服務(wù)器Authentication Server通常是RADIUS。認(rèn)證者和認(rèn)證服務(wù)器之間走RADIUS協(xié)議客戶(hù)端和認(rèn)證者之間走的則是EAPOL??蛻?hù)端源代碼要處理的只有EAPOL這一段但正是因?yàn)椤爸惶幚磉@一段”反而容易讓人忽略整體時(shí)序。我見(jiàn)過(guò)有人寫(xiě)客戶(hù)端時(shí)期望它直接和服務(wù)端交換“用戶(hù)名密碼”明文這是把802.1X當(dāng)成了應(yīng)用層協(xié)議。實(shí)際機(jī)制是客戶(hù)端發(fā)出的EAP-Response/Identity、EAP-Response/MD5-Challenge這些報(bào)文會(huì)被認(rèn)證者原封不動(dòng)地封裝成RADIUS屬性轉(zhuǎn)給認(rèn)證服務(wù)器。認(rèn)證者在這里就是一個(gè)“翻譯官”而客戶(hù)端源碼關(guān)心的永遠(yuǎn)是兩件事下一幀該發(fā)什么收到這一幀該干什么。1.2 協(xié)議邊界與代碼分層從代碼結(jié)構(gòu)上看一個(gè)自研客戶(hù)端至少要分成三層每層的邊界必須非常清晰分層職責(zé)Linux常見(jiàn)實(shí)現(xiàn)鏈路收發(fā)包組幀、解幀、收發(fā)EAPOLAF_PACKET原始套接字會(huì)話(huà)狀態(tài)機(jī)管理認(rèn)證生命周期、超時(shí)重傳核心狀態(tài)機(jī)EAP方法具體認(rèn)證算法MD5/TLS/PEAP可插拔方法模塊為什么一定要這樣分因?yàn)?02.1X的EtherType是0x888E屬于二層協(xié)議不走TCP/IP協(xié)議棧。你用普通socket去connect根本收不到認(rèn)證者發(fā)來(lái)的EAP-Request。Linux下最直接的辦法是用AF_PACKET套接字綁定網(wǎng)卡抓取0x888E類(lèi)型幀Windows平臺(tái)則要借助WinPcap/Npcap或者在NDIS驅(qū)動(dòng)層做過(guò)濾。這個(gè)邊界如果搞錯(cuò)后面的代碼不管寫(xiě)得多漂亮都是白寫(xiě)。2. 手寫(xiě)EAPOL幀構(gòu)造代碼與常見(jiàn)坑2.1 EAPOL幀格式拆解EAPOL幀的結(jié)構(gòu)是14字節(jié)以太網(wǎng)頭 4字節(jié)EAPOL頭 幀載荷。EAPOL頭包含版本號(hào)1字節(jié)、幀類(lèi)型1字節(jié)、載荷長(zhǎng)度2字節(jié)網(wǎng)絡(luò)字節(jié)序。幀類(lèi)型里0是EAP-Packet1是EAPOL-Start2是EAPOL-Logoff3是EAPOL-Key。新手最容易犯的錯(cuò)誤是把這幾種類(lèi)型混在一個(gè)框架里實(shí)際上它們承載的內(nèi)容完全不同。最簡(jiǎn)的EAPOL-Start沒(méi)有載荷長(zhǎng)度字段為0構(gòu)幀代碼大致長(zhǎng)這樣unsigned char frame[18]; memset(frame, 0, sizeof(frame)); memcpy(frame, dst_mac, 6); /* 認(rèn)證者M(jìn)AC */ memcpy(frame 6, src_mac, 6); /* 本機(jī)MAC */ frame[12] 0x88; /* EtherType高字節(jié) */ frame[13] 0x8E; /* EtherType低字節(jié) */ frame[14] 0x01; /* 版本號(hào) */ frame[15] 0x01; /* 類(lèi)型: EAPOL-Start */ frame[16] 0x00; /* 長(zhǎng)度高字節(jié) */ frame[17] 0x00; /* 長(zhǎng)度低字節(jié): 0 */這里版本號(hào)值得多說(shuō)一句。802.1X-2001里版本號(hào)固定為1802.1X-2004標(biāo)準(zhǔn)里因?yàn)橐肓诵绿匦园姹咎?hào)是2??蓪?shí)際聯(lián)調(diào)中某些老交換機(jī)只認(rèn)版本號(hào)1看到2直接丟棄。穩(wěn)妥做法是把版本號(hào)做成配置項(xiàng)默認(rèn)按1發(fā)送碰到新設(shè)備再調(diào)整。這種“標(biāo)準(zhǔn)寫(xiě)得清楚但現(xiàn)實(shí)不按標(biāo)準(zhǔn)走”的情況在802.1X聯(lián)調(diào)里實(shí)在太常見(jiàn)了。2.2 EAP包封裝的長(zhǎng)度陷阱EAPOL頭里的長(zhǎng)度字段只表示后面EAP報(bào)文的長(zhǎng)度不包含EAPOL頭自身更不包含以太網(wǎng)頭。組幀時(shí)容易出問(wèn)題的是EAP包本身也有一個(gè)Length字段2字節(jié)它包含Code、Identifier、Length、Type和Type-Data的全部字節(jié)數(shù)。也就是說(shuō)長(zhǎng)度信息出現(xiàn)了兩處任何一處算錯(cuò)對(duì)端都會(huì)把報(bào)文解析錯(cuò)位。我早期版本就吃過(guò)這個(gè)虧EAPOL長(zhǎng)度寫(xiě)對(duì)了EAP Length少算了2字節(jié)結(jié)果認(rèn)證者一直不回包抓包才發(fā)現(xiàn)對(duì)端把EAP報(bào)文末尾兩個(gè)字節(jié)當(dāng)成了“下一幀的部分內(nèi)容”。排查方法很簡(jiǎn)單看Wireshark有沒(méi)有提示“Malformed Packet”只要它報(bào)了優(yōu)先檢查所有長(zhǎng)度字段正確率遠(yuǎn)高于檢查業(yè)務(wù)邏輯。提示W(wǎng)ireshark報(bào)Malformed Packet時(shí)基本就是報(bào)文頭長(zhǎng)度或字段解析出了問(wèn)題先別懷疑算法和密碼。2.3 EAPOL-Start有線(xiàn)主動(dòng)、無(wú)線(xiàn)被動(dòng)連接在有線(xiàn)交換機(jī)上的客戶(hù)端端口默認(rèn)處于未授權(quán)狀態(tài)很多交換機(jī)要等收到EAPOL-Start才愿意發(fā)起認(rèn)證流程所以客戶(hù)端必須主動(dòng)。但在Wi-Fi環(huán)境下恰恰相反終端關(guān)聯(lián)AP成功后AP會(huì)直接發(fā)送EAP-Request/Identity如果終端還主動(dòng)發(fā)EAPOL-Start部分AP反而會(huì)誤判甚至直接丟棄后續(xù)報(bào)文。所以客戶(hù)端源碼里要把“是否主動(dòng)發(fā)送Start”做成介質(zhì)相關(guān)的可配置參數(shù)有線(xiàn)默認(rèn)開(kāi)無(wú)線(xiàn)默認(rèn)關(guān)。我在一個(gè)多平臺(tái)客戶(hù)端項(xiàng)目里就吃過(guò)“無(wú)線(xiàn)主動(dòng)Start”的虧。當(dāng)時(shí)在辦公室用有線(xiàn)驗(yàn)證一切正常換到測(cè)試Wi-Fi上就頻繁認(rèn)證失敗。抓包一看AP發(fā)來(lái)的EAP-Request/Identity還在空中飛客戶(hù)端已經(jīng)連發(fā)三個(gè)EAPOL-StartAP端直接進(jìn)入等待或丟棄狀態(tài)。后來(lái)把Start策略改成按接口介質(zhì)區(qū)分問(wèn)題立刻消失。這種細(xì)節(jié)協(xié)議標(biāo)準(zhǔn)里不會(huì)明確告訴你“無(wú)線(xiàn)就別發(fā)”全靠聯(lián)調(diào)時(shí)吃一塹長(zhǎng)一智。EAPOL-Logoff幀類(lèi)型2用于主動(dòng)登出它會(huì)通知認(rèn)證者把端口狀態(tài)改回未授權(quán)。很多實(shí)現(xiàn)只在網(wǎng)口down或者用戶(hù)點(diǎn)擊“注銷(xiāo)”時(shí)才發(fā)。注意發(fā)送后要清理本地狀態(tài)回DISCONNECTED別讓狀態(tài)機(jī)停在半路。3. 狀態(tài)機(jī)實(shí)現(xiàn)這是客戶(hù)端源碼真正的復(fù)雜度所在3.1 為什么狀態(tài)機(jī)不值得自己“拍腦袋”寫(xiě)從零寫(xiě)一個(gè)能通過(guò)EAP-MD5認(rèn)證的客戶(hù)端幀構(gòu)造部分加起來(lái)不過(guò)幾百行但狀態(tài)機(jī)要做到“穩(wěn)定、不掉線(xiàn)、抗異?!边h(yuǎn)比想象中復(fù)雜。原因很簡(jiǎn)單認(rèn)證過(guò)程不是一個(gè)線(xiàn)性的“請(qǐng)求-響應(yīng)-成功”流程中間會(huì)有超時(shí)、重傳、Identifier變更、認(rèn)證服務(wù)器重啟、交換機(jī)端口重新初始化等一堆邊界情況。沒(méi)有清晰的狀態(tài)機(jī)后面的邏輯就會(huì)變成一坨難以維護(hù)的“if-else 圣地”。建議先參考RFC 4137它定義了一套請(qǐng)求者狀態(tài)機(jī)的完整模型邊界條件寫(xiě)得很全然后在自己的項(xiàng)目里實(shí)現(xiàn)一個(gè)簡(jiǎn)化版。核心狀態(tài)至少包括INITIALIZE初始化等待上層使能DISCONNECTED端口未授權(quán)或鏈路斷開(kāi)CONNECTING已觸發(fā)認(rèn)證請(qǐng)求等待EAP-Request/IdentityACQUIRED收到請(qǐng)求正在處理EAP報(bào)文明文AUTHENTICATING認(rèn)證進(jìn)行中OPEN認(rèn)證通過(guò)端口放行HELD認(rèn)證失敗進(jìn)入懲罰性等待。3.2 關(guān)鍵轉(zhuǎn)換條件與實(shí)現(xiàn)細(xì)節(jié)這些狀態(tài)里最容易寫(xiě)錯(cuò)的是AUTHENTICATING狀態(tài)內(nèi)部的循環(huán)。客戶(hù)端收到EAP-Request后要回復(fù)對(duì)應(yīng)的EAP-Response這個(gè)過(guò)程中認(rèn)證者可能連續(xù)發(fā)多個(gè)不同類(lèi)型的Request比如先Identity再M(fèi)D5-Challenge狀態(tài)機(jī)必須能區(qū)分“這是同一個(gè)流程里的下一個(gè)請(qǐng)求”還是“新一輪認(rèn)證”。判斷依據(jù)就是EAP包里的Identifier字段。Identifier的規(guī)則很簡(jiǎn)單認(rèn)證者發(fā)出的每個(gè)Request都帶一個(gè)Identifier客戶(hù)端返回的Response必須完整復(fù)制這個(gè)Identifier否則認(rèn)證服務(wù)器會(huì)把響應(yīng)當(dāng)作亂序包丟棄。很多入門(mén)實(shí)現(xiàn)喜歡自己維護(hù)一個(gè)計(jì)數(shù)器從1開(kāi)始遞增這在認(rèn)證者同時(shí)維護(hù)多個(gè)會(huì)話(huà)時(shí)必然出問(wèn)題。最穩(wěn)妥的做法是直接從接收到的Request里取Identifier填到Response里不要自己造。另一個(gè)高發(fā)問(wèn)題是HELD狀態(tài)。認(rèn)證失敗后客戶(hù)端不能立刻瘋狂重試否則交換機(jī)會(huì)認(rèn)為終端在攻擊端口直接關(guān)閉端口。標(biāo)準(zhǔn)里HELD狀態(tài)有最短保持時(shí)間常見(jiàn)取值為60秒具體時(shí)長(zhǎng)可由上層策略配置。我曾經(jīng)為了測(cè)試方便把它改成3秒結(jié)果接入網(wǎng)管打電話(huà)說(shuō)交換機(jī)把這個(gè)端口關(guān)了就是因?yàn)槲矣|發(fā)了交換機(jī)的防抖動(dòng)策略。誠(chéng)懇建議默認(rèn)值盡量保守不要為了自己調(diào)試方便把懲罰時(shí)間壓得太激進(jìn)。3.3 超時(shí)與重傳策略客戶(hù)端發(fā)出EAPOL-Start或者EAP-Response之后認(rèn)證者可能因?yàn)楦鞣N原因不回包。標(biāo)準(zhǔn)做法是設(shè)置一個(gè)超時(shí)定時(shí)器超時(shí)后重發(fā)超過(guò)最大重試次數(shù)仍未響應(yīng)進(jìn)入FAILURE或回到DISCONNECTED。兩個(gè)參數(shù)必須做成可配置超時(shí)間隔常見(jiàn)1到3秒和最大重試次數(shù)常見(jiàn)3次。重傳時(shí)還有一個(gè)細(xì)節(jié)容易忽略EAP包如果帶Identifier重傳必須用同一個(gè)Identifier不能每次重傳都換一個(gè)。否則對(duì)端可能同時(shí)處理多個(gè)相同請(qǐng)求引發(fā)重復(fù)校驗(yàn)或者會(huì)話(huà)混亂。注意超時(shí)重傳只是“網(wǎng)絡(luò)不通”時(shí)的兜底不要把它當(dāng)成認(rèn)證流程的一部分。真正認(rèn)證出錯(cuò)時(shí)通常很快就能收到EAP-Failure而不是默默超時(shí)。4. EAP方法插件化MD5-Challenge之后的路4.1 方法注冊(cè)表的結(jié)構(gòu)能面向生產(chǎn)的客戶(hù)端源代碼EAP方法不應(yīng)該寫(xiě)死在主流程里。更合理的做法是設(shè)計(jì)一個(gè)方法注冊(cè)表每個(gè)EAP方法注冊(cè)自己的類(lèi)型號(hào)和回調(diào)函數(shù)。以C語(yǔ)言為例可以定義這樣的結(jié)構(gòu)struct eap_method { int eap_type; /* 4MD5, 13TLS, 21TTLS, 25PEAP */ int (*init)(struct eap_sm *sm); int (*process)(struct eap_sm *sm, const u8 *req, size_t len, u8 **resp, size_t *resp_len); void (*deinit)(struct eap_sm *sm); };主狀態(tài)機(jī)根本不關(guān)心具體方法是什么只管轉(zhuǎn)發(fā)。收到EAP-Request后根據(jù)Type字段找到方法模塊交給對(duì)應(yīng)方法處理方法構(gòu)造出Response后由主框架統(tǒng)一封裝成EAPOL幀發(fā)出。這樣設(shè)計(jì)的好處是新增一個(gè)自定義EAP方法時(shí)主流程一行代碼都不用動(dòng)。4.2 EAP-MD5的實(shí)現(xiàn)常見(jiàn)問(wèn)題EAP-MD5Type4流程里認(rèn)證服務(wù)器下發(fā)的Challenge不是單純一個(gè)隨機(jī)數(shù)而是一段結(jié)構(gòu)Value-Size字段 隨機(jī)數(shù) 可選的Name字段。響應(yīng)值的計(jì)算涉及Identifier、密碼、Challenge等字段的拼接任何一個(gè)環(huán)節(jié)的順序錯(cuò)了認(rèn)證服務(wù)器就會(huì)直接回Failure。我調(diào)試時(shí)最常用的手段是在RADIUS服務(wù)器側(cè)打開(kāi)debug日志比對(duì)兩邊計(jì)算的MD5結(jié)果一旦不匹配基本就是拼接順序或長(zhǎng)度字段出了問(wèn)題。另外請(qǐng)務(wù)必處理密碼為空和密碼包含不可見(jiàn)字符的情況。有些內(nèi)部系統(tǒng)使用特殊字符密碼構(gòu)造響應(yīng)時(shí)不能想當(dāng)然地對(duì)密碼做UTF-8歸一化要按認(rèn)證服務(wù)器約定好的編碼方式逐字節(jié)透?jìng)?。這個(gè)坑在對(duì)接老式AD域環(huán)境時(shí)經(jīng)常遇到。4.3 EAP-TLS與PEAP的通病EAP-MD5只是入門(mén)企業(yè)網(wǎng)里真正常用的是EAP-PEAP、EAP-TLS這類(lèi)基于TLS的方法。它們共同的痛點(diǎn)是EAP報(bào)文承載不了大塊數(shù)據(jù)一個(gè)TLS握手可能超過(guò)1500字節(jié)必須拆成多個(gè)EAP幀分段發(fā)送。標(biāo)準(zhǔn)里叫EAP分片和重組客戶(hù)端源碼必須實(shí)現(xiàn)“發(fā)送方分包 接收方緩存重組”。這里最典型的互操作問(wèn)題是客戶(hù)端把EAP分片發(fā)給AP后AP不支持分片重組就直接丟棄。比較穩(wěn)妥的做法是在TLS握手初期通過(guò)max_fragment_length擴(kuò)展協(xié)商限制TLS記錄大小或者客戶(hù)端主動(dòng)做小分片。wpa_supplicant的eap_tls.c里就有相關(guān)邏輯值得反復(fù)讀幾遍。另外在隧道類(lèi)方法PEAP/TTLS里內(nèi)部認(rèn)證方法還有自己的EAP Header必須區(qū)分“外層EAP長(zhǎng)度”和“內(nèi)層EAP長(zhǎng)度”寫(xiě)錯(cuò)一字節(jié)內(nèi)部認(rèn)證必然失敗。5. 從開(kāi)源實(shí)現(xiàn)里借輪子wpa_supplicant源碼的閱讀路線(xiàn)如果你打算基于現(xiàn)成代碼而不是從零實(shí)現(xiàn)wpa_supplicant仍然是繞不開(kāi)的參考實(shí)現(xiàn)。但它的源碼結(jié)構(gòu)宏大直接從頭讀到尾非常勸退。我建議按這個(gè)順序讀src/eapol_supp/eapol_supp_sm.c客戶(hù)端狀態(tài)機(jī)主循環(huán)先看懂認(rèn)證流程的骨架src/eap_peer/eap.cEAP方法分發(fā)邏輯看主框架如何調(diào)用具體方法src/eap_peer/eap_md5.c、eap_tls.c挑一兩個(gè)方法對(duì)照理解src/l2_packet/l2_packet_linux.c看底層收發(fā)包是怎么用AF_PACKET實(shí)現(xiàn)的。閱讀時(shí)不要一開(kāi)始就盯細(xì)節(jié)先把“哪個(gè)函數(shù)發(fā)起幀發(fā)送、哪個(gè)函數(shù)處理接收幀、狀態(tài)機(jī)當(dāng)前狀態(tài)存在哪個(gè)變量里”找出來(lái)整個(gè)脈絡(luò)就清楚了。這三條線(xiàn)連起來(lái)以后再往里面填充細(xì)節(jié)就很快。值得借鑒的設(shè)計(jì)點(diǎn)包括方法注冊(cè)時(shí)的優(yōu)先級(jí)管理、多EAP方法共存的配置清單、以及它對(duì)EAPOL-Start策略的處理。但也別全盤(pán)照抄wpa_supplicant同時(shí)要兼容幾千種網(wǎng)卡驅(qū)動(dòng)和上層NetworkManager代碼里有大量與“核心認(rèn)證功能”無(wú)關(guān)的分支自研項(xiàng)目按需裁剪就好。另一個(gè)常見(jiàn)誤區(qū)是編譯開(kāi)源庫(kù)確實(shí)方便但如果你想嵌入到自己的產(chǎn)品里不是把整個(gè)庫(kù)拉進(jìn)來(lái)就完事更建議只提取eap_peer和l2_packet兩個(gè)子模塊把OS相關(guān)抽象層mock掉。6. 本地聯(lián)調(diào)自己寫(xiě)客戶(hù)端怎么驗(yàn)證“真的能認(rèn)證”6.1 搭一個(gè)最簡(jiǎn)認(rèn)證服務(wù)器很多開(kāi)發(fā)者的測(cè)試環(huán)境里沒(méi)有真實(shí)的802.1X交換機(jī)這會(huì)讓聯(lián)調(diào)變得很困難??梢韵仍诒镜赜肍reeRADIUS模擬認(rèn)證服務(wù)器配合一個(gè)支持端口認(rèn)證的開(kāi)源交換機(jī)實(shí)例來(lái)驗(yàn)證純協(xié)議邏輯。更輕量的做法是寫(xiě)一個(gè)極簡(jiǎn)的Python RADIUS服務(wù)器腳本只處理EAP-MD5把你期望的報(bào)文序列全部打印出來(lái)客戶(hù)端每發(fā)一幀你都能看到非常適合狀態(tài)機(jī)調(diào)試。用這個(gè)方案時(shí)客戶(hù)端側(cè)抓到的是EAPOL幀RADIUS側(cè)打印的是EAP屬性?xún)?nèi)容。兩端能對(duì)上說(shuō)明客戶(hù)端源碼的組幀和狀態(tài)機(jī)基本是通的。6.2 抓包驗(yàn)證的驗(yàn)收清單用Wireshark抓包重點(diǎn)關(guān)注這幾個(gè)時(shí)間點(diǎn)客戶(hù)端是否按配置發(fā)送了EAPOL-Start收到EAP-Request/Identity后是否在預(yù)期時(shí)間內(nèi)返回EAP-Response/IdentityEAP-MD5的Challenge響應(yīng)是否在重傳前送達(dá)EAP-Success到達(dá)后狀態(tài)機(jī)是否真的切到OPEN網(wǎng)線(xiàn)拔掉再插上是否自動(dòng)重新走一遍完整認(rèn)證。任何一個(gè)點(diǎn)卡住先看幀格式和Identifier再查狀態(tài)機(jī)路徑。以我個(gè)人的經(jīng)驗(yàn)成功率最高的驗(yàn)收順序反而是“先測(cè)錯(cuò)誤密碼”錯(cuò)誤密碼能收到Failure說(shuō)明鏈路和狀態(tài)機(jī)完全走通剩下的只是密碼策略或證書(shū)問(wèn)題。6.3 異常場(chǎng)景不能只靠“手工點(diǎn)一點(diǎn)”我建議至少把下面幾種異常做自動(dòng)化測(cè)試認(rèn)證服務(wù)器無(wú)響應(yīng)客戶(hù)端應(yīng)重傳并最終放棄而不是卡死認(rèn)證過(guò)程中斷網(wǎng)狀態(tài)機(jī)應(yīng)回到DISCONNECTED并釋放資源收到非預(yù)期EAP類(lèi)型應(yīng)有明確的NAK響應(yīng)而不是靜默重認(rèn)證定時(shí)器到期能主動(dòng)發(fā)起重認(rèn)證而不是等到網(wǎng)絡(luò)斷了才知道。最后一點(diǎn)多說(shuō)一句。很多客戶(hù)端只做好了“第一次認(rèn)證”但企業(yè)網(wǎng)絡(luò)里一般都會(huì)啟用周期性的重認(rèn)證。如果客戶(hù)端源碼沒(méi)有實(shí)現(xiàn)重認(rèn)證處理即使第一次認(rèn)證成功過(guò)一段時(shí)間也會(huì)被交換機(jī)踢下線(xiàn)。這往往是“看起來(lái)能用一上生產(chǎn)就掉線(xiàn)”的最常見(jiàn)原因。踩過(guò)這一圈坑之后我自己對(duì)802.1x客戶(hù)端源碼最大的體會(huì)是真正決定一個(gè)客戶(hù)端能不能落地的不是某幀怎么寫(xiě)而是狀態(tài)機(jī)對(duì)異常情況的處理夠不夠穩(wěn)。只要把協(xié)議邊界、狀態(tài)轉(zhuǎn)換、超時(shí)重傳這三樣抓牢剩下的EAP方法擴(kuò)展和上層UI接入都只是按部就班的體力活。最后再分享一個(gè)小經(jīng)驗(yàn)每次改動(dòng)狀態(tài)機(jī)后先跑一輪“拔網(wǎng)線(xiàn)-插網(wǎng)線(xiàn)-換密碼-等超時(shí)”四連測(cè)試再去做真實(shí)設(shè)備聯(lián)調(diào)能幫你省掉大量現(xiàn)場(chǎng)排查的時(shí)間。本文還有配套的精品資源點(diǎn)擊獲取