四 川 理 工 学 院.docx
《四 川 理 工 学 院.docx》由会员分享,可在线阅读,更多相关《四 川 理 工 学 院.docx(16页珍藏版)》请在冰点文库上搜索。
四川理工学院
内容摘要
IP协议(网际协议),是TCP/IP协议族中最为核心的协议。
所有的数据在此协议机制下都以IP数据报的格式传输。
当分组过大不适合在所选硬件接口(即不同网络)上发送时,就要对其分片。
在目的主机上再把所有分片组装成一个完整的数据报,提交给上层协议处理。
本次设计开发工具为Turboc2.o+win2000,由我们3人独立完成,采用结构化设计思想完成对所有分片的重装,实现IP协议的重装模快。
(一)IP协议重装原理及功能分析
1:
设计背景
我们知道,每一个数据链路层都有自己的帧格式,在格式里面规定了数据的最大长度,即MTU。
当数据报封装成帧时,长度都应该小于此长度,因此,为了适应不同网络,就要对IP数据报进行分片,分片带来的问题就是要对分片进行重装。
2:
重装依据—>IP数据报首部
0151631
4位版本
4位首部长度
8位TOS
16位总长度(字节)
16位标识
3位标志
13位片偏移
8位TTL
8位协议
16位首部检验和
32位源IP地址
32位目的IP地址
32位选项
数据
(图1)IP首部
首部共20字节。
把一份IP数据报分片后,只有到达目的地才进行重新组装。
重新组装由目的端的IP层来完成,其目的是分片与重新组装过程对传输层是透明的。
如图
(1)IP首部为分片后的重装提供了必要的信息:
首先,其标识字段包含一个唯一的值,该值在分片后被复制到每个片中;其次,标志字段由3个1bit组成,比特0是保留的,必须为0,比特1是“不分片”,比特2是表示“更多的片”标志,标志字段其它13bit指出该片偏移原始数据报开始处的位置,以8字节单元计算,因此,除最后一个分片外,其他每个分片都望是一个8字节倍数的数据,从而使后面的分片从8字节开始。
当数据被分片后,片总长要改为该片的长度。
当IP数据报被分片后,每个片都有自己的首部,这样在目的端就有足够的信息来组装这些数据报分片。
处理思想:
IP协议是个无连接的协议,无连接是指IP并不维护任何关于后续数据报的状态信息,因此它不能保证分片都按序到达,另外,属于一个数据报的分片也可能与另一个数据报分片混杂在一起,。
为了解决上述问题,我们可
以用重装表图
(2)和一些想关链表完成。
重装表做的就是找出当前分片是那个组的,将属于同一个数据报的分片进行排序,当所有的分片都到达时将它们重新组装成一个数据报,当然在接收数据时都有一定时间限制,当建立的超时限已过,同时有的分片丢失了,则将接受到的分片都丢掉。
处理过程:
当接收到一个IP数据报时,如果其的片偏移为0而还有“更多分片“也为0,则将数据报发送到适当对列,反之,就去查找重装表项目,如果没有找到,就建立一个新的项目,找到了就在链表适当地方插入此分片。
当所有的分片都已经到达,就重装这些分片,将其发送到高层协议,反之,就检查是否超时,如果超时,就丢弃所有分片同时发送ICMP差错报文。
功能分析:
通过上述处理就完成了对分片的重装,就实现不同网络中数据帧的传输。
ST
SA
DI
To
F
……….
——>链表
图
(2)重装表
——>链表
ST:
状态;SA:
源址,DI:
数据报ID;TO:
超时;F:
分片
(二)数据结构定义及处理流程
1:
变量定义:
数据报首部:
数据报首部
说明
ip_id
标识字段
ip_off
标志字段
IP_DF
标志字段的”不分片”标志
IP_MF
“更多分片“标志
ip_src
源地址
ip_dst
目的地址
ip_p
协议值
ip_len
数据报长度
记数统计量:
ipstat成员
说明
ips_cantfrag
要求分片但被DF禁止而没有发送的数据报报数
ips_odropped
内存不足而被丢弃的分组数
ips_ofragments
被发送的分组数
ips_fragmented
未输出的分片的分组数
全局变量:
ipq:
类型Struct,说明—>重装表
2:
函数设计:
函数
说明
ipintr()
接收分片并交给ip_reass()处理,最后由它把封装好的数据报传给上层
ip_reass()
接受来自ipintr()的分片,并对其进行重装,最后把重装好的数据报交给ipintr()函数
3用到的数据结构以及必要的说明:
ipq(重装表)结构:
structipq{
structipq*next,*prev;/*重组报头*/
charipq_tll;/*重装生存时间*/
charipq_p;/*此片用到的协议*/
shortipq_id;/*重装序列号*/
structipastrag*ipq_next,*ipq_prve;/*分片的IP报头*/
structin_addr,ipq_src,ipq_dst;/*地址清单、目的与源地址*/
}
ipasfrag(过度结构)结构:
structipasfrag{
/*预处理*/
#ifBYTE_ORDER==LITTLE_ENDIAN
Charip_hl=4,ip_v=4;
#endif
#ifBYTE_ORDER==BIG_ENDIAN
charip_v=4,ip_hl=4;
#endif
charipf_mff;/*ipf_mff成员覆盖ip结构中的服务字段,*/
/*防止报头损坏,从标志字段复制*/
shortip_len/*下是报头定义,与ipq结构类似*/
unsignedshortip_id;
shortip_off;
unsignedcharip_p;
unsignedshortip_sum;
structipasfrag*ipf_next,*ipf_prev;
}
3详细流程:
ipintr先要对接收到的分片进行处理,如果它检查到MF或分片偏移为非0,则分组就是一个必须重装的分片,反之,就可以跳过重装。
当一个缓存区无法容纳分组时,接口就将整个分组返回,在ipintr函数中在处理前应将IP首部移到缓冲区上。
ipintr把一个要处理的分片传给和一个指针传给ip_reass,其中指针指向ipq中匹配的的重装首部,ip_reass可能把分片重装并返回一个完整的数据报(只有一个分片),也可能将该分片链接到数据报的重装链表上(不只一个分片),等其他分片到达后重装。
ip_reass在一个由ipf_next和ipf_prev链接起来的双向循环链表上,并收集某个数据报分片。
当在重装时产生错误,ip_reass就丢弃该分片,返回一个空。
在设计中,最多实现重装576字节的数据报。
ip_reass先创建重装表,然后切断分组,在重装表中找相应位置,插入分组,再重装数据报。
流程图:
图(3)
T
T
以上就是重装的流程图。
在超时检查时,如果没有超时,则继续接收,直到完成为止。
(三)基本代码实现
由于只有一个模快,故没有主函数main(),以下就是函数ipintr()与ip_reass的实现以及必要的说明,其中的数据结构定义包含在chong_z.h中。
IP数据报首部定义:
structip{
/*预处理*/
#ifBYTE_ORDER==LITTLE_ENDIAN
unsignedcharip_hl=4,ip_v=4;
#endif
#ifBYTE_ORDER==BIG_ENDIAN
usignedcharip_v=4,ip_hl=4;
#endif
unsignedcharip_tos;/*服务类型,下面的前面已给出*/
shortip_len;
shortid;
shortip_off;
#defineIP_DF0x4000;/*不分片标志*/
#defineIP_MF0x2000;/*更多分片标志*/
#defineIP_OFFMASK0x1fff;/*分段位*/
unsignedcharip_ttl;
unsignedcharip_p;
unsignedshortip_sum;/*检验和*/
structin_addr,ip_src,ip_dst;
};
mbuf缓冲区定义:
structm_hdr{
structmbuf*mh_next;/*链表的下一个缓冲区*/
structmbuf*mh_nextpkt;/*对列中的下个链表*/
intmh_len;/*缓冲区数据总数*/
shortmh_type;/*数据类型*/
shortmh_flags;/*标志位*/}
structpkthdr{
intlen;/*数据报总长*/
}
structmbuf{
structm_hdr;
union{
struct{structpkthdrMH_pkthdr;
union{
charMH_databuf[MHLEN];
}MH_dat;
charM_databuf[MLEN];
}M_dat;
};
本次设计用到的宏定义:
#definem_nextm_hdr.mh_next
#definem_lenm_hdr.mh_len
#definem_pkthdrM_dat.MH.MH_phthdr
#definedtom(x)((structmbuf*)((int)(x)&~(MSIZE-1)
/*取得一个放在mbuf中任意位置的数据指针,并返回一mbuf
*本身结构的指针*/
分配mbuf函数m_get实现:
structmbuf*
intnowait,type;
m_get(nowait,type)
{
structmbuf*m;
MGET(m,nowait,type);
return(m);}
ipintr代码:
voidipintr()/*对收到分片的处理*/
{
structip*ip;
structmbuf*m;
structipq*fp;
structmbuf*m;/*缓冲区指针*/
inthlen;
next:
s=splimp();/*调用中断*/
IF_DEQUEUE(&ipintrq,m);
splx(s);/*打开网络中断*/
if(m==0)return;
if(ip->ip_off&&ip->IP_DF){/*如果分片位或更多分片为*0,则必须重装,反之则跳过重装*/
if(m->m_flags&M_EXT){
if(m=m_pullup(m,sizeof(structip))==0)
ipstat.ips_toosmall++;
gotonext;}
ip=mtod(m,struct*ip);}/*搜索此数据报分片对列*/
for(fp=ipq.next;fp!
=&ipq;fp=fp->next)
if(ip->ip_id==fp->ipq_id&&ip->src.s_addr==fp->ipq_src.s_addr&&ip->ip_dst.s_addr==fp->ipq_dst.s_addr&&ip->ip_p==fp->ipq_p)gotofound;
found:
/*调整ip_len*/
ip->ip_len-=heln;/*有更多分片就创建ip_mff,并将偏移量*/((structipasfrag*)ip)->ip_mff&=~1;/*转换成字节*/
if(ip->ip_off&IP_MF)((structipasfrag*)ip)->ipf_mff|=1;
ip->ip_off<<=3;
if(((structipasfrag*)ip)->ipf_mff&1||ip->ip_off)
ipstat.ips_fragments++;
/*如果此数据报有更多的分片或者这不是第一个分片,就试图重
*装,调用ip_reass函数,如果成功,就继续*/
ip=ip_reass((structipasfrag*)ip,fp);
if(ip==0)gotonext;
ipstat.ips_reassembled++;
m=dtom(ip);
}elseif(fp)ip_freef(fp);
}elseip->ip_len-=hlen;
}
ip_reass函数实现:
structip*
structipasfrag*ip;
structipq*fp;
ip_reass(ip,fp)
{structmbuf*m=dtom(ip);
structipasfrag*q;
structmbuf*t;
inthlen=ip->ip_hl<<2;/*
inti,next;
m->m_data+=heln;
m->m_len-=hlen;
/*当第一个分片到来时,创建一个重装表*/
if(fp==0){
if(t=m_get(M_DONTWAIT,MT_FTABLE))==null)
gotodropfrag;
fp=mtod(t,structipq*);
insque(fp,&ipq);
fp->ipq_ttl=IPFRAGTTL;
fp->ipq_p=ip->ip_p;
fp->ipq_id=ip->ip_id;
fp->ipq_next=fp->ipq_prev=(structipasfrag*)fp;
fp->ipq_src=((structip*)ip)->ip_src;
p->ipq_dst=((structip*)ip)->ip_dst;
q=(structipasfrag*)fp;
gotoinsert;}
for(q=fp->ipq_next;q!
=(structipasfrag*)fp;q=q->ipf_next)
if(q->ip_off>ip->ip_off)break;
/*搜索数据链表找到一个偏移
*大于ip_off的*分片*/
if(q->ipf_prev!
=(structipasfrag*)fp){
i=q->ipf_prev->ip_off+q->ipf_prev->ip_len-ip->ip_off;
if(i>0){
/*把新片中与早到分片末尾重叠的字节丢弃,切断重复部分,或
*者新片在以前都已经出现,则丢弃所有新片*/
if(i>=ip->ip_len)gotodropfrag;
m_adj(dtom(ip),i);
ip->ip_off+=i;
ip->ip_len-=i;
}}
while(q!
=(structipasfrag*)fp&&ip->ip_off+ip->ip_len>ip->
ip_off){
i=(ip->ip_off)+ip->ip_len-q->ip_off;
q->ip_len-=i;
q->ip_off+=i;
m_adj(dtom(q),i);
break;}
q=q->ipf_next;
m_freem(dtom(q->ipf_prev));
ip_deq(q->ipf_prev);
}/*截断或丢弃已有的分片*/
insert:
ip_enq(ip,q->ipf_prev);
next=0;
for(q=fp->ipq_next;q!
=(structipasfrag*)fp;q=q->ipf_next){
if(q->ip_off!
=next)return0;
next+=q->ip_len;
}
if(q->ipf_prev->ipf_mff&1)return0;/*将到达分片插入重装链*表*/
q=fp->ipq_next;
m=dtom(q);
t=m->m_next;
m->m_next=0;
m_cat(m,t);
q=q->ipf_next;
while(q!
=(structipasfrag*)fp){
t=dtom(q);
q=q->ipf_next;
m_cat(m,t);
}/*如果所有分片都已接收,用m_cat把分片重装成数据报*/
ip=fp->ipq_next;
ip->ip_len=next;
ip->ipf_mff&=~1;
((structip*)ip)->ip_src=fp->ipq_src;
((structip*)ip)->ip_dst=fp->ipq_dst;
remque(fp);
(void)m_free(dtom(fp));
m=dtom(ip);
m->m_len+=(ip->ip_hl<<2);
m->m_data-=(ip->ip_hl<<2);
/*重建数据报首部*/
if(m->m_flags&M_PKTHDR){
intplen=0;
for(t=m;m;m=m->m_next)
plen+=m->m_len;
t->m_pkthdr.len=plen;
}
return((structip*)ip);/*计算缓存链中数据的字节数,并保存在
*m_pkthdr.len中*/
}
心得体会
通过本次课程设计,我们深入理解了IP协议中重装的原理。
完成了重装模快的结构化设计。
感谢指导教师的指导以及网络教研室的老师门,谢谢给我们这次实践机会!
参考文献
1:
RichardStevens<>.机械工业出版社.2005年:
225-236
2:
Forouzan&SophiaChungFegan著,谢希仁译<>.清华大学出版社.2003年:
152-153
、