linux内核的ip头校验和测试

测试程序:

#include "stdio.h"
#include "stdlib.h"

typedef __signed__ char __s8;
typedef unsigned char __u8;

typedef __signed__ short __s16;
typedef unsigned short __u16;

typedef __signed__ int __s32;
typedef unsigned int __u32;

typedef __signed__ long __s64;
typedef unsigned long __u64;

typedef __s8  s8;
typedef __u8  u8;
typedef __s16 s16;
typedef __u16 u16;
typedef __s32 s32;
typedef __u32 u32;
typedef __s64 s64;
typedef __u64 u64;

#ifdef __CHECKER__
#define __bitwise__ __attribute__((bitwise))
#else
#define __bitwise__
#endif
#define __bitwise __bitwise__

typedef __u16 __bitwise __le16;
typedef __u16 __bitwise __be16;
typedef __u32 __bitwise __le32;
typedef __u32 __bitwise __be32;
typedef __u64 __bitwise __le64;
typedef __u64 __bitwise __be64;

typedef __u16 __bitwise __sum16;
typedef __u32 __bitwise __wsum;


struct iphdr {
    __u8    ihl:4,
        version:4;
    __u8    tos;
    __be16  tot_len;
    __be16  id;
    __be16  frag_off;
    __u8    ttl;
    __u8    protocol;
    unsigned short check;
    __be32  saddr;
    __be32  daddr;
};

static inline unsigned short ip_fast_csum(const void *iph, unsigned int ihl)
{
    unsigned int sum;

    asm("  movl (%1), %0\n"
        "  subl $4, %2\n"
        "  jbe 2f\n"
        "  addl 4(%1), %0\n"
        "  adcl 8(%1), %0\n"
        "  adcl 12(%1), %0\n"
        "1: adcl 16(%1), %0\n"
        "  lea 4(%1), %1\n"
        "  decl %2\n"
        "  jne  1b\n"
        "  adcl $0, %0\n"
        "  movl %0, %2\n"
        "  shrl $16, %0\n"
        "  addw %w2, %w0\n"
        "  adcl $0, %0\n"
        "  notl %0\n"
        "2:"
        : "=r" (sum), "=r" (iph), "=r" (ihl)
        : "1" (iph), "2" (ihl)
        : "memory");
    return (unsigned short)sum;
}

struct iphdr iph;

/*ip 16位校验和*/
unsigned short ip4_csum(void)
{
//    struct iphdr iph;
    iph.check   = 0;
    iph.ihl     = 5;
    iph.version = 4;
    iph.tos     = 1;
    iph.tot_len = 2;
    iph.id      = 3;
    iph.frag_off  = 4;
    iph.ttl     = 5;
    iph.protocol  = 6;
    //iph.check   = 0;
    iph.saddr   = 7;
    iph.daddr   = 8;

    return ip_fast_csum((u8 *)&iph, 5);
}

void main(void)
{
    iph.check = ip4_csum();
    printf("校验正确:%x\n", ip_fast_csum((u8 *)&iph, 5));
    iph.id      = 2;
    printf("校验错误:%x\n", ip_fast_csum((u8 *)&iph, 5));
    iph.frag_off  = 5;
    printf("校验错误:%x\n", ip_fast_csum((u8 *)&iph, 5));
    iph.frag_off  = 6;
    printf("校验错误:%x\n", ip_fast_csum((u8 *)&iph, 5));
    iph.ttl     = 6;
    printf("校验错误:%x\n", ip_fast_csum((u8 *)&iph, 5));

}

测试结果:

校验正确:0
校验错误:1
校验错误:0
校验错误:fffe
校验错误:fffd

结果分析:

有一个校验和的错误没有检查出来,校验错误时校验和应该不为0,但第3行的校验和仍然为0。

参考:

ip 封包中checksum计算函数分析-kgd7558-ChinaUnix博客

IP数据包的校验和算法_公清猫的博客_新浪博客 (sina.com.cn)