www.pudn.com > Firewall_PNE_3_3.zip > fwMacFilter.c, change:2009-03-16,size:49715b


/* fwMacFilter.c - MAC Filtering routines */

/*
 * Copyright (c) 2004-2006 Wind River Systems, Inc.
 *
 * The right to copy, distribute, modify or otherwise make use
 * of this software may be licensed only pursuant to the terms
 * of an applicable Wind River license agreement.
 */

/*
modification history
--------------------
01g,03mar06,kch Fixed coverity issues in fwMacFilterInstall() (SPR#118475).
01f,13jul04,myz port to dual stack
01e,20feb04,myz added fwMacFilterRemove() and binding semaphore
01d,29jan04,myz public API name and macro definition changes.
01c,28jan04,myz change to use new FW_GROUP_ATTR
01b,26jan04,myz added fwMacLogInstall function 
01a,15dec03,myz written
*/

/*
DESCRIPTION

This library provides the MAC filter. It performs the packet filtering at the
link header(ethernet header) level. The MAC filter intercepts packets at 
the VxWorks MUX layer.  On the receiving side, it is bound as a SNARF protocol.
On the transmitting side, it is bound as MUX_PROTO_OUTPUT. Because only
one binding protocol as MUX_PROTO_OUTPUT type per interface is allowed, 
transmitting side binding is not guaranteed. The filtering action is a two step 
process.  First it checks whether the MAC address(source for receiving, 
destination for transmitting) matches the ones in the address cache which is 
built through calling fwMacCacheAdd(). If a match is found, either an accept or 
reject action specified by the matched address entry will be taken. If not, 
then the process proceeds to the second step to check against a list of  MAC
rules which is built through calling macFilterRuleAdd(). If a match is found,
the action specified by the rule entry will be taken. If not, it falls through
to take the default action of the MAC filter.  The Mac filter is installed
by calling fwMacFilterInstall(). This function also specifies the default action
of the installed MAC filter.

INTERNAL:
The MAC address cache is a 4 way,16 set associated cache by default. This 
configuration creates a 64 entry table. The equation to derive the search index
to this table is: 
  baseInx = (((macAddr[4] << 8) + macAddr[5]) & (setSize -1)) * 4;
macAddr[4] and macAddr[5] is the last two bytes of the MAC address. The setSize
is 16 by default. The default value of associated way is 4. The search process
will search the 4 entries associated with the base index to find out if there
is a complete MAC address match. For efficiency, the process remembers 
the last matched index within the four entry set. So next time, it always starts
to search at the last matched entry. If the given set is full, the next address
entry will be put in the backup list. The entry in the backup list will be moved
to the corresponding cache set if one in the cache set is deleted, or swapped to
the cache if a hit is found.  The given cache set is always filled first before
entry is put in the backup list. Receive MAC filter and transmitting MAC filter
each maintains a independent address cache.

INCLUDE FILES: fwMacFilter.h

*/

#include "fwMacFilter.h"

/* debug macros */

#undef FW_MACFILTER_DEBUG

#ifdef FW_MACFILTER_DEBUG

int fwMacFilterDebug = 0;
#define DBG_PRINT(X) \
    do { \
    if (fwMacFilterDebug) \
        printf X; \
    }while(0)
#else

#define DBG_PRINT(X)

#endif

/* local defines */

#define MAC_PKT_LOG_LEN     40

/* locals */

LOCAL char * nameStr[2] = {"MAC Receiving Filter","MAC Transmitting Filter"};
LOCAL FW_MAC_FILTER_DESC macDesc[2];
LOCAL FW_MAC_FILTER_DESC * pMacDesc[2] = {NULL,NULL};
LOCAL UINT32 ruleEntryId = 1;

LOCAL FW_LOG_FUNC_PTR pLogFunc = NULL;

LOCAL RULE_FILTER_STAT_DESC fwMacInStats;
LOCAL RULE_FILTER_STAT_DESC fwMacOutStats;

/* prototypes */

LOCAL BOOL macNptOutFilter (void*, long, M_BLK*, void*);
LOCAL BOOL macNptInFilter (void*, long, M_BLK*, void*);
LOCAL BOOL macEndInFilter (void*, long, M_BLK*, LL_HDR_INFO *, void*);
LOCAL BOOL macEndOutFilter (void*, long, M_BLK*, LL_HDR_INFO *, void*);
LOCAL BOOL macFilter (void *, long, M_BLK_ID, char *);
LOCAL BOOL logDataSend (UINT32, struct mbuf *, FW_GROUP_ATTR *, 
                        FW_MAC_FILTER_DESC *);

/*******************************************************************************
*
* fwMacFilterInstall - install MAC filter
*
* This routine initializes either a receiving or a transmitting MAC filter and
* binds it to a list of MAC interfaces if provided.  If the list of interfaces
* is not provided, it can be done individually later by calling 
* fwMacFilterBind(). The list of MAC interfaces is provided by the 
* parameter pIf which points to a array of entries with type FW_MAC_IF_ID. The
* last entry must be a NULL entry with element name[0] = 0.
*
* RETURNS: OK, or ERROR if fails
*
*/

STATUS fwMacFilterInstall 
    (
    int type,  /*FW_MAC_FILTER_RX for receive or FW_MAC_FILTER_TX for transmit*/
    FW_MAC_IF_ID * pIf,          /* a list of mac interfaces to be bound */
    UINT32 dftActions,           /* filter default action */
    FW_MAC_EXT_FUNCPTR pDftFunc, /*default post processing function, optional*/
    void * dftFuncArg            /*the above function's argument */
    )
    {
    FW_MAC_FILTER_DESC * pDesc;
    int i;
    int s;

    if ((type != FW_MAC_FILTER_RX) && (type != FW_MAC_FILTER_TX) )
        return ERROR;

    s = splnet();
    if (pMacDesc[type] != NULL)
        {
        splx(s);
        printf("fwMacFilterInstall: Already installed\n");
        return ERROR;
        }

    pDesc = &macDesc[type];
    bzero((char *)pDesc,sizeof(FW_MAC_FILTER_DESC));

    if ((pDesc->bindSem = semBCreate(SEM_Q_PRIORITY, SEM_FULL)) == NULL)
        {
        splx(s);
        printf("fwMacFilterInstall: ERROR, fail to create the semaphore\n");
        return ERROR;
        }

    pMacDesc[type] = pDesc;

    /* allocate space for interface binding info. */

    pDesc->bindCnt = DEFAULT_INTERFACE_NUM;
    pDesc->pBind = calloc(pDesc->bindCnt,sizeof(FILTER_BIND));

    if (pDesc->pBind == NULL)
        {
        semDelete(pDesc->bindSem);
        bzero((char *)pDesc,sizeof(FW_MAC_FILTER_DESC));
        pMacDesc[type] = NULL;
        splx(s);
        printf("<macFilterInit> Error allocating memory for interface"
                " bindings\n");
        return(ERROR);
        }

    if (type == FW_MAC_FILTER_TX)
        pDesc->pFwStats = &fwMacOutStats;
    else
        pDesc->pFwStats = &fwMacInStats;

    pDesc->type = type;

    /* initialize the default actions */

    pDesc->dftActions = dftActions;
    pDesc->pDftPostFunc = pDftFunc;
    pDesc->funcArg = dftFuncArg;
   
    splx(s);

    if (pIf != NULL)
        { 
        for (i = 0; pIf[i].name[0]; i++)
            fwMacFilterBind(type,pIf[i].name,pIf[i].unit);  
        }
    return(OK);
    }

