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