Index: src/include/netif/etharp.h =================================================================== RCS file: /sources/lwip/lwip/src/include/netif/etharp.h,v retrieving revision 1.46 diff -u -8 -p -r1.46 etharp.h --- src/include/netif/etharp.h 14 Feb 2010 18:08:17 -0000 1.46 +++ src/include/netif/etharp.h 6 Mar 2010 10:39:03 -0000 @@ -144,30 +144,39 @@ PACK_STRUCT_END #define ETHTYPE_PPPOE 0x8864 /* PPP Over Ethernet Session Stage */ #if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ /** ARP message types (opcodes) */ #define ARP_REQUEST 1 #define ARP_REPLY 2 +/** ARP flag types + * Try hard to create a new entry - we want the IP address to appear in + * the cache (even if this means removing an active entry or so). */ +#define ETHARP_TRY_HARD 1 +#define ETHARP_FIND_ONLY 2 +#define ETHARP_LOCK_ENTRY 4 + #if ARP_QUEUEING /** struct for queueing outgoing packets for unknown address * defined here to be accessed by memp.h */ struct etharp_q_entry { struct etharp_q_entry *next; struct pbuf *p; }; #endif /* ARP_QUEUEING */ #define etharp_init() /* Compatibility define, not init needed. */ void etharp_tmr(void); s8_t etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr **eth_ret, ip_addr_t **ip_ret); +err_t etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, + struct eth_addr *ethaddr, u8_t flags); err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr); err_t etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q); err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr); /** For Ethernet network interfaces, we might want to send "gratuitous ARP"; * this is an ARP packet sent by a node in order to spontaneously cause other * nodes to update an entry in their ARP cache. * From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */ #define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr) Index: src/netif/etharp.c =================================================================== RCS file: /sources/lwip/lwip/src/netif/etharp.c,v retrieving revision 1.163 diff -u -8 -p -r1.163 etharp.c --- src/netif/etharp.c 16 Feb 2010 21:05:06 -0000 1.163 +++ src/netif/etharp.c 6 Mar 2010 10:39:04 -0000 @@ -87,17 +87,18 @@ const struct eth_addr ethzero = {{0,0,0, #define ARPH_PROTOLEN(hdr) (ntohs((hdr)->_hwlen_protolen) & 0xff) #define ARPH_HWLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons(ARPH_PROTOLEN(hdr) | ((len) << 8)) #define ARPH_PROTOLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons((len) | (ARPH_HWLEN(hdr) << 8)) enum etharp_state { ETHARP_STATE_EMPTY = 0, ETHARP_STATE_PENDING, - ETHARP_STATE_STABLE + ETHARP_STATE_STABLE, + ETHARP_STATE_LOCKED_STABLE }; struct etharp_entry { #if ARP_QUEUEING /** * Pointer to queue of pending outgoing packets on this ARP entry. */ struct etharp_q_entry *q; @@ -109,32 +110,25 @@ struct etharp_entry { struct netif *netif; }; static struct etharp_entry arp_table[ARP_TABLE_SIZE]; #if !LWIP_NETIF_HWADDRHINT static u8_t etharp_cached_entry; #endif /* !LWIP_NETIF_HWADDRHINT */ -/** - * Try hard to create a new entry - we want the IP address to appear in - * the cache (even if this means removing an active entry or so). */ -#define ETHARP_TRY_HARD 1 -#define ETHARP_FIND_ONLY 2 #if LWIP_NETIF_HWADDRHINT #define NETIF_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \ *((netif)->addr_hint) = (hint); static s8_t find_entry(ip_addr_t *ipaddr, u8_t flags, struct netif *netif); #else /* LWIP_NETIF_HWADDRHINT */ static s8_t find_entry(ip_addr_t *ipaddr, u8_t flags); #endif /* LWIP_NETIF_HWADDRHINT */ -static err_t update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags); - /* Some checks, instead of etharp_init(): */ #if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) #error "If you want to use ARP, ARP_TABLE_SIZE must fit in an s8_t, so, you have to reduce it in your lwipopts.h" #endif #if ARP_QUEUEING @@ -247,27 +241,30 @@ find_entry(ip_addr_t *ipaddr, u8_t flags /* First, test if the last call to this function asked for the * same address. If so, we're really fast! */ if (ipaddr) { /* ipaddr to search for was given */ #if LWIP_NETIF_HWADDRHINT if ((netif != NULL) && (netif->addr_hint != NULL)) { /* per-pcb cached entry was given */ u8_t per_pcb_cache = *(netif->addr_hint); - if ((per_pcb_cache < ARP_TABLE_SIZE) && arp_table[per_pcb_cache].state == ETHARP_STATE_STABLE) { + if (((per_pcb_cache < ARP_TABLE_SIZE) && + ((arp_table[per_pcb_cache].state == ETHARP_STATE_STABLE) || + (arp_table[per_pcb_cache].state == ETHARP_STATE_LOCKED_STABLE))) { /* the per-pcb-cached entry is stable */ if (ip_addr_cmp(ipaddr, &arp_table[per_pcb_cache].ipaddr)) { /* per-pcb cached entry was the right one! */ ETHARP_STATS_INC(etharp.cachehit); return per_pcb_cache; } } } #else /* #if LWIP_NETIF_HWADDRHINT */ - if (arp_table[etharp_cached_entry].state == ETHARP_STATE_STABLE) { + if ((arp_table[etharp_cached_entry].state == ETHARP_STATE_STABLE) || + (arp_table[etharp_cached_entry].state == ETHARP_STATE_LOCKED_STABLE)) { /* the cached entry is stable */ if (ip_addr_cmp(ipaddr, &arp_table[etharp_cached_entry].ipaddr)) { /* cached entry was the right one! */ ETHARP_STATS_INC(etharp.cachehit); return etharp_cached_entry; } } #endif /* #if LWIP_NETIF_HWADDRHINT */ @@ -336,16 +333,31 @@ find_entry(ip_addr_t *ipaddr, u8_t flags #endif /* #if LWIP_NETIF_HWADDRHINT */ return i; /* remember entry with oldest stable entry in oldest, its age in maxtime */ } else if (arp_table[i].ctime >= age_stable) { old_stable = i; age_stable = arp_table[i].ctime; } } + /* locked stable entry? */ + else if (arp_table[i].state == ETHARP_STATE_LOCKED_STABLE) { + /* if given, does IP address match IP address in ARP entry? */ + if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching locked stable entry %"U16_F"\n", (u16_t)i)); + /* found exact IP address match, simply bail out */ +#if LWIP_NETIF_HWADDRHINT + NETIF_SET_HINT(netif, i); +#else /* #if LWIP_NETIF_HWADDRHINT */ + etharp_cached_entry = i; +#endif /* #if LWIP_NETIF_HWADDRHINT */ + return i; + } + } + } /* { we have no match } => try to create a new entry */ /* don't create new entry, only search? */ if (((flags & ETHARP_FIND_ONLY) != 0) || /* or no empty entry found and not allowed to recycle? */ ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_TRY_HARD) == 0))) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty entry found and not allowed to recycle\n")); @@ -386,16 +398,17 @@ find_entry(ip_addr_t *ipaddr, u8_t flags /* recycle oldest pending */ i = old_queue; LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q))); free_etharp_q(arp_table[i].q); arp_table[i].q = NULL; #endif /* ARP_QUEUEING */ /* no empty or recyclable entries found */ } else { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty or recyclable entries found\n")); return (s8_t)ERR_MEM; } /* { empty or recyclable entry found } */ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); if (arp_table[i].state != ETHARP_STATE_EMPTY) { @@ -458,57 +471,79 @@ etharp_send_ip(struct netif *netif, stru * @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_MEM Not allowed to overwrite a static entry with a dynamic entry. * - 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, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags) +err_t +etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags) { s8_t i; u8_t k; - LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry()\n")); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_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", + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_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_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(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")); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_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; + + /* The update policy and the new state of the entry will depend on its current state and the provided flags + * + * New state / update? flags + * ETHARP_LOCK_ENTRY + * Current state + * EMPTY STABLE / update LOCKED_STABLE / update + * PENDING STABLE / update LOCKED_STABLE / update + * STABLE STABLE / update LOCKED_STABLE / update + * LOCKED_STABLE LOCKED_STABLE / no update LOCKED_STABLE / update + */ + + /* update locked entries only if specified so. */ + if ((arp_table[i].state == ETHARP_STATE_LOCKED_STABLE) && ((flags & ETHARP_LOCK_ENTRY) == 0)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: not allowed to update locked entry %"S16_F" state %"S16_F"\n", (s16_t)i, (s16_t)arp_table[i].state)); + return ERR_MEM; + } + + if ((flags & ETHARP_LOCK_ENTRY) != 0) + arp_table[i].state = ETHARP_STATE_LOCKED_STABLE; + else if (arp_table[i].state != ETHARP_STATE_LOCKED_STABLE) + arp_table[i].state = ETHARP_STATE_STABLE; + /* else keep locked state */ + /* 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)); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating entry %"S16_F" state %"S16_F"\n", (s16_t)i, (s16_t)arp_table[i].state)); /* 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; @@ -552,17 +587,18 @@ etharp_find_addr(struct netif *netif, ip 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) { + if((i >= 0) && + ((arp_table[i].state == ETHARP_STATE_STABLE) || (arp_table[i].state == ETHARP_STATE_LOCKED_STABLE))) { *eth_ret = &arp_table[i].ethaddr; *ip_ret = &arp_table[i].ipaddr; return i; } return -1; } #if ETHARP_TRUST_IP_MAC @@ -603,17 +639,17 @@ etharp_ip_input(struct netif *netif, str /* do nothing */ return; } LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n")); /* update the source IP address in the cache, if present */ /* @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, &(iphdr->src), &(ethhdr->src), ETHARP_FIND_ONLY); + etharp_update_arp_entry(netif, &(iphdr->src), &(ethhdr->src), ETHARP_FIND_ONLY); } #endif /* ETHARP_TRUST_IP_MAC */ /** * 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 @@ -696,21 +732,21 @@ etharp_arp_input(struct netif *netif, st /* ARP packet directed to us? */ for_us = (u8_t)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); + etharp_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), ETHARP_FIND_ONLY); + etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), ETHARP_FIND_ONLY); } /* 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, @@ -926,37 +962,39 @@ etharp_query(struct netif *netif, ip_add 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; } - /* { i is either a STABLE or (new or existing) PENDING entry } */ - LWIP_ASSERT("arp_table[i].state == PENDING or STABLE", + /* { i is either a STABLE, LOCKED_STABLE or (new or existing) PENDING entry } */ + LWIP_ASSERT("arp_table[i].state == PENDING, STABLE or LOCKED_STABLE", ((arp_table[i].state == ETHARP_STATE_PENDING) || - (arp_table[i].state == ETHARP_STATE_STABLE))); + (arp_table[i].state == ETHARP_STATE_STABLE) || + (arp_table[i].state == ETHARP_STATE_LOCKED_STABLE))); /* do we have a pending entry? or an implicit query request? */ if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) { /* try to resolve it; send out ARP request */ result = etharp_request(netif, ipaddr); if (result != ERR_OK) { /* ARP request couldn't be sent */ /* We don't re-send arp request in etharp_tmr, but we still queue packets, since this failure could be temporary, and the next packet calling etharp_query again could lead to sending the queued packets. */ } } /* packet given? */ if (q != NULL) { /* stable entry? */ - if (arp_table[i].state == ETHARP_STATE_STABLE) { + if ((arp_table[i].state == ETHARP_STATE_STABLE) || + (arp_table[i].state == ETHARP_STATE_LOCKED_STABLE)) { /* we have a valid IP->Ethernet address mapping */ /* send the packet */ result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr)); /* pending entry? (either just created or already pending */ } else if (arp_table[i].state == ETHARP_STATE_PENDING) { #if ARP_QUEUEING /* queue the given q packet */ struct pbuf *p; int copy_needed = 0;