/*******************************************************************************
*
* fwMacFilterRemove - remove the mac filter.
*
* This routine removes either the receiving MAC filter or the transmitting MAC
* filter from all binding network interfaces.
*
* RETURNS: OK, or ERROR if fails
*
*/

STATUS fwMacFilterRemove
    (
    int type  /*FW_MAC_FILTER_RX for receive or FW_MAC_FILTER_TX for transmit*/
    )
    {
    FW_MAC_FILTER_DESC * pDesc;
    int i;
    int s;

    if ((type != FW_MAC_FILTER_RX) &&
        (type != FW_MAC_FILTER_TX) )
        return ERROR;

    if (pMacDesc[type] == NULL)
        {
        printf("fwMacFilterRemove: filter not installed\n");
        return ERROR;
        }

    pDesc = pMacDesc[type];

    for (i=0;i<pDesc->bindCnt;i++)
        {
        if ((pDesc->pBind)[i].used == TRUE)
            fwMacFilterUnbind(type,(pDesc->pBind)[i].ifName,
                                   (pDesc->pBind)[i].ifUnit);
        (pDesc->pBind)[i].used = FALSE;
        }

    s = splnet();
    if (pDesc->pBind) 
        free(pDesc->pBind);
    if (pDesc->pCacheTbl)
        free(pDesc->pCacheTbl);
    if (pDesc->pBackupList)
        free(pDesc->pBackupList);
    if (pDesc->pRuleList)
        free(pDesc->pRuleList);
    if (pDesc->bindSem)
        semDelete(pDesc->bindSem);
    bzero((char *)pDesc, sizeof(*pDesc));
    pMacDesc[type] = NULL;
    splx(s);
    return OK;
    }

/*******************************************************************************
* fwMacFilterBind - bind the MAC filter to a network interface
*
* This routine binds the MAC filter as a SNARF protocol to the specfied
* network interface for receiving, and as MUX_PROTO_OUTPUT for transmitting if 
* the interface is not already bound with other output protocol.
* 
* RETURNS: OK, or ERROR if the filter is already bound, or invalid parameters
*
* SEE ALSO:
* fwMacFilterUnbind()
*/

STATUS fwMacFilterBind
    (
    int type, /*FW_MAC_FILTER_RX for receive or FW_MAC_FILTER_TX for transmit*/ 
    char *ifName,   /* network interface name */
    int ifUnit      /* network interface unit number */
    )
    {
    void * macFilterCookie;
    int i;
    FW_MAC_FILTER_DESC * pDesc;
    long protoType;
    BOOL (*stackNptRcvRtn) (void* ,long, M_BLK_ID, void * );
    BOOL (*stackEndRcvRtn) (void* , long, M_BLK_ID, LL_HDR_INFO * , void* );

    if ((type != FW_MAC_FILTER_RX) &&
	(type != FW_MAC_FILTER_TX) )
        return ERROR;

    if (ifName == NULL)
        return ERROR;

    if (pMacDesc[type] == NULL)
        return ERROR;

    pDesc = pMacDesc[type];

    semTake(pDesc->bindSem,WAIT_FOREVER);
 
    /* Check to make sure we aren't already bound to this interface */

    for (i=0;i<pDesc->bindCnt;i++)
        {
        if ( ((pDesc->pBind)[i].used == TRUE) && 
             ((pDesc->pBind)[i].ifUnit == ifUnit) &&
             strcmp((pDesc->pBind)[i].ifName, ifName) == 0)
            {
            semGive(pDesc->bindSem);
            printf("macFilterBind: Already bound to %s%d\n", ifName, ifUnit);
            return(ERROR);            
            }
        }

    /* Find a free bind entry.... */
    for (i=0;i<pDesc->bindCnt;i++)
        {
        if (!(pDesc->pBind)[i].used)
            break;            
        }

    if (i == pDesc->bindCnt)
        {
        FILTER_BIND * pTemp;

        /* Maximum number of bound interfaces reached */
        DBG_PRINT(("macFilterBind: Max number of bound interfaces reached\n"));

        /* increase the entries */

        pTemp = (FILTER_BIND *)calloc(pDesc->bindCnt + DEFAULT_INTERFACE_NUM,
                            sizeof(FILTER_BIND));

        if (pTemp == NULL)
            {
            semGive(pDesc->bindSem);
            printf("<macFilterBind> Error allocating memory for interface"
                " bindings\n");
            return(ERROR);
            }

        bcopy((char *)(pDesc->pBind),(char *)pTemp,
              pDesc->bindCnt * sizeof(FILTER_BIND)); 

        free(pDesc->pBind);
        (pDesc->pBind) = pTemp; 
        pDesc->bindCnt += DEFAULT_INTERFACE_NUM;
        }

    (pDesc->pBind)[i].used = TRUE;
    (pDesc->pBind)[i].pDesc = pDesc;
    (pDesc->pBind)[i].ifUnit = ifUnit;
    strcpy((pDesc->pBind)[i].ifName, ifName);

    /* For efficiency consideration, we do muxTkBind for NPT driver model
     * and muxBind for END driver model
     */

    if (muxTkDrvCheck(ifName) == TRUE)
        {
        /* NPT driver model */
        
        if (type == FW_MAC_FILTER_RX)
            {
            protoType = MUX_PROTO_SNARF;
            stackNptRcvRtn = macNptInFilter;
            }
        else
            {
            protoType = MUX_PROTO_OUTPUT;
            stackNptRcvRtn = macNptOutFilter;
            }

        macFilterCookie = muxTkBind(ifName,
                               ifUnit,
                               (FUNCPTR)stackNptRcvRtn,
                               NULL,
                               NULL,
                               NULL,
                               protoType,
                               "macFilter",
                           (void *)&(pDesc->pBind[i]), /* pNetCallBackId */
                               NULL,                 /* pNetSvcInfo */
                               NULL);                /* pNetDrvInfo */

        }
    else  /* end driver */
        {

        if (type == FW_MAC_FILTER_RX)
            {
            protoType = MUX_PROTO_SNARF;
            stackEndRcvRtn = macEndInFilter;
            }
        else
            {
            protoType = MUX_PROTO_OUTPUT;
            stackEndRcvRtn = macEndOutFilter;
            }


        macFilterCookie = muxBind(ifName,
                               ifUnit,
                               (FUNCPTR)stackEndRcvRtn,
                               NULL,
                               NULL,
                               NULL,
                               protoType,
                               "macFilter",
                            &(pDesc->pBind[i]));        /* pNetCallBackId */
        }
       
    if (macFilterCookie == NULL)
        {
        (pDesc->pBind)[i].used = FALSE;
        semGive(pDesc->bindSem);
        printf("macFilterBind: Unable to bind, name: %s, unit:%d \n",
                ifName,ifUnit);    
        return(ERROR);
        }

    (pDesc->pBind)[i].cookie = macFilterCookie;
    semGive(pDesc->bindSem);
    return(OK);
    }

