UDP报头只有4个字段,分别是:源端口号、目的端口号、报文长度和报头checksum,其中的报头checksum这个字段在IPv4中并不是强制的,但在IPv6中是强制的,本文介绍UDP报头中checksum的计算方法,并给出相应的源程序。1。UDP报文结构UDP报文为两部分,报头数据;在Linux下,UDP报头定义在头文件linuxudp。h中;structudphdr{be16source;be16dest;be16len;sum16check;};source来源端口号,可选项,如果不使用,填充0;dest目的端口号;len报文长度;check报头的校验和,在IPv4中是可选的,IPv6中是强制的,如果不使用,应填充0; 图1:UDP数据报结构2。IP报头结构之所以在这里介绍IP报头,是因为在计算UDP报头checksum时会用到IP头中的一些字段;在Linux下,IP报头定义在头文件linuxip。h中,可以自行查看,我们这里仅给出图示; 图2:IP报头结构3。UDP报头checksum的计算UDP报头checksum的定义及计算方法在〔RFC768〕(http:www。faqs。orgrfcsrfc768。html)中有明确的说明;UDP报头checksum的具体算法在〔RFC1071〕(http:www。faqs。orgrfcsrfc1071。html)有明确的说明;在计算UDP报头checksum前要首先为UDP报头加上一个伪报头;加上伪报头的UDP报文格式如下: 图3:伪报头伪报头中源IP地址(SourceIPaddress)、目的IP地址(DestinationIPaddress)和协议(Protocol)这三个字段都是从IP报头中取过来的;源IP地址和目的IP地址是32bit的IP地址,Protocol字段是网络协议号,UDP协议号为17(0X11);如果checksum中没有加入伪报头,UDP报文也是可以安全送达的,但是,如果IP报头有损坏或者被认为修改,报文有可能被送到错误的地址;伪报头中加入protocol字段是为了保证报文为UDP报文,正常情况下protocol为17(0x11),如果传输中这个字段变化了,那么在接收端计算出的checksum就会不正确,接收端会丢弃这个出现错误的报文;checksum计算规则:UDP报头中的check字段填充0;(伪报头UDP报头DATA)的长度应该为16bit字的整数倍,如果不是,DATA的最后部分要填充1个字节0,以使其为16bit字的整数倍;(伪报头UDP报头DATA)看作是很多个16bit字,并逐一相加,结果仍为16bit字,如果出现溢出,则结果1,然后继续,直至完成;结果求反即为所需的checksum;在RFC768中定义的UDP的checksum为:(伪报头UDP报头DATA)按16bit字进行反码求和的结果就是checksum;但实际上原码求和再取反和反码求和是一样的结果,因为求反码再求和的方法对每一组16bit字都要做一次求反运算,因此其运算量更大一些,在实际中没有人使用;以上两种运算方法在本文给出的范例中均有体现,可以用来验证其结果的一致性;按照RFC768的说明,当checksum的运算结果为0时,checksum应该作为全1来传输,因为在UDP协议中,checksum为0表示没有使用checksum,UDP的checksum在ipv4中并不是强制使用的。下面是计算udp报头checksum的完整源代码:includestdio。hincludestdlib。hincludeunistd。hincludestdint。hincludeincludelinuxudp。hudppseudoheaderstructurestructpseudohdr{uint32tsourceip;uint32tdestinationip;uint8tzero;uint8tprotocol;uint16tudplen;};udppacketstructureforcalculatingchecksumstructudpcheckhdr{structpseudohdrpseudohdr;structudphdrudphdr;unsignedchardata〔16〕;};initialpseudoheadervoidinitpseudohdr(structudpcheckhdrp){ppseudohdr。sourceipinetaddr(152。1。51。27);0X01980X1B33ppseudohdr。destinationipinetaddr(152。14。94。75);0X0E980X4B5Eppseudohdr。zero0;0X000X1100ppseudohdr。protocol17;0X11ppseudohdr。udplen13;0X000D}initialudpheadervoidinitudphdr(structudpcheckhdrp){pudphdr。source56020;0xDAD4pudphdr。dest8000;0X1F40pudphdr。len13;0X000Dpudphdr。check0;0X0000}initialudpdatavoidinitudpdata(structudpcheckhdrp){pdata〔0〕h;0X680X6568pdata〔1〕e;0X65pdata〔2〕l;0X6C0X6C6Cpdata〔3〕l;0X6Cpdata〔4〕o;0X6F0X006Fpdata〔5〕0;}uint16tchecksum1(uint16tp,intcount){registerlongsum0;unsignedshortchecksum;uint16ttemp;uint16ti0;while(count1){Thisistheinnerlooptemp(unsignedshort)(unsignedshort)p;printf(Step02d:0X08lX0X04X,i,sum,temp);sumtemp;count2;}Addleftoverbyte,ifanyif(count0){temp(unsignedshort)(unsignedshort)p;printf(Step02d:0X08lX0X04X,i,sum,temp);sum(unsignedchar)p;}printf(Resultbeforefolding:0X08lX,sum);Fold32bitsumto16bitswhile(sum16)sum(sum0xffff)(sum16);printf(Resultafterfolding:0X08lX,sum);checksum(unsignedshort)sum;printf(Checksum0x04X,checksum);returnchecksum;}uint16tchecksum2(uint16tp,intcount){registerlongsum0;unsignedshortchecksum;uint16ttemp;uint16ti0;while(count1){Thisistheinnerlooptemp(unsignedshort)(unsignedshort)p;printf(Step02d:0X08lX0X04X(0X04X),i,sum,(uint16t)temp,temp);sum(uint16t)temp;count2;}Addleftoverbyte,ifanyif(count0){temp(unsignedshort)(unsignedshort)p;printf(Step02d:0X08lX0X04X(0X04X),i,sum,(uint16t)temp,temp);sum(uint16t)temp;}printf(Resultbeforefolding:0X08lX,sum);Fold32bitsumto16bitswhile(sum16)sum(sum0xffff)(sum16);printf(Resultafterfolding:0X08lX,sum);checksum(unsignedshort)sum;printf(Checksum0x04X,checksum);returnchecksum;}intmain(intargc,charargv){structudpcheckhdrudppacket;initpseudohdr(udppacket);initudphdr(udppacket);initudpdata(udppacket);unsignedshortp(unsignedshort)udppacket;intcountsizeof(structpseudohdr)udppacket。udphdr。len;printf(Theonescomplementcodeof16bittruecodesum);checksum1(p,count);printf(Theonescomplementsum);checksum2(p,count);return0;}其中在计算checksum的程序中参考了RFC1071中给出的源代码;checksum1()使用的常规的算法,checksum2()使用的把每个16bit字求反在相加的算法进行的运算,两种算法的结果是一样的。读者可以根据需要适当第调整数据,以使其计算出不同的结果;下面是我的机器上的运行结果截屏 图4:程序运行截屏4。UDP报头checksum的验证UDP报文的接收端是需要对checksum字段进行验证的,方法如下:加入伪报头;将(伪报头UDP头DATA)按16bit分成若干个16bit字,如果最后一个字节无法组成一个16bit字,要在DATA的最后补0;将所有的16bit字相加(包括checksum字段),其结果仍然是16bit字,如果出现溢出则结果1;如果最后结果为全1,即:0XFFFF,则表示UDP报文正确,否则应该认为UDP报文有错误,应该丢弃。 欢迎访问我的博客:https:whowin。cn