ip协议栈

ip

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
    __u8    ihl:4,
            version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)   //网络字节序
    __u8    version:4,
            ihl:4;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
    __u8    tos;
    __be16  tot_len;
    __be16  id;
    __be16  frag_off;
    __u8    ttl;
    __u8    protocol;
    __sum16 check;
    __be32  saddr;
    __be32  daddr;
};

45 00 00 29 38 13 40 00 40 06 7d 60 c0 a8 02 0a c0 a8 02 01
(1) “45”,其中“4”是IP协议的版本(Version),说明是IP4。“5”是IHL位,表示IP头部的长度,是一个4bit字段,最大就是1111了,IP头部的最大长度就是60字节。而这里为“5”,说明是20字节。IP包头最小长度为20字节(IP头长度包含选项)
(2) “00”,服务类型(Type of Service)。这个8bit字段由3bit的优先权子字段(现在已经被忽略),4 bit的TOS子字段以及1 bit的未用字段(现在为0)构成.4 bit的TOS子字段包含:最小延时、最大吞吐量、最高可靠性以及最小费用构成,这四个1bit位最多只能有一个为1,本例中都为0,表示是一般服务
(3) “00 29”,IP数据报文总长,包含头部以及数据,这里表示41字节。这41字节由20字节的IP头部以及21字节的TCP头构成(最后的一个字节为数据)
(7) “40”这个字节就是TTL(Time To Live)了,表示一个IP数据流的生命周期,用Ping显示的结果,能得到TTL的值。每次IP数据包经过一个路由器的时候TTL就减一,当减到0时,这个数据包就消亡了。这也时Tracert的原理。本例中为“40”,转换为十进制就是64了

.ttl = IPDEFTTL; 缺省的IP包生存期为64

(8) “06”,这个字节表示传输层的协议类型

//Protocols
#define IPPROTO_IP 0    /* dummy for IP */
#define IPPROTO_ICMP 1  /* control message protocol */
#define IPPROTO_IGMP 2  /* internet group management protocol */
#define IPPROTO_GGP 3   /* gateway^2 (deprecated) */
#define IPPROTO_TCP 6   /* tcp */
#define IPPROTO_PUP 12  /* pup */
#define IPPROTO_UDP 17  /* user datagram protocol */
#define IPPROTO_IDP 22  /* xns idp */
#define IPPROTO_ND 77   /* UNOFFICIAL net disk proto */
#define IPPROTO_RAW 255 /* raw IP packet */
#define IPPROTO_MAX 256

(9) “7d 60”这个16bit是头校验和(Header Checksum)
(10) “c0 a8 02 0a”,这个是源地址
(11) “c0 a8 02 01”,这个是目标地址

IP校验和

/*
    buffer:IP首部的起始地址
    size:IP首部的大小
*/
SHORT checksum(USHORT* buffer, int size)
{
    unsigned long cksum = 0;
    while(size>1)
    {
        cksum += ntohs(*buffer++);
        size -= sizeof(USHORT);
    }
    if(size)
    {
        cksum += *(UCHAR*)buffer;
    }
    cksum = (cksum >> 16) + (cksum & 0xffff); 
    cksum += (cksum >> 16); 

    return (USHORT)(~cksum);
}
  • 把校验和字段清零
  • 对每16位进行二进制反码求和,反码求和的意思是先对每16位求和,再将得到的和转为反码

(cksum >> 16) + (cksum & 0xffff):将高16位与低16位相加
cksum += (cksum >> 16):第一步相加可能产生进位

举例

45 00   00 31
89 F5   00 00
6E 06   00 00(校验字段)
C0 A8   01 0A   ->  192.168.1.10
C0 A8   04 0C   ->  192.168.4.12

4500 + 0031 + 89F5 + 0000 + 6e06+ 0000 + C0A8 + 010A + C0A8 + 040C = 2 C392
0002 + C392 = C394
~22C7 = 3C6B

注:校验和计算时需要主机序;计算完的校验和需要转换成网络序

posted @ 2016-02-17 20:57  thomas_blog  阅读(369)  评论(0编辑  收藏  举报