[En-Nut-Discussion] ARP cache again

Marek Pavlu marekpavlu at mybox.cz
Sun Feb 20 22:48:12 CET 2005


Hi Harald...

I have slightly different solving which solving the reason, no consequence:

1) If NutArpCacheQuery working with entry so no one can not removing this
entry. This is solving with new flag ATF_WORK. If ATF_WORK is set then no
one can not remove this entry from ARP cache. Flag ATF_WORK is erased only
in instance of NutArpCacheQuery which produced this entry...

Added to if_var.h file >>>
#define ATF_WORK    0x08        /*!< \brief Working with entry */

2) This change enforce modification in ArpCacheAging:
static void ArpCacheAging(void){
...
if ((ae->ae_flags & (ATF_PERM | ATF_WORK)) == 0 &&   /* Not permanent. */
//if ((ae->ae_flags & (ATF_PERM)) == 0 &&   /* Not permanent. */
...
}

3) But in case of running several NutAprCacheQuery instances with same
request for IP address then exist only one entry and working(sending ARP
packet) with this entry only one instance of NutArpCacheQuery. Exactly first
running instance of NutAprCacheQuery with same request for IP address will
be sending ARP packet and have control above ATF_WORK flag. Remainder
NutArpCacheQuery instances with same request waiting to finish first
instance of NutArpCacheQuery with same request. Such a method guarantee
minimal strain for system with several same request to ARP cache...

For several different request is all indentical like original version...

4) Solving problem with released netbuffer is same as solving from Dusan
Ferbas...

5) NutArpCacheQuery is completely rewrite.
int NutArpCacheQuery(NUTDEVICE * dev, CONST u_long ip, u_char * mac)
{
    char rc = -1;

    /*
     * flag == 1 NutArpCacheQuery is now running only one with original IP
     * flag == 0 NutArpCacheQuery is now running several NutArpCacheQuery
     *           with same IP address
     */
    unsigned char flag = 1;
    ARPENTRY *entry;
    IFNET *ifn = dev->dev_icb;
    NETBUF *nb = 0;
    u_char retries = MAX_ARPREQUESTS;
    u_long tmo = MIN_ARPWAIT;
    

    /* Aging the cache on each query adds some processing to the path 
     * which we want to be as fast as possible. But when calling this 
     * function in NutArpCacheNew only, we will never detect when a 
     * node changes its MAC address. Anyway, the previous solution of 
     * running a timer thread consumed too much RAM.
     */
    ArpCacheAging();

    /*
     * Search a matching entry. If none exists, create a new incomplete 
     * entry and a request packet. If another thread has entered this 
     * routine, an incomplete entry exists and the current thread will 
     * not create a request packet and send out requests.
     */

loop:
/* only in case of running several NutArpCacheQuery with same IP address */

    if ((entry = ArpCacheLookup(ifn, ip)) == 0 && flag) {
        if ((entry = ArpCacheNew(ifn, ip, 0))) {
            if ((nb = NutArpAllocNetBuf(ARPOP_REQUEST, ip, 0))) {
                /*
                 *    Set flag ATF_WOTK is for security this entry.
                 *    This entry not be removed till to reset flag
                 *    ATF_WORK...
                 */
                entry->ae_flags = ATF_WORK;
                rc = 1;
            }
        }
    }
    else if (entry) {
        /*
         *    If completed, provide the MAC address and exit.
         */
        if (entry->ae_flags & ATF_COM) {
            memcpy(mac, (void *) entry->ae_ha, 6);
            rc = 0;
        }
        /*
         * If ATF_WORK is set, then waiting to reset this flag...
         * this code ensure that in case of several running NutArpCacheQuery
         * with same request IP address will be sendiding the ARP packet
         * only one
         * instance of NutArpCacheQuery.
         * Exactly first running instance of NutAprCacheQuery with same
         * request IP address
         * will be the sending ARP packet...
         */
        else if (entry->ae_flags & ATF_WORK) {
            //NutSleep(MIN_ARPWAIT);
            NutEventWait(&entry->ae_tq, MIN_ARPWAIT);
            flag = 0;
            goto loop;
        }
    }
#ifdef NUTDEBUG
    if (__tcp_trs) {
        fprintf(__tcp_trs, "[ARPCQ=%d]\r\n", rc);
    }
#endif

    /*
     * if error or finded entry with request IP and set ATF_COM flag set in
     *  cache
     */
    if (rc <= 0)   goto endf;
    rc = -1;
    
    /*
     * We enter a loop, which will send ARP requests on increasing 
     * time intervals until our ARP entry gets completed. Give up 
     * after a configured number of retries.
     */
    
    while (retries-- > 0) {
    #ifdef NUTDEBUG
        if (__tcp_trs) {
            fprintf(__tcp_trs, "[%u.ARP-%s %s]\r\n",   /* */
                    MAX_ARPREQUESTS - retries + 1,      /* */
                    nb ? "QRY" : "WAIT",                /* */
                    inet_ntoa(ip));
        }
    #endif
        /*
         *    If completed, provide the MAC address and exit.
         */
        if (entry->ae_flags & ATF_COM) {
            memcpy(mac, (void *) entry->ae_ha, 6);
            rc = 0;
            break;
        }
        /*On transmit failures. */
        if (NutArpOutput(dev, nb) != 0) {
#ifdef NUTDEBUG
        if (__tcp_trs) {
            fprintf(__tcp_trs, "[ARPQ ERR]\r\n");
        }
#endif
            /*
             * NetBuffer DESTROYED in function NutEtherOutput
             */
            nb = 0;
            break;
        }
        /*
         * Sleep until woken up by an update of this ARP entry
         * or until timeout. Double the timeout on each retry.
         */
        NutEventWait (&entry->ae_tq, tmo);
        tmo += tmo;
    }
    

endf:
    /*
     * Destoy this ARP entry only in case that this
     * instance of NutArpCacheQuery produced this 
     * entry
     */
    if(entry && rc) {
        entry->ae_flags = ATF_REM;
        ArpCacheFlush(ifn);
    }
    /*if(nb)  NutNetBufFree testing the nb for zero*/
    NutNetBufFree(nb);
    
    return rc;
}


6) For save few byte:)))
#include <sys/heap.h>
static ARPENTRY *ArpCacheNew(IFNET * ifn, u_long ip, u_char * ha){
...
if ((entry = NutHeapAllocClear(sizeof(ARPENTRY))) != 0) {
//    if ((entry = malloc (sizeof(ARPENTRY))) != 0) {
//        //memset(entry, 0, sizeof(ARPENTRY));
...
}

My solving save strain in system(only one instance of NutArpCacheQuery with
same request for IP address sending ARP packet) and is more protected(no
with NutEventWait forever) and is not large, approximately 30 bytes addition
against original version from Nut/OS(check with AVRGCC :)...

I thanks Dusan Ferbas for his comments...

PS: Sorry for my terrible english :))).

Enjoy,
		Marek Pavlu...
 
---
avast! Antivirus: Odchozi zprava cista.
Virova databaze (VPS): 0507-4, 18.02.2005
Testovano: 20.2.2005 22:43:56
avast! (c) copyright 2000-2003 ALWIL Software.
http://www.avast.com







More information about the En-Nut-Discussion mailing list