/*******************************************************************************
*
* fwMacFilterUnbind - remove the given MAC filter from a network interface
*
* This routine removes either the receiving MAC filter bound as a SNARF 
* protocol or the transmitting MAC filter bound as MUX_PROTO_OUTPUT from the
* given network interface.
*
* RETURNS: OK, or ERROR if the filter is not bound, or parameter error 
*
* SEE ALSO:
* fwMacFilterBind()
*/

STATUS fwMacFilterUnbind
    (
    int type, /*FW_MAC_FILTER_RX for receive or FW_MAC_FILTER_TX for transmit*/ 
    char *ifName,  /* network interface name */
    int ifUnit     /* network interface unit number */
    )
    {
    STATUS status;
    int i;
    FW_MAC_FILTER_DESC * pDesc;
    FUNCPTR stackRcvRtn;

    if ((type != FW_MAC_FILTER_RX) &&
	(type != FW_MAC_FILTER_TX) )
        return ERROR;
  
    if (pMacDesc[type] == NULL)
        return ERROR;
 
    pDesc = pMacDesc[type];

    semTake(pDesc->bindSem,WAIT_FOREVER);
 
    /* Find the record of the bind, if it exists */
    for (i=0;i<pDesc->bindCnt;i++)
        {
        if ((pDesc->pBind)[i].used &&
            (pDesc->pBind)[i].ifUnit == ifUnit &&
            strcmp((pDesc->pBind)[i].ifName,ifName) == 0)
            break;            
        }
    if (i == pDesc->bindCnt)
        {
        semGive(pDesc->bindSem);
        printf("macFilterUnbind: Unable to find mux interface %s%d\n",
               ifName,ifUnit);    
        return(ERROR);            
        }

    if (muxTkDrvCheck(ifName) == TRUE)
        {
        if (type == FW_MAC_FILTER_TX)
            stackRcvRtn = (FUNCPTR)macNptOutFilter;
        else
            stackRcvRtn = (FUNCPTR)macNptInFilter;
        }    
    else
        {
        if (type == FW_MAC_FILTER_TX)
            stackRcvRtn = (FUNCPTR)macEndOutFilter;
        else
            stackRcvRtn = (FUNCPTR)macEndInFilter;
        }    
 
    /* Call the muxLib to Unbind the filter from the interface */

    status = muxUnbind((pDesc->pBind)[i].cookie, 
                       type == FW_MAC_FILTER_RX? MUX_PROTO_SNARF:
                       MUX_PROTO_OUTPUT,         
		       stackRcvRtn);
    if (status == ERROR)
        {
        semGive(pDesc->bindSem);
        printf("macFilterUnbind: Unable to unbind macFilter Protocol\n");    
        return(ERROR);            
        }
    
    /* Mark the Bind record as unused */
    (pDesc->pBind)[i].used = FALSE;
    semGive(pDesc->bindSem);
    return(OK);
    }

/*******************************************************************************
*
* fwMacFilterDefaultSet - set default action for the given MAC filter
*
* This routine sets the default action for the given MAC filter. User first
* specifies the default action when calling fwMacFilterInstall(). User should
* call this function to change a new default action code.
*
* RETURNS: OK or ERROR
*/

STATUS fwMacFilterDefaultSet
    (
    int type, /*FW_MAC_FILTER_RX for receive or FW_MAC_FILTER_TX for transmit*/ 
    UINT32 actions,           /* default action codes defined in fwLib.h */
    FW_MAC_EXT_FUNCPTR pFunc, /* default extension function, optional */
    void * arg                /* the above function's argument */
    )
    {
    FW_MAC_FILTER_DESC * pDesc;
    int s;

    if ((type != FW_MAC_FILTER_RX) &&
	(type != FW_MAC_FILTER_TX) )
        return ERROR;

    if (pMacDesc[type] == NULL)
        return ERROR;

    pDesc = pMacDesc[type];

    s = splnet();
    pDesc->dftActions = actions;
    pDesc->pDftPostFunc = pFunc;
    pDesc->funcArg = arg;

    splx(s);
 
    return(OK);
    }

/*******************************************************************************
*
* fwMacFilterRuleAdd - add a MAC filter rule entry
*
* This routine adds a rule entry to the given MAC filter rule list. The rule
* entry described by structure type FW_MAC_USER_RULE, has three rule fields to
* specify the MAC source address range, MAC destination address range and
* the ethernet type. User should zero out the unused fields. 
*
* RETURNS: The added rule entry ID or 0 if fails
*/

