diff --git a/src/core/ipv6/nd6.c b/src/core/ipv6/nd6.c index e3acebeb..1f8102fd 100644 --- a/src/core/ipv6/nd6.c +++ b/src/core/ipv6/nd6.c @@ -397,7 +397,11 @@ nd6_input(struct pbuf *p, struct netif *inp) /* This is an unsolicited NA, most likely there was a LLADDR change. */ i = nd6_find_neighbor_cache_entry(&target_address); - if (i >= 0) { + if (i >= 0 +#if LWIP_ND6_SUPPORT_STATIC_ENTRIES + && (neighbor_cache[i].state != ND6_STATIC) +#endif /* LWIP_ND6_SUPPORT_STATIC_ENTRIES */ + ) { if (na_hdr->flags & ND6_FLAG_OVERRIDE) { MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); } @@ -415,6 +419,14 @@ nd6_input(struct pbuf *p, struct netif *inp) return; } +#if LWIP_ND6_SUPPORT_STATIC_ENTRIES + if (neighbor_cache[i].state == ND6_STATIC) { + /* Never overwrite a static entry. */ + pbuf_free(p); + return; + } +#endif /* LWIP_ND6_SUPPORT_STATIC_ENTRIES */ + /* Update cache entry. */ if ((na_hdr->flags & ND6_FLAG_OVERRIDE) || (neighbor_cache[i].state == ND6_INCOMPLETE)) { @@ -1117,6 +1129,11 @@ nd6_tmr(void) nd6_send_neighbor_cache_probe(&neighbor_cache[i], 0); } break; +#if LWIP_ND6_SUPPORT_STATIC_ENTRIES + case ND6_STATIC: + /* Static entries never expire and never trigger solicitations. */ + break; +#endif /* LWIP_ND6_SUPPORT_STATIC_ENTRIES */ case ND6_NO_ENTRY: default: /* Do nothing. */ @@ -1896,7 +1913,12 @@ nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif) * has no neighbor cache entry, due to the netif association tests. */ if (default_router_list[i].neighbor_entry->state != ND6_INCOMPLETE) { /* Is the router known to be reachable? */ - if (default_router_list[i].neighbor_entry->state == ND6_REACHABLE) { + if (default_router_list[i].neighbor_entry->state == ND6_REACHABLE +#if LWIP_ND6_SUPPORT_STATIC_ENTRIES + /* A static entry is always reachable by definition. */ + || default_router_list[i].neighbor_entry->state == ND6_STATIC +#endif /* LWIP_ND6_SUPPORT_STATIC_ENTRIES */ + ) { return i; /* valid and reachable - done! */ } else if (valid_router < 0) { valid_router = i; /* valid but not known to be reachable */ @@ -2555,7 +2577,11 @@ nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_ad neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL; } /* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */ - if ((neighbor_cache[i].state == ND6_REACHABLE) || + if ( +#if LWIP_ND6_SUPPORT_STATIC_ENTRIES + (neighbor_cache[i].state == ND6_STATIC) || +#endif /* LWIP_ND6_SUPPORT_STATIC_ENTRIES */ + (neighbor_cache[i].state == ND6_REACHABLE) || (neighbor_cache[i].state == ND6_DELAY) || (neighbor_cache[i].state == ND6_PROBE)) { @@ -2569,6 +2595,96 @@ nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_ad return nd6_queue_packet(i, q); } +#if LWIP_ND6_SUPPORT_STATIC_ENTRIES +/** + * Add or update a permanent (static) neighbor cache entry, bypassing Neighbor + * Discovery entirely: no Neighbor Solicitation/Advertisement is exchanged for + * this address. The entry is placed in the ND6_STATIC state, which the nd6 timer + * never ages, which the output path always treats as resolved, which incoming + * Neighbor Advertisements never overwrite, and which is never recycled to make + * room for dynamic entries. Call again with the same address to update the + * link-layer address. Intended for links where the peer's link-layer address is + * known a priori (for example, derived from a Modified EUI-64 interface + * identifier). + * + * This is the IPv6 counterpart of etharp_add_static_entry(). + * + * Must be called from the tcpip thread (or with the core lock held). + * + * @param netif the netif on which the neighbor is reachable + * @param ip6addr the neighbor's IPv6 address (zoned to netif internally) + * @param lladdr the neighbor's link-layer address (netif->hwaddr_len bytes) + * @return ERR_OK if the static entry was added/updated, ERR_ARG on bad + * arguments, ERR_MEM if the neighbor cache is full + */ +err_t +nd6_add_static_neighbor(struct netif *netif, const ip6_addr_t *ip6addr, const u8_t *lladdr) +{ + ip6_addr_t target; + s8_t i; + + LWIP_ASSERT_CORE_LOCKED(); + LWIP_ERROR("nd6_add_static_neighbor: invalid arg", + (netif != NULL) && (ip6addr != NULL) && (lladdr != NULL), return ERR_ARG;); + + /* Work on a properly zoned copy so lookup and storage are consistent. */ + ip6_addr_set(&target, ip6addr); + ip6_addr_assign_zone(&target, IP6_UNICAST, netif); + + i = nd6_find_neighbor_cache_entry(&target); + if (i < 0) { + i = nd6_new_neighbor_cache_entry(); + if (i < 0) { + /* Neighbor cache is full. */ + return ERR_MEM; + } + ip6_addr_copy(neighbor_cache[i].next_hop_address, target); + neighbor_cache[i].netif = netif; + } + MEMCPY(neighbor_cache[i].lladdr, lladdr, netif->hwaddr_len); + neighbor_cache[i].isrouter = 0; + neighbor_cache[i].state = ND6_STATIC; + return ERR_OK; +} + +/** + * Remove a static neighbor cache entry previously added with + * nd6_add_static_neighbor(). Dynamic (non-static) entries are left untouched. + * + * This is the IPv6 counterpart of etharp_remove_static_entry(). + * + * Must be called from the tcpip thread (or with the core lock held). + * + * @param netif the netif the entry was added on (used for address zoning) + * @param ip6addr the neighbor's IPv6 address + * @return ERR_OK if a static entry was removed, ERR_ARG on bad arguments, + * ERR_VAL if no matching static entry exists + */ +err_t +nd6_remove_static_neighbor(struct netif *netif, const ip6_addr_t *ip6addr) +{ + ip6_addr_t target; + s8_t i; + + LWIP_ASSERT_CORE_LOCKED(); + LWIP_ERROR("nd6_remove_static_neighbor: invalid arg", + (netif != NULL) && (ip6addr != NULL), return ERR_ARG;); + + ip6_addr_set(&target, ip6addr); + ip6_addr_assign_zone(&target, IP6_UNICAST, netif); + + i = nd6_find_neighbor_cache_entry(&target); + if ((i < 0) || (neighbor_cache[i].state != ND6_STATIC)) { + return ERR_VAL; + } + /* Clear isrouter (an RA may have set it), otherwise + * nd6_free_neighbor_cache_entry() silently refuses to free the entry. */ + neighbor_cache[i].isrouter = 0; + nd6_free_neighbor_cache_entry(i); + return ERR_OK; +} +#endif /* LWIP_ND6_SUPPORT_STATIC_ENTRIES */ + /** * Get the Path MTU for a destination. @@ -2637,6 +2753,12 @@ nd6_reachability_hint(const ip6_addr_t *ip6addr) return; } +#if LWIP_ND6_SUPPORT_STATIC_ENTRIES + if (neighbor_cache[i].state == ND6_STATIC) { + return; /* Static entries are always considered reachable. */ + } +#endif /* LWIP_ND6_SUPPORT_STATIC_ENTRIES */ + /* For safety: don't set as reachable if we don't have a LL address yet. Misuse protection. */ if (neighbor_cache[i].state == ND6_INCOMPLETE || neighbor_cache[i].state == ND6_NO_ENTRY) { return; diff --git a/src/include/lwip/nd6.h b/src/include/lwip/nd6.h index c30e624f..3ec7d05d 100644 --- a/src/include/lwip/nd6.h +++ b/src/include/lwip/nd6.h @@ -81,6 +81,11 @@ void nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_stat #endif /* LWIP_IPV6_MLD */ void nd6_restart_netif(struct netif *netif); +#if LWIP_ND6_SUPPORT_STATIC_ENTRIES +err_t nd6_add_static_neighbor(struct netif *netif, const ip6_addr_t *ip6addr, const u8_t *lladdr); +err_t nd6_remove_static_neighbor(struct netif *netif, const ip6_addr_t *ip6addr); +#endif /* LWIP_ND6_SUPPORT_STATIC_ENTRIES */ + #ifdef __cplusplus } #endif diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h index b6430fac..bea0f4cc 100644 --- a/src/include/lwip/opt.h +++ b/src/include/lwip/opt.h @@ -2715,6 +2715,17 @@ #define LWIP_ND6_SUPPORT_RIO 0 #endif +/** + * LWIP_ND6_SUPPORT_STATIC_ENTRIES==1: enable nd6_add_static_neighbor() and + * nd6_remove_static_neighbor() to manage permanent IPv6->MAC neighbor cache + * entries that bypass Neighbor Discovery (no NS/NA). IPv6 counterpart of + * ETHARP_SUPPORT_STATIC_ENTRIES. These functions must be called on the tcpip + * thread (or with the core lock held). + */ +#if !defined LWIP_ND6_SUPPORT_STATIC_ENTRIES || defined __DOXYGEN__ +#define LWIP_ND6_SUPPORT_STATIC_ENTRIES 0 +#endif + /** * LWIP_ND6_NUM_ROUTES: number of entries in IPv6 route list. */ diff --git a/src/include/lwip/priv/nd6_priv.h b/src/include/lwip/priv/nd6_priv.h index 5083fc32..3955a611 100644 --- a/src/include/lwip/priv/nd6_priv.h +++ b/src/include/lwip/priv/nd6_priv.h @@ -127,6 +127,10 @@ enum nd6_neighbor_cache_entry_state { ND6_STALE, ND6_DELAY, ND6_PROBE +#if LWIP_ND6_SUPPORT_STATIC_ENTRIES + , + ND6_STATIC +#endif /* LWIP_ND6_SUPPORT_STATIC_ENTRIES */ }; #define ND6_HOPLIM 255 /* maximum hop limit, required in all ND packets */