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 
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 

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