IP校验和算法解析与实现

ip_fast_csum

c 复制代码
/**
 * 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;
}
c 复制代码
/**
 * 正确的计算示例
 * 
 * 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

c 复制代码
/**
 * 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

c 复制代码
/**
 * 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;
}
c 复制代码
/**
 * 完整示例: 验证 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,校验正确!
 */
相关推荐
罗湖老棍子2 小时前
括号配对(信息学奥赛一本通- P1572)
算法·动态规划·区间dp·字符串匹配·区间动态规划
那就回到过去2 小时前
交换机特性
网络·hcip·ensp·交换机
fengfuyao9852 小时前
基于MATLAB的表面织构油润滑轴承故障频率提取(改进VMD算法)
人工智能·算法·matlab
机器学习之心2 小时前
基于随机森林模型的轴承剩余寿命预测MATLAB实现!
算法·随机森林·matlab
一只小小的芙厨2 小时前
寒假集训笔记·树上背包
c++·笔记·算法·动态规划
庄周迷蝴蝶2 小时前
四、CUDA排序算法实现
算法·排序算法
以卿a2 小时前
C++(继承)
开发语言·c++·算法
I_LPL2 小时前
day22 代码随想录算法训练营 回溯专题1
算法·回溯算法·求职面试·组合问题
金融RPA机器人丨实在智能2 小时前
2026动态规划新风向:实在智能Agent如何以自适应逻辑重构企业效率?
算法·ai·重构·动态规划