乌鸦喝水获奖教学设计:单片机驱动DM9000网卡芯片(详细调试过程)【下】

来源:百度文库 编辑:中财网 时间:2024/04/29 14:57:36

三、ARP协议的实现

    1、ARP协议原理简述

    ARP协议(Address Resolution Protocol 地址解析协议),在局域网中,网络中实际传输的是“

帧”,帧里面有目标主机的MAC地址。在以太网中,一个注意要和另一个主机进行直接通信,必须要知

道目标主机的MAC地址。这个MAC地址就是标识我们的网卡芯片唯一性的地址。但这个目标MAC地址是如

何获得的呢?这就用到了我们这里讲到的地址解析协议。所有“地址解析”,就是主机在发送帧前将目

标IP地址转换成MAC地址的过程。ARP协议的基本功能就是通过目标设备的IP地址,查询目标设备的MAC

地址,以保证通信的顺利进行。所以在第一次通信前,我们知道目标机的IP地址,想要获知目标机的

MAC地址,就要发送ARP报文(即ARP数据包)。它的传输过程简单的说就是:我知道目标机的IP地址,

那么我就向网络中所有的机器发送一个ARP请求,请求中有目标机的IP地址,请求的意思是目标机要是

收到了此请求,就把你的MAC地址告诉我。如果目标机不存在,那么此请求自然不会有人回应。若目标

机接收到了此请求,它就会发送一个ARP应答,这个应答是明确发给请求者的,应答中有MAC地址。我接

到了这个应答,我就知道了目标机的MAC地址,就可以进行以后的通信了。因为每次通信都要用到MAC地

址。

    ARP报文被封装在以太网帧头部中传输,如图为ARP请求报文的头部格式。 

图6 用于以太网的ARP请求或应答分组格式

    注意,以太网的传输存储是“大端格式”,即先发送高字节后发送低字节。例如,两个字节的数据

,先发送高8位后发送低8位。所以接收数据的时候要注意存储顺序。

    整个报文分成两部分,以太网首部和ARP请求/应答。下面挑重点讲述。

“以太网目的地址”字段:若是发送ARP请求,应填写广播类型的MAC地址FF-FF-FF-FF-FF-FF,意思是

让网络上的所有机器接收到;

“帧类型”字段:填写08-06表示次报文是ARP协议;

“硬件类型”字段:填写00-01表示以太网地址,即MAC地址;

“协议类型”字段:填写08-00表示IP,即通过IP地址查询MAC地址;

“硬件地址长度”字段:MAC地址长度为6(以字节为单位);

“协议地址长度”字段:IP地址长度为4(以字节为单位);

“操作类型”字段:ARP数据包类型,1表示ARP请求,2表示ARP应答;

“目的以太网地址”字段:若是发送ARP请求,这里是需要目标机填充的。


    2、ARP的处理程序

    ARP协议原理很简单,下面我们来编写ARP协议的处理函数。新建文件命名为arp.c,填写如下函数

unsigned char mac_addr[6] = {*,*,*,*,*,*};

unsigned char ip_addr[4] = { 192, 168, *, * };

unsigned char host_ip_addr[4] = { 192, 168, *, * };

