Nut/OS  4.10.3
API Reference
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 
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