UINT32 fwMacFilterRuleAdd
    (
    int type, /*FW_MAC_FILTER_RX for receive or FW_MAC_FILTER_TX for transmit*/ 
    FW_MAC_USER_RULE * pRule,  /* the MAC filter rule entry */
    FW_MAC_EXT_FUNCPTR pFunc,  /* extension function, optional */
    void * arg                 /* the above function's argument */
    )
    {
    FW_MAC_FILTER_DESC * pDesc;
    FILTER_RULE_ENTRY * pTmp;
    FILTER_RULE_ENTRY * pEntry;
    int s;

    if ((type != FW_MAC_FILTER_RX) &&
	(type != FW_MAC_FILTER_TX) )
        return 0;

    if (pMacDesc[type] == NULL)
        return 0;

    s = splnet();

    pDesc = pMacDesc[type];

    if (pDesc->ruleListCnt >= pDesc->ruleListMax)
	{
        if (pDesc->pRuleList != NULL)
            {
            /* previously allocated entry is too small, increase it */

            pTmp = pDesc->pRuleList;  /* save the previous one */
            }
        else
            {
            /* must be first time */

            pTmp = NULL;
            }

        /* Allocate space for MAC filter rule list */

        if ((pDesc->pRuleList = calloc(pDesc->ruleListMax + 
                                       DEFAULT_RULE_ENTRY_NUM, 
                                  sizeof(FILTER_RULE_ENTRY))) == NULL)
            {
            if (pTmp)
                pDesc->pRuleList = pTmp;                
            printf("macRuleAdd: ERROR allocating memory for rule list\n");
            splx(s);
            return(0);
            }

        if (pTmp)
            {
            bcopy((char *)pTmp, (char *)pDesc->pRuleList,
                  sizeof(FILTER_RULE_ENTRY) * pDesc->ruleListMax);
            free(pTmp);
            }

        if (pDesc->ruleListCnt > pDesc->ruleListMax)
            {
            printf("ERROR: macRuleAdd, should not happen, cnt %d > max %d\n",
                   pDesc->ruleListCnt,pDesc->ruleListMax);

            /* in case this happens */

            pDesc->ruleListCnt = pDesc->ruleListMax;
            } 
        pDesc->ruleListMax += DEFAULT_RULE_ENTRY_NUM;
 
        }
 
    pEntry = &pDesc->pRuleList[pDesc->ruleListCnt++];
    pEntry->usrSpec = *pRule;
    pEntry->entryId = ruleEntryId++;

    pEntry->addrOp = 0;
    if (pRule->srcMaskAddrF4 == 0 && pRule->srcMaskAddrL2 == 0) 
        pEntry->addrOp += FW_MAC_SRC_NOT_USED;
    else
        pEntry->addrOp += FW_MAC_SRC_USED;

    /* check the destination */

    if (pRule->dstMaskAddrF4 == 0 && pRule->dstMaskAddrL2 == 0) 
        pEntry->addrOp += FW_MAC_DST_NOT_USED;
    else
        pEntry->addrOp += FW_MAC_DST_USED;

    pEntry->pPostFunc = pFunc;
    pEntry->funcArg   = arg;

    splx(s);
    return pEntry->entryId;
    }

/*******************************************************************************
*
* fwMacCacheAdd - add a new MAC address to the cache 
*
* This routine adds a new MAC address to the given MAC filter cache. Both 
* receiving MAC filter and transmitting MAC filter each maintains a independent 
* address cache. The address is treated as the MAC source address for the 
* receiving MAC filter and destination address for the transmitting MAC filter.
*
* RETURNS: OK or ERROR
*/

STATUS fwMacCacheAdd
    (
    int type, /*FW_MAC_FILTER_RX for receive or FW_MAC_FILTER_TX for transmit*/ 
    UINT8 * pMacAddr,   /* the MAC address */
    BOOL   permitAction /*action upon match, TRUE for permit, FALSE for deny*/
    )
    {
    int i;
    int baseInx;
    BOOL found = FALSE;
    FW_MAC_FILTER_DESC * pDesc;
    MAC_ADDR_ENTRY * pCacheTbl;
    MAC_ADDR_ENTRY * pTmp;
    int s;

    if ((type != FW_MAC_FILTER_RX) &&
	(type != FW_MAC_FILTER_TX) )
        return ERROR;

    if (pMacDesc[type] == NULL)
        return ERROR;

    pDesc = pMacDesc[type];

    s = splnet();

    /* Allocate space for MAC address list cache table */

    if (pDesc->pCacheTbl == NULL)
        {
        if ((pDesc->pCacheTbl = calloc(ADDR_LOOKUP_CACHE_SIZE,
                                  sizeof(MAC_ADDR_ENTRY))) == NULL)
            {
            printf("macBind: ERROR allocating memory for cache table\n");
            splx(s);
            return(ERROR);
            }
        }

    pCacheTbl = pDesc->pCacheTbl;

    /* the hash index from last two bytes of mac address
     * For receiving filter, takes source address, for transmitting, takes
     * destination address
     */

    baseInx = ((pMacAddr[4] << 8) | pMacAddr[5]) & (CACHE_SET_SIZE - 1);
    baseInx = baseInx << CACHE_ASSOCIATIVE_POWER;

    for (i = 0; i < CACHE_ASSOCIATIVE_SIZE; i++)
        {
        /* check if one already exists, if yes, use the last permitAction */

        if (pCacheTbl[baseInx + i].inUse)
            {
            if (!bcmp((char *)pMacAddr,
                     (char *)(pCacheTbl[baseInx + i].mac.addr), 6))
                {
                pCacheTbl[baseInx + i].action = permitAction;
                splx(s);
                return OK;
                }
            }
        }
    
    for (i = 0; i < CACHE_ASSOCIATIVE_SIZE; i++)
        {
        if (!pCacheTbl[baseInx + i].inUse)
            {
            bcopy((char *)pMacAddr,  
		  (char *)(pCacheTbl[baseInx + i].mac.addr), 6);
            pCacheTbl[baseInx + i].inUse = TRUE;
	    pCacheTbl[baseInx + i].action = permitAction;
            found = TRUE;
            break;
            }
        }

    if (found == FALSE)
        {
        /* out of the space in the cache table, put on the backup array */

        if (pDesc->backupCnt >= pDesc->backupMax)
            {
            if (pDesc->pBackupList != NULL)
                {
                /* previously allocated backup array too small, increase it */

                pTmp = pDesc->pBackupList;  /* save the previous one */
                }
            else
                {
                /* must be first time */

                pTmp = NULL;
                }

            pDesc->pBackupList = calloc(pDesc->backupMax + 
                                        ADDR_BACKUP_TABLE_SIZE,
                                          sizeof(MAC_ADDR_ENTRY));

            if (pDesc->pBackupList == NULL)
                {
                printf("ERROR: macFilter, out of memory for backup list\n");
                splx(s);
                return ERROR;
                }

            if (pTmp)
                {
                bcopy((char *)pTmp,(char *)pDesc->pBackupList,
                      pDesc->backupMax * sizeof(MAC_ADDR_ENTRY));
                free(pTmp);
                }

            if (pDesc->backupCnt > pDesc->backupMax)
                {
                printf("ERROR: backupAdd,should not happen, cnt %d > max %d\n",
                       pDesc->backupCnt,pDesc->backupMax);

                /* in case this happens */

                pDesc->backupCnt = pDesc->backupMax;
                }

            pDesc->backupMax += ADDR_BACKUP_TABLE_SIZE;

            }
      
        bcopy((char *)pMacAddr,  
	      (char *)(pDesc->pBackupList[pDesc->backupCnt].mac.addr), 6);
        pDesc->pBackupList[pDesc->backupCnt].inUse = TRUE;
        pDesc->pBackupList[pDesc->backupCnt++].action = permitAction;
        }

    splx(s);
    return(OK);
    }

