summaryrefslogtreecommitdiff
path: root/nuttx/net/arp/arp_send.c
blob: b3803d0f1a03d9d081c7976fd48e8cda9a751b3d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
/****************************************************************************
 * net/arp/arp_send.c
 *
 *   Copyright (C) 2014 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * 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 <nuttx/config.h>

#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <time.h>
#include <debug.h>

#include <netinet/in.h>
#include <net/if.h>

#include <nuttx/net/net.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/ip.h>
#include <nuttx/net/arp.h>

#include "netdev/netdev.h"
#include "devif/devif.h"
#include "route/route.h"
#include "arp/arp.h"

#ifdef CONFIG_NET_ARP_SEND

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define CONFIG_ARP_SEND_DELAYSEC  \
  (CONFIG_ARP_SEND_DELAYMSEC / 1000)
#define CONFIG_ARP_SEND_DELAYNSEC \
  ((CONFIG_ARP_SEND_DELAYMSEC - 1000*CONFIG_ARP_SEND_DELAYSEC) * 1000000)

/****************************************************************************
 * Private Types
 ****************************************************************************/

/****************************************************************************
 * Private Data
 ****************************************************************************/

/****************************************************************************
 * 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((FAR const char *)dev->d_ifname, (FAR const char *)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 (in network order).
 *
 * 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_notify_s notify;
  struct timespec delay;
  struct arp_send_s state;
  net_lock_t save;
  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;
    }

#if defined(CONFIG_NET_IGMP) && !defined(CONFIG_NET_IPv6)
  /* 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 */

#ifdef CONFIG_NET_MULTILINK
  dev = netdev_findbyaddr(g_allzeroaddr, ipaddr);
#else
  dev = netdev_findbyaddr(ipaddr);
#endif
  if (!dev)
    {
      ndbg("ERROR: Unreachable: %08lx\n", (unsigned long)ipaddr);
      ret = -EHOSTUNREACH;
      goto errout;
    }

#ifdef CONFIG_NET_MULTILINK
  /* ARP support is only built if the Ethernet data link is supported.
   * However, if we are supporting multiple network devices and using
   * different link level protocols then we can get here for other
   * link protocals as well.  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;
    }
#endif

  /* Check if the destination address is on the local network. */

  if (!net_ipaddr_maskcmp(ipaddr, dev->d_ipaddr, dev->d_netmask))
    {
      net_ipaddr_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_router(dev, ipaddr, &dripaddr);
#else
      /* Use the device's default router IP address instead of the
       * destination address when determining the MAC address.
       */

      net_ipaddr_copy(dripaddr, dev->d_draddr);
#endif
      ipaddr = 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 = 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((FAR char *)state.snd_ifname, (FAR const char *)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.  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))
        {
          /* 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, &notify);

      /* 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.
       */

      delay.tv_sec  = CONFIG_ARP_SEND_DELAYSEC;
      delay.tv_nsec = CONFIG_ARP_SEND_DELAYNSEC;

      ret = arp_wait(&notify, &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;
        }

      /* 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 */