Example #1
0
int localhost_initialize(void)
{
  FAR struct lo_driver_s *priv;

  /* Get the interface structure associated with this interface number. */

  priv = &g_loopback;

  /* Initialize the driver structure */

  memset(priv, 0, sizeof(struct lo_driver_s));
  priv->lo_dev.d_ifup    = lo_ifup;      /* I/F up (new IP address) callback */
  priv->lo_dev.d_ifdown  = lo_ifdown;    /* I/F down callback */
  priv->lo_dev.d_txavail = lo_txavail;   /* New TX data callback */
#ifdef CONFIG_NET_IGMP
  priv->lo_dev.d_addmac  = lo_addmac;    /* Add multicast MAC address */
  priv->lo_dev.d_rmmac   = lo_rmmac;     /* Remove multicast MAC address */
#endif
#ifdef CONFIG_NET_MULTIBUFFER
  priv->lo_dev.d_buf     = g_iobuffer;   /* Attach the IO buffer */
#endif
  priv->lo_dev.d_private = (FAR void *)priv; /* Used to recover private state from dev */

  /* Create a watchdog for timing polling for and timing of transmissions */

  priv->lo_polldog       = wd_create();  /* Create periodic poll timer */

  /* Register the loopabck device with the OS so that socket IOCTLs can b
   * performed.
   */

  (void)netdev_register(&priv->lo_dev, NET_LL_LOOPBACK);

  /* Set the local loopback IP address */

#ifdef CONFIG_NET_IPv4
  net_ipv4addr_copy(priv->lo_dev.d_ipaddr, g_lo_ipv4addr);
  net_ipv4addr_copy(priv->lo_dev.d_draddr, g_lo_ipv4addr);
  net_ipv4addr_copy(priv->lo_dev.d_netmask, g_lo_ipv4mask);
#endif

#ifdef CONFIG_NET_IPv6
  net_ipv6addr_copy(priv->lo_dev.d_ipv6addr, g_lo_ipv6addr);
  net_ipv6addr_copy(priv->lo_dev.d_ipv6draddr, g_lo_ipv6addr);
  net_ipv6addr_copy(priv->lo_dev.d_ipv6netmask, g_ipv6_alloneaddr);
#endif

  /* Put the network in the UP state */

  priv->lo_dev.d_flags = IFF_UP;
  return lo_ifup(&priv->lo_dev);
}
Example #2
0
int net_delroute_ipv6(net_ipv6addr_t target, net_ipv6addr_t netmask)
{
  struct route_match_ipv6_s match;

  /* Set up the comparison structure */

  match.prev = NULL;
  net_ipv6addr_copy(match.target, target);
  net_ipv6addr_copy(match.netmask, netmask);

  /* Then remove the entry from the routing table */

  return net_foreachroute_ipv6(net_match_ipv6, &match) ? OK : -ENOENT;
}
Example #3
0
ssize_t psock_udp_send(FAR struct socket *psock, FAR const void *buf,
                       size_t len)
{
  FAR struct udp_conn_s *conn;
  union
  {
    struct sockaddr     addr;
#ifdef CONFIG_NET_IPv4
    struct sockaddr_in  addr4;
#endif
#ifdef CONFIG_NET_IPv6
    struct sockaddr_in6 addr6;
#endif
  } to;
  socklen_t tolen;

  DEBUGASSERT(psock != NULL && psock->s_crefs > 0);
  DEBUGASSERT(psock->s_type != SOCK_DGRAM);

  conn = (FAR struct udp_conn_s *)psock->s_conn;
  DEBUGASSERT(conn);

  /* Was the UDP socket connected via connect()? */

  if (!_SS_ISCONNECTED(psock->s_flags))
    {
      /* No, then it is not legal to call send() with this socket. */

      return -ENOTCONN;
    }

  /* Yes, then let psock_sendto to the work */

#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
  if (conn->domain == PF_INET)
#endif
    {
      tolen               = sizeof(struct sockaddr_in);
      to.addr4.sin_family = AF_INET;
      to.addr4.sin_port   = conn->rport;
      net_ipv4addr_copy(to.addr4.sin_addr.s_addr, conn->u.ipv4.raddr);
    }
#endif /* CONFIG_NET_IPv4 */

#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
  else
#endif
    {
      tolen               = sizeof(struct sockaddr_in6);
      to.addr6.sin6_family = AF_INET6;
      to.addr6.sin6_port   = conn->rport;
      net_ipv6addr_copy(to.addr6.sin6_addr.s6_addr, conn->u.ipv6.raddr);
    }
#endif /* CONFIG_NET_IPv6 */

  return psock_udp_sendto(psock, buf, len, 0, &to.addr, tolen);
}
Example #4
0
void neighbor_add(FAR net_ipv6addr_t ipaddr, FAR struct neighbor_addr_s *addr)
{
  uint8_t oldest_time;
  int     oldest_ndx;
  int     i;

  ninfo("Add neighbor: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
        ntohs(ipaddr[0]), ntohs(ipaddr[1]), ntohs(ipaddr[2]),
        ntohs(ipaddr[3]), ntohs(ipaddr[4]), ntohs(ipaddr[5]),
        ntohs(ipaddr[6]), ntohs(ipaddr[7]));
  ninfo("  at: %02x:%02x:%02x:%02x:%02x:%02x\n",
        addr->na_addr.ether_addr_octet[0],
        addr->na_addr.ether_addr_octet[1],
        addr->na_addr.ether_addr_octet[2],
        addr->na_addr.ether_addr_octet[3],
        addr->na_addr.ether_addr_octet[4],
        addr->na_addr.ether_addr_octet[5]);

  /* Find the first unused entry or the oldest used entry. */

  oldest_time = 0;
  oldest_ndx  = 0;

  for (i = 0; i < CONFIG_NET_IPv6_NCONF_ENTRIES; ++i)
    {
      if (g_neighbors[i].ne_time == NEIGHBOR_MAXTIME)
        {
          oldest_ndx = i;
          break;
        }

      if (net_ipv6addr_cmp(g_neighbors[i].ne_ipaddr, ipaddr))
        {
          oldest_ndx = i;
          break;
        }

      if (g_neighbors[i].ne_time > oldest_time)
        {
          oldest_ndx = i;
          oldest_time = g_neighbors[i].ne_time;
        }
    }

  /* Use the oldest or first free entry (either pointed to by the
   * "oldest_ndx" variable).
   */

  g_neighbors[oldest_ndx].ne_time = 0;
  net_ipv6addr_copy(g_neighbors[oldest_ndx].ne_ipaddr, ipaddr);
  memcpy(&g_neighbors[oldest_ndx].ne_addr, addr, sizeof(struct neighbor_addr_s));
}
Example #5
0
static void icmpv6_setaddresses(FAR struct net_driver_s *dev,
                                const net_ipv6addr_t draddr,
                                const net_ipv6addr_t prefix,
                                unsigned int preflen)
{
  unsigned int i;

  /* Make sure that the network is down before changing any addresses */

  netdev_ifdown(dev);

  /* Create an address mask from the prefix */

  if (preflen > 128)
    {
      preflen = 128;
    }

  net_ipv6_pref2mask(preflen, dev->d_ipv6netmask);

  ninfo("preflen=%d netmask=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
        preflen, dev->d_ipv6netmask[0], dev->d_ipv6netmask[1],
        dev->d_ipv6netmask[2], dev->d_ipv6netmask[3], dev->d_ipv6netmask[4],
        dev->d_ipv6netmask[5], dev->d_ipv6netmask[6], dev->d_ipv6netmask[7]);

  /* Copy prefix to the current IPv6 address, applying the mask */

  for (i = 0; i < 7; i++)
    {
      dev->d_ipv6addr[i] = (dev->d_ipv6addr[i] & ~dev->d_ipv6netmask[i]) |
                           (prefix[i] & dev->d_ipv6netmask[i]);
    }

  ninfo("prefix=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
        prefix[0], prefix[1], prefix[2], prefix[3],
        prefix[4], prefix[6], prefix[6], prefix[7]);
  ninfo("IP address=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
        dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
        dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[6],
        dev->d_ipv6addr[6], dev->d_ipv6addr[7]);

  /* Finally, copy the router address */

  net_ipv6addr_copy(dev->d_ipv6draddr, draddr);

  ninfo("DR address=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
        dev->d_ipv6draddr[0], dev->d_ipv6draddr[1], dev->d_ipv6draddr[2],
        dev->d_ipv6draddr[3], dev->d_ipv6draddr[4], dev->d_ipv6draddr[6],
        dev->d_ipv6draddr[6], dev->d_ipv6draddr[7]);
}
Example #6
0
static inline void accept_tcpsender(FAR struct socket *psock,
                                    FAR struct tcp_conn_s *conn,
                                    FAR struct sockaddr *addr,
                                    socklen_t *addrlen)
{
  if (addr)
    {
      /* If an address is provided, then the length must also be provided. */

      DEBUGASSERT(addrlen);

#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
      /* If both IPv4 and IPv6 support are enabled, then we will need to
       * select which one to use when obtaining the sender's IP address.
       */

      if (psock->s_domain == PF_INET)
#endif /* CONFIG_NET_IPv6 */
        {
          FAR struct sockaddr_in *inaddr = (FAR struct sockaddr_in *)addr;

          inaddr->sin_family = AF_INET;
          inaddr->sin_port   = conn->rport;
          net_ipv4addr_copy(inaddr->sin_addr.s_addr, conn->u.ipv4.raddr);

          *addrlen = sizeof(struct sockaddr_in);
        }
#endif /* CONFIG_NET_IPv4 */

#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
      /* Otherwise, this the IPv6 address is needed */

      else
#endif /* CONFIG_NET_IPv4 */
        {
          FAR struct sockaddr_in6 *inaddr = (FAR struct sockaddr_in6 *)addr;

          DEBUGASSERT(psock->s_domain == PF_INET6);
          inaddr->sin6_family = AF_INET6;
          inaddr->sin6_port   = conn->rport;
          net_ipv6addr_copy(inaddr->sin6_addr.s6_addr, conn->u.ipv6.raddr);

          *addrlen = sizeof(struct sockaddr_in6);
        }
#endif /* CONFIG_NET_IPv6 */
    }
}
Example #7
0
static int ioctl_add_ipv6route(FAR struct rtentry *rtentry)
{
  FAR struct sockaddr_in6 *target;
  FAR struct sockaddr_in6 *netmask;
  net_ipv6addr_t router;

  target    = (FAR struct sockaddr_in6 *)rtentry->rt_target;
  netmask    = (FAR struct sockaddr_in6 *)rtentry->rt_netmask;

  /* The router is an optional argument */

  if (rtentry->rt_router)
    {
      FAR struct sockaddr_in6 *addr;
      addr = (FAR struct sockaddr_in6 *)rtentry->rt_router;
      net_ipv6addr_copy(router, addr->sin6_addr.s6_addr16);
    }
  else
    {
      net_ipv6addr_copy(router, in6addr_any.s6_addr16);
    }

  return net_addroute_ipv6(target->sin6_addr.s6_addr16, netmask->sin6_addr.s6_addr16, router);
}
Example #8
0
void icmpv6_wait_setup(const net_ipv6addr_t ipaddr,
                       FAR struct icmpv6_notify_s *notify)
{
  irqstate_t flags;

  /* Initialize the wait structure */

  net_ipv6addr_copy(notify->nt_ipaddr, ipaddr);
  notify->nt_result = -ETIMEDOUT;

  /* This semaphore is used for signaling and, hence, should not have
   * priority inheritance enabled.
   */

  (void)nxsem_init(&notify->nt_sem, 0, 0);
  nxsem_setprotocol(&notify->nt_sem, SEM_PRIO_NONE);

  /* Add the wait structure to the list with interrupts disabled */

  flags             = enter_critical_section();
  notify->nt_flink  = g_icmpv6_waiters;
  g_icmpv6_waiters  = notify;
  leave_critical_section(flags);
}
void icmpv6_solicit(FAR struct net_driver_s *dev,
                    FAR const net_ipv6addr_t ipaddr)
{
    FAR struct icmpv6_iphdr_s *icmp;
    FAR struct icmpv6_neighbor_solicit_s *sol;
    FAR struct eth_hdr_s *eth;

    /* Set up the IPv6 header (most is probably already in place) */

    icmp          = ICMPv6BUF;
    icmp->vtc     = 0x60;                    /* Version/traffic class (MS) */
    icmp->tcf     = 0;                       /* Traffic class (LS)/Flow label (MS) */
    icmp->flow    = 0;                       /* Flow label (LS) */

    /* Length excludes the IPv6 header */

    icmp->len[0]  = (sizeof(struct icmpv6_neighbor_solicit_s) >> 8);
    icmp->len[1]  = (sizeof(struct icmpv6_neighbor_solicit_s) & 0xff);

    icmp->proto   = IP_PROTO_ICMP6;          /* Next header */
    icmp->ttl     = 255;                     /* Hop limit */

    /* Set the multicast destination IP address */

    memcpy(icmp->destipaddr, g_icmpv_mcastaddr, 6*sizeof(uint16_t));
    icmp->destipaddr[6] = ipaddr[6] | HTONS(0xff00);
    icmp->destipaddr[7] = ipaddr[7];

    /* Add out IPv6 address as the source address */

    net_ipv6addr_copy(icmp->srcipaddr, dev->d_ipv6addr);

    /* Set up the ICMPv6 Neighbor Solicitation message */

    sol           = ICMPv6SOLICIT;
    sol->type     = ICMPv6_NEIGHBOR_SOLICIT; /* Message type */
    sol->code     = 0;                       /* Message qualifier */
    sol->flags[0] = 0;                       /* flags */
    sol->flags[1] = 0;
    sol->flags[2] = 0;
    sol->flags[3] = 0;

    /* Copy the target address into the Neighbor Solicitation message */

    net_ipv6addr_copy(sol->tgtaddr, ipaddr);

    /* Set up the options */

    sol->opttype  = ICMPv6_OPT_SRCLLADDR;    /* Option type */
    sol->optlen   = 1;                       /* Option length = 1 octet */

    /* Copy our link layer address into the message
     * REVISIT:  What if the link layer is not Ethernet?
     */

    memcpy(sol->srclladdr, &dev->d_mac, IFHWADDRLEN);

    /* Calculate the checksum over both the ICMP header and payload */

    icmp->chksum  = 0;
    icmp->chksum  = ~icmpv6_chksum(dev);

    /* Set the size to the size of the IPv6 header and the payload size */

    dev->d_len    = IPv6_HDRLEN + sizeof(struct icmpv6_neighbor_solicit_s);

#ifdef CONFIG_NET_ETHERNET
#ifdef CONFIG_NET_MULTILINK
    if (dev->d_lltype == NET_LL_ETHERNET)
#endif
    {
        /* Set the destination IPv6 multicast Ethernet address:
         *
         * For IPv6 multicast addresses, the Ethernet MAC is derived by
         * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
         * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
         * to the Ethernet MAC address 33:33:00:01:00:03.
         *
         * NOTES:  This appears correct for the ICMPv6 Router Solicitation
         * Message, but the ICMPv6 Neighbor Solicitation message seems to
         * use 33:33:ff:01:00:03.
         */

        eth          = ETHBUF;
        eth->dest[0] = 0x33;
        eth->dest[1] = 0x33;
        eth->dest[2] = 0xff;
        eth->dest[3] = ipaddr[6] >> 8;
        eth->dest[4] = ipaddr[7] & 0xff;
        eth->dest[5] = ipaddr[7] >> 8;

        /* Move our source Ethernet addresses into the Ethernet header */

        memcpy(eth->src, dev->d_mac.ether_addr_octet, ETHER_ADDR_LEN);

        /* Set the IPv6 Ethernet type */

        eth->type  = HTONS(ETHTYPE_IP6);
#if 0
        /* No additional neighbor lookup is required on this packet.
         * REVISIT:  It is inappropriate to set this bit if we get here
         * via neighbor_out(); It is no necessary to set this bit if we
         * get here via icmpv6_input().  Is it ever necessary?
         */

        IFF_SET_NOARP(dev->d_flags);
#endif
    }
Example #10
0
static void icmpv6_echo_request(FAR struct net_driver_s *dev,
                                FAR struct icmpv6_ping_s *pstate)
{
    FAR struct icmpv6_iphdr_s *icmp;
    FAR struct icmpv6_echo_request_s *req;
    uint16_t reqlen;
    int i;

    nllvdbg("Send ECHO request: seqno=%d\n", pstate->png_seqno);

    /* Set up the IPv6 header (most is probably already in place) */

    icmp          = ICMPv6BUF;
    icmp->vtc     = 0x60;                    /* Version/traffic class (MS) */
    icmp->tcf     = 0;                       /* Traffic class (LS)/Flow label (MS) */
    icmp->flow    = 0;                       /* Flow label (LS) */

    /* Length excludes the IPv6 header */

    reqlen        = SIZEOF_ICMPV6_ECHO_REQUEST_S(pstate->png_datlen);
    icmp->len[0]  = (reqlen >> 8);
    icmp->len[1]  = (reqlen & 0xff);

    icmp->proto   = IP_PROTO_ICMP6;          /* Next header */
    icmp->ttl     = IP_TTL;                  /* Hop limit */

    /* Set the multicast destination IP address */

    net_ipv6addr_copy(icmp->destipaddr,  pstate->png_addr);

    /* Add out IPv6 address as the source address */

    net_ipv6addr_copy(icmp->srcipaddr, dev->d_ipv6addr);

    /* Set up the ICMPv6 Echo Request message */

    req           = ICMPv6ECHOREQ;
    req->type     = ICMPv6_ECHO_REQUEST; /* Message type */
    req->code     = 0;                   /* Message qualifier */
    req->id       = htons(pstate->png_id);
    req->seqno    = htons(pstate->png_seqno);

    /* Add some easily verifiable data */

    for (i = 0; i < pstate->png_datlen; i++)
    {
        req->data[i] = i;
    }

    /* Calculate the checksum over both the ICMP header and payload */

    icmp->chksum  = 0;
    icmp->chksum  = ~icmpv6_chksum(dev);

    /* Set the size to the size of the IPv6 header and the payload size */

    IFF_SET_IPv6(dev->d_flags);

    dev->d_sndlen = reqlen;
    dev->d_len    = reqlen + IPv6_HDRLEN;

    nllvdbg("Outgoing ICMPv6 Echo Request length: %d (%d)\n",
            dev->d_len, (icmp->len[0] << 8) | icmp->len[1]);

#ifdef CONFIG_NET_STATISTICS
    g_netstats.icmpv6.sent++;
    g_netstats.ipv6.sent++;
#endif
}
Example #11
0
int net_delroute_ipv6(net_ipv6addr_t target, net_ipv6addr_t netmask)
{
  struct route_match_ipv6_s match;
  struct net_route_ipv6_s route;
  struct file fshandle;
  off_t filesize;
  ssize_t nwritten;
  ssize_t nread;
  off_t pos;
  int nentries;
  int index;
  int ret;

  /* We must lock out other accesses to the routing table while we remove
   * entry
   */

  ret = net_lockroute_ipv6();
  if (ret < 0)
    {
       nerr("ERROR: net_lockroute_ipv6 failed: %d\n", ret);
       return ret;
    }

  /* Get the size of the routing table (in entries) */

  nentries = net_routesize_ipv6();
  if (nentries < 0)
    {
      ret = nentries;
      goto errout_with_lock;
    }
  else if (nentries == 0)
    {
      ret = -ENOENT;
      goto errout_with_lock;
    }

  /* Set up the comparison structure */

  net_ipv6addr_copy(match.target, target);
  net_ipv6addr_copy(match.netmask, netmask);
  match.index = 0;

  /* Then find the index into the routing table where the match can be found */

  ret = net_foreachroute_ipv6(net_match_ipv6, &match);
  if (ret < 0)
    {
      /* And error occurred */

      ret = ret;
      goto errout_with_lock;
    }
  else if (ret == 0)
    {
      /* No match found */

      ret = -ENOENT;
      goto errout_with_lock;
    }

  /* Open the routing table for read/write access */

  ret = net_openroute_ipv6(O_RDWR, &fshandle);
  if (ret < 0)
    {
      nerr("ERROR: Could not open IPv6 routing table: %d\n", ret);
      goto errout_with_lock;
    }

#ifdef CONFIG_ROUTE_IPv6_CACHEROUTE
  /* We are committed to modifying the routing table.  Flush the in-memory
   * routing table cache.
   */

  net_flushcache_ipv6();
#endif

  /* Loop, copying each entry, to the previous entry thus removing the entry
   * to be deleted.
   */

  for (index = match.index + 1; index < nentries; index++)
    {
      /* Seek to the current entry to be moved */

      pos = net_seekroute_ipv6(&fshandle, index);
      if (pos < 0)
        {
          nerr("ERROR: net_readroute_ipv6 failed: %ld\n", (long)pos);
          ret =(int)pos;
          goto errout_with_fshandle;
        }

      /* Read the routing table entry at this position */

      nread = net_readroute_ipv6(&fshandle, &route);
      if (nread < 0)
        {
          nerr("ERROR: net_readroute_ipv6 failed: %ld\n", (long)nread);
          ret = (int)nread;
          goto errout_with_fshandle;
        }
      else if (nread == 0)
        {
          nerr("ERROR: Unexpected end of file\n");
          ret = -EINVAL;
          goto errout_with_fshandle;
        }

      /* Seek to the previous entry to be replaced */

      pos = net_seekroute_ipv6(&fshandle, index - 1);
      if (pos < 0)
        {
          nerr("ERROR: net_readroute_ipv6 failed: %ld\n", (long)pos);
          ret =(int)pos;
          goto errout_with_fshandle;
        }

      /* Now write the record to its new location */

      nwritten = net_writeroute_ipv6(&fshandle, &route);
      if (nwritten < 0)
        {
          nerr("ERROR: net_readroute_ipv6 failed: %ld\n", (long)nwritten);
          ret = (int)nwritten;
          goto errout_with_fshandle;
        }
    }

  /* Now truncate the one duplicate entry at the end of the file.  This may
   * result in a zero length file.
   */

  filesize = (nentries - 1) * sizeof(struct net_route_ipv6_s);
  ret = file_truncate(&fshandle, filesize);

errout_with_fshandle:
  (void)net_closeroute_ipv6(&fshandle);

errout_with_lock:
  (void)net_unlockroute_ipv6();
  return ret;
}
Example #12
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;
}
Example #13
0
int tcp_connect(FAR struct tcp_conn_s *conn, FAR const struct sockaddr *addr)
{
  int port;
  int ret;

  /* The connection is expected to be in the TCP_ALLOCATED state.. i.e.,
   * allocated via up_tcpalloc(), but not yet put into the active connections
   * list.
   */

  if (!conn || conn->tcpstateflags != TCP_ALLOCATED)
    {
      return -EISCONN;
    }

  /* If the TCP port has not already been bound to a local port, then select
   * one now.  We assume that the IP address has been bound to a local device,
   * but the port may still be INPORT_ANY.
   */

  net_lock();

#ifdef CONFIG_NETDEV_MULTINIC
  /* If there are multiple network devices, then we need to pass the local,
   * bound address.  This is needed because port unique-ness is only for a
   * given network.
   *
   * This is complicated by the fact that the local address may be either an
   * IPv4 or an IPv6 address.
   */

#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
  if (conn->domain == PF_INET)
#endif
    {
      /* Select a port that is unique for this IPv4 local address (host
       * order).
       */

      port = tcp_selectport(PF_INET,
                            (FAR const union ip_addr_u *)&conn->u.ipv4.laddr,
                            ntohs(conn->lport));
    }
#endif /* CONFIG_NET_IPv4 */

#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
  else
#endif
    {
      /* Select a port that is unique for this IPv6 local address (host
       * order).
       */

      port = tcp_selectport(PF_INET6,
                            (FAR const union ip_addr_u *)conn->u.ipv6.laddr,
                            ntohs(conn->lport));
    }
#endif /* CONFIG_NET_IPv6 */

#else /* CONFIG_NETDEV_MULTINIC */
  /* Select the next available port number.  This is only one network device
   * so we do not have to bother with all of the IPv4/IPv6 local address
   * silliness.
   */

  port = tcp_selectport(ntohs(conn->lport));

#endif /* CONFIG_NETDEV_MULTINIC */

  /* Did we have a port assignment? */

  if (port < 0)
    {
      ret = port;
      goto errout_with_lock;
    }

  /* Set up the local address (laddr) and the remote address (raddr) that
   * describes the TCP connection.
   */

#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
  if (conn->domain == PF_INET)
#endif
    {
      FAR const struct sockaddr_in *inaddr =
        (FAR const struct sockaddr_in *)addr;

      /* Save MSS and the port from the sockaddr (already in network order) */

      conn->mss    = MIN_IPv4_TCP_INITIAL_MSS;
      conn->rport  = inaddr->sin_port;

      /* The sockaddr address is 32-bits in network order. */

      net_ipv4addr_copy(conn->u.ipv4.raddr, inaddr->sin_addr.s_addr);

      /* Find the device that can receive packets on the network associated
       * with this remote address.
       */

      ret = tcp_remote_ipv4_device(conn);
    }
#endif /* CONFIG_NET_IPv4 */

#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
  else
#endif
    {
      FAR const struct sockaddr_in6 *inaddr =
        (FAR const struct sockaddr_in6 *)addr;

      /* Save MSS and the port from the sockaddr (already in network order) */

      conn->mss     = MIN_IPv6_TCP_INITIAL_MSS;
      conn->rport   = inaddr->sin6_port;

      /* The sockaddr address is 128-bits in network order. */

      net_ipv6addr_copy(conn->u.ipv6.raddr, inaddr->sin6_addr.s6_addr16);

      /* Find the device that can receive packets on the network associated
       * with this local address.
       */

      ret = tcp_remote_ipv6_device(conn);
    }
#endif /* CONFIG_NET_IPv6 */

  /* Verify that a network device that can provide packets to this local
   * address was found.
   */

  if (ret < 0)
    {
      /* If no device is found, then the address is not reachable.  That
       * should be impossible in this context and we should probably really
       * just assert here.
       */

      nerr("ERROR: Failed to find network device: %d\n", ret);
      goto errout_with_lock;
    }

  /* Initialize and return the connection structure, bind it to the port
   * number.  At this point, we do not know the size of the initial MSS We
   * know the total size of the packet buffer, but we don't yet know the
   * size of link layer header.
   */

  conn->tcpstateflags = TCP_SYN_SENT;
  tcp_initsequence(conn->sndseq);

  conn->unacked    = 1;    /* TCP length of the SYN is one. */
  conn->nrtx       = 0;
  conn->timer      = 1;    /* Send the SYN next time around. */
  conn->rto        = TCP_RTO;
  conn->sa         = 0;
  conn->sv         = 16;   /* Initial value of the RTT variance. */
  conn->lport      = htons((uint16_t)port);
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
  conn->expired    = 0;
  conn->isn        = 0;
  conn->sent       = 0;
  conn->sndseq_max = 0;
#endif

#ifdef CONFIG_NET_TCP_READAHEAD
  /* Initialize the list of TCP read-ahead buffers */

  IOB_QINIT(&conn->readahead);
#endif

#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
  /* Initialize the TCP write buffer lists */

  sq_init(&conn->write_q);
  sq_init(&conn->unacked_q);
#endif

  /* And, finally, put the connection structure into the active list. */

  dq_addlast(&conn->node, &g_active_tcp_connections);
  ret = OK;

errout_with_lock:
  net_unlock();
  return ret;
}
Example #14
0
static uint16_t udp_datahandler(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn,
                                FAR uint8_t *buffer, uint16_t buflen)
{
  FAR struct iob_s *iob;
  int ret;
#ifdef CONFIG_NET_IPv6
  FAR struct sockaddr_in6 src_addr6;
#endif
#ifdef CONFIG_NET_IPv4
  FAR struct sockaddr_in src_addr4;
#endif
  FAR void  *src_addr;
  uint8_t src_addr_size;

  /* Allocate on I/O buffer to start the chain (throttling as necessary).
   * We will not wait for an I/O buffer to become available in this context.
   */

  iob = iob_tryalloc(true);
  if (iob == NULL)
    {
      nerr("ERROR: Failed to create new I/O buffer chain\n");
      return 0;
    }

#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
  if (IFF_IS_IPv6(dev->d_flags))
#endif
    {
      FAR struct udp_hdr_s *udp   = UDPIPv6BUF;
      FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;

      src_addr6.sin6_family = AF_INET6;
      src_addr6.sin6_port   = udp->srcport;

      net_ipv6addr_copy(src_addr6.sin6_addr.s6_addr, ipv6->srcipaddr);

      src_addr_size = sizeof(src_addr6);
      src_addr = &src_addr6;
    }
#endif /* CONFIG_NET_IPv6 */

#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
  else
#endif
    {
#ifdef CONFIG_NET_IPv6
      /* Hybrid dual-stack IPv6/IPv4 implementations recognize a special
       * class of addresses, the IPv4-mapped IPv6 addresses.
       */

      if (conn->domain == PF_INET6)
        {
          FAR struct udp_hdr_s *udp   = UDPIPv6BUF;
          FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
          in_addr_t ipv4addr;

          /* Encode the IPv4 address as an IPv-mapped IPv6 address */

          src_addr6.sin6_family = AF_INET6;
          src_addr6.sin6_port = udp->srcport;

          ipv4addr = net_ip4addr_conv32(ipv6->srcipaddr);
          ip6_map_ipv4addr(ipv4addr, src_addr6.sin6_addr.s6_addr16);

          src_addr_size = sizeof(src_addr6);
          src_addr = &src_addr6;
        }
      else
#endif
        {
          FAR struct udp_hdr_s *udp   = UDPIPv4BUF;
          FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;

          src_addr4.sin_family = AF_INET;
          src_addr4.sin_port   = udp->srcport;

          net_ipv4addr_copy(src_addr4.sin_addr.s_addr,
                            net_ip4addr_conv32(ipv4->srcipaddr));

          src_addr_size = sizeof(src_addr4);
          src_addr = &src_addr4;
        }
    }
#endif /* CONFIG_NET_IPv4 */

  /* Copy the src address info into the I/O buffer chain.  We will not wait
   * for an I/O buffer to become available in this context.  It there is
   * any failure to allocated, the entire I/O buffer chain will be discarded.
   */

  ret = iob_trycopyin(iob, (FAR const uint8_t *)&src_addr_size,
                      sizeof(uint8_t), 0, true);
  if (ret < 0)
    {
      /* On a failure, iob_trycopyin return a negated error value but does
       * not free any I/O buffers.
       */

      nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret);
      (void)iob_free_chain(iob);
      return 0;
    }

  ret = iob_trycopyin(iob, (FAR const uint8_t *)src_addr, src_addr_size,
                      sizeof(uint8_t), true);
  if (ret < 0)
    {
      /* On a failure, iob_trycopyin return a negated error value but does
       * not free any I/O buffers.
       */

      nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret);
      (void)iob_free_chain(iob);
      return 0;
    }

  if (buflen > 0)
    {
      /* Copy the new appdata into the I/O buffer chain */

      ret = iob_trycopyin(iob, buffer, buflen,
                          src_addr_size + sizeof(uint8_t), true);
      if (ret < 0)
        {
          /* On a failure, iob_trycopyin return a negated error value but
           * does not free any I/O buffers.
           */

          nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n",
               ret);
          (void)iob_free_chain(iob);
          return 0;
        }
    }

  /* Add the new I/O buffer chain to the tail of the read-ahead queue */

  ret = iob_tryadd_queue(iob, &conn->readahead);
  if (ret < 0)
    {
      nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
      (void)iob_free_chain(iob);
      return 0;
    }