/*******************************************************************************
*
* fwMacCacheDelete - delete a MAC address from the given address cache
*
* This routine deletes a MAC address from either the receiving MAC filter
* address cache or the transmitting MAC filter address cache.
*
* RETURNS: OK or ERROR
*/

STATUS fwMacCacheDelete
    (
    int type, /*FW_MAC_FILTER_RX for receive or FW_MAC_FILTER_TX for transmit*/ 
    UINT8 * pMacAddr  /* the MAC address */
    )
    {
    int i;
    int j;
    int baseInx;
    BOOL found = FALSE;
    FW_MAC_FILTER_DESC * pDesc;
    MAC_ADDR_ENTRY * pCacheTbl;
    int s;

    if ((type != FW_MAC_FILTER_RX) &&
	(type != FW_MAC_FILTER_TX) )
        return ERROR;

    if (pMacDesc[type] == NULL)
        return ERROR;

    pDesc = pMacDesc[type];
    if ((pCacheTbl = pDesc->pCacheTbl) == NULL)
        return ERROR;

    s = splnet();

    /* the hash index from last two bytes */

    baseInx = ((pMacAddr[4] << 8) | pMacAddr[5]) & (CACHE_SET_SIZE - 1);
    baseInx = baseInx << CACHE_ASSOCIATIVE_POWER;


    for (i = 0; i < CACHE_ASSOCIATIVE_SIZE; i++)
        {
        if (pCacheTbl[baseInx + i].inUse == TRUE)
           {
           if (bcmp((char *)pMacAddr, 
		    (char *)(pCacheTbl[baseInx + i].mac.addr), 6) == 0)
               {
	       int bkBaseInx;

               /* found one, delete it  */
              
               pCacheTbl[baseInx + i].inUse = 0;
               found = TRUE;

               /* see if there is one in backup list to fill in for this slot*/

               for (j = 0; (j < pDesc->backupCnt) && pDesc->pBackupList; j++)
                   {
		   bkBaseInx = (pDesc->pBackupList[j].mac.addr[4]) << 8;
		   bkBaseInx |= (pDesc->pBackupList[j].mac.addr[5]);
		   bkBaseInx &= (CACHE_SET_SIZE - 1);
		   bkBaseInx = bkBaseInx << CACHE_ASSOCIATIVE_POWER;

		   if (baseInx == bkBaseInx)
                       {
                       int k;

                       /* found one, move to cache table */

                       pCacheTbl[baseInx + i] = pDesc->pBackupList[j];
                      
                       /* delete the one in backup list by shifting up */

                       for (k = j; k < pDesc->backupCnt - 1; k++)
                           pDesc->pBackupList[k] = pDesc->pBackupList[k+1];
                        
                       pDesc->backupCnt--;
                       break;
                       }
                   }     
               /* See if need to adjust the current set index. The set index
                * should always point to a "inUse" entry if there is at least
                * one "inUse" entry in the set. Otherwise it points to the
                * first entry in the set
                */

               if ((i == pCacheTbl[baseInx].setInx) &&
                   (pCacheTbl[baseInx + i].inUse == 0) )
                   {
                   int k;
                   int inx;

                   for (k = 1; k < CACHE_ASSOCIATIVE_SIZE; k++)
                       {
                       inx = (i + k) & CACHE_A_MASK;
                       if (pCacheTbl[baseInx + inx].inUse == TRUE)
                           {
                           pCacheTbl[baseInx].setInx = inx; 
                           break;
                           }
                       }
                   /* If there is no "inUse" entry, reset set index to 0 */

                   if (k == CACHE_ASSOCIATIVE_SIZE)
                       pCacheTbl[baseInx].setInx = 0;
                   } 
               break;
               }                 
            }
        }

    if (found == FALSE)
        {
        /* check the backup list */

        if (pDesc->pBackupList != NULL)
            {
            for (j = 0; j < pDesc->backupCnt; j++)
               {
               if (bcmp((char *)pMacAddr, 
			(char *)(pDesc->pBackupList[j].mac.addr), 6) == 0)
                   { 
                   /* found one, delete it by shifting up */

                   for (i = j; i < pDesc->backupCnt - 1; i++)
                       pDesc->pBackupList[i] = pDesc->pBackupList[i+1];
                             
                   pDesc->backupCnt--;
                   break;
                   }
               }    
           }
        }
    splx(s);
    return OK;
    }

/*******************************************************************************
*
* fwMacFilterRuleDelete - delete a MAC rule from the rule list
*
* This routine deletes a rule entry from either the receiving MAC filter rule
* list or the transmitting MAC filter rule list.
*
* RETURNS: OK or ERROR
*/

STATUS fwMacFilterRuleDelete
    (
    int type, /*FW_MAC_FILTER_RX for receive or FW_MAC_FILTER_TX for transmit*/
    UINT32 ruleId  /* the rule Id returned from fwMacFilterRuleAdd() */
    )
    {
    int i;
    int j;
    FW_MAC_FILTER_DESC * pDesc;
    int s;

    if ((type != FW_MAC_FILTER_RX) &&
	(type != FW_MAC_FILTER_TX) )
        return ERROR;

    if (pMacDesc[type] == NULL)
        return ERROR;

    pDesc = pMacDesc[type];

    if (pDesc->pRuleList == NULL)
        return ERROR;

    s = splnet();

    for (i = 0; i < pDesc->ruleListCnt; i++)
        {
        if (pDesc->pRuleList[i].entryId == ruleId)
            {
            /* found one, shift up to delete it*/

            for (j = i; j < pDesc->ruleListCnt - 1; j++)
                pDesc->pRuleList[j] = pDesc->pRuleList[j+1];

            pDesc->ruleListCnt--;
            break;
            }
        }

    splx(s);

    return OK;
    }

/*****************************************************************************
*
* macNptInFilter - the routine bound as the SNARF protocol for NPT model
*
*/

LOCAL BOOL macNptInFilter
    (
    void*       pNetCallBackId,  /* callback routine argument */
    long        type,            /* protocol type  */
    M_BLK*      pMblk,           /* the received packet */
    void*       pSpareData       
    )
    {
    char * pLinkHdr = pMblk->mBlkHdr.mData;

    /* muxTkReceive guarantee the link header is in the fist mblk before 
     * calling this function
     */

    return macFilter(pNetCallBackId,type | PACKET_FROM_NPT_DRIVER,
                       pMblk,pLinkHdr);
    }
/*****************************************************************************
*
* macNptOutFilter - the routine bound as the OUTPUT protocol for NPT model
*
*/

