uip协议栈,下载

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

竭诚为您提供优质文档/双击可除
uip协议栈,下载
篇一:uip之udp应用笔记
千兆网项目中,移植了uip到mcu中,采用udp通信方式,主要用来做一些控制协议的处理。

刚开始接手的时候,并没有做过网络方面的应用,而且对tcp/ip及udp通信又不太熟悉。

好在网上有一些文档,加上仔细阅读uip_process 代码,一边用抓包软件一边调试,总算把uip很好的应用了起来,而且还针对项目某些应用的特殊性,对uip源码进行了一些修改。

本文前半部分对uip源码的一些重要函数进行介绍,后半部分将对修改的部分做个记录,以备往后查阅。

本次使用的是uip-1.0,抓包软件用的wireshark1.6.7,这个软件真的很不错,居然支持gigevision,这点真的很意外。

一、一个完整的udp数据报文格式
其实uip就是将你要发送到网络上的数据加上报头,好让它被成功发送到目的主机。

所以我们要先搞清楚一个完整的数据报文,才能搞清楚uip到底在做些什么。

ethernetheader:由目标mac和本机mac及type组成,
共14byte,当目标mac全为ff时,表示是udp广播。

type=0x0800表示是ip。

在uip中,ethernetheader结构体定义如下:
ipheader:0x45表示version=4,headerlength=20byte;0028表示ipheader+udpheader+userdata长度为40byte;
6c14为包的id,每发一个包,这个id会自加1。

80的意义是timetolive,表示这个包的存活时间,路由每转发一次,就会对它自减1。

17表示通信协议类型为udp,4a0a为ipheader的校验码。

再后面就是源ip和目的ip地址了。

udpheader:0aaa表示srcport为2730;0f74表示dstprot为3956;14表示udpheader+userdata长度为20byte,c477表示udpheader的校验码,在一般的情况下,这个可以为0。

在uip中,ipheader和udpheader结构体定义如下:
userdata:再后面就是用户的数据了。

二、aRp数据报文格式
网络中是使用ip来标识主机的,而数据链路层的第一
道关卡是mac地址。

因此ip和mac有一张动态映射表,而
这张表就是由aRp协议来建立并维护的。

下面是一个aRp数据报文。

同样的,前面14byte是ethheader。

hardwaretype对
于以太网来说为0001;0800表示是ipV4;06表示mac地址
的长度;04表示ip地址的长度;0001表示是arp请求,如果是0002那就是aRp应答了。

后面就是发送方的mac地址,ip地址,接收方的mac地址,ip地址。

在ethheader中,目的mac全是ff,则表示这是一个广播,所有网络里的主机都能收到。

但只有ip地址为
192.168.1.11的主机才会应答。

实际上,这个aRp包的意义就是在获取ip为192.168.1.11主机的mac地址。

网络里,如果114要与11进行通讯,但又不知道它的mac地址,那么就发一个这样的报文,11收到后会向114发一个aRp应答,114收到应答后,会将11的mac保存在动态映射表中。

当然这个表会动态的更新,也就是说11的mac 会有一个生成时间,当超时后,就要重新建立。

以上介绍的是一种情况,还有一种情况是ip冲突的检测,这个正好本项目中用到,后面会有介绍。

三、uip源码简介
主要介绍如下三个文件。

uip_arp.c:arp的实现;
uip.c:uip_process实现;
tapdev.c:网卡的底层读写调用。

四、建立一个连接
uip_udp_new是用来建立一个udp连接的,入口参数是远程的ip地址和远程的端口。

uip_udp_new函数将远程ip
和端口写入到uip_udp_conns数组中的某一个位置,并返回它的地址。

系统中支持的最大连接数量就是这个数组的大小,可通过uip_udp_conns宏来定义它的值。

当网卡收到数据时,uip_process会遍历uip_udp_conns 数组,如果当前包的目的端口与本机端口不匹配,或者远程端口与uip_udp_new中的端口不匹配,那么uip会直接丢弃这个包。

