static int net_match_ipv6(FAR struct net_route_ipv6_s *route, FAR void *arg)
{
  FAR struct route_match_ipv6_s *match = (FAR struct route_match_ipv6_s *)arg;

  /* To match, the masked target address must be the same, and the masks
   * must be the same.
   */

  if (net_ipv6addr_maskcmp(route->target, match->target, match->netmask) &&
      net_ipv6addr_cmp(route->netmask, match->netmask))
    {
      /* They match.. Remove the entry from the routing table */

      if (match->prev)
        {
          (void)sq_remafter((FAR sq_entry_t *)match->prev,
                            (FAR sq_queue_t *)&g_routes_ipv6);
        }
      else
        {
          (void)sq_remfirst((FAR sq_queue_t *)&g_routes_ipv6);
        }

      /* And free the routing table entry by adding it to the free list */

      net_freeroute_ipv6(route);

      /* Return a non-zero value to terminate the traversal */

      return 1;
    }

  /* Next time we are here, this will be the previous entry */

  match->prev = route;
  return 0;
}
Beispiel #2
0
static int net_match_ipv6(FAR struct net_route_ipv6_s *route, FAR void *arg)
{
  FAR struct route_match_ipv6_s *match = (FAR struct route_match_ipv6_s *)arg;

  /* To match, the masked target address must be the same, and the masks
   * must be the same.
   */

  net_ipv6_dumproute("Comparing", route);
  ninfo("With:\n");
  ninfo("  target:  %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
        htons(match->target[0]),  htons(match->target[1]),
        htons(match->target[2]),  htons(match->target[3]),
        htons(match->target[4]),  htons(match->target[5]),
        htons(match->target[6]),  htons(match->target[7]));
  ninfo("  netmask: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
        htons(match->netmask[0]), htons(match->netmask[1]),
        htons(match->netmask[2]), htons(match->netmask[3]),
        htons(match->netmask[4]), htons(match->netmask[5]),
        htons(match->netmask[6]), htons(match->netmask[7]));

  if (net_ipv6addr_maskcmp(route->target, match->target, match->netmask) &&
      net_ipv6addr_cmp(route->netmask, match->netmask))
    {
      /* They match.. a non-zero value to terminate the traversal.  The last
       * value of index is the index to the matching entry.
       */

      return 1;
    }

  /* Next time we are here, this will be the routing table index */

  match->index++;
  return 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;
}
Beispiel #4
0
static uint16_t ping_interrupt(FAR struct net_driver_s *dev, FAR void *conn,
                               FAR void *pvpriv, uint16_t flags)
{
    FAR struct icmpv6_ping_s *pstate = (struct icmpv6_ping_s *)pvpriv;

    nllvdbg("flags: %04x\n", flags);

    if (pstate)
    {
        /* Check if this is a ICMPv6 ECHO reply.  If so, return the sequence
         * number to the caller.  NOTE: We may not even have sent the
         * requested ECHO request; this could have been the delayed ECHO
         * response from a previous ping.
         */

        if ((flags & ICMPv6_ECHOREPLY) != 0 && conn != NULL)
        {
            FAR struct icmpv6_echo_reply_s *reply = ICMPv6ECHOREPLY;

            nllvdbg("ECHO reply: id=%d seqno=%d\n",
                    ntohs(reply->id), ntohs(reply->seqno));

            if (ntohs(reply->id) == pstate->png_id)
            {
                /* Consume the ECHOREPLY */

                flags     &= ~ICMPv6_ECHOREPLY;
                dev->d_len = 0;

                /* Return the result to the caller */

                pstate->png_result = OK;
                pstate->png_seqno  = ntohs(reply->seqno);
                goto end_wait;
            }
        }

        /* Check:
         *   If the outgoing packet is available (it may have been claimed
         *   by a sendto interrupt serving a different thread)
         * -OR-
         *   If the output buffer currently contains unprocessed incoming
         *   data.
         * -OR-
         *   If we have already sent the ECHO request
         *
         * In the first two cases, we will just have to wait for the next
         * polling cycle.
         */

        if (dev->d_sndlen <= 0 &&             /* Packet available */
                (flags & ICMPv6_NEWDATA) == 0 &&  /* No incoming data */
                !pstate->png_sent)                /* Request not sent */
        {
            /* Send the ECHO request now. */

            icmpv6_echo_request(dev, pstate);
            pstate->png_sent = true;
            return flags;
        }

        /* Check if the selected timeout has elapsed */

        if (ping_timeout(pstate))
        {
            int failcode;

            /* Check if this device is on the same network as the destination
             * device.
             */

            if (!net_ipv6addr_maskcmp(pstate->png_addr, dev->d_ipv6addr,
                                      dev->d_ipv6netmask))
            {
                /* Destination address was not on the local network served by
                 * this device.  If a timeout occurs, then the most likely
                 * reason is that the destination address is not reachable.
                 */

                nlldbg("Not reachable\n");
                failcode = -ENETUNREACH;
            }
            else
            {
                nlldbg("Ping timeout\n");
                failcode = -ETIMEDOUT;
            }

            /* Report the failure */

            pstate->png_result = failcode;
            goto end_wait;
        }

        /* Continue waiting */
    }

    return flags;

end_wait:
    nllvdbg("Resuming\n");

    /* Do not allow any further callbacks */

    pstate->png_cb->flags   = 0;
    pstate->png_cb->priv    = NULL;
    pstate->png_cb->event   = NULL;

    /* Wake up the waiting thread */

    sem_post(&pstate->png_sem);
    return flags;
}
Beispiel #5
0
void neighbor_out(FAR struct net_driver_s *dev)
{
  FAR const struct neighbor_addr_s *naddr;
  FAR struct eth_hdr_s *eth = ETHBUF;
  FAR struct ipv6_hdr_s *ip = IPv6BUF;
  net_ipv6addr_t ipaddr;

  /* Skip sending Neighbor Solicitations when the frame to be transmitted was
   * written into a packet socket or if we are sending certain Neighbor
   * messages (solicitation, advertisement, echo request).
   */

  if (IFF_IS_NOARP(dev->d_flags))
    {
      /* Clear the indication and let the packet continue on its way. */

      IFF_CLR_NOARP(dev->d_flags);
      return;
    }

  /* Find the destination IPv6 address in the Neighbor Table and construct
   * the Ethernet header. If the destination IPv6 address isn't on the local
   * network, we use the default router's IPv6 address instead.
   *
   * If no Neighbor Table entry is found, we overwrite the original IPv6
   * packet with an Neighbor Solicitation Request for the IPv6 address.
   */

  /* First check if destination is a IPv6 multicast address.  IPv6
   * multicast addresses in IPv6 have the prefix ff00::/8
   * 
   *   Bits 120-127: Prefix
   *   Bits 116-119: Flags (1, 2, or 3 defined)
   *   Bits 112-115: Scope
   *
   * REVISIT: Need to revisit IPv6 broadcast support.  Broadcast
   * IP addresses are not used with IPv6; multicast is used instead.
   * Does this mean that all multicast address should go to the
   * broadcast Ethernet address?
   */

  if ((ip->destipaddr[0] & HTONS(0xff00)) == HTONS(0xff00))
    {
      memcpy(eth->dest, g_broadcast_ethaddr.ether_addr_octet,
             ETHER_ADDR_LEN);
    }

#ifdef CONFIG_NET_IGMP
  /* Check if the destination address is a multicast address
   *
   *   IPv6 multicast addresses are have the high-order octet of the
   *   addresses=0xff (ff00::/8.)
   *
   * REVISIT:  See comments above.  How do we distinguish broadcast
   * from IGMP multicast?
   */
#warning Missing logic
#endif

  else
    {
      /* Check if the destination address is on the local network. */

      if (!net_ipv6addr_maskcmp(ip->destipaddr, dev->d_ipv6addr,
                                dev->d_ipv6netmask))
        {
          /* 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 IPv6 address instead of the
           * destination address when determining the MAC address.
           */

          netdev_ipv6_router(dev, ip->destipaddr, ipaddr);
#else
          /* Use the device's default router IPv6 address instead of the
           * destination address when determining the MAC address.
           */

          net_ipv6addr_copy(ipaddr, dev->d_ipv6draddr);
#endif
        }
      else
        {
          /* Else, we use the destination IPv6 address. */

          net_ipv6addr_copy(ipaddr, ip->destipaddr);
        }

      /* Check if we already have this destination address in the Neighbor Table */

      naddr = neighbor_lookup(ipaddr);
      if (!naddr)
        {
           nllvdbg("IPv6 Neighbor solicitation for IPv6\n");

          /* The destination address was not in our Neighbor Table, so we
           * overwrite the IPv6 packet with an ICMDv6 Neighbor Solicitation
           * message.
           */

          icmpv6_solicit(dev, ipaddr);
          return;
        }

      /* Build an Ethernet header. */

      memcpy(eth->dest, naddr->na_addr.ether_addr_octet, ETHER_ADDR_LEN);
    }

  /* Finish populating the Ethernet header */

  memcpy(eth->src, dev->d_mac.ether_addr_octet, ETHER_ADDR_LEN);
  eth->type  = HTONS(ETHTYPE_IP6);

  /* Add the size of the layer layer header to the total size of the
   * outgoing packet.
   */

  dev->d_len += netdev_ipv6_hdrlen(dev);
  nllvdbg("Outgoing IPv6 Packet length: %d (%d)\n",
          dev->d_len, (ip->len[0] << 8) | ip->len[1]);
}