#ifdef CONFIG_UDP_READAHEAD_NOTIFIER
  /* Provided notification(s) that additional UDP read-ahead data is
   * available.
   */

  udp_notifier_signal(conn);
#endif

  ninfo("Buffered %d bytes\n", buflen);
  return buflen;
}
Example #15
0
static inline int tcp_ipv6_bind(FAR struct tcp_conn_s *conn,
                                FAR const struct sockaddr_in6 *addr)
{
  int port;
  int ret;

  /* Verify or select a local port and address */

  net_lock();

  /* Verify or select a local port (host byte order) */

#ifdef CONFIG_NETDEV_MULTINIC
  /* The port number must be unique for this address binding */

  port = tcp_selectport(PF_INET6,
                        (FAR const union ip_addr_u *)addr->sin6_addr.in6_u.u6_addr16,
                        ntohs(addr->sin6_port));
#else
  /* There is only one network device; the port number can be globally
   * unique.
   */

  port = tcp_selectport(ntohs(addr->sin6_port));
#endif

  if (port < 0)
    {
      nerr("ERROR: tcp_selectport failed: %d\n", port);
      return port;
    }

  /* Save the local address in the connection structure (network byte order). */

  conn->lport = htons(port);
#ifdef CONFIG_NETDEV_MULTINIC
  net_ipv6addr_copy(conn->u.ipv6.laddr, addr->sin6_addr.in6_u.u6_addr16);
#endif

  /* Find the device that can receive packets on the network
   * associated with this local address.
   */

  ret = tcp_local_ipv6_device(conn);
  if (ret < 0)
    {
      /* If no device is found, then the address is not reachable */

      nerr("ERROR: tcp_local_ipv6_device failed: %d\n", ret);

      /* Back out the local address setting */

      conn->lport = 0;
#ifdef CONFIG_NETDEV_MULTINIC
      net_ipv6addr_copy(conn->u.ipv6.laddr, g_ipv6_allzeroaddr);
#endif
      return ret;
    }

  net_unlock();
  return OK;
}
Example #16
0
FAR struct tcp_conn_s *tcp_alloc_accept(FAR struct net_driver_s *dev,
                                        FAR struct tcp_hdr_s *tcp)
{
  FAR struct tcp_conn_s *conn;
  uint8_t domain;
  int ret;

  /* Get the appropriate IP domain */

#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv4)
  bool ipv6 = IFF_IS_IPv6(dev->d_flags);
  domain = ipv6 ? PF_INET6 : PF_INET;
#elif defined(CONFIG_NET_IPv4)
  domain = PF_INET;
#else /* defined(CONFIG_NET_IPv6) */
  domain = PF_INET6;
#endif

  /* Allocate the connection structure */

  conn = tcp_alloc(domain);
  if (conn)
    {
      /* Set up the local address (laddr) and the remote address (raddr)
       * that describes the TCP connection.
       */

#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
      if (ipv6)
#endif
        {
          FAR struct ipv6_hdr_s *ip = IPv6BUF;

          /* Set the IPv6 specific MSS and the IPv6 locally bound address */

          conn->mss = TCP_IPv6_INITIAL_MSS(dev);
          net_ipv6addr_copy(conn->u.ipv6.raddr, ip->srcipaddr);
#ifdef CONFIG_NETDEV_MULTINIC
          net_ipv6addr_copy(conn->u.ipv6.laddr, ip->destipaddr);

          /* We now have to filter all outgoing transfers so that they use
           * only the MSS of this device.
           */

          DEBUGASSERT(conn->dev == NULL || conn->dev == dev);
          conn->dev = dev;
#endif

          /* Find the device that can receive packets on the network
           * associated with this local address.
           */

          ret = tcp_remote_ipv6_device(conn);
        }
#endif /* CONFIG_NET_IPv6 */

#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
      else
#endif
        {
          FAR struct ipv4_hdr_s *ip = IPv4BUF;

          /* Set the IPv6 specific MSS and the IPv4 bound remote address. */

          conn->mss = TCP_IPv4_INITIAL_MSS(dev);
          net_ipv4addr_copy(conn->u.ipv4.raddr,
                            net_ip4addr_conv32(ip->srcipaddr));

#ifdef CONFIG_NETDEV_MULTINIC
          /* Set the local address as well */

          net_ipv4addr_copy(conn->u.ipv4.laddr,
                            net_ip4addr_conv32(ip->destipaddr));

          /* We now have to filter all outgoing transfers so that they use
           * only the MSS of this device.
           */

          DEBUGASSERT(conn->dev == NULL || conn->dev == dev);
          conn->dev = dev;
#endif

          /* Find the device that can receive packets on the network
           * associated with this local address.
           */

          ret = tcp_remote_ipv4_device(conn);
        }
#endif /* CONFIG_NET_IPv4 */

      /* Verify that a network device that can provide packets to this
       * local address was found.
       */

      if (ret < 0)
        {
          /* If no device is found, then the address is not reachable.
           * That should be impossible in this context and we should
           * probably really just assert here.
           */

          nerr("ERROR: Failed to find network device: %d\n", ret);
          tcp_free(conn);
          return NULL;
        }

      /* Fill in the necessary fields for the new connection. */

      conn->rto           = TCP_RTO;
      conn->timer         = TCP_RTO;
      conn->sa            = 0;
      conn->sv            = 4;
      conn->nrtx          = 0;
      conn->lport         = tcp->destport;
      conn->rport         = tcp->srcport;
      conn->tcpstateflags = TCP_SYN_RCVD;

      tcp_initsequence(conn->sndseq);
      conn->unacked       = 1;
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
      conn->expired       = 0;
      conn->isn           = 0;
      conn->sent          = 0;
      conn->sndseq_max    = 0;
#endif

      /* rcvseq should be the seqno from the incoming packet + 1. */

      memcpy(conn->rcvseq, tcp->seqno, 4);

#ifdef CONFIG_NET_TCP_READAHEAD
      /* Initialize the list of TCP read-ahead buffers */

      IOB_QINIT(&conn->readahead);
#endif

#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
      /* Initialize the write buffer lists */

      sq_init(&conn->write_q);
      sq_init(&conn->unacked_q);
#endif

      /* And, finally, put the connection structure into the active list.
       * Interrupts should already be disabled in this context.
       */

      dq_addlast(&conn->node, &g_active_tcp_connections);
    }

  return conn;
}
Example #17
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;
    }
}
Example #18
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]);
}
Example #19
0
int icmpv6_autoconfig(FAR struct net_driver_s *dev)
{
#ifndef CONFIG_NET_ETHERNET
  /* Only Ethernet supported for now */

  nerr("ERROR: Only Ethernet is supported\n");
  return -ENOSYS;

#else /* CONFIG_NET_ETHERNET */
  struct icmpv6_rnotify_s notify;
  net_ipv6addr_t lladdr;
  int retries;
  int ret;

  /* Sanity checks */

  DEBUGASSERT(dev);
  ninfo("Auto-configuring %s\n", dev->d_ifname);

#ifdef CONFIG_NET_MULTILINK
  /* Only Ethernet devices are supported for now */

  if (dev->d_lltype != NET_LL_ETHERNET)
    {
      nerr("ERROR: Only Ethernet is supported\n");
      return -ENOSYS;
    }
#endif

  /* The interface should be in the down state */

  net_lock();
  netdev_ifdown(dev);
  net_unlock();

  /* IPv6 Stateless Autoconfiguration
   * Reference: http://www.tcpipguide.com/free/t_IPv6AutoconfigurationandRenumbering.htm
   *
   * The following is a summary of the steps a device takes when using
   * stateless auto-configuration:
   *
   * 1. Link-Local Address Generation: The device generates a link-local
   *    address. Recall that this is one of the two types of local-use IPv6
   *    addresses. Link-local addresses have "1111 1110 10" for the first
   *    ten bits. The generated address uses those ten bits followed by 54
   *    zeroes and then the 64 bit interface identifier. Typically this
   *    will be derived from the data link layer (MAC) address.
   *
   *    IEEE 802 MAC addresses, used by Ethernet and other IEEE 802 Project
   *    networking technologies, have 48 bits.  The IEEE has also defined a
   *    format called the 64-bit extended unique identifier, abbreviated
   *    EUI-64.  To get the modified EUI-64 interface ID for a device, you
   *    simply take the EUI-64 address and change the 7th bit from the left
   *    (the"universal/local" or "U/L" bit) from a zero to a one.
   *
   *    128  112  96   80    64   48   32   16
   *    ---- ---- ---- ----  ---- ---- ---- ----
   *    fe80 0000 0000 0000  0000 xxxx xxxx xxxx
   */

  lladdr[0] = HTONS(0xfe80);                        /* 10-bit address + 6 zeroes */
  memset(&lladdr[1], 0, 4 * sizeof(uint16_t));      /* 64 more zeroes */
  memcpy(&lladdr[5], dev->d_mac.ether_addr_octet,
        sizeof(struct ether_addr));                 /* 48-bit Ethernet address */

  ninfo("lladdr=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
        lladdr[0], lladdr[1], lladdr[2], lladdr[3],
        lladdr[4], lladdr[6], lladdr[6], lladdr[7]);

#ifdef CONFIG_NET_ICMPv6_NEIGHBOR
  /* Bring the interface up with no IP address */

  net_lock();
  netdev_ifup(dev);
  net_unlock();

  /* 2. Link-Local Address Uniqueness Test: The node tests to ensure that
   *    the address it generated isn't for some reason already in use on the
   *    local network. (This is very unlikely to be an issue if the link-local
   *    address came from a MAC address but more likely if it was based on a
   *    generated token.) It sends a Neighbor Solicitation message using the
   *    Neighbor Discovery (ND) protocol. It then listens for a Neighbor
   *    Advertisement in response that indicates that another device is
   *    already using its link-local address; if so, either a new address
   *    must be generated, or auto-configuration fails and another method
   *    must be employed.
   */

  ret = icmpv6_neighbor(lladdr);

  /* Take the interface back down */

  net_lock();
  netdev_ifdown(dev);
  net_unlock();

  if (ret == OK)
    {
      /* Hmmm... someone else responded to our Neighbor Solicitation.  We
       * have not back-up plan in place.  Just bail.
       */

      nerr("ERROR: IP conflict\n");
      return -EEXIST;
    }
#endif

  /* 3. Link-Local Address Assignment: Assuming the uniqueness test passes,
   *    the device assigns the link-local address to its IP interface. This
   *    address can be used for communication on the local network, but not
   *    on the wider Internet (since link-local addresses are not routed).
   */

  net_lock();
  net_ipv6addr_copy(dev->d_ipv6addr, lladdr);

  /* Bring the interface up with the new, temporary IP address */

  netdev_ifup(dev);

  /* 4. Router Contact: The node next attempts to contact a local router for
   *    more information on continuing the configuration. This is done either
   *    by listening for Router Advertisement messages sent periodically by
   *    routers, or by sending a specific Router Solicitation to ask a router
   *    for information on what to do next.
   */

  for (retries = 0; retries < CONFIG_ICMPv6_AUTOCONF_MAXTRIES; retries++)
    {
      /* Set up the Router Advertisement BEFORE we send the Router
       * Solicitation.
       */

      icmpv6_rwait_setup(dev, &notify);

      /* Send the ICMPv6 Router solicitation message */

      ret = icmpv6_send_message(dev, false);
      if (ret < 0)
        {
          nerr("ERROR: Failed send router solicitation: %d\n", ret);
          break;
        }

      /* Wait to receive the Router Advertisement message */

      ret = icmpv6_wait_radvertise(dev, &notify);
      if (ret != -ETIMEDOUT)
        {
          /* ETIMEDOUT is the only expected failure.  We will retry on that
           * case only.
           */

          break;
        }

      ninfo("Timed out... retrying %d\n", retries + 1);
    }

  /* Check for failures.  Note:  On successful return, the network will be 
   * in the down state, but not in the event of failures.
   */

  if (ret < 0)
    {
      nerr("ERROR: Failed to get the router advertisement: %d (retries=%d)\n",
           ret, retries);

      /* Claim the link local address as ours by sending the ICMPv6 Neighbor
       * Advertisement message.
       */

      ret = icmpv6_send_message(dev, true);
      if (ret < 0)
        {
          nerr("ERROR: Failed send neighbor advertisement: %d\n", ret);
          netdev_ifdown(dev);
        }

      /* No off-link communications; No router address. */

      net_ipv6addr_copy(dev->d_ipv6draddr, g_ipv6_allzeroaddr);

      /* Set a netmask for the local link address */

      net_ipv6addr_copy(dev->d_ipv6netmask, g_ipv6_llnetmask);

      /* Leave the network up and return success (even though things did not
       * work out quite the way we wanted).
       */

      net_unlock();
      return ret;
    }

  /* 5. Router Direction: The router provides direction to the node on how to
   *    proceed with the auto-configuration. It may tell the node that on this
   *    network "stateful" auto-configuration is in use, and tell it the
   *    address of a DHCP server to use. Alternately, it will tell the host
   *    how to determine its global Internet address.
   *
   * 6. Global Address Configuration: Assuming that stateless auto-
   *    configuration is in use on the network, the host will configure
   *    itself with its globally-unique Internet address. This address is
   *    generally formed from a network prefix provided to the host by the
   *    router, combined with the device's identifier as generated in the
   *    first step.
   */

  /* On success, the new address was already set (in icmpv_rnotify()).  We
   * need only to bring the network back to the up state and return success.
   */

  netdev_ifup(dev);
  net_unlock();
  return OK;
#endif /* CONFIG_NET_ETHERNET */
}