:從FCS原理到CRC-32故障排查實(shí)戰(zhàn))
做網(wǎng)絡(luò)這一行“Ethernet幀完整性校驗(yàn)”這串詞平時(shí)未必天天掛在嘴邊但真遇到網(wǎng)絡(luò)上莫名其妙的丟包、誤碼、延遲抖動(dòng)繞來繞去最后總會(huì)撞到它身上。我第一次對(duì)它產(chǎn)生強(qiáng)烈印象是在一次聯(lián)調(diào)現(xiàn)場(chǎng)客戶反映兩套系統(tǒng)之間偶發(fā)通訊卡頓接口統(tǒng)計(jì)面板里某個(gè)計(jì)數(shù)在緩慢上漲業(yè)務(wù)卻感知不到太多異常。查了好幾天換線、換模塊、重啟設(shè)備都試過最后才有人點(diǎn)了一句“你怎么不去看看FCS錯(cuò)誤率”。也正是從那一次開始我把幀完整性校驗(yàn)的原理、計(jì)算方式、排查套路徹底過了一遍后面再遇到類似問題基本都能在半小時(shí)內(nèi)找到方向。這篇文章里的內(nèi)容我會(huì)從原理講到實(shí)操包括CRC-32到底怎么算、為什么抓包工具看不見幀尾的FCS、FCS異常增長(zhǎng)時(shí)該怎么由表及里定位故障最后再總結(jié)一些我用過、踩過、后來養(yǎng)成的排查習(xí)慣。適合剛接觸網(wǎng)絡(luò)協(xié)議分析的開發(fā)者也適合在網(wǎng)絡(luò)一線做過一段時(shí)間、想系統(tǒng)梳理一下這類知識(shí)的朋友。1. 一次聯(lián)調(diào)里的意外發(fā)現(xiàn)抓包看不見幀尾FCS1.1 現(xiàn)場(chǎng)現(xiàn)象與第一反應(yīng)那次聯(lián)調(diào)的環(huán)境并不復(fù)雜兩臺(tái)服務(wù)器通過交換機(jī)互通中間走了一段雙絞線再看一眼光模塊轉(zhuǎn)接。業(yè)務(wù)流量本身不大但每天總有那么幾個(gè)時(shí)段出現(xiàn)重傳。起初我沒太在意因?yàn)閼?yīng)用層有重試機(jī)制偶發(fā)重傳也正常。真正引起警覺的是某臺(tái)交換機(jī)端口統(tǒng)計(jì)里兩個(gè)字段在緩慢增長(zhǎng)一個(gè)和CRC錯(cuò)誤相關(guān)另一個(gè)和入方向丟棄相關(guān)。雖然增長(zhǎng)幅度不大對(duì)業(yè)務(wù)暫時(shí)沒有明顯影響但這類計(jì)數(shù)一旦出現(xiàn)說明鏈路里一定在做“無用功”。我第一反應(yīng)是懷疑線纜和光模塊。按照常規(guī)做法先換了一根跳線又把兩端的收發(fā)模塊重新插拔看是否能匹配成功。結(jié)果并不理想CRC錯(cuò)誤計(jì)數(shù)并沒有停。后來又試著把兩條鏈路的速率協(xié)商狀態(tài)改為固定模式同樣沒有明顯改善。這時(shí)候我才意識(shí)到一個(gè)問題我一直在“猜”是物理鏈路的問題但我根本沒有真正搞清楚這個(gè)錯(cuò)誤計(jì)數(shù)到底是在什么時(shí)候產(chǎn)生的。換句話說我該先弄懂“FCS”是什么它校驗(yàn)的是什么才能知道該懷疑誰(shuí)。1.2 以太網(wǎng)幀結(jié)構(gòu)里FCS到底在哪里IEEE 802.3標(biāo)準(zhǔn)定義的基礎(chǔ)以太網(wǎng)幀結(jié)構(gòu)從發(fā)送端的角度來看大致是這樣一個(gè)順序前導(dǎo)碼7字節(jié) 幀起始定界符SFD1字節(jié) 目的MAC地址6字節(jié) 源MAC地址6字節(jié) 長(zhǎng)度/類型字段2字節(jié) 載荷46到1500字節(jié) 幀校驗(yàn)序列FCS4字節(jié)前導(dǎo)碼和SFD是物理層同步用的接收端把它們識(shí)別出來之后就知道“幀數(shù)據(jù)從這兒開始了”。目的MAC、源MAC、長(zhǎng)度/類型字段和載荷是真正被協(xié)議棧解析的內(nèi)容。而FCS這4個(gè)字節(jié)就是我今天要重點(diǎn)聊的“Ethernet幀完整性校驗(yàn)”機(jī)制的核心。FCS的校驗(yàn)范圍是從目的MAC地址開始一直覆蓋到載荷結(jié)束不包含前導(dǎo)碼、SFD也不包含F(xiàn)CS本身。發(fā)送端對(duì)這一段數(shù)據(jù)算出一個(gè)32位的結(jié)果并按固定字節(jié)序填到幀尾。接收端收到整個(gè)幀之后用同樣的算法對(duì)同樣的范圍重新計(jì)算一次再把計(jì)算結(jié)果和收到的FCS做比較。如果一致就認(rèn)為這一跳傳輸過程中數(shù)據(jù)沒有發(fā)生比特級(jí)的變化如果不一致接收端會(huì)把這個(gè)幀直接丟棄。有一點(diǎn)值得注意如果幀里帶了VLAN標(biāo)簽802.1QFCS的校驗(yàn)范圍會(huì)從目的MAC一直算到VLAN Tag和載荷結(jié)束這部分額外字段同樣受保護(hù)。無論是私有VLAN還是其他擴(kuò)展標(biāo)簽只要在幀頭里作為數(shù)據(jù)結(jié)構(gòu)存在、接著載荷發(fā)送它們都會(huì)被納入CRC計(jì)算范圍。實(shí)踐中我見過不少人在用抓包工具查看帶VLAN幀時(shí)下意識(shí)只盯著源目MAC和IP完全沒意識(shí)到FCS覆蓋范圍已經(jīng)發(fā)生了變化。1.3 為什么抓包工具“看不見”FCS很多第一次接觸抓包的朋友都會(huì)問同一個(gè)問題我抓出來的幀里明明能看到MAC頭能看到IP頭為什么找不到文檔里說的那4字節(jié)FCS答案和抓包工具的采集位置有關(guān)。抓包工具讀取的是主機(jī)協(xié)議棧里某個(gè)“鉤子”位置這個(gè)位置通常位于網(wǎng)卡驅(qū)動(dòng)把報(bào)文上拋給系統(tǒng)網(wǎng)絡(luò)棧之后。而絕大多數(shù)網(wǎng)卡在硬件收幀時(shí)會(huì)先做一遍CRC校驗(yàn)校驗(yàn)不過的幀直接丟棄不往上拋校驗(yàn)通過的幀再把尾部的4字節(jié)FCS剝離掉只把有效載荷部分交給驅(qū)動(dòng)。所以應(yīng)用層抓包工具拿到的幀天然就少了尾部這4個(gè)字節(jié)。這就帶來一個(gè)很重要的實(shí)操認(rèn)知用抓包工具抓到的幀“看起來都是好的”并不能證明鏈路就是干凈的。因?yàn)檎嬲龓CS錯(cuò)誤的幀在網(wǎng)卡硬件這一層就已經(jīng)被處理掉了。你如果想驗(yàn)證“這一跳有沒有出錯(cuò)”不能只看抓包文件里的幀內(nèi)容而是要看網(wǎng)卡驅(qū)動(dòng)或者對(duì)端交換機(jī)的底層計(jì)數(shù)統(tǒng)計(jì)比如常見的rx_crc_errors、rx_fcs_errors這類字段。在不少網(wǎng)卡驅(qū)動(dòng)里FCS錯(cuò)誤是單獨(dú)計(jì)數(shù)的也有驅(qū)動(dòng)把它并入CRC錯(cuò)誤一類這取決于具體實(shí)現(xiàn)但整體思路一致它是物理層和MAC層之間的事件普通抓包工具默認(rèn)是看不見的。當(dāng)然也有例外。部分網(wǎng)卡驅(qū)動(dòng)支持關(guān)閉CRC剝離功能或者抓包工具配置為“保留壞幀”模式這時(shí)你可能會(huì)在抓包里看到尾部帶FCS的完整幀甚至看到CRC校驗(yàn)失敗的幀。但這種模式不是默認(rèn)行為而且開啟之后抓包工具本身也容易把壞幀當(dāng)作正常幀顯示反而誤導(dǎo)判斷。所以我個(gè)人并不建議在日常分析中開這種模式除非你明確知道自己想要抓壞幀做實(shí)驗(yàn)。2. CRC-32幀完整性校驗(yàn)的數(shù)學(xué)底子2.1 為什么以太網(wǎng)選擇CRC而不是簡(jiǎn)單校驗(yàn)和如果只是想讓接收端確認(rèn)數(shù)據(jù)沒被改過最直覺的做法是“把所有字節(jié)加起來存一個(gè)和”。IP協(xié)議頭部的校驗(yàn)和就是這么干的。但以太網(wǎng)這種鏈路層技術(shù)面對(duì)的是實(shí)實(shí)在在的物理信道電磁干擾、線纜衰減、連接器氧化、信號(hào)反射都會(huì)造成比特錯(cuò)誤而且這些錯(cuò)誤往往不是孤立的單個(gè)比特翻轉(zhuǎn)而是連續(xù)的突發(fā)錯(cuò)誤。簡(jiǎn)單求和這種方式對(duì)突發(fā)錯(cuò)誤的識(shí)別能力很差。CRC循環(huán)冗余校驗(yàn)的核心思想是把數(shù)據(jù)看成一個(gè)龐大的二進(jìn)制數(shù)用這個(gè)數(shù)去除以一個(gè)固定的“生成多項(xiàng)式”得到的余數(shù)就是校驗(yàn)值。接收端拿到數(shù)據(jù)和校驗(yàn)值之后再做一次同樣的除法如果余數(shù)不為零就說明數(shù)據(jù)在這條鏈路上發(fā)生了改動(dòng)。這個(gè)“除法求余”的過程聽起來復(fù)雜但在硬件上用移位寄存器實(shí)現(xiàn)非常順而且可以通過查表法提升速度這就是它在通信領(lǐng)域被廣泛采用的原因。IEEE 802.3選擇的CRC-32并不是隨便挑了個(gè)多項(xiàng)式。它背后對(duì)應(yīng)了突發(fā)錯(cuò)誤檢測(cè)能力的數(shù)學(xué)保證可以檢測(cè)出所有長(zhǎng)度不超過32比特的突發(fā)錯(cuò)誤可以檢測(cè)所有奇數(shù)個(gè)比特錯(cuò)誤并且在數(shù)據(jù)長(zhǎng)度足夠短的情況下雙比特錯(cuò)誤的漏檢率也極低。對(duì)于一條物理鏈路上常見的噪聲擾動(dòng)來說這套保證已經(jīng)非常夠用。所以別看FCS只有4字節(jié)它在鏈路層協(xié)議里承擔(dān)的角色比很多剛?cè)腴T的人想象中重要得多。2.2 IEEE 802.3 CRC-32的參數(shù)細(xì)節(jié)很多朋友試過自己實(shí)現(xiàn)CRC-32卻發(fā)現(xiàn)自己算出來的值和抓包工具或設(shè)備上的FCS對(duì)不上。真不是你代碼寫錯(cuò)了而是CRC存在多種“變體”IEEE 802.3使用的CRC-32有自己特定的參數(shù)組合。我整理成一張速查表參數(shù)項(xiàng)IEEE 802.3 CRC-32取值生成多項(xiàng)式普通寫法0x04C11DB7生成多項(xiàng)式反射寫法0xEDB88320初始值0xFFFFFFFF輸入數(shù)據(jù)是否反射處理是輸出結(jié)果是否反射處理是最終異或值0xFFFFFFFF這里面的“反射”指的是對(duì)參與計(jì)算的每個(gè)字節(jié)先做位序反轉(zhuǎn)或者對(duì)整段數(shù)據(jù)處理時(shí)按位反序操作。用業(yè)界常見的話說802.3的CRC-32是“反射輸入、反射輸出、初值全1、結(jié)果異或全1”。如果你的CRC計(jì)算工具或者算法實(shí)現(xiàn)沒有配齊這套參數(shù)算出來的結(jié)果自然和真實(shí)FCS對(duì)不上。新手最容易栽的坑有三個(gè)一是多項(xiàng)式寫法搞混普通寫法和反射寫法之間的二進(jìn)制位序正好相反二是忘記處理初始值不置為全1的話算出的結(jié)果只在空數(shù)據(jù)時(shí)恰好一致一旦有實(shí)際數(shù)據(jù)就會(huì)出現(xiàn)偏差三是漏掉最終異或?qū)е虏钜粋€(gè)固定掩碼。還有一點(diǎn)FCS在網(wǎng)絡(luò)上的字節(jié)序也要注意常見網(wǎng)卡把CRC結(jié)果按小端序填到幀尾如果你按大端讀出來再比較一樣會(huì)不匹配。2.3 用生活化的例子理解“多項(xiàng)式除法”我知道“除以多項(xiàng)式”這個(gè)概念對(duì)非數(shù)學(xué)背景的讀者容易勸退。換個(gè)說法可以理解為你有一串特別長(zhǎng)的數(shù)字然后有一個(gè)固定的“模子”發(fā)送端拿著這串?dāng)?shù)字去除以模子得到的余數(shù)就是蓋章。接收端收到數(shù)據(jù)之后也拿著同一個(gè)模子去除如果余數(shù)和發(fā)過來的章對(duì)不上說明數(shù)字在運(yùn)輸途中被改過。為什么它能檢測(cè)出“改了哪個(gè)位”因?yàn)槟W颖旧碛芯脑O(shè)計(jì)的數(shù)學(xué)性質(zhì)。任意一個(gè)比特錯(cuò)誤等價(jià)于原數(shù)據(jù)加上了某個(gè)“誤差向量”。如果這個(gè)誤差向量恰好不是模子的倍數(shù)除法余數(shù)就會(huì)改變就會(huì)被識(shí)別出來。CRC能保證一定長(zhǎng)度范圍內(nèi)的所有突發(fā)錯(cuò)誤都會(huì)被識(shí)別正是因?yàn)檫@個(gè)模子的數(shù)學(xué)設(shè)計(jì)讓這些誤差向量都不會(huì)“正好整除”。實(shí)際做鏈路分析時(shí)我不需要去手推多項(xiàng)式除法但理解這一層之后你會(huì)明白一個(gè)關(guān)鍵道理CRC不是加密不是哈希它不承諾防偽只承諾“傳輸過程有沒有擾動(dòng)”。它也不會(huì)告訴你錯(cuò)誤發(fā)生在哪個(gè)比特只會(huì)告訴你“這一幀壞掉了”。這一點(diǎn)在后面的故障排查里非常重要——FCS異常說明問題出在這條鏈路的某個(gè)環(huán)節(jié)但具體是哪一段還需要進(jìn)一步定位。3. 手寫一個(gè)幀完整性校驗(yàn)工具3.1 構(gòu)造最小以太網(wǎng)幀并計(jì)算FCS要說對(duì)Ethernet幀完整性校驗(yàn)的理解光看書肯定不夠。我當(dāng)時(shí)為了讓自己徹底搞明白FCS的計(jì)算過程寫了一個(gè)很小的腳本去“造幀”自己拼一個(gè)最小以太網(wǎng)幀算出CRC-32再模擬接收方校驗(yàn)一遍。整個(gè)過程不到幾十行代碼但對(duì)理解校驗(yàn)邏輯非常有幫助。思路很簡(jiǎn)單拼一個(gè)目的MAC是全FF廣播地址、源MAC是某個(gè)本地地址的幀EtherType字段填入0x0800載荷就放46個(gè)字節(jié)的測(cè)試數(shù)據(jù)。算一下FCSimport zlib import struct def build_frame(dst_mac, src_mac, ethertype, payload): frame dst_mac src_mac ethertype payload crc_val zlib.crc32(frame) 0xffffffff fcs struct.pack(I, crc_val) return frame fcs def verify_frame(frame): fcs frame[-4:] body frame[:-4] expected struct.pack(I, zlib.crc32(body) 0xffffffff) return fcs expected dst bytes.fromhex(ffffffffffff) src bytes.fromhex(001122334455) etype b\x08\x00 payload bytes(range(46)) full_frame build_frame(dst, src, etype, payload) print(frame length:, len(full_frame)) print(fcs bytes:, full_frame[-4:].hex()) print(verify result:, verify_frame(full_frame)) # 模擬傳輸過程中一個(gè)比特被翻轉(zhuǎn) bad_frame bytearray(full_frame) bad_frame[20] ^ 0x01 print(verify result with flip:, verify_frame(bytes(bad_frame)))這里用到了Python內(nèi)置的zlib.crc32它的底層實(shí)現(xiàn)恰好就是IEEE 802.3這套CRC-32參數(shù)多項(xiàng)式反射寫法是0xEDB88320初始值是全1輸入輸出都帶反射最后再做異或。所以不用自己寫查表邏輯。代碼里定義了兩個(gè)函數(shù)build_frame負(fù)責(zé)構(gòu)造完整幀verify_frame負(fù)責(zé)把幀尾4字節(jié)當(dāng)成FCS重新計(jì)算并比較。運(yùn)行之后你會(huì)看到正常幀校驗(yàn)返回True而把第20個(gè)字節(jié)翻轉(zhuǎn)一位后校驗(yàn)立刻返回False。這個(gè)“翻轉(zhuǎn)一位就翻車”的效果正是CRC-32在鏈路層的務(wù)實(shí)價(jià)值它不保證你不被攻擊但能保證你不會(huì)把壞數(shù)據(jù)當(dāng)成好數(shù)據(jù)繼續(xù)往上拋。3.2 離線驗(yàn)證真實(shí)抓包數(shù)據(jù)自己造完幀之后我接下來做的一件事是拿一批真實(shí)的抓包文件做離線校驗(yàn)。想直接從抓包文件里驗(yàn)證FCS首先要面臨一個(gè)現(xiàn)實(shí)問題多數(shù)抓包文件保存的幀里根本沒有FCS字節(jié)。如果抓包時(shí)啟用了“完整幀保存”或者某些設(shè)備明確保留了幀尾FCS那么離線文件里的每一幀會(huì)包含完整4字節(jié)。需要逐幀解析pcap記錄pcap全局頭通常是24字節(jié)之后每一條記錄頭16字節(jié)記錄頭里的incl_len告訴你這條記錄實(shí)際捕獲了多少字節(jié)orig_len則告訴你網(wǎng)絡(luò)上的原始長(zhǎng)度。如果incl_len比正常最小幀頭加最小載荷還要多出4字節(jié)或者和你已知的載荷長(zhǎng)度對(duì)不上就值得去讀取最后4字節(jié)做校驗(yàn)。import struct import zlib def parse_and_verify_pcap(file_path): with open(file_path, rb) as f: global_header f.read(24) while True: rec_header f.read(16) if len(rec_header) 16: break ts_sec, ts_usec, incl_len, orig_len struct.unpack(IIII, rec_header) packet_data f.read(incl_len) if incl_len 64 and orig_len 64: fcs packet_data[-4:] body packet_data[:-4] expected struct.pack(I, zlib.crc32(body) 0xffffffff) if fcs ! expected: print(ftimestamp {ts_sec}.{ts_usec:06d}: FCS mismatch)這段代碼只是示意不同鏈路類型比如帶VLAN Tag、帶元數(shù)據(jù)封裝會(huì)有差異但整體結(jié)構(gòu)差不多。我實(shí)際跑批處理時(shí)發(fā)現(xiàn)一部分抓包確實(shí)能驗(yàn)證通過另一部分文件里的幀尾4字節(jié)并不是有效FCS而是某些網(wǎng)卡驅(qū)動(dòng)填充的占位數(shù)據(jù)這時(shí)候直接按FCS校驗(yàn)就會(huì)出現(xiàn)大量“誤報(bào)”。所以離線校驗(yàn)前最好先確認(rèn)抓包工具到底有沒有存儲(chǔ)FCS別對(duì)著一堆占位數(shù)據(jù)空跑。3.3 單跳校驗(yàn)通過之后的事從FCS角度看每經(jīng)過一條鏈路、一臺(tái)交換機(jī)幀都會(huì)被重新計(jì)算一次FCS。也就是說端到端抓包時(shí)你看到的FCS已經(jīng)是被中間設(shè)備“重新蓋過章”的版本而不是最初發(fā)送端蓋的章。這是理解幀校驗(yàn)和上層校驗(yàn)關(guān)系的關(guān)鍵。交換機(jī)從入端口收到一幀先校驗(yàn)FCS通過之后再查轉(zhuǎn)發(fā)表修改某些字段比如TTL或MAC等涉及三層轉(zhuǎn)發(fā)的場(chǎng)景再重新計(jì)算一個(gè)新的FCS發(fā)出去。這個(gè)過程保證了“每一跳”的可靠性但每一跳都獨(dú)立完成上下游之間沒有狀態(tài)關(guān)聯(lián)。所以只靠鏈路層FCS沒辦法保證端到端的數(shù)據(jù)完整性應(yīng)用層數(shù)據(jù)跨越多個(gè)網(wǎng)絡(luò)節(jié)點(diǎn)中間只要有一個(gè)節(jié)點(diǎn)軟件內(nèi)部出問題就可能改掉數(shù)據(jù)后重新計(jì)算一個(gè)合法的FCS。這也是為什么TCP層還要有自己的校驗(yàn)和到了更高層有些協(xié)議還會(huì)再做一次完整性摘要。鏈路層FCS是一道重要的防線但它只是縱深防御體系里的一道而不是唯一一道。實(shí)際排查問題的時(shí)候不要把“FCS校驗(yàn)過了”當(dāng)作“整條鏈路沒問題”的結(jié)論要理解每一層校驗(yàn)各自保護(hù)的范圍。4. 從FCS異常反推鏈路故障4.1 CRC錯(cuò)誤到底是在哪個(gè)環(huán)節(jié)產(chǎn)生的FCS異常的本質(zhì)是“接收端算出來的校驗(yàn)值和發(fā)送端寫的不一致”。那這個(gè)不一致通常在哪里被引入首當(dāng)其沖是物理層雙絞線過長(zhǎng)導(dǎo)致信號(hào)衰減、連接器氧化造成接觸電阻增大、網(wǎng)線內(nèi)部線對(duì)絞距被破壞導(dǎo)致的串?dāng)_、電磁干擾源靠近線纜、光模塊老化導(dǎo)致光功率下降、光纖彎曲過大導(dǎo)致反射這些都會(huì)讓比特位在傳輸過程中“翻轉(zhuǎn)”。另一個(gè)常見來源是鏈路雙方的工作狀態(tài)不匹配。比如一端協(xié)商到了全雙工另一端卡在半雙工這種狀態(tài)在低速以太網(wǎng)上會(huì)出現(xiàn)大量沖突嚴(yán)重時(shí)幀會(huì)被截?cái)?、撕裂接收端的FCS校驗(yàn)自然過不了。尤其是強(qiáng)制雙工模式的時(shí)候風(fēng)險(xiǎn)更大因?yàn)殡p方完全放棄了自動(dòng)協(xié)商的握手確認(rèn)過程。還有一個(gè)容易被忽視的環(huán)節(jié)網(wǎng)卡或交換芯片內(nèi)部的處理邏輯。比如芯片收到一個(gè)長(zhǎng)度異常的短幀Runt可能還沒來得及完整接收就已經(jīng)把不完整的數(shù)據(jù)提交給上層模塊又比如交換機(jī)緩存不足時(shí)某個(gè)隊(duì)列被丟棄或部分丟棄也會(huì)表現(xiàn)為CRC錯(cuò)誤或?qū)R錯(cuò)誤。這類問題的物理鏈路本身可能是健康的。我自己的經(jīng)驗(yàn)是遇到FCS異常先不要急著懷疑線纜質(zhì)量而是先看錯(cuò)誤計(jì)數(shù)的分布特征。如果錯(cuò)誤零散分布在多個(gè)端口那更像環(huán)境干擾或設(shè)備共有問題如果錯(cuò)誤集中在一條鏈路的兩端那重點(diǎn)懷疑這條物理路徑如果只有一端報(bào)錯(cuò)另一端完全干凈那可能是接收端硬件或驅(qū)動(dòng)的問題。4.2 一套可以照抄的排查順序下面這套順序是我后來固定下來的排查流程不算特別復(fù)雜但每一步都有明確目的先收集兩側(cè)端口統(tǒng)計(jì)把CRC錯(cuò)誤、FCS錯(cuò)誤、對(duì)齊錯(cuò)誤、短幀錯(cuò)誤、丟包計(jì)數(shù)全部記下來。不要只看其中一個(gè)字段它們組合起來會(huì)給出更多線索。觀察錯(cuò)誤增長(zhǎng)速率和負(fù)載的關(guān)系。如果只在業(yè)務(wù)高峰時(shí)快速增長(zhǎng)說明大概率是信號(hào)質(zhì)量問題在高負(fù)載下被放大如果空載也在漲那問題可能很穩(wěn)定比如硬件損壞或接觸不良。做一次“物理層替換法”換線、換模塊、換端口。每換一個(gè)環(huán)節(jié)觀察一段時(shí)間計(jì)數(shù)。替換法最忌諱一次換好幾個(gè)東西那你就永遠(yuǎn)不知道是哪個(gè)環(huán)節(jié)治好了問題。檢查協(xié)商狀態(tài)。確認(rèn)兩端速率、雙工模式一致關(guān)閉強(qiáng)制的“非標(biāo)準(zhǔn)”配置再做對(duì)比。如果是在僅跑少量流量的測(cè)試環(huán)境可以嘗試顯式固定速率看錯(cuò)誤是否消失。在鏈路兩端同時(shí)抓包并打時(shí)間戳比對(duì)兩端收到的幀數(shù)量和數(shù)據(jù)內(nèi)容。如果入口流量正常、出口出現(xiàn)突發(fā)錯(cuò)誤問題很可能在中間設(shè)備如果兩端都干凈但交換機(jī)計(jì)數(shù)在漲也可能是交換機(jī)內(nèi)部統(tǒng)計(jì)口徑問題。如果錯(cuò)誤只在某個(gè)特定網(wǎng)卡上出現(xiàn)更新驅(qū)動(dòng)、固件并檢查網(wǎng)卡是否啟用了某些卸載特性比如接收側(cè)合并、Large Receive Offload這些特性在驅(qū)動(dòng)實(shí)現(xiàn)有bug時(shí)會(huì)引發(fā)奇怪的數(shù)據(jù)異常。如果以上都做完了還沒找到原因再考慮設(shè)備硬件故障比如交換芯片的背板問題。這種情況通常需要換一臺(tái)設(shè)備或換一個(gè)板卡做隔離驗(yàn)證。這套流程看起來基礎(chǔ)但能覆蓋絕大多數(shù)場(chǎng)景。尤其第3步的“每次只換一個(gè)環(huán)節(jié)”聽起來像廢話恰恰是無數(shù)現(xiàn)場(chǎng)工程師最容易因?yàn)榧痹甓傅腻e(cuò)誤。4.3 故障速查表現(xiàn)象、原因與驗(yàn)證手段為了隨時(shí)查閱我整理了一張速查表也算是對(duì)上面排查順序的一個(gè)濃縮現(xiàn)象可能原因先驗(yàn)證什么可能的動(dòng)作單端口CRC計(jì)數(shù)緩慢增長(zhǎng)業(yè)務(wù)無明顯感知線纜老化、連接器接觸電阻、輕微電磁干擾端口協(xié)商狀態(tài)、線纜長(zhǎng)度、兩端功率更換跳線、重新壓接水晶頭高負(fù)載下CRC計(jì)數(shù)快速上升信號(hào)衰減、光模塊劣化、雙工不匹配光功率、錯(cuò)誤增長(zhǎng)速率與負(fù)載關(guān)系更換光模塊、調(diào)整協(xié)商策略CRC錯(cuò)誤伴隨對(duì)齊錯(cuò)誤出現(xiàn)鏈路沖突、物理層幀截?cái)嚯p工狀態(tài)、線對(duì)序強(qiáng)制全雙工一致或更換線纜多端口同時(shí)出現(xiàn)CRC錯(cuò)誤公共環(huán)境干擾、設(shè)備供電異常設(shè)備側(cè)電源、周邊大功率設(shè)備干擾整改布線路徑、調(diào)整電源更換模塊后計(jì)數(shù)短暫清零又復(fù)漲硬件插槽或背板鏈路問題換一個(gè)端口或設(shè)備對(duì)比進(jìn)一步隔離硬件層級(jí)這里有一條通用原則CRC計(jì)數(shù)是一個(gè)“果”不是“因”。你從它著手去查問題沒錯(cuò)但最終修復(fù)的一定是導(dǎo)致它的環(huán)境、物理鏈路、協(xié)商狀態(tài)或者硬件故障。神化統(tǒng)計(jì)字段容易把自己帶偏。5. 常見誤區(qū)和幾個(gè)實(shí)戰(zhàn)心得5.1 誤區(qū)一看到CRC上升就斷定是線纜質(zhì)量差這類判斷太絕對(duì)了。很多情況下CRC錯(cuò)誤來自驅(qū)動(dòng)或設(shè)備內(nèi)部的異常。我遇到過一次很典型的例子兩端口之間CRC計(jì)數(shù)持續(xù)上漲但把線纜、模塊全部換了一遍問題依舊。最后發(fā)現(xiàn)是某塊板卡內(nèi)部背板鏈路的數(shù)據(jù)通路故障換了整塊線卡后才恢復(fù)。如果當(dāng)時(shí)只盯著“換線”這一條思路根本走不到真正的根因。另一個(gè)容易被忽略的是虛擬化環(huán)境。虛擬交換機(jī)和虛擬網(wǎng)卡驅(qū)動(dòng)在統(tǒng)計(jì)CRC時(shí)很多實(shí)現(xiàn)只是把宿主機(jī)物理網(wǎng)卡的統(tǒng)計(jì)原樣暴露出來并不代表虛擬鏈路上真的發(fā)生了比特錯(cuò)誤。如果在虛擬化平臺(tái)上看到CRC上漲第一件事應(yīng)該是確認(rèn)這個(gè)計(jì)數(shù)到底來自物理網(wǎng)卡還是虛擬數(shù)據(jù)通路否則你容易把平臺(tái)自身的問題嫁禍給底層物理網(wǎng)絡(luò)。5.2 誤區(qū)二抓包工具顯示無錯(cuò)鏈路就一定干凈這個(gè)誤區(qū)在上文已經(jīng)提到過但它真的太常見了值得單獨(dú)拿出來再說一次。抓包工具默認(rèn)拿到的幀是網(wǎng)卡剝離FCS之后的版本壞幀在進(jìn)入?yún)f(xié)議棧之前就被丟棄了。所以抓包結(jié)果里每一條記錄都“光鮮亮麗”根本不能說明物理鏈路上沒有噪聲。想看鏈路健康度必須配合網(wǎng)卡底層計(jì)數(shù)否則你會(huì)被自己的抓包文件欺騙。反過來如果抓包工具里真的出現(xiàn)了CRC錯(cuò)誤標(biāo)記某些工具在捕獲引擎保留了壞幀時(shí)會(huì)有提示那說明這已經(jīng)很嚴(yán)重了鏈路質(zhì)量可能差到了一定程度。但普通場(chǎng)景下抓不到CRC錯(cuò)誤反而是常態(tài)不要因?yàn)樽グY(jié)果好看就放松警惕。5.3 把CRC計(jì)數(shù)當(dāng)作長(zhǎng)期健康度指標(biāo)排查完一個(gè)具體問題之后就可以建立一套自己的觀察習(xí)慣。我對(duì)接口統(tǒng)計(jì)的處理方式不是看某一個(gè)時(shí)間點(diǎn)上的絕對(duì)數(shù)值而是看趨勢(shì)。比如某端口的CRC錯(cuò)誤計(jì)數(shù)今天比昨天多了300這個(gè)數(shù)量級(jí)在業(yè)務(wù)負(fù)載變化范圍內(nèi)是否正常相比硬件報(bào)錯(cuò)我更關(guān)心它是不是持續(xù)、單調(diào)地增長(zhǎng)。持續(xù)增長(zhǎng)意味著某個(gè)劣化過程正在發(fā)生可能是光模塊的激光器在緩慢衰老也可能是連接器在逐漸氧化。建議每隔一段時(shí)間把關(guān)鍵端口的統(tǒng)計(jì)拉出來存一份基線等變更發(fā)生后做對(duì)比。這個(gè)辦法成本極低但對(duì)解決問題幫助很大。真到問題爆發(fā)那一刻你能立刻判斷出“這個(gè)錯(cuò)誤是什么時(shí)候開始變多的”“最近改了什么東西”遠(yuǎn)比現(xiàn)場(chǎng)抓瞎要強(qiáng)。對(duì)于光口順帶關(guān)注光模塊的收發(fā)光功率變化趨勢(shì)光功率下降往往比CRC計(jì)數(shù)增長(zhǎng)提前幾天就發(fā)出預(yù)警這算是我自己比較受用的一個(gè)小技巧。還有一個(gè)小習(xí)慣拿到一臺(tái)新設(shè)備或者新網(wǎng)卡先做一次短時(shí)間高負(fù)載收發(fā)測(cè)試把錯(cuò)誤統(tǒng)計(jì)清零再觀察。每個(gè)硬件對(duì)誤碼的容忍度不一樣過早做基線反而容易在基線里埋入大量背景噪聲。等確認(rèn)鏈路在干凈狀態(tài)下跑穩(wěn)了再開始記錄正?;€之后每一次統(tǒng)計(jì)異常都會(huì)很有參考價(jià)值。做鏈路分析做得時(shí)間長(zhǎng)了我對(duì)“Ethernet幀完整性校驗(yàn)”這串詞的感受已經(jīng)從“一個(gè)CRC寄存器”變成了一整套觀察鏈路健康度的視角。自己動(dòng)手寫校驗(yàn)?zāi)_本、跑真實(shí)數(shù)據(jù)、被虛驚嚇過、也被真正的根因折磨過這些過程確實(shí)是把知識(shí)變成能力的最好方式。如果你下次再遇到CRC相關(guān)告警不妨先冷靜下來把統(tǒng)計(jì)字段、抓包位置、物理鏈路狀態(tài)這三樣?xùn)|西擺到桌面上再?zèng)Q定要不要?jiǎng)邮植鹁€纜。我個(gè)人的體會(huì)是絕大多數(shù)FCS異常都能通過一張清晰的排查順序表快速收斂真正難的從來不是修而是判斷該修哪里。