LOCAL BOOL macNptOutFilter
    (
    void*       pNetCallBackId,  /* callback routine argument */ 
    long        type,            /* protocol type  */ 
    M_BLK*      pMblk,           /* the output packet */
    void*       pSpareData       
    )
    {
    char * pLinkHdr;
    
    if (pMblk->mBlkHdr.mFlags & M_L2HDR)
        {
        /* mblk has the link header */

        pLinkHdr = pMblk->mBlkHdr.mData;
        }
    else
        {
        /* link header is not in the mblk, we can only get the dst address */

        pLinkHdr = (char *)(pMblk->mBlkPktHdr.rcvif); 
        }
    return macFilter(pNetCallBackId,type | PACKET_FROM_NPT_DRIVER,
                       pMblk,pLinkHdr);
    }

/*****************************************************************************
*
* macEndInFilter - the routine bound as the SNARF protocol for END model
*
*/

LOCAL BOOL macEndInFilter
    (
    void*       pCookie,  /* cookie returned fromd muxBind */
    long        type,     /* protocol type  */
    M_BLK*      pMblk,    /* the received packet */
    LL_HDR_INFO *pLLInfo, /* link level header info */
    void*       pSpareData       
    )
    {
    char linkHdr[ETHER_ADDRS_LEN];
    char * pLinkHdr;

    /* muxReceive does not gurantee the link header is in first mblk, and
     * we can't do a m_pullup here unless M_BLK** type is passed into
     */

    if (pMblk->mBlkHdr.mLen < ETHER_ADDRS_LEN) 
        {
        /* hopefully this is not the case */

        if (!netMblkOffsetToBufCopy(pMblk,0,linkHdr,ETHER_ADDRS_LEN,NULL))
            return FALSE;
        pLinkHdr = linkHdr;
        }
    else
        pLinkHdr = pMblk->mBlkHdr.mData;

    return macFilter(pSpareData,type | PACKET_FROM_END_DRIVER,pMblk,pLinkHdr);
    
    }
/*****************************************************************************
*
* macEndOutFilter - the routine bound as the OUTPUT protocol for END model
*
*/

LOCAL BOOL macEndOutFilter
    (
    void*       pCookie,  /* cookie returned fromd muxBind */
    long        type,     /* protocol type  */
    M_BLK*      pMblk,    /* the output packet */
    LL_HDR_INFO *pLLInfo, /* link level header info */
    void*       pSpareData       
    )
    {
    char * pLinkHdr;

    pLinkHdr = pMblk->mBlkHdr.mData;
    return macFilter(pSpareData,type | PACKET_FROM_END_DRIVER,pMblk,pLinkHdr);
    }



/*******************************************************************************
*
* macFilter - MAC filter engine which performs actual packet filtering
*
* This routine peforms the actual filtering when called by muxLib as a SNARF
* protocol for receiving and as a MUX_PROTO_OUTPUT for trnasmitting.  
* The filter performs two step processings. The first is to check whether 
* the MAC address(source for receiving, destination for transmitting) matches
* the ones in the address cache. If so, a either accept or reject action 
* is taken. If not, then the second step is to check against the MAC rule list
* to see if there is a match. If yes, the action specified by the rule will be
* taken. If no, the default action is taken.
*
* RETURNS: TRUE if packet was consumed by filter, FALSE if it should be passed
* up to the stack.
*
* This function should be  protected by splnet/splx for mutual exclusion
* 
*/

