ARP — address resolution protocol
1、物理地址和网络地址
(1)结构体定义
//MAC address
#define ETHARP_HWADDR_LEN 6
PACK_STRUCT_BEGIN
struct eth_addr {
PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
//IP address
struct ip_addr {
u32_t addr;
};
struct ip_addr_packed {
PACK_STRUCT_FIELD(u32_t addr);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
typedef struct ip_addr ip_addr_t;
typedef struct ip_addr_packed ip_addr_p_t;
(2)以太网帧和ARP报文格式

PACK_STRUCT_BEGIN
/** Ethernet header */
struct eth_hdr {
PACK_STRUCT_FIELD(struct eth_addr dest);
PACK_STRUCT_FIELD(struct eth_addr src);
PACK_STRUCT_FIELD(u16_t type);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END

PACK_STRUCT_BEGIN
/** the ARP message, see RFC 826 ("Packet format") */
struct etharp_hdr {
PACK_STRUCT_FIELD(u16_t hwtype);
PACK_STRUCT_FIELD(u16_t proto);
PACK_STRUCT_FIELD(u8_t hwlen);
PACK_STRUCT_FIELD(u8_t protolen);
PACK_STRUCT_FIELD(u16_t opcode);
PACK_STRUCT_FIELD(struct eth_addr shwaddr);
PACK_STRUCT_FIELD(struct ip_addr2 sipaddr);
PACK_STRUCT_FIELD(struct eth_addr dhwaddr);
PACK_STRUCT_FIELD(struct ip_addr2 dipaddr);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
长度宏声明
#define ETH_PAD_SIZE 0
#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE)
#define SIZEOF_ETHARP_HDR 28
#define SIZEOF_ETHARP_PACKET (SIZEOF_ETH_HDR + SIZEOF_ETHARP_HDR)
2、ARP缓存表
(1)结构体声明
static struct etharp_entry arp_table[ARP_TABLE_SIZE];
struct etharp_entry {
struct etharp_q_entry *q; //挂接在该缓存表的数据包缓冲队列
ip_addr_t ipaddr;
struct netif *netif;
struct eth_addr ethaddr;
u8_t state; // 缓存表的状态
u8_t ctime; //生存时间计数器
};
struct etharp_q_entry {
struct etharp_q_entry *next;
struct pbuf *p;
};
enum etharp_state {
ETHARP_STATE_EMPTY = 0,
ETHARP_STATE_PENDING,
ETHARP_STATE_STABLE,
ETHARP_STATE_STABLE_REREQUESTING
};
(2)ARP缓存表的定时机制
定时更新缓存表项的状态
#define ARP_TMR_INTERVAL 5000
#define ARP_MAXAGE 240
#define ARP_AGE_REREQUEST_USED (ARP_MAXAGE - 12)
#define ARP_MAXPENDING 2
void etharp_tmr(void)
{
u8_t i;
//查阅缓存表中的每一项状态、时间计数器
for (i = 0; i < ARP_TABLE_SIZE; ++i) {
u8_t state = arp_table[i].state;
if (state != ETHARP_STATE_EMPTY) {
arp_table[i].ctime++;
if ((arp_table[i].ctime >= ARP_MAXAGE) ||
((arp_table[i].state == ETHARP_STATE_PENDING) &&
(arp_table[i].ctime >= ARP_MAXPENDING))) {
etharp_free_entry(i);
}
else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING) {
arp_table[i].state = ETHARP_STATE_STABLE;
}
}
}
}
static void etharp_free_entry(int i)
{
//判断是否有挂接在缓存表项的数据
if (arp_table[i].q != NULL) {
free_etharp_q(arp_table[i].q);
arp_table[i].q = NULL;
}
/* recycle entry for re-use */
arp_table[i].state = ETHARP_STATE_EMPTY;
}
#define free_etharp_q(q) pbuf_free(q)
3、gratuitous ARP
Gratuitous ARP也称为免费ARP,无故ARP。Gratuitous ARP不同于一般的ARP请求,它并非期待得到ip对应的mac地址,而是当主机启动的时候,将发送一个Gratuitous arp请求,即请求自己的ip地址的mac地址,发送MAC和接收MAC相同,主要作用是更新同一网段内其它主机的ARP缓存表。
网口建立时,函数netif_set_up中调用函数etharp_gratuitous。
#define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr)
const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
const struct eth_addr ethzero = {{0,0,0,0,0,0}};
err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr)
{
return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast,
(struct eth_addr *)netif->hwaddr, &netif->ip_addr, ðzero,
ipaddr, ARP_REQUEST);
}
/**
* Send a raw ARP packet (opcode and all addresses can be modified)
*
* @param netif the lwip network interface on which to send the ARP packet
* @param ethsrc_addr the source MAC address for the ethernet header
* @param ethdst_addr the destination MAC address for the ethernet header
* @param hwsrc_addr the source MAC address for the ARP protocol header
* @param ipsrc_addr the source IP address for the ARP protocol header
* @param hwdst_addr the destination MAC address for the ARP protocol header
* @param ipdst_addr the destination IP address for the ARP protocol header
* @param opcode the type of the ARP packet
* @return ERR_OK if the ARP packet has been sent
* ERR_MEM if the ARP packet couldn't be allocated
* any other err_t on failure
*/
//创建一个ARP请求包
err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
const struct eth_addr *ethdst_addr,
const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr,
const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr,
const u16_t opcode)
{
struct pbuf *p;
err_t result = ERR_OK;
struct eth_hdr *ethhdr;
struct etharp_hdr *hdr;
/* allocate a pbuf for the outgoing ARP request packet */
p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM);
if (p == NULL) {
return ERR_MEM;
}
ethhdr = (struct eth_hdr *)p->payload;
hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
//填充以太网帧首部14字节
/* Write the Ethernet MAC-Addresses */
ETHADDR16_COPY(ðhdr->dest, ethdst_addr);
ETHADDR16_COPY(ðhdr->src, ethsrc_addr);
ethhdr->type = PP_HTONS(ETHTYPE_ARP); //帧类型
//填充ARP报文20个字节
hdr->opcode = htons(opcode); //操作码:申请或回复,转换为网络字节顺序
/* Write the ARP MAC-Addresses */
ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr);
ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr);
/* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
* structure packing. */
IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr);
IPADDR2_COPY(&hdr->dipaddr, ipdst_addr);
hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET); //硬件类型
hdr->proto = PP_HTONS(ETHTYPE_IP); // 协议类型
/* set hwlen and protolen */
hdr->hwlen = ETHARP_HWADDR_LEN; //硬件长度
hdr->protolen = sizeof(ip_addr_t); // 协议长度
/* send ARP query */
result = netif->linkoutput(netif, p);
/* free ARP query packet */
pbuf_free(p);
p = NULL;
return result;
}
对结构体中双字节以上的成员变量,注意小端和大端字节顺序的区别。
相关宏声明
#define HWTYPE_ETHERNET 1
#define ETHTYPE_ARP 0x0806U
#define ETHTYPE_IP 0x0800U
#define ETHTYPE_VLAN 0x8100U
#define ETHTYPE_PPPOEDISC 0x8863U /* PPP Over Ethernet Discovery Stage */
#define ETHTYPE_PPPOE 0x8864U /* PPP Over Ethernet Session Stage */
/** ARP message types (opcodes) */
#define ARP_REQUEST 1
#define ARP_REPLY 2
4、ARP层的数据包输入
函数ethernetif_input封装了网卡数据包的接收,ARP解析主要分为以下几个过程:1、函数low_level_input 实现接收底层网卡数据并转换为pbuf类型的数据包;2、函数ethernet_input 分析接收的数据包是IP报文还是ARP报文;3、若为ARP报文,调用函数etharp_arp_input处理缓存表更新及发送ARP应答报文。
1、ethernet_input
err_t ethernet_input(struct pbuf *p, struct netif *netif)
{
struct eth_hdr* ethhdr;
u16_t type;
s16_t ip_hdr_offset = SIZEOF_ETH_HDR;
if (p->len <= SIZEOF_ETH_HDR) {
goto free_and_return;
}
ethhdr = (struct eth_hdr *)p->payload;
type = ethhdr->type;
if (ethhdr->dest.addr[0] & 1) {
/* this might be a multicast or broadcast packet */
if (ethhdr->dest.addr[0] == LL_MULTICAST_ADDR_0) {
if ((ethhdr->dest.addr[1] == LL_MULTICAST_ADDR_1) &&
(ethhdr->dest.addr[2] == LL_MULTICAST_ADDR_2)) {
/* mark the pbuf as link-layer multicast */
p->flags |= PBUF_FLAG_LLMCAST;
}
} else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) {
/* mark the pbuf as link-layer broadcast */
p->flags |= PBUF_FLAG_LLBCAST;
}
}
switch (type) {
/* IP packet? */
case PP_HTONS(ETHTYPE_IP): //IP报文
if (!(netif->flags & NETIF_FLAG_ETHARP)) {
goto free_and_return;
}
#if ETHARP_TRUST_IP_MAC
/* update ARP table */
etharp_ip_input(netif, p);
#endif /* ETHARP_TRUST_IP_MAC */
/* skip Ethernet header */
if(pbuf_header(p, -ip_hdr_offset)) {
LWIP_ASSERT("Can't move over header in packet", 0);
goto free_and_return;
} else {
/* pass to IP layer */
ip_input(p, netif);
}
break;
case PP_HTONS(ETHTYPE_ARP): //arp报文
if (!(netif->flags & NETIF_FLAG_ETHARP)) {
goto free_and_return;
}
/* pass p to ARP module */
etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p);
break;
default:
goto free_and_return;
}
return ERR_OK;
free_and_return:
pbuf_free(p);
return ERR_OK;
}
2、 etharp_arp_input
static void
etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
{
struct etharp_hdr *hdr;
struct eth_hdr *ethhdr;
ip_addr_t sipaddr, dipaddr;
u8_t for_us;
if (p->len < SIZEOF_ETHARP_PACKET) {
pbuf_free(p);
return;
}
ethhdr = (struct eth_hdr *)p->payload;
hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
/* RFC 826 "Packet Reception": */
if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) ||
(hdr->hwlen != ETHARP_HWADDR_LEN) ||
(hdr->protolen != sizeof(ip_addr_t)) ||
(hdr->proto != PP_HTONS(ETHTYPE_IP))) {
pbuf_free(p);
return;
}
IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
if (ip_addr_isany(&netif->ip_addr)) {
for_us = 0;
} else {
for_us = (u8_t)ip_addr_cmp(&dipaddr, &(netif->ip_addr));
}
//更新ARP缓存表
etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
switch (hdr->opcode) {
case PP_HTONS(ARP_REQUEST):
if (for_us) {
hdr->opcode = htons(ARP_REPLY);
IPADDR2_COPY(&hdr->dipaddr, &hdr->sipaddr);
IPADDR2_COPY(&hdr->sipaddr, &netif->ip_addr);
ETHADDR16_COPY(&hdr->dhwaddr, &hdr->shwaddr);
ETHADDR16_COPY(ðhdr->dest, &hdr->shwaddr);
ETHADDR16_COPY(&hdr->shwaddr, ethaddr);
ETHADDR16_COPY(ðhdr->src, ethaddr);
//发送ARP应答包时,只需更新接收的ARP请求包,更改ARP报文中的目的地址和发送地址及操作码,并调用底层网卡发送数据包函数
/* return ARP reply */
netif->linkoutput(netif, p);
/* we are not configured? */
} else if (ip_addr_isany(&netif->ip_addr)) {
/* { for_us == 0 and netif->ip_addr.addr == 0 } */
/* request was not directed to us */
} else {
/* { for_us == 0 and netif->ip_addr.addr != 0 } */
}
break;
case PP_HTONS(ARP_REPLY):
break;
default:
break;
}
/* free ARP packet */
pbuf_free(p);
}
3、ARP缓存表查找可利用表项索引并更新ARP缓存表
static s8_t etharp_find_entry(ip_addr_t *ipaddr, u8_t flags)
{
s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
s8_t empty = ARP_TABLE_SIZE;
u8_t i = 0, age_pending = 0, age_stable = 0;
s8_t old_queue = ARP_TABLE_SIZE;
u8_t age_queue = 0;
/**
* a) do a search through the cache, remember candidates
* b) select candidate entry
* c) create new entry
*/
/* a) in a single search sweep, do all of this
* 1) remember the first empty entry (if any)
* 2) remember the oldest stable entry (if any)
* 3) remember the oldest pending entry without queued packets (if any)
* 4) remember the oldest pending entry with queued packets (if any)
* 5) search for a matching IP entry, either pending or stable
* until 5 matches, or all entries are searched for.
*/
for (i = 0; i < ARP_TABLE_SIZE; ++i) {
u8_t state = arp_table[i].state;
if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
empty = i;
} else if (state != ETHARP_STATE_EMPTY) {
if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
return i;
}
if (state == ETHARP_STATE_PENDING) {
if (arp_table[i].q != NULL) {
if (arp_table[i].ctime >= age_queue) {
old_queue = i;
age_queue = arp_table[i].ctime;
}
} else
{
if (arp_table[i].ctime >= age_pending) {
old_pending = i;
age_pending = arp_table[i].ctime;
}
}
} else if (state >= ETHARP_STATE_STABLE) {
{
/* remember entry with oldest stable entry in oldest, its age in maxtime */
if (arp_table[i].ctime >= age_stable) {
old_stable = i;
age_stable = arp_table[i].ctime;
}
}
}
}
}
/* { we have no match } => try to create a new entry */
if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) ||
((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) {
return (s8_t)ERR_MEM;
}
/* b) choose the least destructive entry to recycle:
* 1) empty entry
* 2) oldest stable entry
* 3) oldest pending entry without queued packets
* 4) oldest pending entry with queued packets
*
* { ETHARP_FLAG_TRY_HARD is set at this point }
*/
/* 1) empty entry available? */
if (empty < ARP_TABLE_SIZE) {
i = empty;
} else {
/* 2) found recyclable stable entry? */
if (old_stable < ARP_TABLE_SIZE) {
/* recycle oldest stable*/
i = old_stable;
/* 3) found recyclable pending entry without queued packets? */
} else if (old_pending < ARP_TABLE_SIZE) {
i = old_pending;
/* 4) found recyclable pending entry with queued packets? */
} else if (old_queue < ARP_TABLE_SIZE) {
i = old_queue;
} else {
return (s8_t)ERR_MEM;
}
etharp_free_entry(i);
}
/* IP address given? */
if (ipaddr != NULL) {
/* set IP address */
ip_addr_copy(arp_table[i].ipaddr, *ipaddr);
}
arp_table[i].ctime = 0;
return (err_t)i;
}
static err_t etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
{
s8_t i;
if (ip_addr_isany(ipaddr) ||
ip_addr_isbroadcast(ipaddr, netif) ||
ip_addr_ismulticast(ipaddr)) {
return ERR_ARG;
}
i = etharp_find_entry(ipaddr, flags);
if (i < 0) {
return (err_t)i;
}
/* mark it stable */
arp_table[i].state = ETHARP_STATE_STABLE;
arp_table[i].netif = netif;
ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr);
arp_table[i].ctime = 0;
while (arp_table[i].q != NULL) {
struct pbuf *p;
struct etharp_q_entry *q = arp_table[i].q;
arp_table[i].q = q->next;
p = q->p;
memp_free(MEMP_ARP_QUEUE, q);
// 发送挂接的数据包
etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr);
pbuf_free(p);
}
return ERR_OK;
}
5、数据包输出
(1)etharp_output—IP层调用的数据包发送函数
err_t
etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
{
struct eth_addr *dest;
struct eth_addr mcastaddr;
ip_addr_t *dst_addr = ipaddr;
/* make room for Ethernet header - should not fail */
if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
return ERR_BUF;
}
//首先确定发送目的地址是广播地址、多播地址还是单播地址,根据IP地址类型填充MAC地址
if (ip_addr_isbroadcast(ipaddr, netif)) {
dest = (struct eth_addr *)ðbroadcast;
} else if (ip_addr_ismulticast(ipaddr)) {
mcastaddr.addr[0] = LL_MULTICAST_ADDR_0;
mcastaddr.addr[1] = LL_MULTICAST_ADDR_1;
mcastaddr.addr[2] = LL_MULTICAST_ADDR_2;
mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
mcastaddr.addr[4] = ip4_addr3(ipaddr);
mcastaddr.addr[5] = ip4_addr4(ipaddr);
dest = &mcastaddr;
} else {
s8_t i;
//判断发送IP地址与网卡IP地址是否在同一网段内
if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) &&
!ip_addr_islinklocal(ipaddr)) {
//检查网关地址的合法性
if (!ip_addr_isany(&netif->gw)) {
//若不在同一网络,目的地址改为网关IP地址
dst_addr = &(netif->gw);
} else {
return ERR_RTE;
}
}
}
//查找是否与最近一次ARP查找表项IP地址相同,全局变量etharp_cached_entry
if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) &&
(ip_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) {
return etharp_output_to_arp_index(netif, q, etharp_cached_entry);
}
//此代码段为提高查找效率
/* find stable entry: do this here since this is a critical path for
throughput and etharp_find_entry() is kind of slow */
for (i = 0; i < ARP_TABLE_SIZE; i++) {
if ((arp_table[i].state >= ETHARP_STATE_STABLE) &&
(ip_addr_cmp(dst_addr, &arp_table[i].ipaddr))) {
return etharp_output_to_arp_index(netif, q, i);
}
}
//无对stable ARP缓存表项,直接更新ARP缓存表
return etharp_query(netif, dst_addr, q);
}
//多播地址和广播地址直接发送报文
return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest);
}
(2)etharp_query
该函数的主要用于查找可用缓存表表项,并将发送的数据包挂接在缓存表项的队列上。挂接的pbuf在收到ARP应答包时调用函数etharp_update_arp_entry时发送。
err_t
etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
{
struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
err_t result = ERR_MEM;
s8_t i; /* ARP entry index */
/* non-unicast address? */
if (ip_addr_isbroadcast(ipaddr, netif) ||
ip_addr_ismulticast(ipaddr) ||
ip_addr_isany(ipaddr)) {
return ERR_ARG;
}
/* find entry in ARP cache, ask to create entry if queueing packet */
i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD);
/* could not find or create entry? */
if (i < 0) {
return (err_t)i;
}
/* mark a fresh entry as pending (we just sent a request) */
if (arp_table[i].state == ETHARP_STATE_EMPTY) {
arp_table[i].state = ETHARP_STATE_PENDING;
}
if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) {
result = etharp_request(netif, ipaddr);
if (result != ERR_OK) {
}
if (q == NULL) {
return result;
}
}
/* stable entry? */
if (arp_table[i].state >= ETHARP_STATE_STABLE) {
result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr));
} else if (arp_table[i].state == ETHARP_STATE_PENDING) {
struct pbuf *p;
int copy_needed = 0;
p = q;
//不能挂接PBUF_ROM型的pbuf
while (p) {
if(p->type != PBUF_ROM) {
copy_needed = 1;
break;
}
p = p->next;
}
if(copy_needed) {
p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
if(p != NULL) {
if (pbuf_copy(p, q) != ERR_OK) {
pbuf_free(p);
p = NULL;
}
}
} else {
p = q;
pbuf_ref(p);
}
if (p != NULL) {
struct etharp_q_entry *new_entry;
new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
if (new_entry != NULL) {
new_entry->next = 0;
new_entry->p = p;
if(arp_table[i].q != NULL) {
struct etharp_q_entry *r;
r = arp_table[i].q;
while (r->next != NULL) {
r = r->next;
}
r->next = new_entry;
} else {
arp_table[i].q = new_entry;
}
result = ERR_OK;
} else {
pbuf_free(p);
result = ERR_MEM;
}
arp_table[i].q = p;
result = ERR_OK;
} else {
result = ERR_MEM;
}
}
return result;
}
(3)etharp_send_ip
该函数的作用是填充以太网帧首部14个字节,并调用low_level_output底层网卡发送数据包函数。
注意参数为发送方和接收方的MAC地址
static err_t
etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
{
struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload;
ETHADDR32_COPY(ðhdr->dest, dst);
ETHADDR16_COPY(ðhdr->src, src);
ethhdr->type = PP_HTONS(ETHTYPE_IP);
/* send the packet */
return netif->linkoutput(netif, p);
}
|