arpcache.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. /*
  2. * Copyright (C) 2001-2005 by egnite Software GmbH
  3. * Copyright (c) 1993 by Digital Equipment Corporation
  4. * Copyright (c) 1983, 1993 by The Regents of the University of California
  5. *
  6. * All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in the
  16. * documentation and/or other materials provided with the distribution.
  17. * 3. Neither the name of the copyright holders nor the names of
  18. * contributors may be used to endorse or promote products derived
  19. * from this software without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  28. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  29. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  30. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  31. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  32. * SUCH DAMAGE.
  33. *
  34. * For additional information see http://www.ethernut.de/
  35. */
  36. /*!
  37. * \file net/arpcache.c
  38. * \brief ARP cache.
  39. *
  40. * \verbatim
  41. * $Id: arpcache.c 4473 2012-08-20 15:12:45Z haraldkipp $
  42. * \endverbatim
  43. */
  44. #include <cfg/os.h>
  45. #include <cfg/arp.h>
  46. #include <sys/event.h>
  47. #include <sys/timer.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50. #include <memdebug.h>
  51. #include <net/if_var.h>
  52. #include <netinet/if_ether.h>
  53. #include <arpa/inet.h>
  54. #ifdef NUTDEBUG
  55. #include <net/netdebug.h>
  56. #endif
  57. #if 0
  58. /* Use for local debugging. */
  59. #define NUTDEBUG
  60. #include <stdio.h>
  61. #define __tcp_trs stdout
  62. static uint_fast8_t __tcp_trf = 1;
  63. #endif
  64. /*!
  65. * \addtogroup xgARP
  66. */
  67. /*@{*/
  68. /*!
  69. * \name ARP Configuration
  70. *
  71. * The Nut/OS Configurator may be used to override the default values.
  72. */
  73. /*@{*/
  74. /*! \brief Maximum age of an entry in the ARP cache in minutes.
  75. *
  76. * Outdated entries will be regularly removed, forcing the Ethernet
  77. * interface to generate new ARP requests. This way MAC address
  78. * changes are detected.
  79. *
  80. * \showinitializer
  81. */
  82. #ifndef MAX_ARPAGE
  83. #define MAX_ARPAGE 9
  84. #endif
  85. /*! \brief Maximum number of ARP requests generated per query.
  86. *
  87. * If no ARP response is received after sending out the specified
  88. * number of request, the related IP address is considered unreachable.
  89. *
  90. * \showinitializer
  91. */
  92. #ifndef MAX_ARPREQUESTS
  93. #define MAX_ARPREQUESTS 1
  94. #endif
  95. /*! \brief Minimum wait before sending out a new ARP request.
  96. *
  97. * The specified number of milliseconds will be doubled on each retry.
  98. *
  99. * \showinitializer
  100. */
  101. #ifndef MIN_ARPWAIT
  102. #define MIN_ARPWAIT 500
  103. #endif
  104. /*@}*/
  105. /*!
  106. * \brief Remove all entries marked for removal.
  107. *
  108. * \param ifn The network interface.
  109. */
  110. static void ArpCacheFlush(IFNET * ifn)
  111. {
  112. ARPENTRY *ae = ifn->arpTable;
  113. ARPENTRY **aep = &ifn->arpTable;
  114. while (ae) {
  115. if (ae->ae_flags & ATF_REM) {
  116. /* Remove all waiting threads from the queue of this
  117. entry, but do not give up the CPU. If some other
  118. thread takes over and deals with ARP, we are dead. */
  119. NutEventBroadcastAsync(&ae->ae_tq);
  120. #ifdef NUTDEBUG
  121. if (__tcp_trf & NET_DBG_ARP) {
  122. fprintf(__tcp_trs, "[ARP-DEL %s]", inet_ntoa(ae->ae_ip));
  123. }
  124. #endif
  125. *aep = ae->ae_next;
  126. free(ae);
  127. ae = *aep;
  128. } else {
  129. aep = &ae->ae_next;
  130. ae = ae->ae_next;
  131. }
  132. }
  133. }
  134. /*!
  135. * \brief Update the age of all ARP entries of all Ethernet devices.
  136. *
  137. * Increments the age of all ARP entries. Any entry with an age above
  138. * \ref MAX_ARPAGE will be removed.
  139. *
  140. * If less less than one minute elapsed since the last update, the
  141. * routine will return without updating any entry.
  142. */
  143. static void ArpCacheAging(void)
  144. {
  145. static uint32_t last_update;
  146. NUTDEVICE *dev;
  147. if (NutGetSeconds() - last_update >= 60) {
  148. last_update = NutGetSeconds();
  149. /*
  150. * Loop through the list of all registered devices.
  151. */
  152. for (dev = nutDeviceList; dev; dev = dev->dev_next) {
  153. /* Process network devices only. */
  154. if (dev->dev_type == IFTYP_NET) {
  155. IFNET *ifn = dev->dev_icb;
  156. /* Process Ethernet interfaces only. */
  157. if (ifn && ifn->if_type == IFT_ETHER) {
  158. ARPENTRY *ae;
  159. uint8_t rmf = 0;
  160. /* Loop through all ARP entries of this interface. */
  161. for (ae = ifn->arpTable; ae; ae = ae->ae_next) {
  162. if ((ae->ae_flags & ATF_PERM) == 0 && /* Not permanent. */
  163. ae->ae_outdated++ >= MAX_ARPAGE) { /* Outdated. */
  164. ae->ae_flags |= ATF_REM;
  165. }
  166. rmf |= ae->ae_flags;
  167. #ifdef NUTDEBUG
  168. if (__tcp_trf & NET_DBG_ARP) {
  169. fprintf(__tcp_trs, "[ARP-AGE %s %u]", /* */
  170. inet_ntoa(ae->ae_ip), ae->ae_outdated);
  171. }
  172. #endif
  173. }
  174. if (rmf & ATF_REM) {
  175. ArpCacheFlush(ifn);
  176. }
  177. }
  178. }
  179. }
  180. }
  181. }
  182. /*!
  183. * \brief Locate an interface's ARP entry for a given IP address.
  184. *
  185. * \param ifn Pointer to the network interface.
  186. * \param ip IP address to search, given in network byte order.
  187. *
  188. * \return Pointer to the ARP cache entry, if found, or NULL if no
  189. * entry exists.
  190. */
  191. static ARPENTRY *ArpCacheLookup(IFNET * ifn, uint32_t ip)
  192. {
  193. ARPENTRY *entry;
  194. for (entry = ifn->arpTable; entry; entry = entry->ae_next) {
  195. if (entry->ae_ip == ip)
  196. break;
  197. }
  198. return entry;
  199. }
  200. /*!
  201. * \brief Create a new entry in the interface's ARP cache.
  202. *
  203. * The new entry is added on top of the cache list.
  204. *
  205. * \param ifn Pointer to the network interface.
  206. * \param ip IP address of the new entry, given in network byte order.
  207. * \param ha Pointer to the MAC address. If NULL, an incomplete entry
  208. * will be created.
  209. *
  210. * \return Pointer to the new entry or NULL if not enough memory is
  211. * available.
  212. */
  213. static ARPENTRY *ArpCacheNew(IFNET * ifn, uint32_t ip, uint8_t * ha)
  214. {
  215. ARPENTRY *entry;
  216. /* Remove outdated entries before adding a new one. */
  217. ArpCacheAging();
  218. if ((entry = malloc(sizeof(ARPENTRY))) != 0) {
  219. memset(entry, 0, sizeof(ARPENTRY));
  220. entry->ae_ip = ip;
  221. if (ha) {
  222. memcpy(entry->ae_ha, ha, 6);
  223. entry->ae_flags = ATF_COM;
  224. }
  225. entry->ae_next = ifn->arpTable;
  226. ifn->arpTable = entry;
  227. #ifdef NUTDEBUG
  228. if (__tcp_trf & NET_DBG_ARP) {
  229. fprintf(__tcp_trs, "\n[ARP-NEW %s", inet_ntoa(ip));
  230. if (ha) {
  231. fprintf(__tcp_trs, " %02x%02x%02x%02x%02x%02x", /* */
  232. ha[0], ha[1], ha[2], ha[3], ha[4], ha[5]);
  233. }
  234. fputc(']', __tcp_trs);
  235. }
  236. #endif
  237. }
  238. return entry;
  239. }
  240. /*!
  241. * \brief Update an ARP entry.
  242. *
  243. * If an entry with the same IP address exists, then this entry is
  244. * updated. If no entry exists, a new one is created. All threads
  245. * waiting for address resolution are woken up.
  246. *
  247. * \note This function is automatically called on each incoming
  248. * ARP telegram. Applications typically do not call this
  249. * function.
  250. *
  251. * \param dev Identifies the device.
  252. * \param ip Requested IP address in network byte order.
  253. * \param ha Pointer to a buffer which receives the MAC address.
  254. *
  255. */
  256. void NutArpCacheUpdate(NUTDEVICE * dev, uint32_t ip, uint8_t * ha)
  257. {
  258. ARPENTRY *entry;
  259. /*
  260. * If an entry with this IP exists, wake up waiting threads. If the
  261. * entry is not permanent, then update it and mark it completed first.
  262. */
  263. if ((entry = ArpCacheLookup(dev->dev_icb, ip)) != 0) {
  264. #ifdef NUTDEBUG
  265. if (__tcp_trf & NET_DBG_ARP) {
  266. fprintf(__tcp_trs, "[ARP-UPD %s", inet_ntoa(ip));
  267. if (ha) {
  268. fprintf(__tcp_trs, " %02x%02x%02x%02x%02x%02x", /* */
  269. ha[0], ha[1], ha[2], ha[3], ha[4], ha[5]);
  270. }
  271. fputc(']', __tcp_trs);
  272. }
  273. #endif
  274. if ((entry->ae_flags & ATF_PERM) == 0) {
  275. entry->ae_outdated = 0;
  276. memcpy(entry->ae_ha, ha, 6);
  277. entry->ae_flags |= ATF_COM;
  278. }
  279. NutEventBroadcast(&entry->ae_tq);
  280. }
  281. /*
  282. * If no entry with this IP exists, then create a new completed one.
  283. */
  284. else {
  285. ArpCacheNew(dev->dev_icb, ip, ha);
  286. }
  287. }
  288. /*!
  289. * \brief Query MAC address for a specified IP address.
  290. *
  291. * If no entry is available in the ARP cache, an incomplete entry is
  292. * created and ARP requests are generated on increasing time intervals.
  293. * The calling thread is suspended until a matching ARP response is
  294. * received or until a number of requests have been generated without
  295. * receiving a response.
  296. *
  297. * \note This function is automatically called on each outgoing
  298. * IP packet. Applications typically do not call this function.
  299. *
  300. * \param dev Identifies the device.
  301. * \param ip IP address of which the caller asked the MAC address.
  302. * \param mac Buffer for the retrieved MAC address.
  303. *
  304. * \return 0 if address resolved, -1 otherwise.
  305. */
  306. int NutArpCacheQuery(NUTDEVICE * dev, const uint32_t ip, uint8_t * mac)
  307. {
  308. int rc = -1;
  309. ARPENTRY *entry;
  310. IFNET *ifn = dev->dev_icb;
  311. NETBUF *nb = 0;
  312. uint_fast8_t retries = MAX_ARPREQUESTS;
  313. uint32_t tmo = MIN_ARPWAIT;
  314. /* Aging the cache on each query adds some processing to the path
  315. * which we want to be as fast as possible. But when calling this
  316. * function in NutArpCacheNew only, we will never detect when a
  317. * node changes its MAC address. Anyway, the previous solution of
  318. * running a timer thread consumed too much RAM.
  319. */
  320. ArpCacheAging();
  321. /*
  322. * Search a matching entry. If none exists, create a new incomplete
  323. * entry and a request packet. If another thread has entered this
  324. * routine, an incomplete entry exists and the current thread will
  325. * not create a request packet and send out requests.
  326. */
  327. if ((entry = ArpCacheLookup(ifn, ip)) == 0) {
  328. if ((entry = ArpCacheNew(ifn, ip, 0)) == 0) {
  329. return -1;
  330. }
  331. if ((nb = NutArpAllocNetBuf(ARPOP_REQUEST, ip, 0)) == 0) {
  332. entry->ae_flags |= ATF_REM;
  333. ArpCacheFlush(ifn);
  334. return -1;
  335. }
  336. }
  337. /*
  338. * We enter a loop, which will send ARP requests on increasing
  339. * time intervals until our ARP entry gets completed. Give up
  340. * after a configured number of retries.
  341. */
  342. for (;;) {
  343. /* If completed, provide the MAC address and exit. */
  344. if (entry->ae_flags & ATF_COM) {
  345. //Work around for GCC 3.4.3 bug #18251
  346. //memcpy(mac, entry->ae_ha, 6);
  347. //rc = 0;
  348. rc = 6;
  349. do {
  350. rc--;
  351. mac[rc] = entry->ae_ha[rc];
  352. } while(rc);
  353. break;
  354. }
  355. #ifdef NUTDEBUG
  356. if (__tcp_trf & NET_DBG_ARP) {
  357. fprintf(__tcp_trs, "[%u.ARP-%s %s]", /* */
  358. MAX_ARPREQUESTS - retries + 1, /* */
  359. nb ? "QRY" : "WAIT", /* */
  360. inet_ntoa(ip));
  361. }
  362. #endif
  363. /* Give up on too many retries. */
  364. if (retries-- == 0) {
  365. break;
  366. }
  367. /* Mark buffer released and remove incomplete entry on transmit errors. */
  368. if (nb && NutArpOutput(dev, nb)) {
  369. nb = 0;
  370. /* Even if the transmit failed, we may have received a response in the meantime. */
  371. if ((entry = ArpCacheLookup(ifn, ip)) != NULL && (entry->ae_flags & ATF_COM) == 0) {
  372. entry->ae_flags |= ATF_REM;
  373. ArpCacheFlush(ifn);
  374. }
  375. break;
  376. }
  377. /* Sleep until woken up by an update of this ARP entry
  378. or until timeout. Double the timeout on each retry. */
  379. NutEventWait(&entry->ae_tq, tmo);
  380. tmo += tmo;
  381. /* During our sleep, another thread, which created the
  382. incomplete entry, may have given up and removed the entry.
  383. In this case we should also return an error. */
  384. if ((entry = ArpCacheLookup(ifn, ip)) == 0) {
  385. break;
  386. }
  387. }
  388. /* Only the thread that created the entry, allocated a request
  389. packet. If this thread fails, it should also remove the entry. */
  390. if (nb) {
  391. NutNetBufFree(nb);
  392. /* Play save and check, if the entry still exists. */
  393. if (rc && entry) {
  394. entry->ae_flags |= ATF_REM;
  395. ArpCacheFlush(ifn);
  396. }
  397. }
  398. return rc;
  399. }
  400. /*@}*/