arpcache.c

Go to the documentation of this file.
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 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/