技术开发 频道

自己动手编写嵌入式Bootloader之二

  5. IP协议的实现

  IP数据包的格式如下表所示:

  +Bits 034781516181931

  0VersionHeader lengthType of ServiceTotal Length

  32IdentificationFlagsFragment Offset

  64Time to LiveProtocolHeader Checksum

  96Source Address

  128Destination Address

  160Options

  
160 or 192+Data

  IP协议的简化:IP协议在网络中主要完成路由选择和网络分段的功能。起始Bit 0-3表示版本号,对IPv4来说取值为4即0100即可。Header length域指明IP数据包header的长度(不包括数据Data域),以四字节为单位,因为Options域是可选的所以IP Header的长度并不固定。我们不使用Option域,所以取最小值5,表示Header长度为20字节。服务类型域(Type of Service, TOS)是为特殊的应用如VoIP等保留的,我们不使用,赋值为零即可。接下来2个字节的Total Length域表示整个数据包的长度,包括Header和Data,以字节为单位。 标识域(Identification)用来给数据包一个唯一的编号,用于验证和跟踪等,我们不使用,直接赋值为零即可。Flags和Offset用于分段包的重组,我们不使用,把Flags的第2位设为1表示是不可分段的,Offset赋值为零即可。生存时间(Time to Live, TTL)表示该数据包在网络上的有效期,我们简单的把它设为最大值0xFF即可。协议域(Protocol)表示传输层使用什么协议,RFC790文档为每个协议都规定了唯一的编号,如UDP编号为17。Header Checksum为Header区域的校验和,在校验之前该域初始为0,然后计算整个头部的校验和,把结果存放在该域,计算校验的方法是把头部看成以16位为单位的数字组成,依次进行二进制反码求和。接下来的八个字节是源IP地址和目的IP地址,没什么可说的。

  综上所述,我们只保留了IP协议中必须的关键字段,因而简化了设计,对IP数据包进行填充的代码段如下:

  struct ip_header *p = (struct ip_header*)(buf);

  p
->ver_ihl = 0x45; // 1 Byte

  p
->tos = 0x00; // 1 Byte

  p
->tlen = htons(len); // 2 Byte

  p
->identification = htons(0x00); // 2 Byte

  p
->flags_fo = htons(0x4000); // 2 Byte

  p
->ttl = 0xFF; // 1 Byte

  p
->proto = 17; // 1 Byte, 17 for UDP

  p
->ip_src = htonl(NetOurIP); // 4 Byte

  p
->ip_dest = htonl(NetServerIP); // 4 Byte

  p
->crc = 0x0; // 2 Byte, To be

  p
->crc = checksum( buf, sizeof(struct ip_header) );

  CheckSum 校验和:

  IP,TCP,UDP等许多协议的头部都设置了校验和项,它们采用的算法是一样的,将被校验的数据按16位进行划分(若数据字节长度为奇数,则在数据尾部补一个字节0),对每16位求反码和,然后再对和取反码。 代码如下:

  unsigned short checksum(unsigned char *ptr, int len)

  {

  unsigned
long sum = 0;

  unsigned
short *p = (unsigned short *)ptr;

  
while (len > 1)

  {

  sum
+= *p++;

  len
-= 2;

  }

  
if(len == 1)

  sum
+= *(unsigned char *)p;

  
while(sum>>16)

  sum
= (sum&0xffff) + (sum>>16);

  
return (unsigned short)((~sum)&0xffff);

  }
0
相关文章