如下例,建立的是一个目的ip为255.255
.255.255,目的端口为1234的连接,本地端口
号为5678。

在本项目中,是用来向网络中广播设备信息。

所有网络中的主机1234端口都能收到这个数据包。

五、uip主调函数
uip协议的处理都是通过主调函数来调用的,它应该被
放在主程序while循环中,而用户的数据接收处理,和数据发送则是在回调函数中。

主调函数分成三个部分,第一个是检查网卡上有没有接收到数据,第二个是检查有没有用户数据需要发送,最后一个是用来维护aRp映射表的。

这里不得不提到两个时间基准:
timer_set(
timer_set(
一个是periodic_timer,这个为20ms,用来周期性的
检查有没有用户数据需要发送。

另一个是arp_timer,用来更新aRp表,每十秒表更新一次,旧的条目会被丢弃,默认的aRp表条目生存时间是20分钟。

voidprocess_net_data(void)
{
inti;
uip_len=tapdev_read();
if(uip_len>0)//从网络上收到数据
{
if(buF->type==htons(uip_ethtype_ip))
{
uip_arp_ipin();
uip_input();//接收数据处理
/*iftheabovefunctioninvocationresultedindatathat shouldbesentoutonthenetwork,theglobalvariable
uip_lenissettoavalue>0.*/
if(uip_len>0)//如果有数据需要返回给主机
{
uip_arp_out();
篇二:uip-中文说明
uip一个免费的tcp/ip栈
原文:adamdunkels
翻译:张伟林
摘要
这个文档描述uiptcp/ip栈。

uiptcp/ip栈是使用于低至8位或16位微处理器的嵌入式系统的一个可实现的极小的tcp/ip协议栈。

现时,uip代码的大小和Ram的需求比其它一般的tcp/ip栈要小。

uip栈使用一个基于编程模块事件去减少代码的大小和Ram的使用量。

基于系统的底层和uip之间的接口的回应会在文档里描述。

系统的底层和uip之间的接口是隐蔽的。

文档后面包含了一些uip应用编程例子。

uip的代码和这个文档的新版本可以在uip的主页下载/adam/uip/。

这个文档描述了uip的0.6版。

1引言
新近这些年里,人们对连接一个甚至只是小装置到一个现有的ip网络例如全球因特网的兴趣增加了。

为了可以通过因特网通讯,一个可实现的tcp/ip协议栈是必须的。

uip 是一个可实现的tcp/ip协议组件的一个非常重要的部分。

uip的实现目标是保持代码大小和储存器使用量最小。

现时,uip代码的大小和Ram的需求比其它一般的tcp/ip栈要小。

uip使用c编程语言,它可以自用分发和使用于商业和非商
业目的。

其它的tcp/ip栈,储存器经常用于数据缓存,等待一个数据已经成功送达的确应信号。

事实上,数据包丢失了,数据必须重发。

有特色的是,数据是缓存在Ram里,如果需要重发数据,应用程序可以快速重生数据。

例如,一个http 服务器服务的大部分是Rom里的静态和半静态页,不需要在Ram里缓存静态内容。

所以,如果一个包丢失了,http服务器可以容易地从Rom里重生数据。

数据简单地从原先的位置读回来。

uip的优越性是允许应用程序参加数据重发。

这个文档由以下部分组成,第2节描述在系统和应用的立场上怎样使用uip。

第3节详细讨论协议实现细节。

第4节覆盖了uip的配置,第5节描述uip的结构部分。

最后,第6节提供一些uip的应用编程实例。

2uip的接口技术
uip可以看作是一个代码库为系统提供确定的函数。

图1展示了uip,系统底层和应用程序之间的关系。

uip提供三个函数到系统底层,uip_init(),uip_input(),和
uip_periodic()。

应用程序必须提供一个回应函数给uip。

当网络或定时事件发生时,调用回应函数。

uip提供许多函数和堆栈交互。

要注意的就是uip提供的大部分函数是作为c的宏命令实现的,主要是为了速度,代码大小,效率和堆栈的使用。

图1uip就好像一个库
2.1uip应用接口
bsd套节字接口使用于大部分的操作系统,它不适合微系统,因为在应用设计里,它逼使一个线程基于编程模块。

一个多线程环境代价重大,因为,不但在线程管理里涉及增加代码的复杂性,而且保存每线程堆栈需要额外的储存器,还有执行任务切换的时间开销也摊派在这里。

微型系统不会有足够的资源去实现一个多线程环境,因此需要这个环境的应用接口不适合uip。

相反,uip使用一个基于编程模块的事件,模块是实现应用程序作为一个c函数被uip调用的地方,uip响应一定的事件。

uip调用应用在,当接收数据时,当数据成功送达另一方中止连接时,当一个新的连接建立时,或者当数据需要重发时。

应用程序也周期性地循环等待新数据。

应用程序只提供一个回应函数;它提升了应用程序处理不同的网络服务的不同的端口和连接的映射
uip与其它tcp/ip栈不同的是,当正在重发工作,它需要应用程序的帮助。

其它tcp/ip栈缓存传输数据在储存器里,直到在连接的最后数据确应成功发送。

如果数据需要重传,堆栈在没有通知应用程序下监视着重传工作。

通过这种方法,当要等待一个确应,数据必须缓存在储存器里,如果产生一个重发,应用程序可以快速重新生成数据。

为了减少
储存器的使用量,uip利用的论据是应用程序可以重新生成发送的数据和让应用程序参加重发。

2.1.1uip应用事件
应用程序必须作为c函数去实现,uip在任何一个事件发生时调用uip_appcall()。

表1列出可能的事件和每个事件的对应测试函数。

测试函数用于区别不同的事件。

函数是作为c宏命令实现的,将会是零值或非零值。

注意的是某些函数可以在互相连接时发生(也就是新数据可以在数据确应的同时到达)。

表1:uip应用事件和对应的测试参数
当应用程序调用时,uip设置全局变量uip_conn去指向当前连接的uip_conn结构(图5)。

这可以用来区别不同的服务。

一个典型的应用是检查uip_conn->lport(当地tcp端口号)去决定那个
服务连接应该提供。

例如,如果值uip_conn->lport等于80,应用程序可以决定启动一个http服务,值是23是启动telnet服务。

2.1.2接收数据
如果uip测试函数uip_newdata()值为1,远程连接的主机有发送新数据。

uip_appdata指针指向实际数据。

数据的大小通过uip函数uip_datalen()获得。

在数据不是被缓冲后,应用程序必须立刻启动。

2.1.3发送数据
应用程序通过使用uip函数uip_send()发送数据。

uip_send()函数采用两个参数;一个指针指向发送数据和数据的长度。

如果应用程序为了产生要发送的实际数据需要Ram空间,包缓存(通过uip_appdata指针指向)可以用于这
方面。

在一个时间里应用程序只能在连接中发送一块数据。

因此不可以在每个应用程序启用中调用uip_send()超过一次;只有上一次调用的数据将会发出后才可以。

注意,调用
uip_send()以后会改变某些全局变量,在应用函数返回前它不能被调用。

2.1.4重发数据
如果数据在网络中丢失,应用程序必须重发数据。

无论数据收到或没有收到,uip保持跟踪,和通知应用程序什么
时候察觉出数据是丢失了。

如果测试函数uip_rexmit()为真,应用程序要重发上一次发出的数据。

重发就好像原来那样发送,也就是通过uip_send()。

2.1.5关闭连接
应用程序通过调用uip_close()关闭当前连接。

这会导
致连接干净地关闭。

为了指出致命的错误,应用程序可以中止连接和调用uip_abort()函数完成这个工作。

如果连接已经被远端关闭,测试函数uip_closed()为真。

应用程序接着可以做一些必要的清理工作。

2.1.6报告错误
有两个致命的错误可以发生在连接中,不是连接由远程主机中止,就是连接多次重发上一数据和被中止。

uip通过调用函数报告这些问题。

应用程序使用两个测试函数
uip_aborted()和uip_timedout()去测试那些错误情况。

2.1.7轮询
当连接空闲时,uip在每一个时候周期性地轮询应用程序。

应用程序使用测试函数uip_poll()去检查它是否被轮询过。

2.1.8监听端口
uip维持一个监听tcp端口列表。

通过uip_listen()函数,一个新的监听端口打开。

当一个连接请求在一个监听端口到达,uip产生一个新的连接和调用应用程序函数。

如果一个新连接产生,应用程序被调用,测试函数
uip_connected()为真。

2.1.9打开连接
作为uip的0.6版,在uip里面通过使用uip_connect()函数打开一个新连接。

这个函数打开一个新连接到指定的ip 地址和端口,返回一个新连接的指针到uip_conn结构。

如果没有空余的连接槽,函数返回空值。

为了方便,函数
uip_ipaddr()可以用于将ip地址打包进两个单元16位数组
里,通过uip去代表ip地址。

使用两个例子,在图2和图3展示。

第一个例子展示了怎样打开一个连接去远端tcp端口8080。

如果没有足够的tcp连接插槽去允许一个新连接打开,uip_connect()函数返回null和通过uip_abort()中止当前连接。

第二个例子展示怎样打开一个新连接去指定的ip地址。

这例子里没有错误检查。

voidconnect_example1_app(void){
if(uip_connect(uip_conn->ripaddr,8080)==null){ uip_abort();
}
}
图2:打开一个连接去当前连接的远端的端口8080
voidconnect_example2(void){
u16_tipaddr[2];
uip_ipaddr(ipaddr,192,168,0,1);
uip_connect(ipaddr,8080);
}
图3:打开一个到主机192.168.0.1上端口8080的连接
2.1.10数据流控制
通过函数uip_stop()和uip_restart(),uip提供存取tcp数据流的控制途径。

设想一个应用程序下载数据到一个
慢速设备,例如磁盘驱动器。

如果磁盘驱动器的作业队列满了,应用程序不会准备从服务器接收更多的数据,直到队列排出空位。

函数uip_stop()可以用于维护流控制和停止远程主机发送数据。

当应用程序准备好接收更多数据,函数
uip_restart()用于告知远程终端再次发送数据。

函数
uip_stopped()可以用于检查当前连接是否停止。

2.2uip/系统接口
从系统的立场看,uip由3个c函数
uip_init(),uip_input(),和uip_periodic()。

uip_init()函数用于初始化uip堆栈和在系统启动期间调用。

当网络设备驱动器读一个ip包到包缓存时,调用函数uip_input()。

周期性运行是调用uip_periodic(),代表的是一秒一次。

调用uip函数是系统的职责。

2.2.1uip/设备驱动接口
当设备驱动放一个输入包在包缓存里(uip_buf),系统应该调用uip_input()函数。

函数将会处理这个包和需要时调用应用程序。

当uip_input()返回,一个输出包放在包缓存里。

包的大小由全局变量uip_len约束。

如果uip_len是0,没有包要发送。

2.2.2uip/周期计时接口
周期计时是用于驱动所有uip内部时钟事件,例如包重发。

当周期计时激发,每一个tcp连接应该调用uip函数
uip_periodic()。

连接编号传递是作为自变量给
uip_periodic()函数的。

类似于uip_input()函数,当
uip_periodic()函数返回,输出的ip包要放在包缓存里。

图4展示了调用uip_periodic()函数和监视输出包的一小段代码。

在这个特别的例子,函数netdev_send()是网络驱动的部分,将uip_buf数组的目录发出到网上。

for(i=0;i uip_periodic(i);
if(uip_len>0)
netdev_send();
}
图4:周期计时和uip的接口的例子代码.
2.3uip函数总结
表2包含了所有uip提供的函数
表2:uip函数总结
3实现协议
uip实现了tcp/ip协议组的四个基本协议;
aRp[plu82],ip[pos81b],icmp[pos81a]和tcp
[pos81c]。

链路层协议例如ppp可以实现作为uip下面的设备驱动。

应用层协议例如http,Ftp或smtp可以实现为uip之上的应用程序。

3.1地址解析协议|aRp
aRp协议映射了ip地址和以太网mac物理地址,它在以
太网上的tcp/ip操作是需要的。

aRp在uip里实现的是包含一个ip到mac地址的映射。

当一个ip包要在以太网上发出,查询aRp表,去找出包要发送去的mac地址。

如果在表里找不到ip地址,aRp请求包就会发出。

请求包在网络里广播和请求给出ip地址的mac地址。

主机通过发出一个aRp回应,响应请求ip地址。

当uip给出一个aRp回应,更新aRp表。

为了节省储存器,一个ip地址的aRp请求覆盖发出的请求输出ip包。

它是假定上层将重新发送那些被覆盖了的数据。

每十秒表更新一次,旧的条目会被丢弃。

默认的aRp表条目生存时间是20分钟。

3.2网际协议|ip
uip的ip层代码有两个职责:验证输入包的ip头的正确性和icmp和tcp协议之间多路复用。

ip层代码是非常简单的,由9条语句组成。

事实上,uip的ip层极大地简化了,它没有实现碎片和重组。

3.3因特网信息控制协议|icmp
在uip里,只有一种icmp信息实现了:icmp回响信息。

icmp回响信息常常用于ping程序里
篇三:uip源码分析
uip的aRp协议代码分析之一aRp请求(是根据ip地址获取物理地址的一个同时将ip地址和硬件地址存入本机aRp
缓存中,下次请求时直接查询aRp缓存。

)
对于一个设备用的aRp协议而言,重要的东西包括三方面:
1.一个本地的ip与mac地址的缓存表.以有对应的更新和查询操作.
2.一个发送aRp请求的函数.
3.一个处理aRp回复的函数.
下面我们来看uip中是如何实现的(代码见uip_arp.c: 首先,定义一个缓存表的数据结构,99行起:
structarp_entry{
u16_tipaddr[2];
structuip_eth_addrethaddr;
u8_ttime;
};
只有三个项,很简单
第一项是ip地址,16*2=4*8位的,保存四个八位组.
第二项是mac地址.
第三项是缓存更新时间.
下来是aRp请求发送函数:uip_arp.cl325
/*-----------------------------------------------------------------------------------*/
/**
*prependethernetheadertoanoutboundippacketandseeifw eneed*tosendoutanaRprequest.
*为传出的ip包添加以太网头并看是否需要发送aRp请求.
*thisfunctionshouldbecalledbeforesendingoutanippack et.the*functionchecksthedestinationipaddressoftheip packettosee*whatethernetmacaddressthatshouldbeuseda sadestinationmac*addressontheethernet.
*此函数应该在发送ip包时调用,它会检查ip包的目的ip地址,看看以太网应该使用什么目的mac地址.
*ifthedestinationipaddressisinthelocalnetwork(deter mined*bylogicalandingofnetmaskandouripaddress),thef unction*checkstheaRpcachetoseeifanentryforthedestin ationip*addressisfound.ifso,anethernetheaderisprepe ndedandthe*functionreturns.ifnoaRpcacheentryisfound forthe
*destinationipaddress,thepacketintheuip_buf[]isrepl
acedby*anaRprequestpacketfortheipaddress.theippacke tisdropped*anditisassumedthattheyhigherlevelprotoco ls(e.g.,tcp)*eventuallywillretransmitthedroppedpack et.
*如果目的ip地址是在局域网中(由ip地址与子网掩码的与逻辑决定),函数就会从aRp缓存表中查找有
*无对应项.若有,就取对应的mac地址,加上以太网头,并返回,否则uip_buf[]中的数据包会被替换成一个*目的ip在址的aRp请求.原来的ip包会被简单的仍掉,此函数假设高层协议(如tcp)会最终重传扔掉的包.
*ifthedestinationipaddressisnotonthelocalnetwork,th eip
*addressofthedefaultrouterisusedinstead.
*如果目标ip地址并非一个局域网ip,则会使用默认路由的ip地址.
*whenthefunctionreturns,apacketispresentintheuip_bu f[]
*buffer,andthelengthofthepacketisintheglobalvariabl e
*uip_len.函数返回时,uip_buf[]中已经有了一个包,
其长度由uip_len指定.
*/
/*-----------------------------------------------------------------------------------*/
void
uip_arp_out(void)
{
structarp_entry*tabptr;
/*FindthedestinationipaddressintheaRptableandconstr uct
theethernetheader.ifthedestinationipaddresisntonthe
localnetwork,weusethedefaultroutersipaddressinstead.
//在aRp表中找到目的ip地址,构成以太网头.如果目的ip地址不在局域网中,则使用默认//路由的ip.
ifnotaRptableentryisfound,weoverwritetheoriginalip packetwithanaRprequestfortheipaddress.*/
//如果aRp表中找不到,则将原来的ip包替换成一个aRp请求.
/*Firstcheckifdestinationisalocalbroadcast.首先检查目标是不是广播
*/if(uip_ipaddr_cmp(ipbuF->destipaddr,broadcast_ipa ddr)){
memcpy(ipbuF->ethhdr.dest.addr,broadcast_ethaddr.ad dr,6);
}else{
/*checkifthedestinationaddressisonthelocalnetwork.检查目标地址是否在局域网内*/
if(!uip_ipaddr_maskcmp(ipbuF->destipaddr,uip_hostad dr,uip_netmask)){/*destinationaddresswasnotontheloc alnetwork,soweneedto
usethedefaultroutersipaddressinsteadofthedestinatio n
addresswhendeterminingthemacaddress.目的地址不在局域网内,所以保用默认路由器的地址来确在mac地址*/
uip_ipaddr_copy(ipaddr,uip_draddr);
}else{
/*else,weusethedestinationipaddress.否则,使用目
标ip地址*/
uip_ipaddr_copy(ipaddr,ipbuF->destipaddr);
}
for(i=0;i tabptr=
if(uip_ipaddr_cmp(ipaddr,tabptr->ipaddr)){
break;
}
}
if(i==uip_aRptab_size){
/*thedestinationaddresswasnotinouraRptable,sowe
overwritetheippacketwithanaRprequest.如果遍历到
头没找到,将原ip包替换为aRp请求并返回*/
memset(buF->ethhdr.dest.addr,0xff,6);
memset(buF->dhwaddr.addr,0x00,6);
memcpy(buF->ethhdr.src.addr,uip_ethaddr.addr,6);
memcpy(buF->shwaddr.addr,uip_ethaddr.addr,6);
uip_ipaddr_copy(buF->dipaddr,ipaddr);
uip_ipaddr_copy(buF->sipaddr,uip_hostaddr);
buF->opcode=htons(aRp_Request);/*aRprequest.*/
buF->hwtype=htons(aRp_hwtype_eth);
buF->protocol=htons(uip_ethtype_ip);
buF->hwlen=6;
buF->protolen=4;
buF->ethhdr.type=htons(uip_ethtype_aRp);
uip_appdata=
uip_len=sizeof(structarp_hdr);
return;
}
/*buildanethernetheader.如果是在局域网中,且在aRp缓存中找到了(如果没找到进行不到这一步,在上面就返回了),则构建以太网头*/
memcpy(ipbuF->ethhdr.dest.addr,tabptr->ethaddr.addr ,6);
}
memcpy(ipbuF->ethhdr.src.addr,uip_ethaddr.addr,6);
ipbuF->ethhdr.type=htons(uip_ethtype_ip);
uip_len+=sizeof(structuip_eth_hdr);
}
以上内容自325行起.下面再总结一下其基本顺序:
用ipbuF->ethhdr.dest.addr来存储目的ip地址,它有可能是局域网内一主机ip,也可能是路由器ip(如果是发往外网,即原来的目的ip不在局域网内),还有可能是广播专用的ip.先看是不是在广播:如果是广播,将
ipbuF->ethhdr.dest.addr设为广播ip.
再看是不是在局域网内:如果不是,则将
ipbuF->ethhdr.dest.addr设为路由器ip.
如果在局域网内,查看是否已经存在于aRp缓存表中:如果不在,将要发送的换成aRp请求,返回.
如果已存在,则查找使用查找到的mac地址为ip包添加以太网头.
这里还要解释一些细节问题,主要是:
1.如何在ip包上添加以太网头
2.如果将ip包替换成aRp请求,aRp请求的格式是什么.
3.广播地址
这些问题将在二楼来说.
将ip包替换成aRp请求部分代码(实际上ip包是放在uip_buf[]里的,这里只是将uip_buf[]填充上aRp请求即可),于uip_arp.c的388行:
/*thedestinationaddresswasnotinouraRptable,sowe overwritetheippacketwithanaRprequest.*/
memset(buF->ethhdr.dest.addr,0xff,6);
memset(buF->dhwaddr.addr,0x00,6);
memcpy(buF->ethhdr.src.addr,uip_ethaddr.addr,6); memcpy(buF->shwaddr.addr,uip_ethaddr.addr,6);
uip_ipaddr_copy(buF->dipaddr,ipaddr);
uip_ipaddr_copy(buF->sipaddr,uip_hostaddr);
buF->opcode=htons(aRp_Request);/*aRprequest.*/ buF->hwtype=htons(aRp_hwtype_eth);
buF->protocol=htons(uip_ethtype_ip);
buF->hwlen=6;
buF->protolen=4;
buF->ethhdr.type=htons(uip_ethtype_aRp);
uip_appdata=
uip_len=sizeof(structarp_hdr);
return;
首先解释这里的buF(于uip_arp.c的116行):
#definebuF((structarp_hdr*)
u16_thwtype;//硬件类型
u16_tprotocol;//协议类型
u8_thwlen;
u8_tprotolen;
u16_topcode;//操作码
structuip_eth_addrshwaddr;//源以太网地址
u16_tsipaddr[2];//源ip地址
structuip_eth_addrdhwaddr;//目的以太网地址
u16_tdipaddr[2];//目的ip地址
};
structuip_eth_hdr{
structuip_eth_addrdest;
structuip_eth_addrsrc;
u16_ttype;
};
这是arp_hdr的第一个成员ethhdr的类型定义,对应图片中的前三项:6+6+2,目的以太网地址,源以太网地址,2字
节数据类型(aRp请求和应答为0x0806).
structarp_hdr的第二个成员u16_thwtype,对应图片中第三项,2字节硬件类型(值为1表示以太网).
structarp_hdr的第三个成员u16_tprotocol,对应图片中第四项,2字节要映射的协议地址类型(ip地址为0x0800).
structarp_hdr的第四个成员u8_thwlen,对应图片中第五项,1字节硬件地址长度(对mac地址来说为6).
structarp_hdr的第五个成员u8_tprotolen,对应图片中第六项,1字节协议地址长度(对ip地址来说为4).
structarp_hdr的第六个成员u16_topcode,对应图片中第七项,2字节操作码(1aRp请求,2aRp应答,3RaRp请求,
4RaRp应答,必须字段).
structarp_hdr的第七个成员
structuip_eth_addrshwaddr,对应图片中第八项,6字节源
以太网地址.
structarp_hdr的第八个成员u16_tsipaddr[2];,对应
图片中第九项,2字节源ip地址.
structarp_hdr的第九个成员
structuip_eth_addrdhwaddr,对应图片中第十项,6字节目
标以太网地址.
structarp_hdr的第十个成员u16_tdipaddr[2];,对应
图片中第十一项,2字节目标ip地址.上面绿色的表示已经详解的,红字的表示要进一步说明的.
这就是一个aRp帧的结构,可以看到,里面的源和目的以太网地址都是重复的.
我们再看函数中的代码:
memset(buF->ethhdr.dest.addr,0xff,6);
memset(buF->dhwaddr.addr,0x00,6);
memcpy(buF->ethhdr.src.addr,uip_ethaddr.addr,6);。

相关文档
最新文档