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地址。
知道了包结构,我们就可以设计一个结构体:
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应答的功能,因此只用一个简单的函数就可实现:
{
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()发送出去。