|
EncEthernet 应该是最简单的以太网协议,我这里学习了一下,注释了一些,现在分享,看看是否对大家的理解有帮助
// TCP 协议中几个关系:
// ARP,即地址解析协议,实现通过IP地址得知其物理地址。
// 反向地址转换协议(RARP:Reverse Address Resolution Protocol) 反向地址转换协议(RARP)
// 允许局域网的物理机器从网关服务器的 ARP 表或者缓存上请求其 IP 地址。
static uchar wwwport=80;
static uchar macaddr[6];
static uchar ipaddr[4];
static uint info_hdr_len=0;
static uint info_data_len=0;
static uchar seqnum=0xa; // my initial tcp sequence number
// IP 和校验只有在刚开始的时候在IP头部进行,同时IP的长度是
// 在开始计算之前,一定要设置IP和校验区域为 0 IP 的长度是 20.
// For UDP/TCP we do not make up the required pseudo header. Instead we
// 我们采用的是 ip.src 和 ip.dst 区域代替实际的数据包
// UDP的和校验程序从 ip.src 区域开始
// Ip.src=4bytes,Ip.dst=4 bytes,Udp header=8bytes + data length=16+len
// 换句话说,这里讨论的数据长度相对于你真正要校验的长度是 8 + length
// 在计算前,你必须先将校验的区域清 0
// udp 的长度是: 8 + 8 + data length
// tcp 的长度是: 4+4 + 20 + option len + data length
//
// 更多的资料,可以从以下网址获取:
// http://www.netfor2.com/checksum.html
// http://www.msc.uky.edu/ken/cs471/notes/chap3.htm
// The RFC has also a C code example: http://www.faqs.org/rfcs/rfc1071.html
// IP、ICMP、UDP和TCP报文头部都有校验和字段,大小都是16bit,算法也基本一样:
/* 在发送数据时,为了计算数据包的校验和。应该按如下步骤:
(1)把校验和字段置为0;
(2)把需校验的数据看成以16位为单位的数字组成,依次进行二进制反码求和;
(3)把得到的结果存入校验和字段中。
在接收数据时,计算数据包的校验和相对简单,按如下步骤:
(1)把首部看成以16位为单位的数字组成,依次进行二进制反码求和,包括校验和字段;
(2)检查计算出的校验和的结果是否为0;
(3)如果等于0,说明被整除,校验是和正确。否则,校验和就是错误的,协议栈要抛弃这个数据包。
虽然上面四种报文的校验和算法一样,但在作用范围存在不同:
IP校验和只校验20字节的IP报头;
而ICMP校验和覆盖整个报文(ICMP报头+ICMP数据);
UDP和TCP校验和不仅覆盖整个报文,而且还有12字节的IP伪首部,
IP伪首部: 包括源IP地址(4字节)、目的IP地址(4字节)、协议(2字节,第一字节补0)和TCP/UDP包长(2字节)。
另外UDP、TCP数据报的长度可以为奇数字节,所以在计算校验和时需要在最后增加填充字节0
(注意,填充字节只是为了计算校验和,可以不被传送)。
*/
uint checksum(uchar *buf, uint len,uchar type)//参数type是数据的类型,分为IP,UDP,TCP;1=udp;2=tcp
{
ulong sum = 0;//cksum必须是32bit的unsigned long型,高16bit用于保存累加过程中的进位;
uint *ibuf=(uint*)(buf);
//if(type==0){
// // do not add anything
//}
if (type==1)//UDP协议的校验
{
sum+=IP_PROTO_UDP_V; // udp协议,这里IP_PROTO_UDP_V=17
// the length here is the length of udp (data+header len)
// =length given to this function - (IP.scr+IP.dst length)
sum+=len-8; // = real tcp len
}
if (type==2)//TCP协议的校验
{
sum+=IP_PROTO_TCP_V; //IP_PROTO_TCP_V宏定义中定义为6
// the length here is the length of tcp (data+header len)
// =length given to this function - (IP.scr+IP.dst length)
sum+=len-8; // = real tcp len
}
// 对数据按16bit累加求和 build the sum of 16bit words
while (len >1)
{
sum += *ibuf;
ibuf++;
len-=2;
}
// if there is a byte left then add it (padded with zero)
if (len){
sum += (*ibuf) & 0xff00;
}
// 保证相加后cksum的高16位为0,
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >>16);
return (uint)(~sum);
}
// 在使用其他函数之前,必须先调用本段函数:这是一个初始化的过程
// 本函数用于初始化 IP地址、MAC地址、以及端口
void init_ip_arp_udp_tcp(uchar *mymac,uchar *myip,uchar wwwp)
{
uchar i=0;
wwwport=wwwp;
while (i<4){
ipaddr=myip;
i++;
}
i=0;
while (i<6){
macaddr=mymac;
i++;
}
}
// ARP,即地址解析协议,实现通过IP地址得知其物理地址。
// 这段代码就是ARP的实现
uchar eth_type_is_arp_and_my_ip(uchar *buf,uint len)
{
uchar i=0;
//
if (len<41) //如果长度不足 41 ,则返回,arp协议的长度是42字节
{
return(0);
}
if (buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V || buf[ETH_TYPE_L_P] != ETHTYPE_ARP_L_V)
{
return(0);//如果输入的buf,12和13,上层协议0806代表ARP协议,这段是帧类型,这个arp的协议
}
while (i<4)
{
if (buf[ETH_ARP_DST_IP_P+i] != ipaddr)//从数据包的第0x26开始 =2*16+6=32
{
return(0);
}
i++;
}
return(1);
}
// 判断是不是一个IP包
uchar eth_type_is_ip_and_my_ip(uchar *buf,uint len)
{
uchar i=0;
//eth+ip+udp header is 42
//
if (len<42){
return(0);//按照定义IP数据包应该大于46,这个大于42? 20字节的包头
}
if (buf[ETH_TYPE_H_P]!=ETHTYPE_IP_H_V || buf[ETH_TYPE_L_P]!=ETHTYPE_IP_L_V)
{
return(0);//如果输入的buf,12和13,上层协议0800代表ARP协议
}
if (buf[IP_HEADER_LEN_VER_P]!=0x45)
{
// must be IP V4 and 20 byte header
return(0);
}
while (i<4)
{
if (buf[IP_DST_P+i]!=ipaddr){
return(0);
}
i++;
}
return(1);
}
// make a return eth header from a received eth packet
// 生成一个 eth的头部
// 这个过程很简单 先加上目的MAC地址,在加上本机MAC地址
// 目的在前,本机在后
void make_eth(uchar *buf)
{
uchar i=0;
//
//copy the destination mac from the source and fill my mac into src
while (i<6)
{
buf[ETH_DST_MAC +i]=buf[ETH_SRC_MAC +i];
buf[ETH_SRC_MAC +i]=macaddr;
i++;
}
}
void fill_ip_hdr_checksum(uchar *buf)
{
uint ck;
// 先将两字节的和校验的值清空,以备后面计算
buf[IP_CHECKSUM_P]=0; // buf[0X18]=0
buf[IP_CHECKSUM_P+1]=0; // buf[0X19]=0
buf[IP_FLAGS_P]=0x40; // don't fragment BUF[0X14]=0X40
buf[IP_FLAGS_P+1]=0; // fragement offset 分段偏移(fragment offset):说明分段在当前数据报的什么位置
buf[IP_TTL_P]=64; // ttl 8位生存时间 TTL=0X40
// 计算校验和的过程:
ck=checksum(&buf[IP_P], IP_HEADER_LEN,0);
buf[IP_CHECKSUM_P]=ck>>8; // 高位
buf[IP_CHECKSUM_P+1]=ck& 0xff; // 低位
}
// 生成一个IP头
// IP的头部是20个字节 先本机IP"1A" 再目的IP "1E"
void make_ip(uchar *buf)
{
uchar i=0;
while (i<4){
buf[IP_DST_P+i]=buf[IP_SRC_P+i];
buf[IP_SRC_P+i]=ipaddr;
i++;
}
fill_ip_hdr_checksum(buf);
}
更多部分,请参考详细的源文件ourdev_719429PH5BSL.rar(文件大小:7K) (原文件名:Net.rar) |
阿莫论坛20周年了!感谢大家的支持与爱护!!
曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
|