diff --git a/src/core/ipv6/ethip6.c b/src/core/ipv6/ethip6.c index 35d424e3..32d0edf6 100644 --- a/src/core/ipv6/ethip6.c +++ b/src/core/ipv6/ethip6.c @@ -62,7 +62,9 @@ * For IPv6 multicast, corresponding Ethernet addresses * are selected and the packet is transmitted on the link. * - * For unicast addresses, ... + * For unicast addresses, ask the ND6 module what to do. It will either let us + * send the the packet right away, or queue the packet for later itself, unless + * an error occurs. * * @todo anycast addresses * @@ -71,14 +73,14 @@ * @param ip6addr 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 nd6_queue_packet() or ethernet_output(). + * - ERR_OK or the return value of nd6_packet_send_check. */ err_t ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr) { struct eth_addr dest; - s8_t i; + const u8_t *hwaddr; + err_t result; /* multicast destination IP address? */ if (ip6_addr_ismulticast(ip6addr)) { @@ -96,31 +98,21 @@ ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr) /* We have a unicast destination IP address */ /* @todo anycast? */ - /* Get next hop record. */ - i = nd6_get_next_hop_entry(ip6addr, netif); - if (i < 0) { - /* failed to get a next hop neighbor record. */ - return i; + + /* Ask ND6 what to do with the packet. */ + result = nd6_packet_send_check(netif, q, ip6addr, &hwaddr); + if (result != ERR_OK) { + return result; } - /* Now that we have a destination record, send or queue the packet. */ - if (neighbor_cache[i].state == ND6_STALE) { - /* Switch to delay state. */ - neighbor_cache[i].state = ND6_DELAY; - 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) || - (neighbor_cache[i].state == ND6_DELAY) || - (neighbor_cache[i].state == ND6_PROBE)) { - - /* Send out. */ - SMEMCPY(dest.addr, neighbor_cache[i].lladdr, 6); - return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6); + /* If no hardware address is returned, nd6 has queued the packet for later. */ + if (hwaddr == NULL) { + return ERR_OK; } - /* We should queue packet on this interface. */ - return nd6_queue_packet(i, q); + /* Send out the packet using the returned hardware address. */ + SMEMCPY(dest.addr, hwaddr, 6); + return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6); } #endif /* LWIP_IPV6 && LWIP_ETHERNET */ diff --git a/src/core/ipv6/nd6.c b/src/core/ipv6/nd6.c index fb4e7339..8bb337f1 100644 --- a/src/core/ipv6/nd6.c +++ b/src/core/ipv6/nd6.c @@ -98,6 +98,8 @@ static s8_t nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif); static s8_t nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif); static s8_t nd6_get_onlink_prefix(ip6_addr_t *prefix, struct netif *netif); static s8_t nd6_new_onlink_prefix(ip6_addr_t *prefix, struct netif *netif); +static s8_t nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif); +static err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf *q); #define ND6_SEND_FLAG_MULTICAST_DEST 0x01 #define ND6_SEND_FLAG_ALLNODES_DEST 0x02 @@ -1562,7 +1564,7 @@ nd6_new_onlink_prefix(ip6_addr_t *prefix, struct netif *netif) * suitable next hop was found, ERR_MEM if no cache entry * could be created */ -s8_t +static s8_t nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif) { #ifdef LWIP_HOOK_ND6_GET_GW @@ -1684,7 +1686,7 @@ nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif) * @param q packet to be queued * @return ERR_OK if succeeded, ERR_MEM if out of memory */ -err_t +static err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf *q) { err_t result = ERR_MEM; @@ -1861,6 +1863,60 @@ nd6_send_q(s8_t i) #endif /* LWIP_ND6_QUEUEING */ } +/** + * A packet is to be transmitted to a specific IPv6 destination on a specific + * interface. Check if we can find the hardware address of the next hop to use + * for the packet. If so, give the hardware address to the caller, which should + * use it to send the packet right away. Otherwise, enqueue the packet for + * later transmission while looking up the hardware address, if possible. + * + * As such, this function returns one of three different possible results: + * + * - ERR_OK with a non-NULL 'hwaddrp': the caller should send the packet now. + * - ERR_OK with a NULL 'hwaddrp': the packet has been enqueued for later. + * - not ERR_OK: something went wrong; forward the error upward in the stack. + * + * @param netif The lwIP network interface on which the IP packet will be sent. + * @param q The pbuf(s) containing the IP packet to be sent. + * @param ip6addr The destination IPv6 address of the packet. + * @param hwaddrp On success, filled with a pointer to a HW address or NULL. + * @return + * - ERR_OK on success, ERR_RTE if no route was found for the packet, + * or ERR_MEM if low memory conditions prohibit sending the packet at all. + */ +err_t +nd6_packet_send_check(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp) +{ + s8_t i; + + /* Get next hop record. */ + i = nd6_get_next_hop_entry(ip6addr, netif); + if (i < 0) { + /* failed to get a next hop neighbor record. */ + return i; + } + + /* Now that we have a destination record, send or queue the packet. */ + if (neighbor_cache[i].state == ND6_STALE) { + /* Switch to delay state. */ + neighbor_cache[i].state = ND6_DELAY; + 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) || + (neighbor_cache[i].state == ND6_DELAY) || + (neighbor_cache[i].state == ND6_PROBE)) { + + /* Tell the caller to send out the packet now. */ + *hwaddrp = neighbor_cache[i].lladdr; + return ERR_OK; + } + + /* We should queue packet on this interface. */ + *hwaddrp = NULL; + return nd6_queue_packet(i, q); +} + /** * Get the Path MTU for a destination. diff --git a/src/include/lwip/nd6.h b/src/include/lwip/nd6.h index 1d4b2f26..97df6b0b 100644 --- a/src/include/lwip/nd6.h +++ b/src/include/lwip/nd6.h @@ -142,10 +142,9 @@ extern u32_t retrans_timer; void nd6_tmr(void); void nd6_input(struct pbuf *p, struct netif *inp); void nd6_clear_destination_cache(void); -s8_t nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif); struct netif *nd6_find_route(const ip6_addr_t *ip6addr); +err_t nd6_packet_send_check(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp); u16_t nd6_get_destination_mtu(const ip6_addr_t *ip6addr, struct netif *netif); -err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf *p); #if LWIP_ND6_TCP_REACHABILITY_HINTS void nd6_reachability_hint(const ip6_addr_t *ip6addr); #endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ diff --git a/src/netif/lowpan6.c b/src/netif/lowpan6.c index eca9a453..36c041ad 100644 --- a/src/netif/lowpan6.c +++ b/src/netif/lowpan6.c @@ -616,7 +616,8 @@ lowpan4_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr) err_t lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr) { - s8_t i; + err_t result; + const u8_t *hwaddr; struct ieee_802154_addr src, dest; #if LWIP_6LOWPAN_INFER_SHORT_ADDRESS ip6_addr_t ip6_src; @@ -663,35 +664,23 @@ lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr) } #endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */ - - /* Get next hop record. */ - i = nd6_get_next_hop_entry(ip6addr, netif); - if (i < 0) { + /* Ask ND6 what to do with the packet. */ + result = nd6_packet_send_check(netif, q, ip6addr, &hwaddr); + if (result != ERR_OK) { MIB2_STATS_NETIF_INC(netif, ifoutdiscards); - /* failed to get a next hop neighbor record. */ - return ERR_MEM; + return result; } - /* Now that we have a destination record, send or queue the packet. */ - if (neighbor_cache[i].state == ND6_STALE) { - /* Switch to delay state. */ - neighbor_cache[i].state = ND6_DELAY; - neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; - } - /* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */ - if ((neighbor_cache[i].state == ND6_REACHABLE) || - (neighbor_cache[i].state == ND6_DELAY) || - (neighbor_cache[i].state == ND6_PROBE)) { - - /* Send out. */ - dest.addr_len = netif->hwaddr_len; - SMEMCPY(dest.addr, neighbor_cache[i].lladdr, netif->hwaddr_len); - MIB2_STATS_NETIF_INC(netif, ifoutucastpkts); - return lowpan6_frag(netif, q, &src, &dest); + /* If no hardware address is returned, nd6 has queued the packet for later. */ + if (hwaddr == NULL) { + return ERR_OK; } - /* We should queue packet on this interface. */ - return nd6_queue_packet(i, q); + /* Send out the packet using the returned hardware address. */ + dest.addr_len = netif->hwaddr_len; + SMEMCPY(dest.addr, hwaddr, netif->hwaddr_len); + MIB2_STATS_NETIF_INC(netif, ifoutucastpkts); + return lowpan6_frag(netif, q, &src, &dest); } static struct pbuf *