2017年9月18日 星期一

Checksum

核對總值(checksum)是一個冗餘的欄位,網路協定可藉以察覺傳輸錯誤。有些checksum不僅能偵測錯誤,而且還能自動修復特定類型的錯誤

傳輸封包之前,傳送者會計算一個小而長度固定的欄位(checksum),其內包含了資料的某種雜湊值。如果資料的一些位元在傳輸時遭到改變,很有可能損毀之資料會產生不同的checksum。取決你用來產生checksum的函式,checksum的可靠度會有不同的等級
錯誤不僅是在傳輸期間發生,也可能會在層級間移動資料時發生。每個協定都要負責確保其自身傳輸的正確性,而且不能假設其上或其下之層級會把這件任務搞定

IP checksum
在IPv4中,checksum是一個16位元的欄位,其中包括整個IP header(含選項在內),只牽涉到總和以及一的補數,因此過於薄弱而不可靠想做比較可靠的健全度檢查(sanity check),得倚賴L2 CRC或是SSL/IPSec訊息鑑定碼(Message Authentication Codes,簡稱MACs)

checksum會先由封包的來源地被計算出來,然後往其目的地的沿路上,一個轉運點接著一個轉運點地予以更新,以反應出每個router所施加的header。更新checksum之前,每個轉運點得先檢查封包的健全狀態(比較封包裡的checksum本地計算出來的checksum)。如果健全度檢查失敗,封包就會被丟棄,但是不會產生ICMP: L4協定會搞定此事(例如,利用計時器,如果特定時間內沒接收到確認通知訊息,就會強迫重新傳輸)
觸發checksum更新需求的情況
情況1: 遞減TTL
router轉送封包之前,必須遞減其IP header中的TTL欄位
net/ipv4/ip_forward.c
int ip_forward(struct sk_buff *skb)
{
...
    /* Decrease ttl after skb cow done */
    ip_decrease_ttl(iph);
...
}

include/net/ip.h
變更IP header的一個欄位時,替IP checksum做增值式更新(incremental update)比從頭計算要快
static inline
int ip_decrease_ttl(struct iphdr *iph)
{
    u32 check = (__force u32)iph->check;
    check += (__force u32)htons(0x0100);
    iph->check = (__force __sum16)(check + (check>=0xFFFF));
    return --iph->ttl;
}

情況2: 封包的調整(包括NAT)
牽涉到改變一個或數個IP header欄位的所有功能,都會迫時checksum重新計算
情況3: IP選項的處理
因為option是header的一部分,它們被包含在checksum內。所以,每當option的處理方式使得IP header必須有所新增(例如,新增時間戳記)或修改時,就會迫時checksum重新計算
情況4: 分段
當封包被分段時,每個片段都會具有不同的header。多數欄位依然不變,但是和分段有關的欄位就不同了,像是偏移量。因此,checksum也得重新計算
註1. 計算TCP/IP Checksum

TCP/UDP checksum
(2) 多數L4協定的checksum則包括其header和資料
多數L2和L4協定都會提供checksum,讓L3也做這件事,就不見得非做不可了,基於相同的理由,IPv6已把checksum移除了
某些情況下,已接收的frame上,於硬體中所計算的L4 checksum會失效
情況1: 一個輸入之L2 frame,包含了一些補白,以滿足最小frame尺寸所需,但是NIC不夠聰
          明,在計算checksum時沒有跳過補白
情況2: 當一個輸入之IP片段和前一個已接收之片段重疊時
情況3: 當一個輸入之IP封包使用IPsec suite的協定之一時。NIC再也無法計算正確的L4
checksum,因為L4 header和payload會被壓縮、被摘要或是被加密
情況4: IP層發生NAT或類似之干擾時,checksum就必須重算

➠ L2 CRC (Cyclic redundancy check)

參考資料

註1. 計算TCP/IP Checksum

沒有留言:

張貼留言