unsigned char host_mac_addr[6]={ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

unsigned char Buffer[1000];

uint16 packet_len;

/*这些全局变量,在前面将的文件中有些已经有过定义,这里要注意在前面加上“extern”关键字。“

*”应该根据自己的机器修改*/

#define ARPBUF ((struct arp_hdr*)(&Buffer[0]))

#define HON(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))

/*此宏定义是将小端格式存储的字(两个字节)转换成大端格式存储*/

void arp_request(void) //发送ARP请求数据包

{

//以太网首部

memcpy(ARPBUF->ethhdr.d_mac, host_mac_addr, 6);

/*字符串拷贝函数,文件要包含头文件。参数依次是,拷贝目标指针,拷贝数据源指针,拷

贝字符数*/

memcpy(ARPBUF->ethhdr.s_mac, mac_addr, 6);

ARPBUF->ethhdr.type = HON( 0x0806 );

/*小端格式的编译器,可以用HON()宏来转换成大端格式,如果你的编译器是大端格式,直接填写

0x0806即可*/

/*就是简单的按照协议格式填充,以下同*/

//ARP首部

ARPBUF->hwtype = HON( 1 );

ARPBUF->protocol = HON( 0x0800 );

ARPBUF->hwlen = 6;

ARPBUF->protolen = 4;

ARPBUF->opcode = HON( 1 );

memcpy(ARPBUF->smac, mac_addr, 6);

memcpy(ARPBUF->sipaddr, ip_addr, 4);

memcpy(ARPBUF->dipaddr, host_ip_addr, 4);

packet_len = 42;//14+28=42

sendpacket( Buffer, packet_len );

}

注释:ARPBUF的宏定义和ARP首部结构,在前面已经讲过。同时注意执行该函数时中断的处理。这里没

作处理。

    看上去很easy吧,下面函数实现接收ARP请求或接收ARP应答的处理。

unsigned char arp_process(void)//ARP接收函数,成功返回1,否则返回0

{

//简单判断ARP数据包有无损坏,有损坏则丢弃,不予处理

if( packet_len < 28 )//ARP数据长度为28字节为无效数据

{

return 0;

}

switch ( HON( ARPBUF->opcode ) )

{

   case 1    : //处理ARP请求

         if( ARPBUF->dipaddr[0] == ip_addr[0] &&

             ARPBUF->dipaddr[1] == ip_addr[1] &&

             ARPBUF->dipaddr[2] == ip_addr[2] &&

             ARPBUF->dipaddr[3] == ip_addr[3] )//判断是否是自己的IP,是否向自己询问MAC地址

         { 
             ARPBUF->opcode = HON( 2 );//设置为ARP应答

             memcpy(ARPBUF->dmac, ARPBUF->smac, 6);

             memcpy(ARPBUF->ethhdr.d_mac, ARPBUF->smac, 6);

             memcpy(ARPBUF->smac, mac_addr, 6);

             memcpy(ARPBUF->ethhdr.s_mac, mac_addr, 6);

             memcpy(ARPBUF->dipaddr, ARPBUF->sipaddr, 4);

             memcpy(ARPBUF->sipaddr, ip_addr, 4);

             ARPBUF->ethhdr.type = HON( 0x0806 );

             packet_len = 42;

             sendpacket( Buffer, packet_len );//发送ARP数据包

             return 1;

         }

         else

         {

             return 0;

         }

         break;

   case 2    : //处理ARP应答

         if( ARPBUF->dipaddr[0] == ip_addr[0] &&

             ARPBUF->dipaddr[1] == ip_addr[1] &&

             ARPBUF->dipaddr[2] == ip_addr[2] &&

             ARPBUF->dipaddr[3] == ip_addr[3] )//再次判断IP,是否是给自己的应答

         {

          memcpy(host_mac_addr, ARPBUF->smac, 6);//保存服务器MAC地址

          return 1;

         }

         else

         {

             return 0;

         }

         break;

default     ://不是ARP协议

         return 0;

}

}

    根据ARP协议格式看这两个函数并不困难。于是我们又得到两个函数:arp_request()和

arp_process()。

    3、ARP程序调试

    下面我们修改主函数和中断处理函数。

    将mian()函数中的“sendpacket(60);”语句换成“arp_request();”语句。

void int_issue(void) //中断处理函数,需要根据自己的处理器进行设置

{

    unsigned int i;

    i = receivepacket(Buffer);//将数据读取到Buffer中。

    if(i == 0)

    {

        return;

    }

     else

     {

         i = arp_process();

         if(i == 1)//判断是否是ARP协议

             print_hostmacaddr();//打印目标机的MAC地址,就是用串口打印host_mac_addr[]中的6

个字节

     }

}

    保存运行调试。 

图7 主机MAC地址

    至此,关于DM9000的调试过程就完成了。之后我还调试了UDP通讯、TCP通讯等,主要是关于协议的

处理了,这里就不介绍了。有兴趣的朋友可以参看《TCP/IP协议》第一卷,将会有很大帮助。希望这些

调试过程能为读者或多或少的提供些有用的信息,也欢迎大家和我一起讨论。

我的Email:mengqx25@163.com

补充:增加了udp实现 http://hi.baidu.com/firstm25/blog/item/d22f3443e373781f73f05d9b.html