LOCAL BOOL macFilter 
    (
    void *netCallbackId,
    long protoType, 
    M_BLK_ID pMblk, 
    char * pLinkHdr
    )
    {
    int i;
    int baseInx;
    BOOL found = FALSE;
    FW_MAC_FILTER_DESC * pDesc;
    UINT32 macFirst4;
    UINT32 macLast2;
    UINT32 macDstFirst4;
    UINT32 macDstLast2;
    UINT32 actions = FW_REJECT;
    UINT8 setInx;
    int s;

    pDesc = ((FILTER_BIND *)netCallbackId)->pDesc;

    /* for receiving cache list, use source address as hash index,
     * for transmitting cache list, use destination as hash index
     */ 

    if (pDesc->type == FW_MAC_FILTER_TX)
        {
        /* use destination address as hash index */

        MAC_ADDR_CONVERT(pLinkHdr,macFirst4,macLast2);
        }  
    else
        {
        /* use source address as hash index */

        MAC_ADDR_CONVERT(pLinkHdr+6,macFirst4,macLast2);
        }         

    s = splnet();
               
    /* first we'll go through the cache address list */

    if (pDesc->pCacheTbl)
        {
        BOOL cacheSetFull = TRUE;

        /* the hash index from last two bytes */

        baseInx = (ntohs(macLast2)) & (CACHE_SET_SIZE - 1);
        baseInx = baseInx << CACHE_ASSOCIATIVE_POWER;

        for (i = 0; i < CACHE_ASSOCIATIVE_SIZE; i++)
            {
            /* check the address against the entries starting at the one pointed
             * to by the set index(pDesc->pCacheTbl[baseInx].setInx). 
             * This is to ensure the entry matched the last packet's address 
             * get checked first for efficiency reason.
             */  
             
	    setInx = (i + pDesc->pCacheTbl[baseInx].setInx) & CACHE_A_MASK;
            if (pDesc->pCacheTbl[baseInx+setInx].inUse == TRUE)
                {
                if ((macFirst4 == pDesc->pCacheTbl[baseInx + setInx].macf4) &&
                    (macLast2 == pDesc->pCacheTbl[baseInx + setInx].macl2))
                    {
                    if (pDesc->pCacheTbl[baseInx].setInx != setInx)
                        pDesc->pCacheTbl[baseInx].setInx = setInx;

                    DBG_PRINT(("cache table entry hit at %d try\n",i+1)); 
 
		    actions = pDesc->pCacheTbl[baseInx+setInx].action?
			      FW_ACCEPT: FW_REJECT;
                    goto doMatchActions;
                    }
                }
            else
                {
		/* indicate there is unused cache entry for this base index.
		 * If so, no need to search the backup list since the cache
		 * entries for a specific base index must be filled first 
		 * before putting to the backup list.
		 */

                cacheSetFull = FALSE; 
                
                /* if the first checked entry is unused, then there is no
                 * more valid entries for this base index. So break out the 
                 * for loop.
                 */
                if (setInx == pDesc->pCacheTbl[baseInx].setInx)
                    break;
                }
            }

        if ((cacheSetFull == TRUE) && (pDesc->pBackupList))
            {
            /*
             * not in the cache table, Search the backup list for this mac 
             * address. It is assumed that the entry up to backupCnt 
             * is valid entry. The add and delete routines must ensure this is
             * the case.
             */
            
            for (i = 0; i < pDesc->backupCnt; i++)
                {
                if ((macFirst4 == pDesc->pBackupList[i].macf4) &&
                    (macLast2  == pDesc->pBackupList[i].macl2) )
                    {
                    MAC_ADDR_ENTRY  swapCacheEntry; 

                    DBG_PRINT(("backup list searched at entry %d\n",i+1));
  
                    /* found a match in the backup list */

                    /* swap with a existing cache entry which is the one next
                     * to the current setInx in reverse order
                     */

		    setInx = pDesc->pCacheTbl[baseInx].setInx;
                    setInx = (setInx + CACHE_A_MASK) & CACHE_A_MASK;
                    swapCacheEntry = pDesc->pCacheTbl[baseInx + setInx];
                    pDesc->pCacheTbl[baseInx + setInx] = pDesc->pBackupList[i];
		    pDesc->pCacheTbl[baseInx+setInx].inUse = TRUE;
                    pDesc->pBackupList[i] = swapCacheEntry;
                    
                    /* set the setInx to point to the swapped in entry */

                    pDesc->pCacheTbl[baseInx].setInx = setInx;
		    actions = pDesc->pCacheTbl[baseInx + setInx].action?
			      FW_ACCEPT: FW_REJECT;
                    goto doMatchActions; 
                    }
                }
            }
        }

    /* there is no match in the cache table, search the rule list */

    if (pDesc->pRuleList)
        {
        FILTER_RULE_ENTRY * pEntry;
        BOOL pktHasSrcAddr = TRUE;

	/* retrive the driver type, NPT or END driver */

        if (pDesc->type == FW_MAC_FILTER_TX)
            {
            int driverType;
	    driverType = protoType >> 16; 

            macDstFirst4 = macFirst4;
            macDstLast2 = macLast2;

	    /* If sending to a NPT driver,  this function called from MUX layer
	     * does not know the MAC source address unless the M_L2HDR bit
	     * is set.
	     */
	     
            if (driverType == PACKET_FROM_END_DRIVER ||
		(pMblk->mBlkHdr.mFlags & M_L2HDR))
                {
		/* get the MAC source address */

                MAC_ADDR_CONVERT(pLinkHdr + 6,macFirst4,macLast2);
                }
            else
		pktHasSrcAddr = FALSE;
            }
        else
            {
            MAC_ADDR_CONVERT(pLinkHdr,macDstFirst4,macDstLast2);
            }

        DBG_PRINT(("pkt dstF4: 0x%x, dstL2: 0x%x, srcF4: 0x%x, srcL2: 0x%x\n",
                   macDstFirst4,macDstLast2, macFirst4,macLast2));

        pEntry=pDesc->pRuleList;
        for (i = 0; i < pDesc->ruleListCnt; i++)
            {
            found = TRUE;

            if ((pEntry[i].addrOp & FW_MAC_DST_OP_MASK) != FW_MAC_DST_NOT_USED)
                {
                if (!MAC_DST_ADDR_MATCH(pEntry+i,macDstFirst4,macDstLast2))
                    continue;
                }

    
            if ((pEntry[i].addrOp & FW_MAC_SRC_OP_MASK) != FW_MAC_SRC_NOT_USED)
                {
                if (pktHasSrcAddr == FALSE)
		    continue;

                if (!MAC_SRC_ADDR_MATCH(pEntry+i, macFirst4,macLast2) )
                    continue;
                }

            if (pEntry[i].usrSpec.type) 
                {
                if (!(pEntry[i].usrSpec.type == (UINT16)protoType))
                    continue;
                }
 
            if (pEntry[i].pPostFunc)
		{
		DBG_PRINT(("Post function called: entry: %d, param: %d\n", 
			  i,(int)(pEntry[i].funcArg)));
                actions = (*pEntry[i].pPostFunc)(pDesc->type,pMblk, 
                             &pEntry[i].usrSpec,pEntry[i].funcArg);
                }

            if (pEntry[i].usrSpec.actions != FW_USER_ACTION)
                actions = pEntry[i].usrSpec.actions;

            goto doMatchActions;
            }
        }

    /* if program reaches here, this indicates that there is no match for 
     * the packet in the address cache table and the rule list. We takes
     * the default actions
     */

    if (pDesc->pDftPostFunc)
        actions = (*pDesc->pDftPostFunc)(pDesc->type,pMblk, NULL,
                   pDesc->funcArg); 

    if (pDesc->dftActions != FW_USER_ACTION)
        actions = pDesc->dftActions;
   
doMatchActions:    
    splx(s);
    if (actions & FW_LOG)
        {
        int nameLen;
        FW_GROUP_ATTR attr;
        char tempNameBuf[50];

        attr.logLen = (protoType & 0xFFFF0000) | MAC_PKT_LOG_LEN;

        if (pDesc->type == FW_MAC_FILTER_TX)
            strcpy(tempNameBuf,"TX macFilter");
        else
            strcpy(tempNameBuf,"RX macFilter");

        /* find the interface name */

        nameLen = strlen(tempNameBuf);
        tempNameBuf[nameLen++] = ' ';
        bcopy(((FILTER_BIND *)netCallbackId)->ifName,tempNameBuf+nameLen,
              END_NAME_MAX);
        tempNameBuf[nameLen+END_NAME_MAX] = 0;
        nameLen = strlen(tempNameBuf);
        tempNameBuf[nameLen++] = ((FILTER_BIND *)netCallbackId)->ifUnit + 0x30;
        tempNameBuf[nameLen] = 0;
        attr.pGrpName = tempNameBuf;
        
        logDataSend(actions,pMblk,&attr, pDesc);

        actions &= ~FW_LOG;
        }

    if (actions & FW_ACCEPT)
        {
        pDesc->pFwStats->accepted++; 
        return FALSE;
        }
    else   /* this is the reject */
        {
        pDesc->pFwStats->dropped++; 
        m_freem(pMblk);

        return TRUE;
        }
    }
                        
/******************************************************************************
*
* logDataSend - process the logging reqeust 
*
* RETURNS: TRUE or FALSE 
*/

LOCAL BOOL logDataSend
    (
    UINT32 actions,
    struct mbuf * m,
    FW_GROUP_ATTR * pAttr,
    FW_MAC_FILTER_DESC * pDesc
    )
    {
    if (actions & FW_LOG)
        {
        /* check if the packet is a syslog message generated on the local host.
         * If so, don't send a log message on the log message itself
         */

        if (pDesc->type == FW_MAC_FILTER_TX)
            {
            if ((m->m_flags & M_FORWARD) == 0)
                {
		int offset;
                int err;
                UINT16 udpPort;
                UINT8 protoType;
                int dir;

		dir = (pAttr->logLen >> 16);
                pAttr->logLen &= 0xFFFF;

                if (dir == PACKET_FROM_END_DRIVER)
                    offset = 14 + OFFSET(struct ip, ip_p);
                else
                    offset = OFFSET(struct ip, ip_p);

             
                protoType = m_x8(m,offset,&err);

                if (protoType == IPPROTO_UDP)
                    { 
                    offset =  OFFSET(struct udphdr,uh_dport);

                    udpPort = m_x16(m,offset,&err);

                    if (udpPort == htons(SYSLOGC_DEFAULT_PORT))
                        actions &= ~FW_LOG;
                    }
                }
            }
        }

    if (actions & FW_LOG)
        {
        if (pLogFunc != NULL)
            (*pLogFunc)(m,actions | IPFWF_DONTFREE, pAttr);
        }
    return TRUE;
    }

