ip_fast_csum
/**
* ip_fast_csum - Verify IP header checksum 验证 IP 头的校验和是否正确
* @iph: pointer to IP header (network byte order)
* @ihl: IP header length in 32-bit words (typically 5) 互联网首部长度 5*4个字节
*
* Returns 0 if checksum is valid, non-zero if invalid.
*/
static inline int ip_fast_csum(const void *iph, unsigned int ihl)
{
const u16 *w = (const u16 *)iph;
u32 sum = 0;
unsigned int count = ihl * 2; /* convert to 16-bit word count */
while (count--)
sum += *w++; /* accumulate in network byte order */
/* Fold 32-bit sum to 16 bits */
sum = (sum & 0xFFFF) + (sum >> 16);
sum += (sum >> 16);
/* Valid checksum yields 0xFFFF, inverted = 0 */
return (u16)~sum;
}
/**
* 正确的计算示例
*
* IP 头(checksum 字段先设为 0 来计算):
* 45 00 00 3c 1c 46 40 00 40 06 [00 00] c0 a8 01 64 c0 a8 01 01
*/
/* 计算流程 (checksum=0x0000): */
u16 words[] = {0x4500, 0x003c, 0x1c46, 0x4000, 0x4006,
0x0000, /* checksum 置 0 */
0xc0a8, 0x0164, 0xc0a8, 0x0101};
/*
* Step 1: 累加
* sum = 0x4500 + 0x003c + 0x1c46 + 0x4000 + 0x4006
* + 0x0000 + 0xc0a8 + 0x0164 + 0xc0a8 + 0x0101
*
* 0x4500
* + 0x003c = 0x453c
* + 0x1c46 = 0x6182
* + 0x4000 = 0xa182
* + 0x4006 = 0xe188
* + 0x0000 = 0xe188
* + 0xc0a8 = 0x1a230 (溢出)
* + 0x0164 = 0x1a394
* + 0xc0a8 = 0x2643c
* + 0x0101 = 0x2653d
*
* Step 2: 折叠
* sum = (0x2653d & 0xFFFF) + (0x2653d >> 16)
* = 0x653d + 0x0002
* = 0x653f
*
* Step 3: 再次折叠
* sum = 0x653f + 0 = 0x653f
*
* Step 4: 取反得到 checksum
* checksum = ~0x653f = 0x9ac0
*
* 所以正确的 IP checksum = 0x9ac0
*/
/* 验证时(包含正确的 checksum 0x9ac0): */
u16 words_with_csum[] = {0x4500, 0x003c, 0x1c46, 0x4000, 0x4006,
0x9ac0, /* 正确的 checksum */
0xc0a8, 0x0164, 0xc0a8, 0x0101};
/*
* 累加: 0x2653d + 0x9ac0 = 0x2fffd
* 折叠: 0xfffd + 0x0002 = 0xffff
* 取反: ~0xffff = 0x0000 ← 返回 0 表示校验正确!
*/
skb_checksum
/**
* skb_checksum - Compute checksum of SKB data
* @skb: socket buffer
* @offset: offset from skb->data to start checksumming
* @len: length of data to checksum
* @seed: initial checksum value (usually 0)
*
* Returns the raw checksum for use in tcp_v4_check().
*/
static inline u32 skb_checksum(struct sk_buff *skb, int offset, int len, u32 seed)
{
const u16 *w = (const u16 *)(skb->data + offset);
u32 sum = seed;
for (; len > 1; len -= 2)
sum += *w++;
if (len)
sum += *(const u8 *)w;
sum = (sum & 0xFFFF) + (sum >> 16);
sum += (sum >> 16);
return sum;
}
tcp_v4_check
/**
* tcp_v4_check - Verify TCP checksum with IPv4 pseudo-header TCP 完整校验和验证
* @len: TCP segment length (header + data)
* @saddr: source IP address (network byte order)
* @daddr: destination IP address (network byte order)
* @csum: pre-computed checksum of TCP segment (from skb_checksum)
*
* Returns 0 if checksum is valid, non-zero if invalid.
*/
static inline u16 tcp_v4_check(int len, u32 saddr, u32 daddr, u32 csum)
{
u32 sum = csum;
/* Add pseudo-header: saddr, daddr are already network order */
sum += (saddr & 0xFFFF) + (saddr >> 16);
sum += (daddr & 0xFFFF) + (daddr >> 16);
sum += htons(IPPROTO_TCP);
sum += htons((u16)len);
/* Fold 32-bit sum to 16 bits */
sum = (sum & 0xFFFF) + (sum >> 16);
sum += (sum >> 16);
/* Return 0 if valid */
return (u16)~sum;
}
/**
* 完整示例: 验证 TCP 校验和
*
* IP 伪头信息:
* Src IP: 192.168.1.100 = 0xc0a80164 (网络序)
* Dst IP: 192.168.1.1 = 0xc0a80101 (网络序)
* Protocol: TCP = 6
* TCP Length: 20 字节
*
* TCP 段校验和 (已通过 skb_checksum 计算): 0xc735
*/
u32 saddr = 0xc0a80164; /* 网络字节序 */
u32 daddr = 0xc0a80101; /* 网络字节序 */
u32 csum = 0xc735; /* 从 skb_checksum 得到 */
int len = 20;
/*
* tcp_v4_check(20, 0xc0a80164, 0xc0a80101, 0xc735) 计算过程:
*
* Step 1: 初始化
* sum = csum = 0xc735
*
* Step 2: 加源 IP (网络序直接拆分)
* saddr = 0xc0a80164
* sum += (saddr & 0xFFFF) = sum + 0x0164 = 0xc899
* sum += (saddr >> 16) = sum + 0xc0a8 = 0x18941
*
* Step 3: 加目的 IP
* daddr = 0xc0a80101
* sum += (daddr & 0xFFFF) = sum + 0x0101 = 0x18a42
* sum += (daddr >> 16) = sum + 0xc0a8 = 0x24aea
*
* Step 4: 加协议号 (转网络序)
* htons(IPPROTO_TCP) = htons(6) = 0x0600 (小端机器)
* sum += 0x0600 = 0x250ea
*
* Step 5: 加 TCP 长度 (转网络序)
* htons(20) = 0x1400 (小端机器)
* sum += 0x1400 = 0x264ea
*
* Step 6: 折叠
* sum = (0x264ea & 0xFFFF) + (0x264ea >> 16)
* = 0x64ea + 0x0002
* = 0x64ec
*
* Step 7: 再次折叠
* sum = 0x64ec + 0 = 0x64ec
*
* Step 8: 取反
* ~0x64ec = 0x9b13
*
* 返回: 0x9b13 (非零,表示校验和不正确)
*/
/*
* 如果 TCP checksum 字段填入正确的值,验证时应返回 0
*
* 正确的 TCP checksum 计算:
* 先将 TCP 头中 checksum 字段设为 0
* 计算 skb_checksum → 0xc735
* 计算 tcp_v4_check(20, saddr, daddr, 0xc735)
* 取反前的 sum = 0x64ec
* 正确的 checksum = ~0x64ec = 0x9b13
*
* 验证时:
* TCP 头中 checksum = 0x9b13
* skb_checksum 会把 0x9b13 也累加进去
* 新的 csum = 0xc735 + 0x9b13 = 0x16248
* 折叠: 0x6248 + 0x0001 = 0x6249
*
* tcp_v4_check 计算:
* sum = 0x6249 + 0x0164 + 0xc0a8 + 0x0101 + 0xc0a8 + 0x0600 + 0x1400
* = 0x6249 + 0x0164 = 0x63ad
* = 0x63ad + 0xc0a8 = 0x12455
* = 0x12455 + 0x0101 = 0x12556
* = 0x12556 + 0xc0a8 = 0x1e5fe
* = 0x1e5fe + 0x0600 = 0x1ebfe
* = 0x1ebfe + 0x1400 = 0x1fffe
*
* 折叠: 0xfffe + 0x0001 = 0xffff
* 取反: ~0xffff = 0x0000 ← 返回 0,校验正确!
*/