计算 ip udp tcp 效验和
下面叙述的是ipv4的 ip udp tcp 效验和的计算。
ip效验和计算相对简单,只需对ip协议头进行计算;
ip协议头长20字节;
udp协议头长8字节;
tcp协议头长20~60字节;
udp和tcp的校验和不仅要对整个ip协议负载(包括
udp/tcp协议头和udp/tcp协议负载)进行计算,
还要先对一个伪协议头进行计算。
在计算效验和前,协议头效验和字段要清0。
伪协议头结构如下:
0 7 8 15 16 23 24 31
+--------+--------+--------+--------+
| source address |
+--------+--------+--------+--------+
| destination address |
+--------+--------+--------+--------+
| zero |protocol| udp/tcp length |
+--------+--------+--------+--------+
伪协议头中的字段同样都是网络字节序;
下面代码片段展现的是在gnu/linux中具体的计算方式:
//=====================================================
#include <stdint.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ether.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
struct pseudo_head {
uint32_t saddr;
uint32_t daddr;
char zero;
char proto;
unsigned short len;
};
int cal_cksum(char *p)
{
struct iphdr *ip = (struct iphdr *)
(p + sizeof(struct ether_header));
cal_ip_cksum(ip);
struct tcphdr *tcp;
struct udphdr *udp;
char *f = (char *)ip + 20;
switch(ip->protocol) {
case ipproto_tcp:
tcp = (struct tcphdr *)f;
tcp->check = 0;
tcp->check = cal_udptcp_cksum(
(unsigned short *)f, ip);
break;
case ipproto_udp:
udp = (struct udphdr *)f;
udp->check = 0;
udp->check = cal_udptcp_cksum(
(unsigned short *)f, ip);
break;
case ipproto_ip:
break;
default:
return 1;
}
return 0;
}
inline static void cal_ip_cksum(struct iphdr *ip)
{
ip->check = 0;
ip->check = in_cksum((unsigned short *)ip,
20, 0);
}
inline static unsigned short cal_udptcp_cksum(
unsigned short *p, struct iphdr *ip)
{
int len = ntohs(ip->tot_len) - 20;
struct pseudo_head ph;
ph.len = htons(len);
ph.saddr = ip->saddr;
ph.daddr = ip->daddr;
ph.proto = ip->protocol;
ph.zero = 0;
int sum = cal_sum(
(unsigned short *)&ph, sizeof(ph));
return in_cksum(p, len, sum);
}
unsigned short in_cksum(unsigned short *p,
int len, int sum)
{
int n = len;
while(n > 1) {
sum += *p++;
n -= 2;
}
if(n == 1)
sum += *(unsigned char *)p;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
unsigned short ret = ~sum;
return ret;
}
inline static int cal_sum(unsigned short *p, int len)
{
int sum = 0;
while(len > 0) {
sum += *p++;
len -= 2;
}
return sum;
}
摘自 leeshuheng的专栏
上一篇: 数字转换成指定进制的数字字符串
下一篇: JFXtras 0.6最终版发布