static int net_match_ipv4(FAR struct net_route_ipv4_s *route, FAR void *arg) { FAR struct route_match_ipv4_s *match = (FAR struct route_match_ipv4_s *)arg; /* To match, the masked target address must be the same, and the masks * must be the same. */ net_ipv4_dumproute("Comparing", route); ninfo("With:\n"); ninfo(" target=%08lx netmask=%08lx\n", htonl(match->target), htonl(match->netmask)); if (net_ipv4addr_maskcmp(route->target, match->target, match->netmask) && net_ipv4addr_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; }
void arp_ipin(FAR struct net_driver_s *dev) { in_addr_t srcipaddr; /* Only insert/update an entry if the source IP address of the incoming IP * packet comes from a host on the local network. */ srcipaddr = net_ip4addr_conv32(IPBUF->eh_srcipaddr); if (net_ipv4addr_maskcmp(srcipaddr, dev->d_ipaddr, dev->d_netmask)) { arp_hdr_update(IPBUF->eh_srcipaddr, ETHBUF->src); } }
static bool lib_lo_ipv4match(FAR const void *addr, socklen_t len, int type) { FAR struct in_addr *ipv4addr; if (type == AF_INET && len >= sizeof(struct in_addr)) { ipv4addr = (FAR struct in_addr *)addr; return net_ipv4addr_maskcmp(ipv4addr->sin_addr.s_addr, g_lo_ipv4addr->s_addr, g_lo_ipv4addr->s_addr); } return false; }
static int net_match(FAR struct net_route_s *route, FAR void *arg) { FAR struct route_match_s *match = ( FAR struct route_match_s *)arg; /* To match, the masked target address must be the same, and the masks * must be the same. */ if (net_ipv4addr_maskcmp(route->target, match->target, match->netmask) && net_ipv4addr_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); } else { (void)sq_remfirst((FAR sq_queue_t *)&g_routes); } /* And free the routing table entry by adding it to the free list */ net_freeroute(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; }
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; }
static uint16_t ping_interrupt(FAR struct net_driver_s *dev, FAR void *conn, FAR void *pvpriv, uint16_t flags) { FAR struct icmp_ping_s *pstate = (struct icmp_ping_s *)pvpriv; FAR uint8_t *ptr; int i; nllvdbg("flags: %04x\n", flags); if (pstate) { /* Check if this is a ICMP ECHO reply. If so, return the sequence * number to the caller. NOTE: We may not even have sent the * requested ECHO request; this could have been the delayed ECHO * response from a previous ping. */ if ((flags & ICMP_ECHOREPLY) != 0 && conn != NULL) { FAR struct icmp_iphdr_s *icmp = (FAR struct icmp_iphdr_s *)conn; nllvdbg("ECHO reply: id=%d seqno=%d\n", ntohs(icmp->id), ntohs(icmp->seqno)); if (ntohs(icmp->id) == pstate->png_id) { /* Consume the ECHOREPLY */ flags &= ~ICMP_ECHOREPLY; dev->d_len = 0; /* Return the result to the caller */ pstate->png_result = OK; pstate->png_seqno = ntohs(icmp->seqno); goto end_wait; } } /* Check: * If the outgoing packet is available (it may have been claimed * by a sendto interrupt serving a different thread) * -OR- * If the output buffer currently contains unprocessed incoming * data. * -OR- * If we have already sent the ECHO request * * In the first two cases, we will just have to wait for the next * polling cycle. */ if (dev->d_sndlen <= 0 && /* Packet available */ (flags & ICMP_NEWDATA) == 0 && /* No incoming data */ !pstate->png_sent) /* Request not sent */ { FAR struct icmp_iphdr_s *picmp = ICMPBUF; /* We can send the ECHO request now. * * Format the ICMP ECHO request packet */ picmp->type = ICMP_ECHO_REQUEST; picmp->icode = 0; picmp->id = htons(pstate->png_id); picmp->seqno = htons(pstate->png_seqno); /* Add some easily verifiable data */ for (i = 0, ptr = ICMPDAT; i < pstate->png_datlen; i++) { *ptr++ = i; } /* Send the ICMP echo request. Note that d_sndlen is set to * the size of the ICMP payload and does not include the size * of the ICMP header. */ nllvdbg("Send ECHO request: seqno=%d\n", pstate->png_seqno); dev->d_sndlen = pstate->png_datlen + 4; icmp_send(dev, &pstate->png_addr); pstate->png_sent = true; return flags; } /* Check if the selected timeout has elapsed */ if (ping_timeout(pstate)) { int failcode; /* Check if this device is on the same network as the destination * device. */ if (!net_ipv4addr_maskcmp(pstate->png_addr, dev->d_ipaddr, dev->d_netmask)) { /* Destination address was not on the local network served by this * device. If a timeout occurs, then the most likely reason is * that the destination address is not reachable. */ nlldbg("Not reachable\n"); failcode = -ENETUNREACH; } else { nlldbg("Ping timeout\n"); failcode = -ETIMEDOUT; } /* Report the failure */ pstate->png_result = failcode; goto end_wait; } /* Continue waiting */ } return flags; end_wait: nllvdbg("Resuming\n"); /* Do not allow any further callbacks */ pstate->png_cb->flags = 0; pstate->png_cb->priv = NULL; pstate->png_cb->event = NULL; /* Wake up the waiting thread */ sem_post(&pstate->png_sem); return flags; }
void arp_out(FAR struct net_driver_s *dev) { struct ether_addr ethaddr; FAR struct eth_hdr_s *peth = ETHBUF; FAR struct arp_iphdr_s *pip = IPBUF; in_addr_t ipaddr; in_addr_t destipaddr; int ret; #if defined(CONFIG_NET_PKT) || defined(CONFIG_NET_ARP_SEND) /* Skip sending ARP requests when the frame to be transmitted was * written into a packet socket. */ 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; } #endif /* Find the destination IP address in the ARP table and construct * the Ethernet header. If the destination IP address isn't on the * local network, we use the default router's IP address instead. * * If not ARP table entry is found, we overwrite the original IP * packet with an ARP request for the IP address. */ /* First check if destination is a local broadcast. */ if (net_ipv4addr_hdrcmp(pip->eh_destipaddr, g_broadcast_ipaddr)) { memcpy(peth->dest, g_broadcast_ethaddr.ether_addr_octet, ETHER_ADDR_LEN); goto finish_header; } #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 (NTOHS(pip->eh_destipaddr[0]) >= 0xe000 && NTOHS(pip->eh_destipaddr[0]) <= 0xefff) { /* Build the well-known IPv4 IGMP Ethernet address. The first * three bytes are fixed; the final three variable come from the * last three bytes of the IPv4 address (network order). * * Address range : 01:00:5e:00:00:00 to 01:00:5e:7f:ff:ff */ FAR const uint8_t *ip = (FAR uint8_t *)pip->eh_destipaddr; peth->dest[0] = g_multicast_ethaddr[0]; peth->dest[1] = g_multicast_ethaddr[1]; peth->dest[2] = g_multicast_ethaddr[2]; peth->dest[3] = ip[1] & 0x7f; peth->dest[4] = ip[2]; peth->dest[5] = ip[3]; goto finish_header; } #endif /* Check if the destination address is on the local network. */ destipaddr = net_ip4addr_conv32(pip->eh_destipaddr); if (!net_ipv4addr_maskcmp(destipaddr, dev->d_ipaddr, dev->d_netmask)) { /* 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, destipaddr, &ipaddr); #else /* Use the device's default router IP address instead of the * destination address when determining the MAC address. */ net_ipv4addr_copy(ipaddr, dev->d_draddr); #endif } /* The destination address is on the local network. Check if it is * the sub-net broadcast address. */ else if (net_ipv4addr_broadcast(destipaddr, dev->d_netmask)) { /* Yes.. then we won't need to know the destination MAC address */ memcpy(peth->dest, g_broadcast_ethaddr.ether_addr_octet, ETHER_ADDR_LEN); goto finish_header; } else { /* Else, we use the destination IP address. */ net_ipv4addr_copy(ipaddr, destipaddr); } /* Check if we already have this destination address in the ARP table */ ret = arp_find(ipaddr, ðaddr); if (ret < 0) { ninfo("ARP request for IP %08lx\n", (unsigned long)ipaddr); /* The destination address was not in our ARP table, so we overwrite * the IP packet with an ARP request. */ arp_format(dev, ipaddr); arp_dump(ARPBUF); return; } /* Build an Ethernet header. */ memcpy(peth->dest, ethaddr.ether_addr_octet, ETHER_ADDR_LEN); /* Finish populating the Ethernet header */ finish_header: memcpy(peth->src, dev->d_mac.ether.ether_addr_octet, ETHER_ADDR_LEN); peth->type = HTONS(ETHTYPE_IP); dev->d_len += ETH_HDRLEN; }