版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Internet核心原理与应用技术第17章 OSPF路由协议的软件模拟与设计PAGE 4PAGE 82第17章OSPF路由协议(xiy)的软件模拟与设计Internet是一个分组交换网络,一个源节点应用程序所产生的数据分组要到达目的节点的对等层应用程序,通常要经过若干中间交换节点的转发中继,除非源和目的节点既在同一条数据链路上又在同一个子网中。如果转发设备是路由器或三层交换机,则设备中的数据链路层协议负责将待转发数据帧沿给定的数据链路传递到相邻的下一个节点,而网络层协议则要负责路由选择,即从可达目的节点的多条转发链路中选择出一条最佳的数据链路。路由有两种类型:静态路由和动态路由。静态路由即网络
2、管理员手工配置的路由,这种路由有两个主要的缺陷(quxin):一个是不能对路由设备或链路的失效以及网络拓扑的变化做出反应,一个是不适合于具有复杂拓扑结构的大规模网络(可扩展性)。因此,当需要管理的网络不是小型网络时,就需要使用动态路由。动态路由是路由选择协议根据网络中所有路由器及其接口链路的当前状态自动计算生成的。路由选择协议能够自动检测路由设备及其接口链路的状态,能够自动适应网络拓扑的变化。开放最短路径优先协议(OSPF,Open Shortest Path First)是一个基于(jy)链路状态算法、适合大型复杂网络、具有高效健壮特性的动态内部网关协议(工业上将路由器被称为网关)。“开放最
3、短路径优先”的名字来源于以下事实:(1)这个协议基于Dijkstra的最短路径优先(SPF,Shortest Path First)路由算法。(2)“开放”反映了这个标准是开放的标准而不是一个私有的标准,这与一系列由特定厂商专有的或专用于某种特定网络的链路状态协议形成了对比,如Novell Netware的链路服务协议NLSP(Netware Link Servise Protocol)、IBM SNA的高级对等网络协议APPN(Advanced Peer-to-Peer Networking)和ATM的专用网间接口协议PNNI(Private Network-to-Network Inter
4、ace)。作为开放和通用标准运动的主要倡议者DEC公司为该协议做了最初的大部分工作,并把它作为了私有产品DECnet体系结构的一部分。OSPF起源于1979年Bolt、Beranek和Newman公司为ARPANET开发的“新的实验性路由算法”以及随后ISO为OSI开发的IS-IS路由协议,是John T. Moy等人在1987年为克服RIP协议耗费带宽资源多、应对链路故障或拓扑变化收敛速度慢、适应树状拓扑结构难等缺点而开发的。OSPF最初的其它功能需求还包括:(1)采用更具描述性的路由度量值,以摆脱RIP协议网络直径最大15个路由器跳步的限制,以及可以将延时、带宽等其它路由度量因素考虑进来。
5、(2)可利用等代价的多条路径,以达到均衡负载等转发策略。(3)具有路由选择的层次(cngc)结构,以适应大规模复杂网络以及树状层次网络拓扑结构。(4)支持(zhch)更灵活的子网化技术,以适应变长子网掩码以及更进一步的无类别域间路由。(5)保证安全性,避免伪路由器通过发布默认或其它路由而使路由选择(xunz)过程陷入混乱。(6)为保证可信性而区分内部和外部路由信息,内部路由信息可覆盖外部路由信息。虽然OSPF还支持基于服务类型TOS的路由选择,允许为每个不同的TOS配置不同的链路度量值并建立不同的路由表,但基于TOS的路由选择从未在Internet上流行。OSPF的各个版本间是不兼容的。第1版
6、的RFC 1131定义于1989年,目前的第2版在RFC 2328中有详细说明,1999年12月,IETF发布了基于IPv6的OSPF标准RFC2740,并称之为OSPFv3。17.1 对等层协议模拟基础知识17.1.1 标识符命名和编写规范标识符命名和编写规范为了使程序易于理解和维护,在一个项目中的代码编写要遵循统一的规范。由于这种规范并不是强制的,因此,有时也将其称为风格。在此只对本章程序代码中结构、类、函数、变量、常量等标识符在命名时所遵循的规范和保持的风格进行说明。标识符命名有两种风格:Unix风格和Windows风格。以网络接口(Network Interface)的数据结构为例,按
7、照两种风格定义的数据结构比较如下: Unix风格 Windows风格 struct ifnet struct IfNet struct ifnet *if_next; struct ifaddr *if_addrlist; struct IfNet* m_pNext; char *if_name; struct IfAddr* m_pAddrList; char* m_szName; struct ifqueue struct IfQueue struct mbuf *ifq_head; struct Mbuf* m_pHead; if_snd m_Send 综合上述两种标识符命名风格,本章(
8、bn zhn)的程序代码采用了如下的标识符命名规范:结构、类和函数的标识符均要包括一个标识其所属功能层次(cngc)或程序包的前缀,前缀和标识符以下划线“_”隔开( ki),如PHY_、DRV_、DEV_、ARP_、OSPF_、SNMP_等。另外,组成标识符的每个英文单词的首字母大写,其余字母小写。常量标识符均采用大写字母。另外,函数返回的提示性常量标识符均添加“RV_”前缀。例如:const int RV_OK 1const int RV_FAILURE 0在协议分组格式的设计中,为了使协议分组在常见的计算机体系结构下易于处理,协议分组中各字节通常都是对齐的:4字节字段从偏移量为4的倍数的字
9、节单元开始,而2字节字段从偏移量为偶数的字节单元开始。排列整齐的分组使得在协议实现时可以采用诸如“分组模板”等数据结构,从而使分组收发程序的实现得以简化,效率也得以提高。为了便于将描述协议分组的数据结构与分组格式相对照,从而使之更容易阅读和理解,在通用头文件中定义了常量标识符BITS_8、BITS_16和BITS_32。#ifndef BITS_8#define BITS_8 unsigned char#endif#ifndef BITS_16#define BITS_16 unsigned short int#endif#ifndef BITS_32#define BITS_32 unsig
10、ned long#endif变量标识符中单词均使用小写字母,结构和类的成员变量名前加“m_”前缀。除了指针和句柄变量名前必须分别添加前缀“p-”和“h-”外,其余变量名前通常不加表示数据类型的前缀(如数值前缀“n-”,布尔前缀“b-”,字符串前缀“sz”,等等),只有在通过以下两种方式无法标识变量的数据类型时,才采用标注前缀的方法:一种方式是通过变量名中包含的特征单词来标识数据类型,例如,包含size、number、length、count、interval、time、index等单词的变量均为数值变量,包含单词name、path的变量总是字符串变量,布尔变量名均以助词is或do开始。另外一种
11、方式是通过 “就近声明”的原则来省略数据类型前缀。对于过长或过多单词构成的标识符,使用了通用和易理解的单词缩写或单词首字头缩写形式。这样做是为了(wi le)使学生熟悉规范代码中的简写形式,从而有助于学生去阅读源代码。循环变量(binling)使用字母i及其组合(zh)(如ii和iii,字母i的重复次数表示循环嵌套层数),或i、j和k分别表示循环嵌套的第1、2和3层。循环嵌套层次不得超过3层。出于占用篇幅和易于阅读的考虑,在描述函数的实现时,构造函数、析构函数和一些实现代码比较简单的类成员函数都在头文件的类定义中进行了描述。但在实际的课程设计中,还是应该将其定义在实现文件中。另外,许多语句的编
12、写没有遵循统一的风格也是出于同样的原因。如有的条件语句是分行编写的,有的则直接编写在了同一行上。在实际实现时,都应分行编写。17.1.2 进程间通信机制和线程间同步机制在进行模拟实现时,每台网络设备通过一个单独运行的进程来模拟,而网络设备上的每个驱动程序则通过进程下各个单独运行的线程来模拟。这样,就涉及到数据包在模拟不同设备的进程之间和在模拟不同协议层驱动程序的线程之间的传递问题。由于在单进程多线程环境中诸线程共享单进程的内存空间,因此,模拟不同协议层驱动程序的线程之间的数据传递问题并不难解决,只要将多线程共享的内存资源定义为进程中的全局变量即可,这时不需要进程间的通信机制。但在多进程环境中,
13、每个进程都有自己的运行空间,不同进程的运行空间是相互隔离的,因此,数据在不同进程之间的传递则要通过各种进程间通信机制(IPC,InterProcess Communication)来实现。在模拟实现中,该问题是通过内存映射文件(MMF,Memory Mapping File)这一进程间通信机制来实现的。MMF缓冲区共享方案的原理是:首先,在一个单独的进程中调用函数CreateMMF生成一个由系统页文件支持的被命名的共享缓冲区。接着,需要使用缓冲区的进程用生成该缓冲区时所使用的名字调用函数OpenMMF打开共享缓冲区,并通过函数GetMMFView将其映射到自己的进程地址空间中,就可以使用该缓冲
14、区与其它进程交换数据了。注意,无论是生成还是打开共享缓冲区的进程,最后都必须调用FreeMMF,以释放文件映射(映射到内存页)句柄和文件视(映射到进程空间)句柄。在模拟实现中,线程间的同步问题是采用事件通知的方式解决的。其中,处理数据的线程调用函数WaitForEvent睡眠等待一个命名事件(Named Event);提供数据的线程将数据送入队列后,调用函数SignalEvent通知该命名事件,唤醒因等待数据而睡眠的线程。os.cpp _ #ifndef _OS_H_#define _OS_H_#include HANDLE CreateMMF(char* name, unsigned int
15、 size) / Create a paging file-backed MMF. HANDLE hFileMappingObject = NULL; hFileMappingObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, TEXT(name); if (!hFileMappingObject) return NULL; if (GetLastError() = ERROR_ALREADY_EXISTS) CloseHandle(hFileMappingObject); return
16、 hFileMappingObject;/HANDLE OpenMMF(char* name) / Map a view of the file into the address space. HANDLE hFileMappingObject = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, TEXT(name); if (!hFileMappingObject) return NULL; return hFileMappingObject;/PVOID GetMMFView(HANDLE hFileMappingObject)
17、 / map The MMF into the processs address space. PVOID pView = MapViewOfFile(hFileMappingObject, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (!pView) return NULL; return pView;/void FreeMMF(HANDLE hFileMappingObject, PVOID pView) if (pView) UnmapViewOfFile(pView); if (hFileMappingObject) CloseHandle
18、(hFileMappingObject);/void WaitForEvent(char* peventname) / 以0结束表明(biomng)事件名的字符串 LPSECURITY_ATTRIBUTES pinherited = NULL; BOOL ismanualreset = TRUE; BOOL isinitstatesignaled = FALSE; HANDLE hevent = CreateEvent(pinherited, ismanualreset, isinitstatesignaled, peventname); if (hevent = NULL) return;
19、DWORD milliseconds = INFINITE; WaitForSingleObject(hevent, milliseconds); CloseHandle(hevent); return;/void SignalEvent(char* peventname) DWORD desiredaccess = EVENT_ALL_ACCESS; BOOL is_inherited = FALSE; HANDLE hevent = OpenEvent(desiredaccess, is_inherited, peventname); if (hevent = NULL) return;
20、SetEvent(hevent); CloseHandle(hevent);/#include inline unsigned long BeginThreadEx(unsigned ( _stdcall *pFuncName )( void* ) PSECURITY_ATTRIBUTES psa = NULL; unsigned cbStackSize = 0; void *pvParam = NULL; unsigned initfalg = 0; unsigned threadID; unsigned *threadaddr=&threadID; return _beginthreade
21、x(psa, cbStackSize, pFuncName, pvParam, initfalg, threadaddr);#endif_ os.cpp17.1.3 通用(tngyng)的数据结构和函数 1内存(ni cn)缓冲区 网络协议对于内存缓冲区的管理提出了许多要求,这些要求包括:能够很容易地处理变长缓冲区;当低层协议对来自高层(o cn)协议的数据进行封装时,要能够很容易地在缓冲区的头部或尾部添加数据;相反,当数据包在协议栈中向上传递时,要能够很容易地对缓冲区中的协议头结构进行定位;另外,在这些操作过程中要尽可能地减少数据的复制。内存缓冲区的管理方式直接关系着网络协议的性能。 通过对
22、开放源代码操作系统实现中所使用的缓冲区管理方式的研究,在本篇网络协议的模拟实现中使用了一个MBuf结构及其相关函数,来对不同协议层间传递的数据块进行操作。与该结构有关的函数提供了申请数据缓冲区和设置/获取各协议层头指针的接口。由于数据缓冲区是一次性申请和分配的,所以,在发送数据时要根据实际需要估算所需的内存空间。内存空间按照三种规格进行分配:128字节的缓冲区用于较小的数据帧,如ARP请求应答帧、TCP握手控制帧等;2048字节的缓冲区用于一般的数据帧,如以太帧最大长度为1518字节;而65535字节的缓冲区通常用于高层协议封装大量数据时使用,如IP数据包的数据部分最大长度可达65535字节。
23、分配的缓冲区中已经包含了用于协议头结构的空间。mbuf.h_#ifndef _MBUF_H#define _MBUF_Hstruct Mbuf / Mbuf是为操作在协议层间传递的内存数据块而定义的数据结构 unsigned int m_size; / 缓冲区大小 #define MBUF_SIZE_SMALL 128; #define MBUF_SIZE_MIDDLE 2048; #define MBUF_SIZE_LARGE 65535; unsigned int m_datasize; / 服务数据单元SDU的大小 char* m_pdatahead; / 指明SDU/PDU的开始位置
24、char* m_pdatatail; / 指明SDU/PDU的结束位置 int m_direction; #define MBUF_DIRECTION_UP 1 / 用于向协议栈上层传递(chund)的数据块 #define MBUF_DIRECTION_DOWN 2 / 用于向协议栈下层(xicng)传递的数据块 struct DEV_NetIf* m_pif; / 接口层填写(tinxi)收到该数据的接口指针 unsigned long m_ip; / IP层填写源IP地址 BITS_16 m_flag; #define MBUF_FLAG_BCAST 0 x0001 / 广播 #defi
25、ne MBUF_FLAG_MCAST 0 x0002 / 多播;#endif_ mbuf.h在内存缓冲区的实现中,除了申请缓冲区的函数MBuf_Get外,其它函数都以一个MBuf缓冲区指针作为第一个参数。通过修改函数MBuf_Get和函数MBuf_Free的实现,还可以使该结构用于其它类型的缓冲区。如上述的用于进程间进行数据传递的MMF缓冲区。mbuf.cpp_ / 函数MBuf_Get供最初收到或生成数据的例程调用,以分配缓冲数据和协议控制信息/ 的内存块。该数据块在协议层间传递,最后处理数据的协议层负责释放该内存块。struct Mbuf* MBuf_Get(unsigned int si
26、ze, int direction) struct Mbuf* ptr = NULL; / MBuf首地址 unsigned int bytes = 0; / 初始化数据指针时从尾部计算的字节数 if (size m_size = MBUF_SIZE_SMALL; bytes = 32; else if (size m_size = MBUF_SIZE_MIDDLE; bytes = 64; else if (size m_size = MBUF_SIZE_LARGE; bytes = 128; ptr-m_datasize = 0; if (direction = MBUF_DIRECTIO
27、N_UP) / 向上层协议传递(chund)的数据块 ptr-m_datahead = ptr-m_datatail = ptr + sizeof(struct MBuf); else if (direction = MBUF_DIRECTION_DOWN) / 向下层协议(xiy)传递的数据块 ptr-m_datahead = ptr-m_datatail = ptr + ptr-m_size - bytes; ptr-m_flag = ptr-m_flag & 0 x00000000; return ptr;/void MBuf_Free(char* pmbuf) / 释放(shfng)不
28、再使用的空间。 if (pmbuf ) delete pmbuf;/ 函数MBuf_PrependHdr用于添加数据或协议头int MBuf_PrependHdr(struct MBuf* pmbuf, char* pbuf, unsigned int size ) int rv = RV_OK if (pmbuf-m_pdatahead pmbuf sizeof(struct MBuf) size ) / 前面有足够空间 pmbuf-m_pdatahead = size; / 为复制调整头指针 memcpy(pmbuf-m_pdatahead, pbuf, size); / 复制数据或协议头
29、 pmbuf-m_datasize += size; / 加上协议头长度 else rv = RV_MBUF_NOROOM; return ptr;/ 函数(hnsh)MBuf_AppendTail用于添加(tin ji)协议尾,如校验字节等。int MBuf_AppendTail(struct MBuf* pmbuf, char* pbuf, unsigned int size ) int rv = RV_OK if (pmbuf-m_datatail + size) m_size) / 后面有足够(zgu)空间 memcpy(pmbuf-m_pdatatail, pbuf, size);
30、/ 复制协议尾 pmbuf-m_pdatatail += size; / 调整指针以包括协议尾部 pmbuf-m_datasize += size; / 加上协议尾部的长度 else rv = RV_MBUF_NOROOM; return ptr;/int MBuf_GetPDUSize(struct MBuf* pmbuf) / 返回当前协议层的PDU大小。 return (pmbuf-m_datatail pmbuf-m_pdatahead);/char* MBuf_GetHdrPtr(struct MBuf* pmbuf) / 获得本层协议头指针。 return pmbuf-pdatah
31、ead;/ 函数MBuf_AdjustHdrPtr为向上层协议传递数据块而调整指针,即将指针调整到/ 上层协议数据的开始处。int MBuf_AdjustHdrPtr(struct MBuf* pmbuf, unsigned int size) int rv = RV_OK; if (pmbuf-m_pdatahead + size) pmbuf-m_pdatatail) rv = RV_MBUF_SIZETOOLARGE; else pmbuf-m_pdatahead += size; return rv;/ 函数(hnsh)MBuf_SetNetIfPtr用于设置(shzh)指向接收数据包
32、的网络接口的指针void MBuf_SetNetIfPtr(struct MBuf* pmbuf, struct DEV_NetIf* pif) m_pif = pif;/ 函数(hnsh)MBuf_GetNetIfPtr用于获取指向接收数据包的网络接口的指针struct DEV_NetIf* MBuf_GetNetIfPtr(struct MBuf* pmbuf ) return m_pif;/ 函数MBuf_SetSrcIPAddr用于设置接收到的数据包的源IP地址void MBuf_SetSrcIPAddr(struct MBuf* pmbuf, unsigned long srcip)
33、 pmbuf-m_ip = srcip;/ 函数MBuf_ GetSrcIPAddr用于获取接收到的数据包的源IP地址unsigned long MBuf_GetSrcIPAddr(struct MBuf* pmbuf ) return pmbuf-m_ip;/void MBuf_SetFlag(struct MBuf* pmbuf, int flag) / 设置数据帧为广播或多播帧。 m_flag |= flag;/int flag MBuf_GetFlag(struct MBuf* pmbuf) / 获取广播或多播帧标志。 return (pmbuf-m_flag);_ mbuf.cpp
34、2队列(duli)和缓冲表相邻协议层间的数据传递接口是队列。此外,协议实现中还经常(jngchng)要用到列表和缓冲(hunchng)表。在模拟实现中用同一个类Cache来完成这三种数据结构的功能。这里没有使用C+ STL库中的模板类list,主要是因为该类没有重载下标运算符“”,且通过迭代子进行循环操作的方式不容易理解。在实现中应该注意,模板类的定义和实现必须在同一个文件中,否则,无法进行编译。cache.h_#ifndef _CACHE_H_#define _CACHE_H_#include rv.h#define RV_CACHE 2 / 避免绝对性的宏定义#define RV_CACH
35、E_ALLOCERROR RV_CACHE + 1#define RV_CACHE_FULL RV_CACHE + 2template class Cache public: struct LinkList struct LinkList* m_next; / 下一个表项的指针 struct LinkList* m_prev; / 前一个表项的指针 T m_entry; / 在抽象数据类型T中有指针成员时,要 / 注意必须实现赋值操作符= *m_head; / 表头指针 int m_entrynum; / 当前链表中表项个数 int m_size; / 用户设置的链表大小 #define CA
36、CHE_SIZE_INFINITE 0 /不设置相当于INFINITE / 构造函数和析构函数 Cache(); Cache(int size); Cache(); / 操作(cozu)函数 const T& operator(const unsigned int index); / 下标(xi bio)操作符重载 int Add(const T entry, int pos); / 添加(tin ji)一个表项 #define CACHE_ADD_POS_HEAD 0 #define CACHE_ADD_POS_TAIL 1 void Remove(unsigned int index);
37、/ 删除一个表项 #define Prepend(a) Add(a, CACHE_ADD_POS_HEAD); / 添加表项到表头 #define Append(a) Add(a, CACHE_ADD_POS_TAIL); / 添加表项到表尾 unsigned int GetEntryNum(); / 获得链表中表项个数 bool IsEmpty(); bool IsFull(); #define Enqueue(a) Append(a); / 入队列 #define Dequeue() Remove(0); / 出队列 #define Push(a) Prepend(a); / 入栈 #de
38、fine Pop() Remove(0); / 出栈 const T& Top(); / 读队首/栈顶表项 void Clear(); / 清除所有表项;/template Cache:Cache() : m_head(NULL), m_entrynum(0), m_size(0) /template Cache:Cache(int size) : m_head(NULL), m_entrynum(0), m_size(size) /template Cache:Cache() if (m_entrynum 0) Clear(); /template const T& Cache:operat
39、or(const unsigned int index) struct LinkList* op = m_head; / 元素0 if (index =0 & index m_entrynum) for (unsigned int i = 0; i m_next; else op = m_head; / 若下标越界则返回第一个元素 return op-m_entry;/template int Cache:Add(const T entry, int pos) f (m_size 0 & m_entrynum = m_size) return RV_CACHE_FULL; struct Lin
40、kList *tmp = new LinkList; / 新节点(ji din) if (tmp = NULL) return RV_CACHE_ALLOCERROR; tmp-m_entry = entry; if (m_head = NULL) / 第一个元素(yun s)(空链表) tmp-m_next = tmp; tmp-m_prev = tmp; m_head = tmp; else struct LinkList* op = m_head-m_prev; / 新节点插入(ch r)到链表尾部 tmp-m_next = op-m_next; tmp-m_prev = op; op-
41、m_next-m_prev = tmp; op-m_next = tmp; if (pos = CACHE_ADD_POS_HEAD) / 调整新节点为链表首部 m_head = tmp; m_entrynum+; return RV_OK;/template unsigned int Cache:GetEntryNum() return m_entrynum; /template bool Cache:IsEmpty() return (m_entrynum = 0); /template bool Cache:IsFull() return (m_entrynum = m_size); /
42、template void Cache:Remove(unsigned int index) if (m_entrynum m_entrynum ) return; struct LinkList* op = m_head; if (index = 0 & m_entrynum =1) delete op; op = NULL; m_head = NULL; else if (index m_entrynum / 2 ) for (unsigned int i = 0; i m_next; else for (unsigned int i = 0; i m_prev; op-m_prev-m_
43、next = op-m_next; op-m_next-m_prev = op-m_prev; if (index = 0) m_head = op-m_next; / m_entrynum1 delete op; m_entrynum-;/template const T& Cache:Top() return m_head-m_entry;/template void Cache:Clear() while (m_entrynum 0) struct LinkList* op = m_head; op-m_next-m_prev = op-m_prev; op-m_prev-m_next
44、= op-m_next; m_head = op-m_next; delete op; m_entrynum-; m_head = NULL;#endif_ cache.h 3网络接口数据结构(sh j ji u)DEV_NetIf用于描述(mio sh)模拟实现中使用TCP/IP协议(xiy)簇的网络接口。在实际环境中可以使用多种协议簇,如OSI协议簇、UNIX协议簇等。因此,操作系统中实际的网络接口数据结构与这里DEV_NetIf在协议簇地址的描述上有所不同。即使是对TCP/IP协议簇。这里也只考虑了一个接口只具有一个IP地址的情况,实际情况通常是可以将多个IP地址指定给同一个网络接口。另
45、外,在DEV_NetIf结构中只是描述了部分统计量。dev_netif.h_#ifndef _DEV_NETIF_H#define _DEV_NETIF_Hstrcut DEV_NetIf / 网络接口 #define DEV_NETIF_MAXNAMELEN 32 char* m_nameDEV_NETIF_MAXNAMELEN; / 接口名,如eth0, lo 等 unsigned short m_index; / 接口号 BITS_32 m_ip; / 网络接口IP地址 BITS_32 m_mask; / 网络接口子网掩码 BITS_32 m_bcastaddr; / 广播地址 unsi
46、gned int m_physaddrlen; / 物理地址长度 #define DEV_NETIF_MAXPHYSADDRLEN 16 char* m_physaddrDEV_NETIF_MAXPHYSADDRLEN; / 物理地址 BITS_32 m_state; / 接口状态 #define DEV_NETIF_STATE_UP 0 x0001 / 启用 #define DEV_NETIF_STATE_RUNNING 0 x0002 / 已初始化 #define DEV_NETIF_STATE_ACTIVE 0 x0004 / 网卡正在(zhngzi)发送帧 #define DEV_NE
47、TIF_STATE_BCAST 0 x0008 / 允许(ynx)广播 #define DEV_NETIF_STATE_MCAST 0 x0010 / 允许(ynx)多播 #define DEV_NETIF_STATE_PROMISC 0 x0020 / 混杂模式 BITS_32 m_property; / 接口特性 #define DEV_NETIF_PROPERTY_BCAST 0 x0001 / 支持广播 #define DEV_NETIF_PROPERTY_MCAST 0 x0002 /支持多播 #define DEV_NETIF_PROPERTY_SIMPLEX 0 x0004 /不
48、接收自己发出的帧 #define DEV_NETIF_ PROPERTY_PROMISC 0 x0008 /支持混杂模式 BITS_32 m_type; / 接口链路类型 #define DEV_NETIF_TYPE_OTHER 0 x0001; #define DEV_NETIF_TYPE_ETHER 0 x0002; / 以太II #define DEV_NETIF_TYPE_IEEE802_3 0 x0004; / IEEE802_3 #define DEV_NETIF_TYPE_802_3 SNAP 0 x0008; / SNAP #define DEV_NETIF_TYPE_LOOP
49、BACK 0 x0010; / LOOPBACK #define DEV_NETIF_TYPE_SERIAL 0 x0020; / 串行接口 unsigned long m_mtu; / 网络接口最大传输单元 unsigned long m_speed; / 网络接口速度, bit/s / 网卡DMA操作变量 char* m_phostbuf_out; / 帧输出缓冲区指针 char* m_phostbuf_in; / 帧输入缓冲区指针 unsigned long m_usedtxbufsize = 0; / NIC RAM已用输出缓冲区大小 unsigned long m_maxtxbufs
50、ize = PHY_NICMAXBUFFERSIZE/4; / 参看xxx / 统计量 unsigned long m_pktrecvd; / 已收包数 unsigned long m_pktsent; / 已发包数 unsigned long m_outbytes; / 已发字节数 unsigned long m_inbytes; / 已收字节数 unsigned long m_inerrors; / 接收错误数 Cache m_outq(256); / 上层协议的包输出队列 int m_mutex; int (*DEV_NetIfInit)( int); / 接口初始化例程 /压帧入队列例
51、程 int (*DEV_NetIfOutput)( struct DEV_NetIf*, strcut Net_SocketAddr*, char*); / 启动帧输出例程 int (*DEV_NetIfStart)( struct DEV_NetIf*, strcut Net_SocketAddr*, char*); ;#endif_ dev_netif.h数据结构(sh j ji u)DEV_NetIf必须在初始化后才能够被使用。在操作系统中,内核首先定位(dngwi)所连接的网络接口设备,并调用网络驱动程序中的设备初始化函数。在模拟实现中,可先将网络接口设备信息保存在文件中,每当启动了一
52、个模拟网络设备的新进程时,就从文件中读出相应于该进程的网络接口设备信息来完成其初始化工作。 4定时器几乎每个协议(xiy)都会包含定时器事件及其处理函数。在模拟实现中没有使用系统提供的定时器,这主要是出于两个原因:一是系统提供的定时器的精度只能达到ms级,而像CSMA/CD这样的协议却要求定时器的精度达到级;二是Windows的API参数通常都较多,使用起来较为烦琐。为此,在模拟实现中专门设计了一个定时器函数,精度可达到级,只要将定时时间和时间单位作为参数传递给该函数即可。os.h_VOID CALLBACK TimerAPCProc(LPVOID lpArgToCompletionRouti
53、ne, DWORD dwTimerLowValue, DWORD dwTimerHighValue) / 为定时器唤醒作用而设置的空函数 enum TimeUnit timeunit_us = 10, / 微秒,1000ns timeunit_ms = 10000, / 毫秒,1000000ns timeunit_s = 10000000 / 秒;void Timer(unsigned int waitime, enum TimeUnit unit) HANDLE htimer = CreateWaitableTimer(NULL,FALSE,NULL); LARGE_INTEGER time
54、; time.QuadPart = -(signed int) waitime * unit); SetWaitableTimer(htimer, &time, 0, TimerAPCProc, NULL, FALSE); SleepEx(INFINITE,TRUE); CloseHandle(htimer);_ os.h17.2OSPF基本概念和原理(yunl)OSPF是为Internet路由体系结构和IP路由寻址方案而设计的,专用于自治系统内部。OSPF协议由两个(lin )主要部分组成:“Hello协议(xiy)”和“可靠泛洪”。Hello协议用于发现邻居、检测链路是否双向活动可用以及为
55、多点访问网络推举指定路由器和后备指定路由器;可靠泛洪则用于同步OSPF区域内所有路由器上的链路状态数据库,一个OSPF区域内的所有路由器都有一致的视图是OSPF协议运行的基础。17.2.1链路状态和OSPF图论模型所谓“链路”是指通过各种不同类型的链接技术连接各个路由设备接口而形成的数据传输通路。常见的链接技术有电话线、以太网、无线分组网、卫星线路、帧中继、ATM、FDDI、令牌环、SMDS以及AppleTalk等。TCP/IP可以通过定义好的方法运行在上述的任何数据链路上。一条数据链路是有方向的,从某个OSPF路由器的角度来看,其接口链路属于哪个OSPF区域,该链路的另一端连接的是哪个路由器
56、或网络,该链路以某种尺度衡量时的度量值(称为链路的代价、开销或费用),该链路是否活动可用,等等,这些关于链路的属性信息就称为“链路状态”。其中,作为网络操作者参考的度量尺度可以是跃距、带宽、延迟、成本、负载、可靠性、拥塞程度等。一条链路两个方向上的链路代价不一定相同。路由器外发接口上的链路代价可能是由网络管理员在配置路由器时指定的一个值,也可能是在带宽、延迟、负载、可靠性等基础上自动计算的结果。通过自动计算得到的值可能会随着随着链路延迟、负载和可靠性的变化而变化。链路代价为一个无量纲的参数,这个值越小表明这条路径越“短”。 当有多条等值的路径到达同一目标时,数据流量将在这些路径上平均分摊。 对
57、于物理网络通常用图论模型来描述。从图论拓扑学的观点来看,网络中所有的设备都可以抽象为“点”,通信链路可以抽象为“线”,从而形成由点、线构成的几何图形网络拓扑图。在OSPF图论模型中,每个节点对应一个路由器、网络或主机,每条有向链路对应一条有向边,从而构成了OSPF路由拓扑图。路由算法是要在最低代价意义上确定到达每个目标节点的最短路径,路由协议则为路由器之间共享路由信息并通过路由算法计算路由表提供基础。为了计算最短路径,OSPF路由拓扑图中的每条有向边都标记了与链路代价相对应的权值,路径代价就是从源节点沿该路径到达目的节点所经过的所有有向边上的权值之和。当源和目的节点之间存在多条等代价路径的时候
58、,OSPF路由器可以根据转发策略在多条链路上进行负载均衡。另外,所有OSPF路由器都设置有默认链路代价。许多厂商设置的默认值与RIP的度量值相一致为1,即选择跃距最小的路由。但也有厂商将默认代价设置为与链路的传输带宽成正比。在OSPF协议中有两种类型的链路:(1)点到点链路。如直接连接两台路由器的串行线链路。(2)多点访问链路。又分为广播式多点访问链路(如连接到同一以太或令牌LAN的路由器之间的共享总线链路)和非广播多点访问链路(NBMA,NonBroadcast Multiple Access。如X.25、帧中继或ATM网络(wnglu)中路由器之间由PVC或SVC构成的全通网格中的虚链路)
59、。这两种类型(lixng)的数据链路及其对应的OSPF路由拓扑图如图17.1所示。 (a)点到点链路及其OSPF路由拓扑图 (b)多点访问(fngwn)链路及其OSPF路由拓扑图图17.1 两种链路类型及其对应的OSPF路由拓扑图需要指出的很重要的一点是,从网络到路由器的链路代价总是为0,这是因为分组在从一台路由器通过LAN转发到另一台路由器的这个过程中,只经过了一次路由选择,因此,路由代价只是第一台路由器的外发接口上的代价。对于图17.1(b)所示的多点访问链路来说,为什么不像图(a)那样去掉表示网络的节点N1、N2和N3,而将路由器R1、R2和R3直接连接在一起呢?换言之,为什么要区分为这
60、样两种链路呢?这是因为在OSPF协议中,任何通过点到点链路连接的两个路由器都既是邻居(即经过跃距1可达)又都相互间具有邻接关系(即它们相互之间要交换路由信息),而通过多点访问链路连接的两个路由器是邻居,但相互间却不一定具有邻接关系(即它们相互之间不一定会交换路由信息)。只有建立了邻接关系的路由器之间才相互交换路由信息。对于一条连接n个路由器的多点访问链路来说,如果所有路由器之间都建立邻接关系,则会有n(n-1)/ 2对邻接关系,如图17.2(a)所示。这样,为实现每对路由器之间的数据库同步和邻接关系的维持会产生大量的数据流,占用大量的带宽、内存和计算资源。在OSPF协议中,通过将LAN中的某个
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年太原客运资格证必考题答案
- 重庆三峡学院《酒店人力资源管理原理》2021-2022学年第一学期期末试卷
- 重庆人文科技学院《幼儿舞蹈表演及创编》2022-2023学年第一学期期末试卷
- 重庆人文科技学院《学前教育学》2021-2022学年第一学期期末试卷
- 重庆人文科技学院《西方音乐史》2023-2024学年第一学期期末试卷
- 重庆人文科技学院《程序设计与实践》2022-2023学年期末试卷
- 重庆三峡学院《计算机控制技术》2022-2023学年期末试卷
- 重庆三峡学院《机械测试技术课程设计》2023-2024学年第一学期期末试卷
- 重庆三峡学院《行政法与行政诉讼法》2022-2023学年期末试卷
- 重庆三峡学院《电路分析基础实验》2021-2022学年期末试卷
- 湖北省盐业调查
- (完整PPT)半导体物理与器件物理课件
- ASTM B366 B366M-20 工厂制造的变形镍和镍合金配件标准规范
- 汽车维修工时收费标准二类企业
- JIS G4304-2021 热轧不锈钢板材、薄板材和带材
- 钢筋直螺纹连接课件PPT
- 小学综合实践活动《认识校园植物》优秀PPT课件
- 变压器专业词汇英文翻译
- 藏传佛教英文词汇
- 铁路杂费收费项目和标准
- 定量订货与定期订货习题练习.
评论
0/150
提交评论