Exemplo n.º 1
0
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;
    }
}
Exemplo n.º 2
0
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, &notify);

      /* 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(&notify, &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;
}
Exemplo n.º 3
0
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;
}