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 00153 #include <cfg/os.h> 00154 #include <cfg/arp.h> 00155 00156 #include <sys/event.h> 00157 #include <sys/timer.h> 00158 00159 #include <stdlib.h> 00160 #include <string.h> 00161 #include <memdebug.h> 00162 00163 #include <net/if_var.h> 00164 #include <netinet/if_ether.h> 00165 #include <arpa/inet.h> 00166 00167 #ifdef NUTDEBUG 00168 #include <net/netdebug.h> 00169 #endif 00170 00171 #if 0 00172 /* Use for local debugging. */ 00173 #define NUTDEBUG 00174 #include <stdio.h> 00175 #define __tcp_trs stdout 00176 static uint_fast8_t __tcp_trf = 1; 00177 #endif 00178 00183 00190 00199 #ifndef MAX_ARPAGE 00200 #define MAX_ARPAGE 9 00201 #endif 00202 00210 #ifndef MAX_ARPREQUESTS 00211 #define MAX_ARPREQUESTS 1 00212 #endif 00213 00220 #ifndef MIN_ARPWAIT 00221 #define MIN_ARPWAIT 500 00222 #endif 00223 00232 static void ArpCacheFlush(IFNET * ifn) 00233 { 00234 ARPENTRY *ae = ifn->arpTable; 00235 ARPENTRY **aep = &ifn->arpTable; 00236 00237 while (ae) { 00238 if (ae->ae_flags & ATF_REM) { 00239 /* Remove all waiting threads from the queue of this 00240 entry, but do not give up the CPU. If some other 00241 thread takes over and deals with ARP, we are dead. */ 00242 NutEventBroadcastAsync(&ae->ae_tq); 00243 #ifdef NUTDEBUG 00244 if (__tcp_trf & NET_DBG_ARP) { 00245 fprintf(__tcp_trs, "[ARP-DEL %s]", inet_ntoa(ae->ae_ip)); 00246 } 00247 #endif 00248 *aep = ae->ae_next; 00249 free(ae); 00250 ae = *aep; 00251 } else { 00252 aep = &ae->ae_next; 00253 ae = ae->ae_next; 00254 } 00255 } 00256 } 00257 00267 static void ArpCacheAging(void) 00268 { 00269 static uint32_t last_update; 00270 NUTDEVICE *dev; 00271 00272 if (NutGetSeconds() - last_update >= 60) { 00273 last_update = NutGetSeconds(); 00274 00275 /* 00276 * Loop through the list of all registered devices. 00277 */ 00278 for (dev = nutDeviceList; dev; dev = dev->dev_next) { 00279 00280 /* Process network devices only. */ 00281 if (dev->dev_type == IFTYP_NET) { 00282 IFNET *ifn = dev->dev_icb; 00283 00284 /* Process Ethernet interfaces only. */ 00285 if (ifn && ifn->if_type == IFT_ETHER) { 00286 ARPENTRY *ae; 00287 uint8_t rmf = 0; 00288 00289 /* Loop through all ARP entries of this interface. */ 00290 for (ae = ifn->arpTable; ae; ae = ae->ae_next) { 00291 if ((ae->ae_flags & ATF_PERM) == 0 && /* Not permanent. */ 00292 ae->ae_outdated++ >= MAX_ARPAGE) { /* Outdated. */ 00293 ae->ae_flags |= ATF_REM; 00294 } 00295 rmf |= ae->ae_flags; 00296 #ifdef NUTDEBUG 00297 if (__tcp_trf & NET_DBG_ARP) { 00298 fprintf(__tcp_trs, "[ARP-AGE %s %u]", /* */ 00299 inet_ntoa(ae->ae_ip), ae->ae_outdated); 00300 } 00301 #endif 00302 } 00303 if (rmf & ATF_REM) { 00304 ArpCacheFlush(ifn); 00305 } 00306 } 00307 } 00308 } 00309 } 00310 } 00311 00321 static ARPENTRY *ArpCacheLookup(IFNET * ifn, uint32_t ip) 00322 { 00323 ARPENTRY *entry; 00324 00325 for (entry = ifn->arpTable; entry; entry = entry->ae_next) { 00326 if (entry->ae_ip == ip) 00327 break; 00328 } 00329 return entry; 00330 } 00331 00332 00346 static ARPENTRY *ArpCacheNew(IFNET * ifn, uint32_t ip, uint8_t * ha) 00347 { 00348 ARPENTRY *entry; 00349 00350 /* Remove outdated entries before adding a new one. */ 00351 ArpCacheAging(); 00352 00353 if ((entry = malloc(sizeof(ARPENTRY))) != 0) { 00354 memset(entry, 0, sizeof(ARPENTRY)); 00355 entry->ae_ip = ip; 00356 if (ha) { 00357 memcpy(entry->ae_ha, ha, 6); 00358 entry->ae_flags = ATF_COM; 00359 } 00360 entry->ae_next = ifn->arpTable; 00361 ifn->arpTable = entry; 00362 00363 #ifdef NUTDEBUG 00364 if (__tcp_trf & NET_DBG_ARP) { 00365 fprintf(__tcp_trs, "\n[ARP-NEW %s", inet_ntoa(ip)); 00366 if (ha) { 00367 fprintf(__tcp_trs, " %02x%02x%02x%02x%02x%02x", /* */ 00368 ha[0], ha[1], ha[2], ha[3], ha[4], ha[5]); 00369 } 00370 fputc(']', __tcp_trs); 00371 } 00372 #endif 00373 } 00374 return entry; 00375 } 00376 00393 void NutArpCacheUpdate(NUTDEVICE * dev, uint32_t ip, uint8_t * ha) 00394 { 00395 ARPENTRY *entry; 00396 00397 /* 00398 * If an entry with this IP exists, wake up waiting threads. If the 00399 * entry is not permanent, then update it and mark it completed first. 00400 */ 00401 if ((entry = ArpCacheLookup(dev->dev_icb, ip)) != 0) { 00402 00403 #ifdef NUTDEBUG 00404 if (__tcp_trf & NET_DBG_ARP) { 00405 fprintf(__tcp_trs, "[ARP-UPD %s", inet_ntoa(ip)); 00406 if (ha) { 00407 fprintf(__tcp_trs, " %02x%02x%02x%02x%02x%02x", /* */ 00408 ha[0], ha[1], ha[2], ha[3], ha[4], ha[5]); 00409 } 00410 fputc(']', __tcp_trs); 00411 } 00412 #endif 00413 00414 if ((entry->ae_flags & ATF_PERM) == 0) { 00415 entry->ae_outdated = 0; 00416 memcpy(entry->ae_ha, ha, 6); 00417 entry->ae_flags |= ATF_COM; 00418 } 00419 NutEventBroadcast(&entry->ae_tq); 00420 } 00421 00422 /* 00423 * If no entry with this IP exists, then create a new completed one. 00424 */ 00425 else { 00426 ArpCacheNew(dev->dev_icb, ip, ha); 00427 } 00428 } 00429 00448 int NutArpCacheQuery(NUTDEVICE * dev, CONST uint32_t ip, uint8_t * mac) 00449 { 00450 int rc = -1; 00451 ARPENTRY *entry; 00452 IFNET *ifn = dev->dev_icb; 00453 NETBUF *nb = 0; 00454 uint_fast8_t retries = MAX_ARPREQUESTS; 00455 uint32_t tmo = MIN_ARPWAIT; 00456 00457 /* Aging the cache on each query adds some processing to the path 00458 * which we want to be as fast as possible. But when calling this 00459 * function in NutArpCacheNew only, we will never detect when a 00460 * node changes its MAC address. Anyway, the previous solution of 00461 * running a timer thread consumed too much RAM. 00462 */ 00463 ArpCacheAging(); 00464 00465 /* 00466 * Search a matching entry. If none exists, create a new incomplete 00467 * entry and a request packet. If another thread has entered this 00468 * routine, an incomplete entry exists and the current thread will 00469 * not create a request packet and send out requests. 00470 */ 00471 if ((entry = ArpCacheLookup(ifn, ip)) == 0) { 00472 if ((entry = ArpCacheNew(ifn, ip, 0)) == 0) { 00473 return -1; 00474 } 00475 if ((nb = NutArpAllocNetBuf(ARPOP_REQUEST, ip, 0)) == 0) { 00476 entry->ae_flags |= ATF_REM; 00477 ArpCacheFlush(ifn); 00478 return -1; 00479 } 00480 } 00481 00482 /* 00483 * We enter a loop, which will send ARP requests on increasing 00484 * time intervals until our ARP entry gets completed. Give up 00485 * after a configured number of retries. 00486 */ 00487 for (;;) { 00488 /* If completed, provide the MAC address and exit. */ 00489 if (entry->ae_flags & ATF_COM) { 00490 //Work around for GCC 3.4.3 bug #18251 00491 //memcpy(mac, entry->ae_ha, 6); 00492 //rc = 0; 00493 rc = 6; 00494 do { 00495 rc--; 00496 mac[rc] = entry->ae_ha[rc]; 00497 } while(rc); 00498 break; 00499 } 00500 #ifdef NUTDEBUG 00501 if (__tcp_trf & NET_DBG_ARP) { 00502 fprintf(__tcp_trs, "[%u.ARP-%s %s]", /* */ 00503 MAX_ARPREQUESTS - retries + 1, /* */ 00504 nb ? "QRY" : "WAIT", /* */ 00505 inet_ntoa(ip)); 00506 } 00507 #endif 00508 00509 /* Give up on too many retries. */ 00510 if (retries-- == 0) { 00511 break; 00512 } 00513 /* Mark buffer released and remove incomplete entry on transmit errors. */ 00514 if (nb && NutArpOutput(dev, nb)) { 00515 nb = 0; 00516 /* Even if the transmit failed, we may have received a response in the meantime. */ 00517 if ((entry = ArpCacheLookup(ifn, ip)) != NULL && (entry->ae_flags & ATF_COM) == 0) { 00518 entry->ae_flags |= ATF_REM; 00519 ArpCacheFlush(ifn); 00520 } 00521 break; 00522 } 00523 /* Sleep until woken up by an update of this ARP entry 00524 or until timeout. Double the timeout on each retry. */ 00525 NutEventWait(&entry->ae_tq, tmo); 00526 tmo += tmo; 00527 00528 /* During our sleep, another thread, which created the 00529 incomplete entry, may have given up and removed the entry. 00530 In this case we should also return an error. */ 00531 if ((entry = ArpCacheLookup(ifn, ip)) == 0) { 00532 break; 00533 } 00534 } 00535 00536 /* Only the thread that created the entry, allocated a request 00537 packet. If this thread fails, it should also remove the entry. */ 00538 if (nb) { 00539 NutNetBufFree(nb); 00540 /* Play save and check, if the entry still exists. */ 00541 if (rc && entry) { 00542 entry->ae_flags |= ATF_REM; 00543 ArpCacheFlush(ifn); 00544 } 00545 } 00546 return rc; 00547 } 00548