GPL关于RIP协议的实现代码分析.docx
- 文档编号:7553236
- 上传时间:2023-01-24
- 格式:DOCX
- 页数:16
- 大小:100.82KB
GPL关于RIP协议的实现代码分析.docx
《GPL关于RIP协议的实现代码分析.docx》由会员分享,可在线阅读,更多相关《GPL关于RIP协议的实现代码分析.docx(16页珍藏版)》请在冰豆网上搜索。
GPL关于RIP协议的实现代码分析
GPL关于RIP协议的实现代码分析
文档编号:
00-6201-100
当前版本:
1.0.0.0
创建日期:
2011-12-22
编写作者:
ganjingwei
RIP代码分析
前言3
关于此文档3
参考资料3
第一章RIP协议报文格式4
1.1报文位置4
1.2RIP版本1报文4
1.3RIP版本2报文5
第二章动态学习过程7
2.1请求与应答7
2.1.1初始化7
2.1.2接收到request7
2.1.3接收到response7
2.2更新8
2.2.1定期选路更新8
2.2.2触发更新8
2.3其他机制与策略9
2.3.1水平分割9
2.3.2定时删除9
前言
关于此文档
此文档是本人这段时间内研究GPL发布的RIP协议源码,总结并且整理出来的文档。
供大家参考。
本文档阐述代码相关研究,各章节说明如下:
1前言,即此章节;
2重要数据结构
3接收与发送处理;
4路由表结构
5线程机制
6ripd配置文件
参考资料
网络资源。
源码以linux2.6内核userspace为准。
第一章数据结构
1.1收发处理相关
1.1.1structrip
structrip
{
/*RIPsocket.*/
intsock;
/*Defaultversionofripinstance.*/
u_charversion;
/*OutputbufferofRIP.*/
structstream*obuf;
/*RIProutinginformationbase.*/
structroute_table*table;
/*RIPonlystaticroutinginformation.*/
structroute_table*route;
/*RIPneighbor.*/
structroute_table*neighbor;
/*RIPthreads.*/
structthread*t_read;
/*Updateandgarbagetimer.*/
structthread*t_update;
/*Triggeredupdatehack.*/
inttrigger;
structthread*t_triggered_update;
structthread*t_triggered_interval;
/*RIPtimervalues.*/
unsignedlongupdate_time;
unsignedlongtimeout_time;
unsignedlonggarbage_time;
/*RIPdefaultmetric.*/
intdefault_metric;
/*RIPdefault-informationoriginate.*/
u_chardefault_information;
char*default_information_route_map;
/*RIPdefaultdistance.*/
u_chardistance;
structroute_table*distance_table;
/*Forredistributeroutemap.*/
struct
{
char*name;
structroute_map*map;
intmetric_config;
u_int32_tmetric;
}route_map[ZEBRA_ROUTE_MAX];
};
这个结构体包含ripd进程的所有信息,一个ripd进程只有一个structrip对象作为全局变量。
structroute_table*table是ripd进程维护的路由表的指针;structthread*类型的数据为进程中的伪线程链表头指针(下文详细描述);此外还包含其他各种信息。
1.1.2structrte
structrte
{
u_int16_tfamily;/*Addressfamilyofthisroute.*/
u_int16_ttag;/*RouteTagwhichincludedinRIP2packet.*/
structin_addrprefix;/*Prefixofriproute.*/
structin_addrmask;/*Netmaskofriproute.*/
structin_addrnexthop;/*Nexthopofriproute.*/
u_int32_tmetric;/*Metricvalueofriproute.*/
};
这个结构体保存rip报文的每个路由信息单元,按照rip协议规定的格式定义。
1.1.3structrip_packet
structrip_packet
{
unsignedcharcommand;/*CommandtypeofRIPpacket.*/
unsignedcharversion;/*RIPversionwhichcomingfrompeer.*/
unsignedcharpad1;/*PaddingofRIPpacketheader.*/
unsignedcharpad2;/*Sameasabove.*/
structrterte[1];/*Addressstructure.*/
};
这个结构体是包含一个路由信息单元的rip报文,也可以把它当做rip报文的首部,因为没有路由信息单元的报文是非法的。
1.1.4unionrip_buf
unionrip_buf
{
structrip_packetrip_packet;
charbuf[RIP_PACKET_MAXSIZ];
};
这个联合体表示一个rip报文,rip_packet表示报文头,buf作为后续的空间,在代码流程中,这个数据通常伴随有一个值来表示其长度。
1.2RIP路由表相关
1.2.1structrip_info
structrip_info
{
/*Thisroute'stype.*/
inttype;
/*Subtype.*/
intsub_type;
/*RIPnexthop.*/
structin_addrnexthop;
structin_addrfrom;
/*Whichinterfacedoesthisroutecomefrom.*/
unsignedintifindex;
/*Metricofthisroute.*/
u_int32_tmetric;
/*Taginformationofthisroute.*/
u_int16_ttag;
/*FlagsofRIProute.*/
#defineRIP_RTF_FIB1
#defineRIP_RTF_CHANGED2
u_charflags;
/*Garbagecollecttimer.*/
structthread*t_timeout;
structthread*t_garbage_collect;
/*Route-mapfutures-thisvariablescanbechanged.*/
structin_addrnexthop_out;
u_charmetric_set;
u_int32_tmetric_out;
unsignedintifindex_out;
structroute_node*rp;
u_chardistance;
#ifdefNEW_RIP_TABLE
structrip_info*next;
structrip_info*prev;
#endif/*NEW_RIP_TABLE*/
};
这个结构体封装一个路由信息的各种元素,rip进程维护一个完整的路由表。
1.2.2structroute_node
structroute_node
{
/*Actualprefixofthisradix.*/
structprefixp;
/*Treelink.*/
structroute_table*table;
structroute_node*parent;
structroute_node*link[2];
#definel_leftlink[0]
#definel_rightlink[1]
/*Lockofthisradix*/
unsignedintlock;
/*Eachnodeofroute.*/
void*info;
/*Aggregation.*/
void*aggregate;
};
这个结构体是RIP进程维护的路由表的表的节点。
路由表是一个二叉树的结构,这个结构体就是二叉树的叶子。
1.3RIP线程相关
1.3.1structthread_list
structthread_list
{
structthread*head;
structthread*tail;
intcount;
};
线程链表。
1.3.2structthread_master
structthread_master
{
structthread_listread;
structthread_listwrite;
structthread_listtimer;
structthread_listevent;
structthread_listready;
structthread_listunuse;
fd_setreadfd;
fd_setwritefd;
fd_setexceptfd;
unsignedlongalloc;
};
顾名思义,这个结构体申明的对象管理所有线程,其中包括读写、计时器、触发、就绪以及废弃的线程,同时也包含用于轮询触发信号的文件描述符。
1.3.3structthread
structthread
{
unsignedchartype;/*threadtype*/
structthread*next;/*nextpointerofthethread*/
structthread*prev;/*previouspointerofthethread*/
structthread_master*master;/*pointertothestructthread_master.*/
int(*func)(structthread*);/*eventfunction*/
void*arg;/*eventargument*/
union{
intval;/*secondargumentoftheevent.*/
intfd;/*filedescriptorincaseofread/write.*/
structtimevalsands;/*restoftimesandsvalue.*/
}u;
RUSAGE_Tru;/*Indepthusageinfo.*/
};
这个结构体实际描述一个伪线程,其中包含了线程要执行的函数,还有一些内嵌链表的指针。
线程要加入哪个队列,只要操作这些指针。
第二章接收与发送处理
2.1请求报文处理
当收到一个rip请求报文(command字段为1),轮询程序(后文介绍)会调用rip_request_process()函数来处理这个报文。
请求报文分为两种,一种是请求整个路由表信息的报文(只有一个单元,度量为16,地址系列为0)。
当收到此类报文时,直接调用rip_output_process()并制定参数发送整个路由表:
rip_output_process(ifp,from,rip_all_route,packet->version)。
另一种是请求某一路由表项的报文。
收到此类报文以后,遍历这个报文的每个单元,分别在路由表中查找每个单元的信息,如果找到则填入报文,找不到就把度量设置为16。
然后调用rip_send_packet()来发送这个填充过的报文。
2.2应答报文处理
当收到一个rip应答报文(command字段为2),轮询程序会调用rip_response_process()来处理这个报文。
这个函数先检查源端口、源地址,如果不符合协议规范就丢弃它,然后进入一个循环来遍历报文中的每个单元。
对于每个单元中的信息,先进行一些检查,并根据路由表的信息对报文信息进行更改(例如已经有的表项,如果是rip的表项,则设置下一跳为源地址),然后进入rip_rte_process()来进行处理。
rip_rte_process()函数中,同样要进行一些检查和封装,然后把rte也就是路由信息作为依据在路由表中查找表项。
这里有两种结果,如果没有找到,则说明这个路由信息是新增的,我们需要在我们的路由表中创建这一项,所以通过rip_info_new()对他进行创建,并初始化它的计时器,注册一个新事件(讲线程机制时会详细说明),然后通过rip_zebra_ipv4_add()把这个新建的表项加入路由表。
如果路由表中已经有关于这个地址的路由,这里就涉及一个简单的最短路径优先算法:
在已有表项和新发来的信息中,选择一条最优的(最短的)。
但是如果这个报文来自于和表项和告知者是同一个地址,那么说明这个路由发生了变化,无论谁最优,我们都需要进行更新。
代码的实现依据这个算法,并在更新路由的同时进行计时器更新和线程注册。
第三章RIP进程路由表
3.1关于路由表
路由表是由内核维护的,作用于ISO层次模型的网络层的,被用于发送和转发数据包的,包含地址信息、度量、接口、源地址等元素的一张散列表。
RIP进程为了提高效率和安全性,本身维护一张路由表的创建删除更新,从而避免了系统调用浪费的时间和新增内核接口造成的安全隐患。
3.2RIP维护的路由表结构
图3-1哈夫曼树
RIP的路由表结构是一个二叉树,类似“哈夫曼树”,如图3-1所示。
路由表结构以前缀(代码中是prefix,其实就是网络掩码)为哈夫曼编码依据,从xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx开始,分支出0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx和1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,逐步类推。
structroute_node表示的就是哈夫曼树的叶子节点,其中structroute_node*link[2]这个变量分别指向此节点的左右两个子节点。
如果一个叶子的左子节点或右子节点不存在(路由表暂时没有关于这个前缀的路由项),那么该指针为NULL。
在这样的结构中,查找一个节点是从树根开始,根据前缀的每一位为0或为1,找到下一个叶子。
这个查找的时间复杂度为ln(n)同阶。
3.3相关操作接口
这里只列出每个函数的功能,并稍微分析其实现,具体设计实现细节以及参数、返回值,见代码。
3.3.1route_table_init()
初始化一张路由表,申请空间。
3.3.2route_table_finish()
删除一张路由表。
3.3.3route_node_new()
为一个节点申请空间。
3.3.4route_node_set()
为一个节点赋值。
3.3.5route_node_free()
释放一个节点的空间。
3.3.6route_table_free()
释放一张表的空间,需要释放每个节点。
3.3.7route_lock_node()
这个函数为一个路由表项加锁。
3.3.8route_unlock_node()
解锁。
3.3.9route_dump_node()
终端打印整个路由表。
3.3.10route_node_match()
在表中找到匹配的前缀节点,执行这个函数如果找到对应节点,会将该节点加锁。
3.3.11route_node_lookup()
这个函数和route_node_match()几乎一样。
3.3.12route_node_get()
这个函数也是在表中查找,但是如果找不到会在适合的位置创建一个节点并返回。
3.3.13route_node_delete()
删除一个节点,如果他的父节点不需要存在,而是为了它而存在,一并删除,递归调用。
第四章RIP伪线程机制
RIP协议中的线程机制是一个“伪线程”机制,它没有创建真正的线程来运行,而是通过一些数据结构、指针的调度,来模拟一个多线程环境。
4.1线程结构
RIP进程通过一个thread_master结构体的对象来管理所有进程。
这个结构体包含6个链表头,read、write、timer、event、ready、unuse。
其中,event队列的执行优先级最高,表示一个触发事件,比如路由表发生了变化;其次是timer队列,这个队列中存放的是计时用的线程;再次是ready队列;然后是read和write队列,这两个队列一起进行轮询。
图4-1RIP线程结构
图4-1简单描述了上述结构。
4.2线程创建与销毁
4.2.1thread_get()
创建一个线程,先从unuse队列中寻找废弃的线程,如果有,直接从unuse队列中移除,如果没有,申请新的空间。
注意这个函数是个静态局部函数,只能给本源文件使用。
4.2.2thread_add_read()
创建一个线程,并加入read队列。
4.2.3thread_add_write()
创建一个线程,并加入write队列。
4.2.4thread_add_timer()
创建一个线程,并加入timer队列。
4.2.5thread_add_event()
创建一个线程,并加入event队列。
4.2.6thread_cancel()
销毁一个线程,将它从对应队列中取出并放入unuse队列。
4.2.7thread_cancel_event()
销毁所有触发事件线程。
4.3RIP进程执行过程
在/userspace/gpl/apps/zebra/ripd/rip_main.c中有如下代码:
intmain(intargc,char**argv)
{
…………………………………………………
/*Executeeachthread.*/
while(thread_fetch(master,&thread))
thread_call(&thread);
…………………………………………………
}
这个循环执行RIP进程,我们来看thread_fetch():
structthread*
thread_fetch(structthread_master*m,structthread*fetch)
{
…………………………………………………
while
(1)
{
/*Normaleventisthehighestpriority.*/
if((thread=thread_trim_head(&m->event))!
=NULL)
returnthread_run(m,thread,fetch);
/*Executetimer.*/
gettimeofday(&timer_now,NULL);
for(thread=m->timer.head;thread;thread=thread->next)
if(timeval_cmp(timer_now,thread->u.sands)>=0)
{
thread_list_delete(&m->timer,thread);
returnthread_run(m,thread,fetch);
}
/*Ifthereareanyreadythreads,processtopofthem.*/
if((thread=thread_trim_head(&m->ready))!
=NULL)
returnthread_run(m,thread,fetch);
/*Structurecopy.*/
readfd=m->readfd;
writefd=m->writefd;
exceptfd=m->exceptfd;
/*Calculateselectwaittimer.*/
timer_wait=thread_timer_wait(m,&timer_val);
num=select(FD_SETSIZE,&readfd,&writefd,&exceptfd,timer_wait);
if(num==0)
continue;
if(num<0)
{
if(errno==EINTR)
continue;
#ifdefBRCM_RIP_DEBUG
zlog_warn("select()error:
%s",strerror(errno));
#endif
returnNULL;
}
/*Normalpriorityreadthead.*/
ready=thread_process_fd(m,&m->read,&readfd,&m->readfd);
/*Writethead.*/
ready=thread_process_fd(m,&m->write,&writefd,&m->writefd);
if((thread=thread_trim_head(&m->ready))!
=NULL)
returnthread_run(m,thread,fetch);
}
}
这个函数主要是一个循环,反复检查上述队列是否为空,如果不为空,则把这个线程从当前队列取出,返回这个线程。
其中event队列最先检查,因为优先级最高,其次是ti
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- GPL 关于 RIP 协议 实现 代码 分析
![提示](https://static.bdocx.com/images/bang_tan.gif)