int icmpv6_ping(net_ipv6addr_t addr, uint16_t id, uint16_t seqno, uint16_t datalen, int dsecs) { struct icmpv6_ping_s state; net_lock_t save; #ifdef CONFIG_NET_ICMPv6_NEIGHBOR int ret; /* Make sure that the IP address mapping is in the Neighbor Table */ ret = icmpv6_neighbor(addr); if (ret < 0) { ndbg("ERROR: Not reachable\n"); return -ENETUNREACH; } #endif /* CONFIG_NET_ICMPv6_NEIGHBOR */ /* Initialize the state structure */ sem_init(&state.png_sem, 0, 0); state.png_ticks = DSEC2TICK(dsecs); /* System ticks to wait */ state.png_result = -ENOMEM; /* Assume allocation failure */ state.png_id = id; /* The ID to use in the ECHO request */ state.png_seqno = seqno; /* The seqno to use in the ECHO request */ state.png_datlen = datalen; /* The length of data to send in the ECHO request */ state.png_sent = false; /* ECHO request not yet sent */ net_ipv6addr_copy(state.png_addr, addr); /* Address of the peer to be ping'ed */ save = net_lock(); state.png_time = clock_systimer(); /* Set up the callback */ state.png_cb = icmpv6_callback_alloc(); if (state.png_cb) { state.png_cb->flags = (ICMPv6_POLL | ICMPv6_ECHOREPLY); state.png_cb->priv = (void*)&state; state.png_cb->event = ping_interrupt; state.png_result = -EINTR; /* Assume sem-wait interrupted by signal */ /* Notify the device driver of the availability of TX data */ #ifdef CONFIG_NETDEV_MULTINIC netdev_ipv6_txnotify(g_ipv6_allzeroaddr, state.png_addr); #else netdev_ipv6_txnotify(state.png_addr); #endif /* Wait for either the full round trip transfer to complete or * for timeout to occur. (1) net_lockedwait will also terminate if a * signal is received, (2) interrupts may be disabled! They will * be re-enabled while the task sleeps and automatically * re-enabled when the task restarts. */ nllvdbg("Start time: 0x%08x seqno: %d\n", state.png_time, seqno); net_lockedwait(&state.png_sem); icmpv6_callback_free(state.png_cb); } net_unlock(save); /* Return the negated error number in the event of a failure, or the * sequence number of the ECHO reply on success. */ if (!state.png_result) { nllvdbg("Return seqno=%d\n", state.png_seqno); return (int)state.png_seqno; } else { nlldbg("Return error=%d\n", -state.png_result); return state.png_result; } }
int icmpv6_neighbor(const net_ipv6addr_t ipaddr) { FAR struct net_driver_s *dev; struct icmpv6_notify_s notify; struct timespec delay; struct icmpv6_neighbor_s state; FAR const uint16_t *lookup; net_lock_t save; int ret; /* First check if destination is a local broadcast or a multicast address. * * - IPv6 multicast addresses are have the high-order octet of the * addresses=0xff (ff00::/8.) */ if (net_ipv6addr_cmp(ipaddr, g_ipv6_allzeroaddr) || (ipaddr[0] & NTOHS(0xff00)) == NTOHS(0xff00)) { /* We don't need to send the Neighbor Solicitation */ return OK; } /* Get the device that can route this request */ #ifdef CONFIG_NETDEV_MULTINIC dev = netdev_findby_ipv6addr(g_ipv6_allzeroaddr, ipaddr); #else dev = netdev_findby_ipv6addr(ipaddr); #endif if (!dev) { ndbg("ERROR: Unreachable: %08lx\n", (unsigned long)ipaddr); ret = -EHOSTUNREACH; goto errout; } #ifdef CONFIG_NET_MULTILINK /* If we are supporting multiple network devices and using different * link level protocols then we can get here for other link protocols * as well. Continue and send the Neighbor Solicitation request only * if this device uses the Ethernet data link protocol. * * REVISIT: Other link layer protocols may require Neighbor Discovery * as well (but not SLIP which is the only other option at the moment). */ if (dev->d_lltype != NET_LL_ETHERNET) { return OK; } #endif /* Check if the destination address is on the local network. */ if (net_ipv6addr_maskcmp(ipaddr, dev->d_ipv6addr, dev->d_ipv6netmask)) { /* Yes.. use the input address for the lookup */ lookup = ipaddr; } else { net_ipv6addr_t dripaddr; /* Destination address is not on the local network */ #ifdef CONFIG_NET_ROUTE /* We have a routing table.. find the correct router to use in * this case (or, as a fall-back, use the device's default router * address). We will use the router IP address instead of the * destination address when determining the MAC address. */ netdev_ipv6_router(dev, ipaddr, dripaddr); #else /* Use the device's default router IP address instead of the * destination address when determining the MAC address. */ net_ipv6addr_copy(dripaddr, dev->d_ipv6draddr); #endif /* Use the router address for the lookup */ lookup = dripaddr; } /* Allocate resources to receive a callback. This and the following * initialization is performed with the network lock because we don't * want anything to happen until we are ready. */ save = net_lock(); state.snd_cb = icmpv6_callback_alloc(); if (!state.snd_cb) { ndbg("ERROR: Failed to allocate a cllback\n"); ret = -ENOMEM; goto errout_with_lock; } /* Initialize the state structure. This is done with interrupts * disabled */ (void)sem_init(&state.snd_sem, 0, 0); /* Doesn't really fail */ state.snd_retries = 0; /* No retries yet */ net_ipv6addr_copy(state.snd_ipaddr, lookup); /* IP address to query */ #ifdef CONFIG_NETDEV_MULTINIC /* Remember the routing device name */ strncpy((FAR char *)state.snd_ifname, (FAR const char *)dev->d_ifname, IFNAMSIZ); #endif /* Now loop, testing if the address mapping is in the Neighbor Table and * re-sending the Neighbor Solicitation if it is not. */ ret = -ETIMEDOUT; /* Assume a timeout failure */ while (state.snd_retries < CONFIG_ICMPv6_NEIGHBOR_MAXTRIES) { /* Check if the address mapping is present in the Neighbor Table. This * is only really meaningful on the first time through the loop. * * NOTE: If the Neighbor Table is large than this could be a performance * issue. */ if (neighbor_findentry(lookup) != NULL) { /* We have it! Break out with success */ ret = OK; break; } /* Set up the Neighbor Advertisement wait BEFORE we send the Neighbor * Solicitation. */ icmpv6_wait_setup(lookup, ¬ify); /* Arm/re-arm the callback */ state.snd_sent = false; state.snd_cb->flags = ICMPv6_POLL; state.snd_cb->priv = (FAR void *)&state; state.snd_cb->event = icmpv6_neighbor_interrupt; /* Notify the device driver that new TX data is available. */ dev->d_txavail(dev); /* Wait for the send to complete or an error to occur: NOTES: (1) * net_lockedwait will also terminate if a signal is received, (2) * interrupts may be disabled! They will be re-enabled while the * task sleeps and automatically re-enabled when the task restarts. */ do { (void)net_lockedwait(&state.snd_sem); } while (!state.snd_sent); /* Now wait for response to the Neighbor Advertisement to be received. * The optimal delay would be the work case round trip time. * NOTE: The network is locked. */ delay.tv_sec = CONFIG_ICMPv6_NEIGHBOR_DELAYSEC; delay.tv_nsec = CONFIG_ICMPv6_NEIGHBOR_DELAYNSEC; ret = icmpv6_wait(¬ify, &delay); /* icmpv6_wait will return OK if and only if the matching Neighbor * Advertisement is received. Otherwise, it will return -ETIMEDOUT. */ if (ret == OK) { break; } /* Increment the retry count */ state.snd_retries++; } sem_destroy(&state.snd_sem); icmpv6_callback_free(state.snd_cb); errout_with_lock: net_unlock(save); errout: return ret; }
static int icmpv6_send_message(FAR struct net_driver_s *dev, bool advertise) { struct icmpv6_router_s state; int ret; /* Initialize the state structure. This is done with interrupts * disabled */ /* This semaphore is used for signaling and, hence, should not have * priority inheritance enabled. */ (void)sem_init(&state.snd_sem, 0, 0); /* Doesn't really fail */ sem_setprotocol(&state.snd_sem, SEM_PRIO_NONE); #ifdef CONFIG_NETDEV_MULTINIC /* Remember the routing device name */ strncpy((FAR char *)state.snd_ifname, (FAR const char *)dev->d_ifname, IFNAMSIZ); #endif /* Allocate resources to receive a callback. This and the following * initialization is performed with the network lock because we don't * want anything to happen until we are ready. */ state.snd_cb = icmpv6_callback_alloc(dev); if (!state.snd_cb) { nerr("ERROR: Failed to allocate a cllback\n"); ret = -ENOMEM; goto errout_with_semaphore; } /* Arm the callback */ state.snd_sent = false; state.snd_result = -EBUSY; state.snd_advertise = advertise; state.snd_cb->flags = (ICMPv6_POLL | NETDEV_DOWN); state.snd_cb->priv = (FAR void *)&state; state.snd_cb->event = icmpv6_router_interrupt; /* Notify the device driver that new TX data is available. */ dev->d_txavail(dev); /* Wait for the send to complete or an error to occur: NOTES: (1) * net_lockedwait will also terminate if a signal is received, (2) * interrupts may be disabled! They will be re-enabled while the * task sleeps and automatically re-enabled when the task restarts. */ do { (void)net_lockedwait(&state.snd_sem); } while (!state.snd_sent); ret = state.snd_result; icmpv6_callback_free(dev, state.snd_cb); errout_with_semaphore: sem_destroy(&state.snd_sem); return ret; }