void netdev_ipv6_txnotify(FAR const net_ipv6addr_t ripaddr) #endif { FAR struct net_driver_s *dev; /* Find the device driver that serves the subnet of the remote address */ #ifdef CONFIG_NETDEV_MULTINIC dev = netdev_findby_ipv6addr(lipaddr, ripaddr); #else dev = netdev_findby_ipv6addr(ripaddr); #endif if (dev && dev->d_txavail) { /* Notify the device driver that new TX data is available. */ (void)dev->d_txavail(dev); } }
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 }
int ipv6_getsockname(FAR struct socket *psock, FAR struct sockaddr *addr, FAR socklen_t *addrlen) { FAR struct net_driver_s *dev; #if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP) FAR struct sockaddr_in6 *outaddr = (FAR struct sockaddr_in6 *)addr; #endif #ifdef CONFIG_NETDEV_MULTINIC net_ipv6addr_t *lipaddr; net_ipv6addr_t *ripaddr; #endif /* Check if enough space has been provided for the full address */ if (*addrlen < sizeof(struct sockaddr_in6)) { /* This function is supposed to return the partial address if * a smaller buffer has been provided. This support has not * been implemented. */ return -ENOSYS; } /* Set the port number */ switch (psock->s_type) { #ifdef CONFIG_NET_TCP case SOCK_STREAM: { FAR struct tcp_conn_s *tcp_conn = (FAR struct tcp_conn_s *)psock->s_conn; outaddr->sin6_port = tcp_conn->lport; /* Already in network byte order */ #ifdef CONFIG_NETDEV_MULTINIC lipaddr = &tcp_conn->u.ipv6.laddr; ripaddr = &tcp_conn->u.ipv6.raddr; #endif } break; #endif #ifdef CONFIG_NET_UDP case SOCK_DGRAM: { FAR struct udp_conn_s *udp_conn = (FAR struct udp_conn_s *)psock->s_conn; outaddr->sin6_port = udp_conn->lport; /* Already in network byte order */ #ifdef CONFIG_NETDEV_MULTINIC lipaddr = &udp_conn->u.ipv6.laddr; ripaddr = &udp_conn->u.ipv6.raddr; #endif } break; #endif default: return -EOPNOTSUPP; } /* The socket/connection does not know its IP address unless * CONFIG_NETDEV_MULTINIC is selected. Otherwise the design supports only * a single network device and only the network device knows the IP address. */ netdev_semtake(); #ifdef CONFIG_NETDEV_MULTINIC /* Find the device matching the IPv6 address in the connection structure */ dev = netdev_findby_ipv6addr(*lipaddr, *ripaddr); #else /* There is only one, the first network device in the list. */ dev = g_netdevices; #endif if (!dev) { netdev_semgive(); return -EINVAL; } /* Set the address family and the IP address */ #if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP) outaddr->sin6_family = AF_INET6; memcpy(outaddr->sin6_addr.in6_u.u6_addr8, dev->d_ipv6addr, 16); *addrlen = sizeof(struct sockaddr_in6); #endif netdev_semgive(); /* Return success */ return OK; }
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; }