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); }
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; }
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); }
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)); }
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]); }
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 */ } }
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); }
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(¬ify->nt_sem, 0, 0); nxsem_setprotocol(¬ify->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 }
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 }
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; }
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; }
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; }
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; }
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; }
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; }
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; } }
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]); }
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, ¬ify); /* 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, ¬ify); 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 */ }