void netdev_ipv4_txnotify(in_addr_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_ipv4addr(lipaddr, ripaddr); #else dev = netdev_findby_ipv4addr(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_ipv4_device(FAR struct tcp_conn_s *conn, in_addr_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 INADDR_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_ipv4addr_cmp(addr, INADDR_ANY)) { 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_ipv4addr(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 ipv4_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_in *outaddr = (FAR struct sockaddr_in *)addr; #endif #ifdef CONFIG_NETDEV_MULTINIC in_addr_t lipaddr; in_addr_t ripaddr; #endif /* Check if enough space has been provided for the full address */ if (*addrlen < sizeof(struct sockaddr_in)) { /* 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->sin_port = tcp_conn->lport; /* Already in network byte order */ #ifdef CONFIG_NETDEV_MULTINIC lipaddr = tcp_conn->u.ipv4.laddr; ripaddr = tcp_conn->u.ipv4.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->sin_port = udp_conn->lport; /* Already in network byte order */ #ifdef CONFIG_NETDEV_MULTINIC lipaddr = udp_conn->u.ipv4.laddr; ripaddr = udp_conn->u.ipv4.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 IPv4 address in the connection structure */ dev = netdev_findby_ipv4addr(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->sin_family = AF_INET; outaddr->sin_addr.s_addr = dev->d_ipaddr; *addrlen = sizeof(struct sockaddr_in); #endif netdev_semgive(); /* Return success */ return OK; }
int arp_send(in_addr_t ipaddr) { FAR struct net_driver_s *dev; struct arp_notify_s notify; struct timespec delay; struct arp_send_s state; int ret; /* First check if destination is a local broadcast. */ if (ipaddr == INADDR_BROADCAST) { /* We don't need to send the ARP request */ return OK; } #ifdef CONFIG_NET_IGMP /* Check if the destination address is a multicast address * * - IPv4: multicast addresses lie in the class D group -- The address range * 224.0.0.0 to 239.255.255.255 (224.0.0.0/4) * * - IPv6 multicast addresses are have the high-order octet of the * addresses=0xff (ff00::/8.) */ if (NTOHL(ipaddr) >= 0xe0000000 && NTOHL(ipaddr) <= 0xefffffff) { /* We don't need to send the ARP request */ return OK; } #endif /* Get the device that can route this request */ dev = netdev_findby_ipv4addr(INADDR_ANY, ipaddr); if (!dev) { nerr("ERROR: Unreachable: %08lx\n", (unsigned long)ipaddr); ret = -EHOSTUNREACH; goto errout; } /* ARP support is only built if the Ethernet data link is supported. * Continue and send the ARP request only if this device uses the * Ethernet data link protocol. */ if (dev->d_lltype != NET_LL_ETHERNET) { return OK; } /* Check if the destination address is on the local network. */ if (!net_ipv4addr_maskcmp(ipaddr, dev->d_ipaddr, dev->d_netmask)) { in_addr_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_ipv4_router(dev, ipaddr, &dripaddr); #else /* Use the device's default router IP address instead of the * destination address when determining the MAC address. */ net_ipv4addr_copy(dripaddr, dev->d_draddr); #endif ipaddr = dripaddr; } /* The destination address is on the local network. Check if it is * the sub-net broadcast address. */ else if (net_ipv4addr_broadcast(ipaddr, dev->d_netmask)) { /* Yes.. We don't need to send the ARP request */ return OK; } /* 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. */ net_lock(); state.snd_cb = arp_callback_alloc(dev); if (!state.snd_cb) { nerr("ERROR: Failed to allocate a callback\n"); ret = -ENOMEM; goto errout_with_lock; } /* This semaphore is used for signaling and, hence, should not have * priority inheritance enabled. */ (void)nxsem_init(&state.snd_sem, 0, 0); /* Doesn't really fail */ nxsem_setprotocol(&state.snd_sem, SEM_PRIO_NONE); state.snd_retries = 0; /* No retries yet */ state.snd_ipaddr = ipaddr; /* IP address to query */ /* Remember the routing device name */ strncpy((FAR char *)state.snd_ifname, (FAR const char *)dev->d_ifname, IFNAMSIZ); /* Now loop, testing if the address mapping is in the ARP table and re- * sending the ARP request if it is not. */ ret = -ETIMEDOUT; /* Assume a timeout failure */ while (state.snd_retries < CONFIG_ARP_SEND_MAXTRIES) { /* Check if the address mapping is present in the ARP table. This * is only really meaningful on the first time through the loop. * * NOTE: If the ARP table is large than this could be a performance * issue. */ if (arp_find(ipaddr, NULL) >= 0) { /* We have it! Break out with success */ ret = OK; break; } /* Set up the ARP response wait BEFORE we send the ARP request */ arp_wait_setup(ipaddr, ¬ify); /* Arm/re-arm the callback */ state.snd_sent = false; state.snd_result = -EBUSY; state.snd_cb->flags = (ARP_POLL | NETDEV_DOWN); state.snd_cb->priv = (FAR void *)&state; state.snd_cb->event = arp_send_eventhandler; /* Notify the device driver that new TX data is available. * NOTES: This is in essence what netdev_ipv4_txnotify() does, which * is not possible to call since it expects a in_addr_t as * its single argument to lookup the network interface. */ if (dev->d_txavail) { dev->d_txavail(dev); } /* Wait for the send to complete or an error to occur. * net_lockedwait will also terminate if a signal is received. */ do { (void)net_lockedwait(&state.snd_sem); } while (!state.snd_sent); /* Check the result of the send operation */ ret = state.snd_result; if (ret < 0) { /* Break out on a send failure */ nerr("ERROR: Send failed: %d\n", ret); break; } /* Now wait for response to the ARP response to be received. The * optimal delay would be the work case round trip time. * NOTE: The network is locked. */ delay.tv_sec = CONFIG_ARP_SEND_DELAYSEC; delay.tv_nsec = CONFIG_ARP_SEND_DELAYNSEC; ret = arp_wait(¬ify, &delay); /* arp_wait will return OK if and only if the matching ARP response * is received. Otherwise, it will return -ETIMEDOUT. */ if (ret >= OK) { /* Break out if arp_wait() fails */ break; } /* Increment the retry count */ state.snd_retries++; nerr("ERROR: arp_wait failed: %d\n", ret); } nxsem_destroy(&state.snd_sem); arp_callback_free(dev, state.snd_cb); errout_with_lock: net_unlock(); errout: return ret; }
int icmp_ping(in_addr_t addr, uint16_t id, uint16_t seqno, uint16_t datalen, int dsecs) { FAR struct net_driver_s *dev; struct icmp_ping_s state; net_lock_t save; #ifdef CONFIG_NET_ARP_SEND int ret; #endif /* Get the device that will be used to route this ICMP ECHO request */ #ifdef CONFIG_NETDEV_MULTINIC dev = netdev_findby_ipv4addr(INADDR_ANY, addr); #else dev = netdev_findby_ipv4addr(addr); #endif if (dev == 0) { ndbg("ERROR: Not reachable\n"); return -ENETUNREACH; } #ifdef CONFIG_NET_ARP_SEND /* Make sure that the IP address mapping is in the ARP table */ ret = arp_send(addr); if (ret < 0) { ndbg("ERROR: Not reachable\n"); return -ENETUNREACH; } #endif /* 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_addr = addr; /* Address of the peer to be ping'ed */ 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 */ save = net_lock(); state.png_time = clock_systimer(); /* Set up the callback */ state.png_cb = icmp_callback_alloc(dev); if (state.png_cb) { state.png_cb->flags = (ICMP_POLL | ICMP_ECHOREPLY | NETDEV_DOWN); state.png_cb->priv = (FAR 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 */ netdev_txnotify_dev(dev); /* 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); icmp_callback_free(dev, 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; } }