/*******************************************************************************
*
* fwMacFilterShow - display the given MAC filter information
*
* This routine displays the MAC filter's binding network interfaces, default
* action, address cache and the rule entry list if created. The parameter type
* selects either the receiving MAC filter or the transmitting MAC filter.
* 
* RETURNS: N/A.
*
*/
void fwMacFilterShow
    (
    int type /*FW_MAC_FILTER_RX for receive or FW_MAC_FILTER_TX for transmit*/
    )
    {
    UINT32 i;
    FW_MAC_FILTER_DESC * pDesc;
    MAC_ADDR_ENTRY * pCacheTbl;
    FILTER_RULE_ENTRY * pRuleList;

    if ((type != FW_MAC_FILTER_RX) &&
        (type != FW_MAC_FILTER_TX) )
        {
        printf("Invalid type: %d\n",type);
        return;
        }

    if (pMacDesc[type] == NULL)
        {
        printf("Mac Filter(type: %d) not installed\n",type);
        return;
        }

    pDesc = pMacDesc[type];
 
    printf("*********************************************************************\n");
    printf("Filter Name     = %s\n\n", nameStr[type]);
    printf("Current bindings:\n");
    for (i = 0; i < pDesc->bindCnt; i++)
        {
        if( !(pDesc->pBind)[i].used ) continue;
           printf("    %s%d\n", (pDesc->pBind)[i].ifName, 
                  (pDesc->pBind)[i].ifUnit);
    
        }
    printf("\n");
    printf("******** MAC Filtering Information ********\n");
    printf("Default rule            = %s", (pDesc->dftActions) & FW_ACCEPT?
            "accept" : "reject");
    
    if (pDesc->dftActions & FW_LOG) 
        printf(",log\n");
    else
        printf("\n");

    pCacheTbl = pDesc->pCacheTbl;

    if (pCacheTbl)
        {
        printf("Address Cache Table\n");
        for (i = 0; i < ADDR_LOOKUP_CACHE_SIZE; i++)
            {
            if (pCacheTbl[i].inUse)
                {
                printf("    %02X:%02X:%02X:%02X:%02X:%02X  %s  index: %d\n",
                        pCacheTbl[i].mac.addr[0],
                        pCacheTbl[i].mac.addr[1],
                        pCacheTbl[i].mac.addr[2],
                        pCacheTbl[i].mac.addr[3],
                        pCacheTbl[i].mac.addr[4],
                        pCacheTbl[i].mac.addr[5],
			pCacheTbl[i].action? "accept": "reject",
                        i);
                } 
            }
        }
    printf("\n");

    if (pDesc->pBackupList)
	{
        printf("The list of addresses not in the cache: \n");
	for (i = 0; i < pDesc->backupCnt; i++)
	    {
            printf("    %02X:%02X:%02X:%02X:%02X:%02X  number: %d\n",
                        (pDesc->pBackupList)[i].mac.addr[0],
                        (pDesc->pBackupList)[i].mac.addr[1],
                        (pDesc->pBackupList)[i].mac.addr[2],
                        (pDesc->pBackupList)[i].mac.addr[3],
                        (pDesc->pBackupList)[i].mac.addr[4],
                        (pDesc->pBackupList)[i].mac.addr[5],
                        i);
            }
        }

    pRuleList = pDesc->pRuleList;
    if (pRuleList)
        {
        printf("The MAC Filter Rule list(All values except ID are hex "
	       "number)\n");

        printf("ID:  Destination Address/Mask       Source Address/Mask"
               "            Type Actions\n");
        for (i = 0; i < pDesc->ruleListCnt; i++)
            {
            printf("%04d %02X:%02X:%02X:%02X:%02X:%02X/%02X%02X%02X%02X%02X"
                   "%02X",
		   pRuleList[i].entryId,
                   pRuleList[i].uDstAddr[0], pRuleList[i].uDstAddr[1],
                   pRuleList[i].uDstAddr[2], pRuleList[i].uDstAddr[3],
                   pRuleList[i].uDstAddr[4], pRuleList[i].uDstAddr[5],
		   pRuleList[i].uDMask[0],pRuleList[i].uDMask[1],
		   pRuleList[i].uDMask[2],pRuleList[i].uDMask[3],
		   pRuleList[i].uDMask[4],pRuleList[i].uDMask[5]);

            printf(" %02X:%02X:%02X:%02X:%02X:%02X/%02X%02X%02X%02X%02X%02X",
                   pRuleList[i].uSrcAddr[0], pRuleList[i].uSrcAddr[1],
                   pRuleList[i].uSrcAddr[2], pRuleList[i].uSrcAddr[3],
                   pRuleList[i].uSrcAddr[4], pRuleList[i].uSrcAddr[5],
		   pRuleList[i].uSMask[0],pRuleList[i].uSMask[1],
		   pRuleList[i].uSMask[2],pRuleList[i].uSMask[3],
		   pRuleList[i].uSMask[4],pRuleList[i].uSMask[5]);

            printf(" %04x %08x\n", pRuleList[i].usrSpec.type,
                   pRuleList[i].usrSpec.actions);
            }
        }
    printf("*********************************************************************\n");
    }

/*******************************************************************************
*
* fwMacDescGet - get the MAC Filter descriptor
*
* used for internal testing
*
* NOMANUAL
*
*/

FW_MAC_FILTER_DESC * fwMacDescGet
    (
    int type
    )
    {
    if ((type != FW_MAC_FILTER_RX) &&
        (type != FW_MAC_FILTER_TX) )
        {
        printf("Invalid type: %d\n",type);
        return 0;
        }
    if (pMacDesc[type] == 0)
        {
        printf("Mac Filter not initialized\n");
        return 0;
        }
    return pMacDesc[type];
    }

/*******************************************************************************
*
* fwMacLogInstall - install the MAC Filter logging interface function 
*
* This function installs the logging interface function for the MAC filter. 
*
* RETURN: OK
*
*/

STATUS fwMacLogInstall
    (
    FW_LOG_FUNC_PTR pFunc /* the logging facility interface function: fwLog */
    )
    {
    pLogFunc = pFunc;
    return OK;
    }