技术开发 频道

自己动手编写嵌入式Bootloader之二

  4. ARP协议的实现

  一般的方式是建立一个全局的ARP映射缓存表,随着系统的运行不断查找、更新该表。但是我们要完成的功能仅仅是从TFTP服务器下载内核和文件系统映像,而服务器的IP和MAC地址都是固定的,因此可以简化ARP映射表,只用两个变量分别保存服务器IP和MAC,再用两个变量保存开发板IP和MAC即可。并且更新映射表的功能也可以省略,只在系统初始化时把这四个地址都设置好,使用过程中不会发生改变,所以不需要更新。这样,我们的ARP协议只需要完成接受ARP请求、发送ARP应答的功能,而发送ARP请求和接受ARP应答的功能可以省略,这样大大简化了协议栈的设计。

  按照维基百科上的介绍(http://en.wikipedia.org/wiki/Address_Resolution_Protocol),ARP 是一个数据链路层协议,(我感觉它应该是网络层的协议),它的作用是在只知道一个主机网络层IP地址的情况下找到它的硬件地址。在以太网上,它主要用来把 IP地址转换为以太网MAC地址。由于是链路层协议,ARP的作用范围仅限于本地局域网。

  ARP数据包长度为28字节,其中各字节的含义如下图所示:

  对各个段作简单的解释:

  Hardware type (HTYPE) 每个数据链路层协议都被分配到一个数,比如,Ethernet 是 1

  Protocol type (PTYPE) 在这个域,每个网络层协议都被分配到一个数(标号),比如,IP是0x0800

  Hardware length (HLEN) 硬件地址的长度。以太网Ethernet的MAC地址长度是6个字节

  Protocol length (PLEN) 维基上写的是“逻辑地址”的长度,其实也就是网络层地址的长度。IPv4地址的长度为4个字节。

  Operation 表明发送者的操作,也就是数据包的类型:1表示ARP请求;2表示ARP回应;3表示RARP请求;4表示RARP回应。

  Sender hardware address (SHA) 发送者的硬件地址

  Sender protocol address (SPA) 发送者的协议地址,也就是发送者IP地址。

  Target hardware address (THA) 目标接收者的硬件MAC地址。如果是ARP请求,这个域被忽略。

  Target protocol address (TPA) 目标接收者的IP地址。

  知道了包结构,我们就可以设计一个结构体:

  struct arp_header{

  unsigned
short ar_hrd; /* Format of hardware address */

  unsigned
short ar_pro; /* Format of protocol address */

  unsigned
char ar_hln; /* Length of hardware address */

  unsigned
char ar_pln; /* Length of protocol address */

  unsigned
short ar_op; /* Operation */

  unsigned
char ar_sha[6]; /* Sender hardware address */

  unsigned
long ar_spa; /* Sender protocol address */

  unsigned
char ar_tha[6]; /* Target hardware address */

  unsigned
long ar_tpa; /* Target protocol address */

  }__attribute__ ((packed));

  属性 __attribute__((packet)) 告诉编译器使用紧缩方式存放结构体内容(1 Byte align), 不使用默认的4字节对齐, 这样就不会产生冗余字节。此时的 sizeof(struct arp_header) = 28。 如果不加packed属性, 运行 sizeof(struct arp_header) 得到 32, 而不是 28。 数据段就产生了错位。

  前面已经说过,我们只实现接收ARP请求并发送ARP应答的功能,因此只用一个简单的函数就可实现:

  static int arp_handle( unsigned char *buf, unsigned int len )

  {

  
struct arp_header *pRx, *pTx;

  pRx
= (struct arp_header *)(buf);

  pTx
= (struct arp_header *)&TxBuf[256];

  
switch (htons(pRx->ar_op))

  {

  
case ARP_REQUEST:

  
if (pRx->ar_tpa == htonl(NetOurIP))

  {

  pTx
->ar_hrd = htons(0x01);

  pTx
->ar_pro = htons(PROTO_IP);

  pTx
->ar_hln = 0x06;

  pTx
->ar_pln = 0x04;

  pTx
->ar_op = htons(ARP_REPLY);

  memcpy(pTx
->ar_sha, NetOurEther, 6);

  pTx
->ar_spa = htonl(NetOurIP);

  memcpy (pTx
->ar_tha, pRx->ar_sha, 6);

  pTx
->ar_tpa = pRx->ar_spa;

  mac_send( (unsigned
char*)pTx, sizeof(struct arp_header), PROTO_ARP);

  }

  
break;

  
case ARP_REPLY:

  printf(
"\n\rGot ARP reply\n");

  
break;

  
default:

  printf(
"\n\r ar_op Not Support.\n");

  
break;

  }

  
return 0;

  }

  接收到的数据保存在pRx地址处,要发送的数据地址指定为pTx位于发送缓冲区中。如果接收到的是ARP请求包并且IP地址也符合,则在pTx处构造一个ARP应答包并交给mac_send()发送出去。

0
相关文章