From b440fa18ca2f27745add93276609d9dee8f809a3 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Mon, 18 Aug 2014 14:29:02 -0600 Subject: [PATCH] arp_send.c: Partial implementation of logic to send ARP requests to assure that an IP address mapping is present in the ARP table --- net/arp/Kconfig | 31 +++++ net/arp/Make.defs | 4 + net/arp/arp.h | 8 ++ net/arp/arp_send.c | 325 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 368 insertions(+) create mode 100644 net/arp/arp_send.c diff --git a/net/arp/Kconfig b/net/arp/Kconfig index 9cb5f4d9dc..37f25f8e67 100644 --- a/net/arp/Kconfig +++ b/net/arp/Kconfig @@ -34,6 +34,37 @@ config NET_ARP_IPIN Harvest IP/MAC address mappings from the ARP table from incoming IP packets. +config NET_ARP_SEND + bool "ARP send" + default n + depends on EXPERIMENTAL + ---help--- + Enable logic to send ARP requests if the target IP address mapping + does not appear in the ARP table. + +if NET_ARP_SEND + +config ARP_SEND_MAXTRIES + int "ARP send retries" + default 5 + ---help--- + Send the ARP request this number of times before giving up and + deciding that the target IP address is non reachable. + +config ARP_SEND_DELAYMSEC + int "ARP re-send delay" + default 20 + ---help--- + Wait this number of milliseconds after sending the ARP request + before checking if the IP address mapping is present in the ARP + table. This time should be related to the maximum round trip time + on the network since it is basically the time from when an ARP + request is sent until the response is received. + +#endif + +endif # NET_ARP_SEND + config NET_ARP_DUMP bool "Dump ARP packet header" default n diff --git a/net/arp/Make.defs b/net/arp/Make.defs index 688c74f000..dc06872133 100644 --- a/net/arp/Make.defs +++ b/net/arp/Make.defs @@ -42,6 +42,10 @@ ifeq ($(CONFIG_NET_ARP_IPIN),y) NET_CSRCS += arp_ipin.c endif +ifeq ($(CONFIG_NET_ARP_SEND),y) +NET_CSRCS += arp_send.c +endif + ifeq ($(CONFIG_NET_ARP_DUMP),y) NET_CSRCS += arp_dump.c endif diff --git a/net/arp/arp.h b/net/arp/arp.h index 399ecae3e5..857c5bf7fe 100644 --- a/net/arp/arp.h +++ b/net/arp/arp.h @@ -64,6 +64,14 @@ # undef CONFIG_NET_ARP_DUMP #endif +#ifndef CONFIG_ARP_SEND_MAXTRIES +# define CONFIG_ARP_SEND_MAXTRIES 5 +#endif + +#ifndef CONFIG_ARP_SEND_DELAYMSEC +# define CONFIG_ARP_SEND_DELAYMSEC 20 +#endif + /* ARP Definitions **********************************************************/ #define ARP_REQUEST 1 diff --git a/net/arp/arp_send.c b/net/arp/arp_send.c new file mode 100644 index 0000000000..750e5097f0 --- /dev/null +++ b/net/arp/arp_send.c @@ -0,0 +1,325 @@ +/**************************************************************************** + * net/arp/arp_send.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "netdev/netdev.h" +#include "devif/devif.h" +#include "arp/arp.h" + +#ifdef CONFIG_NET_ARP_SEND + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Allocate a new packet socket data callback */ + +#define arp_callback_alloc(conn) devif_callback_alloc(&(conn)->list) +#define arp_callback_free(conn,cb) devif_callback_free(cb, &(conn)->list) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure holds the state of the send operation until it can be + * operated upon from the interrupt level. + */ + +struct arp_send_s +{ + FAR struct devif_callback_s *snd_cb; /* Reference to callback instance */ + sem_t snd_sem; /* Used to wake up the waiting thread */ + uint8_t snd_retries; /* Retry count */ + volatile bool snd_sent; /* True: if request sent */ +#ifdef CONFIG_NETDEV_MULTINIC + uint8_t snd_ifname[IFNAMSIZ]; /* Interface name */ +#endif + in_addr_t snd_ipaddr; /* The IP address to be queried */ +}; + +/* For compatibility with other protocols, a "connection" structure is + * provided. But it is a singleton for the case of ARP pack transfers. + */ + +struct arp_conn_s +{ + FAR struct devif_callback_s *list; /* ARP callbacks */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This is the singleton "connection" structure */ + +static struct arp_conn_s g_arp_conn; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: arp_send_interrupt + ****************************************************************************/ + +static uint16_t arp_send_interrupt(FAR struct net_driver_s *dev, + FAR void *pvconn, + FAR void *priv, uint16_t flags) +{ + FAR struct arp_send_s *state = (FAR struct arp_send_s *)priv; + + nllvdbg("flags: %04x sent: %d\n", flags, state->snd_sent); + + if (state) + { +#ifdef CONFIG_NETDEV_MULTINIC + /* Is this the device that we need to route this request? */ + + if (strncmp(dev->d_ifname, state->snd_ifname, IFNAMSIZ) != 0) + { + /* No... pass on this one and wait for the device that we want */ + + return flags; + } + +#endif + + /* Check if the outgoing packet is available. It may have been claimed + * by a send interrupt serving a different thread -OR- if the output + * buffer currently contains unprocessed incoming data. In these cases + * we will just have to wait for the next polling cycle. + */ + + if (dev->d_sndlen > 0 || (flags & PKT_NEWDATA) != 0) + { + /* Another thread has beat us sending data or the buffer is busy, + * Check for a timeout. If not timed out, wait for the next + * polling cycle and check again. + */ + + /* REVISIT: No timeout. Just wait for the next polling cycle */ + + return flags; + } + + /* It looks like we are good to send the data */ + /* Copy the packet data into the device packet buffer and send it */ + + arp_format(dev, state->snd_ipaddr); + + /* Make sure no ARP request overwrites this ARP request. This + * flag will be cleared in arp_out(). + */ + + dev->d_flags |= IFF_NOARP; + + /* Don't allow any further call backs. */ + + state->snd_sent = true; + state->snd_cb->flags = 0; + state->snd_cb->priv = NULL; + state->snd_cb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&state->snd_sem); + } + + return flags; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: arp_send + * + * Description: + * The arp_send() call may be to send an ARP request to resolve an IP + * address. This function first checks if the IP address is already in + * ARP table. If so, then it returns success immediately. + * + * If the requested IP address in not in the ARP table, then this function + * will send an ARP request, delay, then check if the IP address is now in + * the ARP table. It will repeat this sequence until either (1) the IP + * address mapping is now in the ARP table, or (2) a configurable number + * of timeouts occur without receiving the ARP replay. + * + * Parameters: + * ipaddr The IP address to be queried. + * + * Returned Value: + * Zero (OK) is returned on success and the IP address mapping can now be + * found in the ARP table. On error a negated errno value is returned: + * + * -ETIMEDOUT: The number or retry counts has been exceed. + * -EHOSTUNREACH: Could not find a route to the host + * + * Assumptions: + * This function is called from the normal tasking context. + * + ****************************************************************************/ + +int arp_send(in_addr_t ipaddr) +{ + FAR struct net_driver_s *dev; + struct arp_send_s state; + net_lock_t save; + int ret; + + /* Get the device that can route this request */ + + dev = netdev_findbyaddr(ipaddr); + if (!dev) + { + ndbg("ERROR: Unreachable: %08lx\n", (unsigned long)ipaddr); + ret = -EHOSTUNREACH; + goto errout; + } + + /* 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 = arp_callback_alloc(&g_arp_conn); + 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 */ + state.snd_ipaddr = ipaddr; /* IP address to query */ + +#ifdef CONFIG_NETDEV_MULTINIC + /* Remember the routing device name */ + + strncpy(state->snd_ifname, dev->d_ifname, IFNAMSIZ); +#endif + + /* 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 */ + + if (arp_find(ipaddr)) + { + /* We have it! Break out with success */ + + ret = OK; + break; + } + + /* Arm/re-arm the callback */ + + state.snd_sent = false; + state.snd_cb->flags = PKT_POLL; + state.snd_cb->priv = (FAR void *)&state; + state.snd_cb->event = arp_send_interrupt; + + /* Notify the device driver that new TX data is available. + * NOTES: This is in essence what netdev_txnotify() does, which + * is not possible to call since it expects a net_ipaddr_t as + * its single argument to lookup the network interface. + */ + + 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 ARP response to be received. The + * optimal delay would be the work case round trip time. + * + * REVISIT: We would get better performance if there were signalling + * from the arp_in() logic when the ARP response were received. But + * this should be okay for testing purposes. + */ + + usleep(1000*CONFIG_ARP_SEND_DELAYMSEC); + + /* Increment the retry count */ + + state.snd_retries++; + } + + sem_destroy(&state.snd_sem); + arp_callback_free(&g_arp_conn, state.snd_cb); +errout_with_lock: + net_unlock(save); +errout: + return ret; +} + +#endif /* CONFIG_NET_ARP_SEND */