static inline FAR struct tcp_conn_s * tcp_ipv6_active(FAR struct net_driver_s *dev, FAR struct tcp_hdr_s *tcp) { FAR struct ipv6_hdr_s *ip = IPv6BUF; FAR struct tcp_conn_s *conn; net_ipv6addr_t *srcipaddr; #ifdef CONFIG_NETDEV_MULTINIC net_ipv6addr_t *destipaddr; #endif conn = (FAR struct tcp_conn_s *)g_active_tcp_connections.head; srcipaddr = (net_ipv6addr_t *)ip->srcipaddr; #ifdef CONFIG_NETDEV_MULTINIC destipaddr = (net_ipv6addr_t *)ip->destipaddr; #endif while (conn) { /* Find an open connection matching the TCP input. The following * checks are performed: * * - The local port number is checked against the destination port * number in the received packet. * - The remote port number is checked if the connection is bound * to a remote port. * - If multiple network interfaces are supported, then the local * IP address is available and we will insist that the * destination IP matches the bound address. If a socket is * bound to INADDRY_ANY, then it should receive all packets * directed to the port. * - Finally, if the connection is bound to a remote IP address, * the source IP address of the packet is checked. * * If all of the above are true then the newly received TCP packet * is destined for this TCP connection. */ if (conn->tcpstateflags != TCP_CLOSED && tcp->destport == conn->lport && tcp->srcport == conn->rport && #ifdef CONFIG_NETDEV_MULTINIC (net_ipv6addr_cmp(conn->u.ipv6.laddr, g_ipv6_allzeroaddr) || net_ipv6addr_cmp(*destipaddr, conn->u.ipv6.laddr)) && #endif net_ipv6addr_cmp(*srcipaddr, conn->u.ipv6.raddr)) { /* Matching connection found.. break out of the loop and return a * reference to it. */ break; } /* Look at the next active connection */ conn = (FAR struct tcp_conn_s *)conn->node.flink; } return conn; }
static bool lib_lo_ipv6match(FAR const void *addr, socklen_t len, int type) { FAR struct in_addr6 *ipv6addr; if (type == AF_INE6T && len >= sizeof(struct in_addr6)) { ipv6addr = (FAR struct in_addr6 *)addr; return net_ipv6addr_cmp(ipv6addr->sin6_addr.s6_addr16, g_lo_ipv6addr); } return false; }
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 inline FAR struct tcp_conn_s * tcp_ipv6_listener(const net_ipv6addr_t ipaddr, uint16_t portno) { FAR struct tcp_conn_s *conn; int i; /* Check if this port number is in use by any active UIP TCP connection */ for (i = 0; i < CONFIG_NET_TCP_CONNS; i++) { conn = &g_tcp_connections[i]; /* Check if this connection is open and the local port assignment * matches the requested port number. */ if (conn->tcpstateflags != TCP_CLOSED && conn->lport == portno) { /* If there are multiple interface devices, then the local IP * address of the connection must also match. INADDR_ANY is a * special case: There can only be instance of a port number * with INADDR_ANY. */ if (net_ipv6addr_cmp(conn->u.ipv6.laddr, ipaddr) || net_ipv6addr_cmp(conn->u.ipv6.laddr, g_ipv6_allzeroaddr)) { /* The port number is in use, return the connection */ return conn; } } } return NULL; }
static int tcp_find_ipv6_device(FAR struct tcp_conn_s *conn, const net_ipv6addr_t addr) { #ifdef CONFIG_NETDEV_MULTINIC /* Do nothing if a device is already bound to the connection */ if (conn->dev != NULL) { return OK; } /* Return success without using device notification if the locally bound * address is IN6ADDR_ANY. In this case, there may be multiple devices * that can provide data so the exceptional events from any particular * device are not important. */ if (net_ipv6addr_cmp(addr, g_ipv6_allzeroaddr)) { return OK; } /* There are multiple network devices. We need to select the device that * is going to route the TCP packet based on the provided IP address. */ conn->dev = netdev_findby_ipv6addr(addr, addr); /* Return success if we found the device */ return conn->dev != NULL ? OK : -ENETUNREACH; #else /* There is only a single network device... the one at the head of the * g_netdevices list. */ return (g_netdevices != NULL) ? OK : -ENETUNREACH; #endif }
void icmpv6_notify(net_ipv6addr_t ipaddr) { FAR struct icmpv6_notify_s *curr; /* Find an entry with the matching IP address in the list of waiters */ for (curr = g_icmpv6_waiters; curr; curr = curr->nt_flink) { /* Does this entry match? If the result is okay, then we have * already notified this waiter and it has not yet taken the * entry from the list. */ if (curr->nt_result != OK && net_ipv6addr_cmp(curr->nt_ipaddr, ipaddr)) { /* Yes.. Signal the waiting, returning success */ curr->nt_result = OK; nxsem_post(&curr->nt_sem); break; } } }
static int net_match_ipv6(FAR struct net_route_ipv6_s *route, FAR void *arg) { FAR struct route_match_ipv6_s *match = (FAR struct route_match_ipv6_s *)arg; /* To match, the masked target address must be the same, and the masks * must be the same. */ if (net_ipv6addr_maskcmp(route->target, match->target, match->netmask) && net_ipv6addr_cmp(route->netmask, match->netmask)) { /* They match.. Remove the entry from the routing table */ if (match->prev) { (void)sq_remafter((FAR sq_entry_t *)match->prev, (FAR sq_queue_t *)&g_routes_ipv6); } else { (void)sq_remfirst((FAR sq_queue_t *)&g_routes_ipv6); } /* And free the routing table entry by adding it to the free list */ net_freeroute_ipv6(route); /* Return a non-zero value to terminate the traversal */ return 1; } /* Next time we are here, this will be the previous entry */ match->prev = route; return 0; }
static int net_match_ipv6(FAR struct net_route_ipv6_s *route, FAR void *arg) { FAR struct route_match_ipv6_s *match = (FAR struct route_match_ipv6_s *)arg; /* To match, the masked target address must be the same, and the masks * must be the same. */ net_ipv6_dumproute("Comparing", route); ninfo("With:\n"); ninfo(" target: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", htons(match->target[0]), htons(match->target[1]), htons(match->target[2]), htons(match->target[3]), htons(match->target[4]), htons(match->target[5]), htons(match->target[6]), htons(match->target[7])); ninfo(" netmask: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", htons(match->netmask[0]), htons(match->netmask[1]), htons(match->netmask[2]), htons(match->netmask[3]), htons(match->netmask[4]), htons(match->netmask[5]), htons(match->netmask[6]), htons(match->netmask[7])); if (net_ipv6addr_maskcmp(route->target, match->target, match->netmask) && net_ipv6addr_cmp(route->netmask, match->netmask)) { /* They match.. a non-zero value to terminate the traversal. The last * value of index is the index to the matching entry. */ return 1; } /* Next time we are here, this will be the routing table index */ match->index++; return 0; }
int icmpv6_neighbor(const net_ipv6addr_t ipaddr) { FAR struct net_driver_s *dev; struct icmpv6_notify_s notify; struct timespec delay; struct icmpv6_neighbor_s state; FAR const uint16_t *lookup; net_lock_t save; int ret; /* First check if destination is a local broadcast or a multicast address. * * - IPv6 multicast addresses are have the high-order octet of the * addresses=0xff (ff00::/8.) */ if (net_ipv6addr_cmp(ipaddr, g_ipv6_allzeroaddr) || (ipaddr[0] & NTOHS(0xff00)) == NTOHS(0xff00)) { /* We don't need to send the Neighbor Solicitation */ return OK; } /* Get the device that can route this request */ #ifdef CONFIG_NETDEV_MULTINIC dev = netdev_findby_ipv6addr(g_ipv6_allzeroaddr, ipaddr); #else dev = netdev_findby_ipv6addr(ipaddr); #endif if (!dev) { ndbg("ERROR: Unreachable: %08lx\n", (unsigned long)ipaddr); ret = -EHOSTUNREACH; goto errout; } #ifdef CONFIG_NET_MULTILINK /* If we are supporting multiple network devices and using different * link level protocols then we can get here for other link protocols * as well. Continue and send the Neighbor Solicitation request only * if this device uses the Ethernet data link protocol. * * REVISIT: Other link layer protocols may require Neighbor Discovery * as well (but not SLIP which is the only other option at the moment). */ if (dev->d_lltype != NET_LL_ETHERNET) { return OK; } #endif /* Check if the destination address is on the local network. */ if (net_ipv6addr_maskcmp(ipaddr, dev->d_ipv6addr, dev->d_ipv6netmask)) { /* Yes.. use the input address for the lookup */ lookup = ipaddr; } else { net_ipv6addr_t dripaddr; /* Destination address is not on the local network */ #ifdef CONFIG_NET_ROUTE /* We have a routing table.. find the correct router to use in * this case (or, as a fall-back, use the device's default router * address). We will use the router IP address instead of the * destination address when determining the MAC address. */ netdev_ipv6_router(dev, ipaddr, dripaddr); #else /* Use the device's default router IP address instead of the * destination address when determining the MAC address. */ net_ipv6addr_copy(dripaddr, dev->d_ipv6draddr); #endif /* Use the router address for the lookup */ lookup = dripaddr; } /* Allocate resources to receive a callback. This and the following * initialization is performed with the network lock because we don't * want anything to happen until we are ready. */ save = net_lock(); state.snd_cb = icmpv6_callback_alloc(); if (!state.snd_cb) { ndbg("ERROR: Failed to allocate a cllback\n"); ret = -ENOMEM; goto errout_with_lock; } /* Initialize the state structure. This is done with interrupts * disabled */ (void)sem_init(&state.snd_sem, 0, 0); /* Doesn't really fail */ state.snd_retries = 0; /* No retries yet */ net_ipv6addr_copy(state.snd_ipaddr, lookup); /* IP address to query */ #ifdef CONFIG_NETDEV_MULTINIC /* Remember the routing device name */ strncpy((FAR char *)state.snd_ifname, (FAR const char *)dev->d_ifname, IFNAMSIZ); #endif /* Now loop, testing if the address mapping is in the Neighbor Table and * re-sending the Neighbor Solicitation if it is not. */ ret = -ETIMEDOUT; /* Assume a timeout failure */ while (state.snd_retries < CONFIG_ICMPv6_NEIGHBOR_MAXTRIES) { /* Check if the address mapping is present in the Neighbor Table. This * is only really meaningful on the first time through the loop. * * NOTE: If the Neighbor Table is large than this could be a performance * issue. */ if (neighbor_findentry(lookup) != NULL) { /* We have it! Break out with success */ ret = OK; break; } /* Set up the Neighbor Advertisement wait BEFORE we send the Neighbor * Solicitation. */ icmpv6_wait_setup(lookup, ¬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; }