📄 etharp.c
字号:
/* { empty or recyclable entry found } */
LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
if (arp_table[i].state != ETHARP_STATE_EMPTY)
{
snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
}
/* recycle entry (no-op for an already empty entry) */
arp_table[i].state = ETHARP_STATE_EMPTY;
/* IP address given? */
if (ipaddr != NULL) {
/* set IP address */
ip_addr_set(&arp_table[i].ipaddr, ipaddr);
}
arp_table[i].ctime = 0;
#if LWIP_NETIF_HWADDRHINT
NETIF_SET_HINT(netif, i);
#else /* #if LWIP_NETIF_HWADDRHINT */
etharp_cached_entry = i;
#endif /* #if LWIP_NETIF_HWADDRHINT */
return (err_t)i;
}
/**
* Send an IP packet on the network using netif->linkoutput
* The ethernet header is filled in before sending.
*
* @params netif the lwIP network interface on which to send the packet
* @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header
* @params src the source MAC address to be copied into the ethernet header
* @params dst the destination MAC address to be copied into the ethernet header
* @return ERR_OK if the packet was sent, any other err_t on failure
*/
static err_t
etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
{
struct eth_hdr *ethhdr = p->payload;
u8_t k;
LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
(netif->hwaddr_len == ETHARP_HWADDR_LEN));
k = ETHARP_HWADDR_LEN;
while(k > 0) {
k--;
ethhdr->dest.addr[k] = dst->addr[k];
ethhdr->src.addr[k] = src->addr[k];
}
ethhdr->type = htons(ETHTYPE_IP);
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p));
/* send the packet */
return netif->linkoutput(netif, p);
}
/**
* Update (or insert) a IP/MAC address pair in the ARP cache.
*
* If a pending entry is resolved, any queued packets will be sent
* at this point.
*
* @param ipaddr IP address of the inserted ARP entry.
* @param ethaddr Ethernet address of the inserted ARP entry.
* @param flags Defines behaviour:
* - ETHARP_TRY_HARD Allows ARP to insert this as a new item. If not specified,
* only existing ARP entries will be updated.
*
* @return
* - ERR_OK Succesfully updated ARP cache.
* - ERR_MEM If we could not add a new ARP entry when ETHARP_TRY_HARD was set.
* - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
*
* @see pbuf_free()
*/
static err_t
update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags)
{
s8_t i;
u8_t k;
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | 3, ("update_arp_entry()\n"));
LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN);
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
ip4_addr1(ipaddr), ip4_addr2(ipaddr), ip4_addr3(ipaddr), ip4_addr4(ipaddr),
ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
/* non-unicast address? */
if (ip_addr_isany(ipaddr) ||
ip_addr_isbroadcast(ipaddr, netif) ||
ip_addr_ismulticast(ipaddr)) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
return ERR_ARG;
}
/* find or create ARP entry */
#if LWIP_NETIF_HWADDRHINT
i = find_entry(ipaddr, flags, netif);
#else /* LWIP_NETIF_HWADDRHINT */
i = find_entry(ipaddr, flags);
#endif /* LWIP_NETIF_HWADDRHINT */
/* bail out if no entry could be found */
if (i < 0)
return (err_t)i;
/* mark it stable */
arp_table[i].state = ETHARP_STATE_STABLE;
/* record network interface */
arp_table[i].netif = netif;
/* insert in SNMP ARP index tree */
snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr);
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
/* update address */
k = ETHARP_HWADDR_LEN;
while (k > 0) {
k--;
arp_table[i].ethaddr.addr[k] = ethaddr->addr[k];
}
/* reset time stamp */
arp_table[i].ctime = 0;
#if ARP_QUEUEING
/* this is where we will send out queued packets! */
while (arp_table[i].q != NULL) {
struct pbuf *p;
/* remember remainder of queue */
struct etharp_q_entry *q = arp_table[i].q;
/* pop first item off the queue */
arp_table[i].q = q->next;
/* get the packet pointer */
p = q->p;
/* now queue entry can be freed */
memp_free(MEMP_ARP_QUEUE, q);
/* send the queued IP packet */
etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr);
/* free the queued IP packet */
pbuf_free(p);
}
#endif
return ERR_OK;
}
/**
* Finds (stable) ethernet/IP address pair from ARP table
* using interface and IP address index.
* @note the addresses in the ARP table are in network order!
*
* @param netif points to interface index
* @param ipaddr points to the (network order) IP address index
* @param eth_ret points to return pointer
* @param ip_ret points to return pointer
* @return table index if found, -1 otherwise
*/
s8_t
etharp_find_addr(struct netif *netif, struct ip_addr *ipaddr,
struct eth_addr **eth_ret, struct ip_addr **ip_ret)
{
s8_t i;
LWIP_UNUSED_ARG(netif);
#if LWIP_NETIF_HWADDRHINT
i = find_entry(ipaddr, ETHARP_FIND_ONLY, NULL);
#else /* LWIP_NETIF_HWADDRHINT */
i = find_entry(ipaddr, ETHARP_FIND_ONLY);
#endif /* LWIP_NETIF_HWADDRHINT */
if((i >= 0) && arp_table[i].state == ETHARP_STATE_STABLE) {
*eth_ret = &arp_table[i].ethaddr;
*ip_ret = &arp_table[i].ipaddr;
return i;
}
return -1;
}
/**
* Updates the ARP table using the given IP packet.
*
* Uses the incoming IP packet's source address to update the
* ARP cache for the local network. The function does not alter
* or free the packet. This function must be called before the
* packet p is passed to the IP layer.
*
* @param netif The lwIP network interface on which the IP packet pbuf arrived.
* @param p The IP packet that arrived on netif.
*
* @return NULL
*
* @see pbuf_free()
*/
void
etharp_ip_input(struct netif *netif, struct pbuf *p)
{
struct ethip_hdr *hdr;
LWIP_ERROR("netif != NULL", (netif != NULL), return;);
/* Only insert an entry if the source IP address of the
incoming IP packet comes from a host on the local network. */
hdr = p->payload;
/* source is not on the local network? */
if (!ip_addr_netcmp(&(hdr->ip.src), &(netif->ip_addr), &(netif->netmask))) {
/* do nothing */
return;
}
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n"));
/* update ARP table */
/* @todo We could use ETHARP_TRY_HARD if we think we are going to talk
* back soon (for example, if the destination IP address is ours. */
update_arp_entry(netif, &(hdr->ip.src), &(hdr->eth.src), 0);
}
/**
* Responds to ARP requests to us. Upon ARP replies to us, add entry to cache
* send out queued IP packets. Updates cache with snooped address pairs.
*
* Should be called for incoming ARP packets. The pbuf in the argument
* is freed by this function.
*
* @param netif The lwIP network interface on which the ARP packet pbuf arrived.
* @param ethaddr Ethernet address of netif.
* @param p The ARP packet that arrived on netif. Is freed by this function.
*
* @return NULL
*
* @see pbuf_free()
*/
void
etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
{
struct etharp_hdr *hdr;
/* these are aligned properly, whereas the ARP header fields might not be */
struct ip_addr sipaddr, dipaddr;
u8_t i;
u8_t for_us;
#if LWIP_AUTOIP
const u8_t * ethdst_hwaddr;
#endif /* LWIP_AUTOIP */
LWIP_ERROR("netif != NULL", (netif != NULL), return;);
/* drop short ARP packets: we have to check for p->len instead of p->tot_len here
since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */
if (p->len < sizeof(struct etharp_hdr)) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | 1, ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len, (s16_t)sizeof(struct etharp_hdr)));
ETHARP_STATS_INC(etharp.lenerr);
ETHARP_STATS_INC(etharp.drop);
pbuf_free(p);
return;
}
hdr = p->payload;
/* RFC 826 "Packet Reception": */
if ((hdr->hwtype != htons(HWTYPE_ETHERNET)) ||
(hdr->_hwlen_protolen != htons((ETHARP_HWADDR_LEN << 8) | sizeof(struct ip_addr))) ||
(hdr->proto != htons(ETHTYPE_IP)) ||
(hdr->ethhdr.type != htons(ETHTYPE_ARP))) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | 1,
("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
hdr->hwtype, ARPH_HWLEN(hdr), hdr->proto, ARPH_PROTOLEN(hdr), hdr->ethhdr.type));
ETHARP_STATS_INC(etharp.proterr);
ETHARP_STATS_INC(etharp.drop);
pbuf_free(p);
return;
}
ETHARP_STATS_INC(etharp.recv);
#if LWIP_AUTOIP
/* We have to check if a host already has configured our random
* created link local address and continously check if there is
* a host with this IP-address so we can detect collisions */
autoip_arp_reply(netif, hdr);
#endif /* LWIP_AUTOIP */
/* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
* structure packing (not using structure copy which breaks strict-aliasing rules). */
SMEMCPY(&sipaddr, &hdr->sipaddr, sizeof(sipaddr));
SMEMCPY(&dipaddr, &hdr->dipaddr, sizeof(dipaddr));
/* this interface is not configured? */
if (netif->ip_addr.addr == 0) {
for_us = 0;
} else {
/* ARP packet directed to us? */
for_us = ip_addr_cmp(&dipaddr, &(netif->ip_addr));
}
/* ARP message directed to us? */
if (for_us) {
/* add IP address in ARP cache; assume requester wants to talk to us.
* can result in directly sending the queued packets for this host. */
update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), ETHARP_TRY_HARD);
/* ARP message not directed to us? */
} else {
/* update the source IP address in the cache, if present */
update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), 0);
}
/* now act on the message itself */
switch (htons(hdr->opcode)) {
/* ARP request? */
case ARP_REQUEST:
/* ARP request. If it asked for our address, we send out a
* reply. In any case, we time-stamp any existing ARP entry,
* and possiby send out an IP packet that was queued on it. */
LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n"));
/* ARP request for our address? */
if (for_us) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n"));
/* Re-use pbuf to send ARP reply.
Since we are re-using an existing pbuf, we can't call etharp_raw since
that would allocate a new pbuf. */
hdr->opcode = htons(ARP_REPLY);
hdr->dipaddr = hdr->sipaddr;
hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr;
LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
(netif->hwaddr_len == ETHARP_HWADDR_LEN));
i = ETHARP_HWADDR_LEN;
#if LWIP_AUTOIP
/* If we are using Link-Local, ARP packets must be broadcast on the
* link layer. (See RFC3927 Section 2.5) */
ethdst_hwaddr = ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr;
#endif /* LWIP_AUTOIP */
while(i > 0) {
i--;
hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i];
#if LWIP_AUTOIP
hdr->ethhdr.dest.addr[i] = ethdst_hwaddr[i];
#else /* LWIP_AUTOIP */
hdr->ethhdr.dest.addr[i] = hdr->shwaddr.addr[i];
#endif /* LWIP_AUTOIP */
hdr->shwaddr.addr[i] = ethaddr->addr[i];
hdr->ethhdr.src.addr[i] = ethaddr->addr[i];
}
/* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header
are already correct, we tested that before */
/* return ARP reply */
netif->linkoutput(netif, p);
/* we are not configured? */
} else if (netif->ip_addr.addr == 0) {
/* { for_us == 0 and netif->ip_addr.addr == 0 } */
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n"));
/* request was not directed to us */
} else {
/* { for_us == 0 and netif->ip_addr.addr != 0 } */
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n"));
}
break;
case ARP_REPLY:
/* ARP reply. We already updated the ARP cache earlier. */
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n"));
#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
/* DHCP wants to know about ARP replies from any host with an
* IP address also offered to us by the DHCP server. We do not
* want to take a duplicate IP address on a single network.
* @todo How should we handle redundant (fail-over) interfaces? */
dhcp_arp_reply(netif, &sipaddr);
#endif
break;
default:
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode)));
ETHARP_STATS_INC(etharp.err);
break;
}
/* free ARP packet */
pbuf_free(p);
}
/**
* Resolve and fill-in Ethernet address header for outgoing IP packet.
*
* For IP multicast and broadcast, corresponding Ethernet addresses
* are selected and the packet is transmitted on the link.
*
* For unicast addresses, the packet is submitted to etharp_query(). In
* case the IP address is outside the local network, the IP address of
* the gateway is used.
*
* @param netif The lwIP network interface which the IP packet will be sent on.
* @param q The pbuf(s) containing the IP packet to be sent.
* @param ipaddr The IP address of the packet destination.
*
* @return
* - ERR_RTE No route to destination (no gateway to external networks),
* or the return type of either etharp_query() or etharp_send_ip().
*/
err_t
etharp_output(struct netif *netif, struct pbuf *q, struct ip_addr *ipaddr)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -