00001 /* 00002 * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved. 00003 * 00004 * Redistribution and use in source and binary forms, with or without 00005 * modification, are permitted provided that the following conditions 00006 * are met: 00007 * 00008 * 1. Redistributions of source code must retain the above copyright 00009 * notice, this list of conditions and the following disclaimer. 00010 * 2. Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * 3. Neither the name of the copyright holders nor the names of 00014 * contributors may be used to endorse or promote products derived 00015 * from this software without specific prior written permission. 00016 * 00017 * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS 00018 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00019 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00020 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE 00021 * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00022 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00023 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 00024 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 00025 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 00026 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 00027 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00028 * SUCH DAMAGE. 00029 * 00030 * For additional information see http://www.ethernut.de/ 00031 * 00032 * - 00033 * Portions Copyright (c) 1983, 1993 by 00034 * The Regents of the University of California. All rights reserved. 00035 * 00036 * Redistribution and use in source and binary forms, with or without 00037 * modification, are permitted provided that the following conditions 00038 * are met: 00039 * 1. Redistributions of source code must retain the above copyright 00040 * notice, this list of conditions and the following disclaimer. 00041 * 2. Redistributions in binary form must reproduce the above copyright 00042 * notice, this list of conditions and the following disclaimer in the 00043 * documentation and/or other materials provided with the distribution. 00044 * 3. Neither the name of the University nor the names of its contributors 00045 * may be used to endorse or promote products derived from this software 00046 * without specific prior written permission. 00047 * 00048 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 00049 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00050 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00051 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 00052 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00053 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00054 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00055 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00056 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00057 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00058 * SUCH DAMAGE. 00059 * - 00060 * Portions Copyright (c) 1993 by Digital Equipment Corporation. 00061 * 00062 * Permission to use, copy, modify, and distribute this software for any 00063 * purpose with or without fee is hereby granted, provided that the above 00064 * copyright notice and this permission notice appear in all copies, and that 00065 * the name of Digital Equipment Corporation not be used in advertising or 00066 * publicity pertaining to distribution of the document or software without 00067 * specific, written prior permission. 00068 * 00069 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL 00070 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES 00071 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT 00072 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 00073 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 00074 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 00075 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 00076 * SOFTWARE. 00077 */ 00078 00150 #include <cfg/os.h> 00151 #include <cfg/arp.h> 00152 00153 #include <sys/event.h> 00154 #include <sys/timer.h> 00155 00156 #include <stdlib.h> 00157 #include <string.h> 00158 00159 #include <net/if_var.h> 00160 #include <netinet/if_ether.h> 00161 #include <arpa/inet.h> 00162 00163 #ifdef NUTDEBUG 00164 #include <net/netdebug.h> 00165 #endif 00166 00167 #if 0 00168 /* Use for local debugging. */ 00169 #define NUTDEBUG 00170 #include <stdio.h> 00171 #define __tcp_trs stdout 00172 static uint_fast8_t __tcp_trf = 1; 00173 #endif 00174 00179 00186 00195 #ifndef MAX_ARPAGE 00196 #define MAX_ARPAGE 9 00197 #endif 00198 00206 #ifndef MAX_ARPREQUESTS 00207 #define MAX_ARPREQUESTS 1 00208 #endif 00209 00216 #ifndef MIN_ARPWAIT 00217 #define MIN_ARPWAIT 500 00218 #endif 00219 00228 static void ArpCacheFlush(IFNET * ifn) 00229 { 00230 ARPENTRY *ae = ifn->arpTable; 00231 ARPENTRY **aep = &ifn->arpTable; 00232 00233 while (ae) { 00234 if (ae->ae_flags & ATF_REM) { 00235 /* Remove all waiting threads from the queue of this 00236 entry, but do not give up the CPU. If some other 00237 thread takes over and deals with ARP, we are dead. */ 00238 NutEventBroadcastAsync(&ae->ae_tq); 00239 #ifdef NUTDEBUG 00240 if (__tcp_trf) { 00241 fprintf(__tcp_trs, "[ARP-DEL %s]", inet_ntoa(ae->ae_ip)); 00242 } 00243 #endif 00244 *aep = ae->ae_next; 00245 free(ae); 00246 ae = *aep; 00247 } else { 00248 aep = &ae->ae_next; 00249 ae = ae->ae_next; 00250 } 00251 } 00252 } 00253 00263 static void ArpCacheAging(void) 00264 { 00265 static uint32_t last_update; 00266 NUTDEVICE *dev; 00267 00268 if (NutGetSeconds() - last_update >= 60) { 00269 last_update = NutGetSeconds(); 00270 00271 /* 00272 * Loop through the list of all registered devices. 00273 */ 00274 for (dev = nutDeviceList; dev; dev = dev->dev_next) { 00275 00276 /* Process network devices only. */ 00277 if (dev->dev_type == IFTYP_NET) { 00278 IFNET *ifn = dev->dev_icb; 00279 00280 /* Process Ethernet interfaces only. */ 00281 if (ifn && ifn->if_type == IFT_ETHER) { 00282 ARPENTRY *ae; 00283 uint8_t rmf = 0; 00284 00285 /* Loop through all ARP entries of this interface. */ 00286 for (ae = ifn->arpTable; ae; ae = ae->ae_next) { 00287 if ((ae->ae_flags & ATF_PERM) == 0 && /* Not permanent. */ 00288 ae->ae_outdated++ >= MAX_ARPAGE) { /* Outdated. */ 00289 ae->ae_flags |= ATF_REM; 00290 } 00291 rmf |= ae->ae_flags; 00292 #ifdef NUTDEBUG 00293 if (__tcp_trf) { 00294 fprintf(__tcp_trs, "[ARP-AGE %s %u]", /* */ 00295 inet_ntoa(ae->ae_ip), ae->ae_outdated); 00296 } 00297 #endif 00298 } 00299 if (rmf & ATF_REM) { 00300 ArpCacheFlush(ifn); 00301 } 00302 } 00303 } 00304 } 00305 } 00306 } 00307 00317 static ARPENTRY *ArpCacheLookup(IFNET * ifn, uint32_t ip) 00318 { 00319 ARPENTRY *entry; 00320 00321 for (entry = ifn->arpTable; entry; entry = entry->ae_next) { 00322 if (entry->ae_ip == ip) 00323 break; 00324 } 00325 return entry; 00326 } 00327 00328 00342 static ARPENTRY *ArpCacheNew(IFNET * ifn, uint32_t ip, uint8_t * ha) 00343 { 00344 ARPENTRY *entry; 00345 00346 /* Remove outdated entries before adding a new one. */ 00347 ArpCacheAging(); 00348 00349 if ((entry = malloc(sizeof(ARPENTRY))) != 0) { 00350 memset(entry, 0, sizeof(ARPENTRY)); 00351 entry->ae_ip = ip; 00352 if (ha) { 00353 memcpy(entry->ae_ha, ha, 6); 00354 entry->ae_flags = ATF_COM; 00355 } 00356 entry->ae_next = ifn->arpTable; 00357 ifn->arpTable = entry; 00358 00359 #ifdef NUTDEBUG 00360 if (__tcp_trf) { 00361 fprintf(__tcp_trs, "\n[ARP-NEW %s", inet_ntoa(ip)); 00362 if (ha) { 00363 fprintf(__tcp_trs, " %02x%02x%02x%02x%02x%02x", /* */ 00364 ha[0], ha[1], ha[2], ha[3], ha[4], ha[5]); 00365 } 00366 fputc(']', __tcp_trs); 00367 } 00368 #endif 00369 } 00370 return entry; 00371 } 00372 00389 void NutArpCacheUpdate(NUTDEVICE * dev, uint32_t ip, uint8_t * ha) 00390 { 00391 ARPENTRY *entry; 00392 00393 /* 00394 * If an entry with this IP exists, wake up waiting threads. If the 00395 * entry is not permanent, then update it and mark it completed first. 00396 */ 00397 if ((entry = ArpCacheLookup(dev->dev_icb, ip)) != 0) { 00398 00399 #ifdef NUTDEBUG 00400 if (__tcp_trf) { 00401 fprintf(__tcp_trs, "[ARP-UPD %s", inet_ntoa(ip)); 00402 if (ha) { 00403 fprintf(__tcp_trs, " %02x%02x%02x%02x%02x%02x", /* */ 00404 ha[0], ha[1], ha[2], ha[3], ha[4], ha[5]); 00405 } 00406 fputc(']', __tcp_trs); 00407 } 00408 #endif 00409 00410 if ((entry->ae_flags & ATF_PERM) == 0) { 00411 entry->ae_outdated = 0; 00412 memcpy(entry->ae_ha, ha, 6); 00413 entry->ae_flags |= ATF_COM; 00414 } 00415 NutEventBroadcast(&entry->ae_tq); 00416 } 00417 00418 /* 00419 * If no entry with this IP exists, then create a new completed one. 00420 */ 00421 else { 00422 ArpCacheNew(dev->dev_icb, ip, ha); 00423 } 00424 } 00425 00444 int NutArpCacheQuery(NUTDEVICE * dev, CONST uint32_t ip, uint8_t * mac) 00445 { 00446 int rc = -1; 00447 ARPENTRY *entry; 00448 IFNET *ifn = dev->dev_icb; 00449 NETBUF *nb = 0; 00450 uint_fast8_t retries = MAX_ARPREQUESTS; 00451 uint32_t tmo = MIN_ARPWAIT; 00452 00453 /* Aging the cache on each query adds some processing to the path 00454 * which we want to be as fast as possible. But when calling this 00455 * function in NutArpCacheNew only, we will never detect when a 00456 * node changes its MAC address. Anyway, the previous solution of 00457 * running a timer thread consumed too much RAM. 00458 */ 00459 ArpCacheAging(); 00460 00461 /* 00462 * Search a matching entry. If none exists, create a new incomplete 00463 * entry and a request packet. If another thread has entered this 00464 * routine, an incomplete entry exists and the current thread will 00465 * not create a request packet and send out requests. 00466 */ 00467 if ((entry = ArpCacheLookup(ifn, ip)) == 0) { 00468 if ((entry = ArpCacheNew(ifn, ip, 0)) == 0) { 00469 return -1; 00470 } 00471 if ((nb = NutArpAllocNetBuf(ARPOP_REQUEST, ip, 0)) == 0) { 00472 entry->ae_flags |= ATF_REM; 00473 ArpCacheFlush(ifn); 00474 return -1; 00475 } 00476 } 00477 00478 /* 00479 * We enter a loop, which will send ARP requests on increasing 00480 * time intervals until our ARP entry gets completed. Give up 00481 * after a configured number of retries. 00482 */ 00483 for (;;) { 00484 /* If completed, provide the MAC address and exit. */ 00485 if (entry->ae_flags & ATF_COM) { 00486 //Work around for GCC 3.4.3 bug #18251 00487 //memcpy(mac, entry->ae_ha, 6); 00488 //rc = 0; 00489 rc = 6; 00490 do { 00491 rc--; 00492 mac[rc] = entry->ae_ha[rc]; 00493 } while(rc); 00494 break; 00495 } 00496 #ifdef NUTDEBUG 00497 if (__tcp_trf) { 00498 fprintf(__tcp_trs, "[%u.ARP-%s %s]", /* */ 00499 MAX_ARPREQUESTS - retries + 1, /* */ 00500 nb ? "QRY" : "WAIT", /* */ 00501 inet_ntoa(ip)); 00502 } 00503 #endif 00504 00505 /* Give up on too many retries. */ 00506 if (retries-- == 0) { 00507 break; 00508 } 00509 /* Mark buffer released and remove incomplete entry on transmit errors. */ 00510 if (nb && NutArpOutput(dev, nb)) { 00511 nb = 0; 00512 /* Even if the transmit failed, we may have received a response in the meantime. */ 00513 if ((entry = ArpCacheLookup(ifn, ip)) != NULL && (entry->ae_flags & ATF_COM) == 0) { 00514 entry->ae_flags |= ATF_REM; 00515 ArpCacheFlush(ifn); 00516 } 00517 break; 00518 } 00519 /* Sleep until woken up by an update of this ARP entry 00520 or until timeout. Double the timeout on each retry. */ 00521 NutEventWait(&entry->ae_tq, tmo); 00522 tmo += tmo; 00523 00524 /* During our sleep, another thread, which created the 00525 incomplete entry, may have given up and removed the entry. 00526 In this case we should also return an error. */ 00527 if ((entry = ArpCacheLookup(ifn, ip)) == 0) { 00528 break; 00529 } 00530 } 00531 00532 /* Only the thread that created the entry, allocated a request 00533 packet. If this thread fails, it should also remove the entry. */ 00534 if (nb) { 00535 NutNetBufFree(nb); 00536 /* Play save and check, if the entry still exists. */ 00537 if (rc && entry) { 00538 entry->ae_flags |= ATF_REM; 00539 ArpCacheFlush(ifn); 00540 } 00541 } 00542 return rc; 00543 } 00544