/**
 * ============================================================================
 * = COPYRIGHT
 *              INTEL CORPORATION PROPRIETARY INFORMATION
 *   This software is supplied under the terms of a license agreement or
 *   nondisclosure agreement with Intel Corporation and may not be copied 
 *   or disclosed except in accordance with the terms in that agreement.
 *      Copyright (C) 2000-2001 Intel Corporation. All rights reserved.
 *
 * = PRODUCT
 *      Intel(r) IXA SDK 3.0 for the IXP2000 Network Processor, Release 6
 *
 * = FILENAME
 *      ix_cc_stkdrv_pktclass.c
 *
 * = DESCRIPTION
 *      Source for the packet classifier functions of the Stack Driver.
 *
 * = AUTHOR
 *      Aaron Luk
 *      aaron.luk@intel.com
 *
 * = CHANGE HISTORY
 *       1/8/2002 1:53:42 PM - creation time
 *
 * ============================================================================
 */

#define IX_ERROR_FILE_IDENT "$Id: ix_cc_stkdrv_pktclass.c,v 1.30 2003/12/16 23:45:50 ktseng Exp $"

#if defined(IX_PLATFORM_2401) || defined(IX_PLATFORM_2801)
/* Improvement
 * Cleanup in headers - don't include user space stdio.h, use <> when including
 * headers from global include paths. The string.h is excessive here, OSSL
 * covers this.
 */
#include <ix_ossl.h>
#include <ix_rm.h>

#include <ix_cc_error.h>

#else /* IX_PLATFORM_2x01 */

/**
 * System defined include files required.
 */

/**
 * User defined include files required.
 */

#include "ix_cc_error.h"
#include <stdio.h>

#if (_IX_OS_TYPE_ != _IX_OS_LINUX_KERNEL_)
#include <string.h>
#endif

#include <ix_ossl.h>
#include "ix_rm.h"

#endif /* IX_PLATFORM_2x01 */

#include "ix_cc.h"
#include "cc/ix_cc_stkdrv_common.h"

#if defined(IX_CC_STKDRV_PKT_CLASS)

#include "cc/ix_cc_stkdrv.h"
#include "cc/ix_cc_stkdrv_pktclass.h"
#include "cc/ix_cc_msup.h"
#include "bindings.h"
#include "ix_netmacros.h"

#include "cc/internal/internal_stkdrv_pktclass.h"

#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)
#include "definitions.h"

/* 
 * Global outgoing filter control structure pointer. Memory for this shall be 
 * allocated only when outgoing filter is initialized 
 */
ix_cc_stkdrv_og_filter_ctrl *g_pFilterCtrl;


/**
 * Pre-processor symbols and macros used in this file.
 */
#define IX_CC_STKDRV_IPV4_OUTPUT IX_CC_IPV4_PKT_STKDRV_INPUT
#define IX_CC_STKDRV_IPV6_OUTPUT IX_CC_IPV6_PKT_STKDRV_INPUT 

/**
 * Types definitions whose scope is limited to this file.
 */

/**
 * Variable declarations global to this file only.  Externs are followed by
 * static variables.
 */

/**
 * Extern function prototypes.
 */

/**
 * Static function prototypes.
 */

PRIVATE ix_error _ix_cc_stkdrv_og_chk_pkt_type_filter(              
                        ix_buffer_handle              arg_hBuffer,
                        ix_uint8                      arg_pktType,
                        ix_cc_stkdrv_physical_if_node *arg_pPhysicalIf,
                        ix_cc_stkdrv_og_filter        *pFilter,
                        ix_cc_stkdrv_og_cc_type       *arg_pCcType,
                        void                          **arg_ppData
                         );

PRIVATE ix_error _ix_cc_stkdrv_og_chk_usr_def0_filter (              
                        ix_buffer_handle              arg_hBuffer,
                        ix_uint8                      arg_pktType,
                        ix_cc_stkdrv_physical_if_node *arg_pPhysicalIf,
                        ix_cc_stkdrv_og_filter        *pFilter,
                        ix_cc_stkdrv_og_cc_type       *arg_pCcType,
                        void                          **arg_ppData
                         );

PRIVATE void _ix_128_bit_chk_grtr_than(
                        ix_uint128 *arg1, 
                        ix_uint128 *arg2, 
                        ix_uint8 *arg_status);

PRIVATE void _ix_128_bit_chk_less_than(
                        ix_uint128 *arg1, 
                        ix_uint128 *arg2, 
                        ix_uint8 *arg_status);

PRIVATE ix_error _ix_cc_stkdrv_check_ipv6_dip_inport_filter(
                                          ix_buffer_handle arg_hBuffer,
                                          ix_cc_stkdrv_filter* arg_pFilter,
                                          ix_uint32* arg_pFoundMatch
                                                    );
#endif /* _IX_OS_LINUX_KERNEL_ */



PRIVATE ix_error _ix_cc_stkdrv_check_ipv4_dip_inport_filter(ix_buffer_handle arg_hBuffer, ix_cc_stkdrv_filter* arg_pFilter, ix_uint32* arg_pFoundMatch);

/* Internal callbacks for asynchronous operations. */
PRIVATE ix_error _ix_cc_stkdrv_cb_return_filter_handle(ix_error arg_Result, void *arg_pContext, void *arg_pMsg, ix_uint32 arg_MsgLen);
PRIVATE ix_error _ix_cc_stkdrv_cb_generic_filter_op(ix_error arg_Result, void *arg_pContext, void *arg_pMsg, ix_uint32 arg_MsgLen);


/**
 * Function definitions.
 */

/**
 * NAME: ix_cc_stkdrv_init_filters
 *
 * DESCRIPTION: This function creates an empty array of filter handlers
 * and initializes each priority list to NULL.
 * 
 * @Param:  - OUT ix_cc_stkdrv_filter_ctrl** arg_ppFilterCtrl - location of
 *          pointer to filter control structure.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_OOM_SYSTEM
 *
 */
ix_error ix_cc_stkdrv_init_filters(
                                   ix_cc_stkdrv_filter_ctrl** arg_ppFilterCtrl
                                   )
{
    ix_uint32 i;
    ix_cc_stkdrv_filter_index_node* pIndex = NULL;
    ix_cc_stkdrv_filter_index_node* pPrevIndex = NULL;
    ix_cc_stkdrv_filter_priority priority;

    /* Allocate memory for filter control structure. */
    *arg_ppFilterCtrl = ix_ossl_malloc(sizeof(ix_cc_stkdrv_filter_ctrl));
    if(*arg_ppFilterCtrl == NULL)
    {
        return IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM,
            ("Could not allocate memory for filter ctrl structure"));
    }

    /* Reset filter array entries to 0. */
    ix_ossl_memset((*arg_ppFilterCtrl)->filterArray, 0,
        sizeof(ix_cc_stkdrv_filter) * IX_CC_STKDRV_NUM_FILTERS);

    /* Initialize priority lists to be empty. */
    (*arg_ppFilterCtrl)->filterCount = 0;
    for(priority = IX_CC_STKDRV_FILTER_PRIORITY_HIGH;
        priority < IX_CC_STKDRV_FILTER_PRIORITY_LAST;
        priority++)
    {
        (*arg_ppFilterCtrl)->pUsedFilterIndices[priority] = NULL;
    }

    /* Initialize free filter list. */
    (*arg_ppFilterCtrl)->pFreeFilterIndices = NULL;
    for(i=0; i < IX_CC_STKDRV_NUM_FILTERS; i++)
    {
        pIndex = &(*arg_ppFilterCtrl)->aAllFilterIndices[i];

        if(pPrevIndex != NULL)
        {
            /* Link this node to previous entry in list. */
            pPrevIndex->pNext = pIndex;
            pIndex->pPrev = pPrevIndex;
        }
        else
        {
            /* Set head of list of free filter indices. */
            pIndex->pPrev = NULL;
            (*arg_ppFilterCtrl)->pFreeFilterIndices = pIndex;
        }

        pIndex->filterIndex = i;
        pIndex->pNext = NULL;

        pPrevIndex = pIndex;
    }

    /* Initialize function pointer array to all NULLs. */
    ix_ossl_memset((*arg_ppFilterCtrl)->aCheckFilter, 0,
        sizeof(ix_cc_stkdrv_cb_check_filter) * IX_CC_STKDRV_FILTER_TYPE_LAST);

    /* Install filter for IPv4 destination IP and input port ID. */
    _ix_cc_stkdrv_register_filter_type(IX_CC_STKDRV_FILTER_TYPE_IPV4_DIP_INPORT,
       (ix_cc_stkdrv_cb_check_filter)_ix_cc_stkdrv_check_ipv4_dip_inport_filter,
        *arg_ppFilterCtrl);

#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)
    /* Install filter for IPv6 destination IP and input port ID. */
    _ix_cc_stkdrv_register_filter_type(IX_CC_STKDRV_FILTER_TYPE_IPV6_DIP_INPORT,
       (ix_cc_stkdrv_cb_check_filter)_ix_cc_stkdrv_check_ipv6_dip_inport_filter,
        *arg_ppFilterCtrl);

    /* Initialize the outgoing filter */
    ix_cc_stkdrv_init_og_filters(&g_pFilterCtrl);
#endif

    return IX_SUCCESS;
}


/**
 * NAME: ix_cc_stkdrv_fini_filters
 *
 * DESCRIPTION: This function finalizes the filter control structure.
 * 
 * @Param:  - INOUT ix_cc_stkdrv_filter_ctrl** arg_ppFilterCtrl - location of
 *          pointer to filter control structure.
 * @Return: IX_SUCCESS
 */
ix_error ix_cc_stkdrv_fini_filters(
                                   ix_cc_stkdrv_filter_ctrl** arg_ppFilterCtrl
                                   )
{

    /* Free memory for filter control structure. */
    ix_ossl_free(*arg_ppFilterCtrl);
    *arg_ppFilterCtrl = NULL;

    /* Un-Initialize the outgoing filter */
#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)
    ix_cc_stkdrv_fini_og_filters(&g_pFilterCtrl);
#endif

    return IX_SUCCESS;
}

/**
 * TYPENAME: _ix_cc_stkdrv_check_ipv4_dip_inport_filter
 * 
 * DESCRIPTION: Check a filter by destination IP and input port ID.
 *
 * @Param:  - IN ix_buffer_handle arg_hBuffer - handle to the buffer to be classified.
 * @Param:  - IN ix_cc_stkdrv_filter* arg_pFilter - pointer to the filter to be checked.
 * @Param:  - OUT ix_uint32* arg_pFoundMatch - pointer to integer indicating whether
 *          a filter matched.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_INTERNAL
 *
 */
ix_error _ix_cc_stkdrv_check_ipv4_dip_inport_filter(
                                                    ix_buffer_handle arg_hBuffer,
                                                    ix_cc_stkdrv_filter* arg_pFilter,
                                                    ix_uint32* arg_pFoundMatch
                                                    )
{
    /**
     * If we have reached this point, we have already verified that this is a
     * HW buffer, at least in debug mode.
     */
    ix_hw_buffer_meta* pMeta;
    void* pData;

    ix_uint16 portID;
    ix_uint32 dstIP;

    /* Get port ID from the packet. */
    IX_ERROR_CRT(ix_rm_buffer_get_meta(arg_hBuffer,(void**)&pMeta),
        IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);

    portID = IX_NTOH16(IX_RM_MEM_UINT16_READ(&pMeta->m_InputPort));

    /* Get destination IP address from the packet. */
    IX_ERROR_CRT(ix_cc_hw_get_packet_data(arg_hBuffer,&pData),
        IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);

    dstIP = IX_NTOH32(*(((ix_uint32*)pData) + IX_CC_STKDRV_IPV4_DST_IP_OFFSET));

    /* Classify by destination IP */
    if(arg_pFilter->filterCriteria.flag & IX_CC_STKDRV_FILTER_DIP_CLASS)
    {
        if((arg_pFilter->filterCriteria.dstAddrs.start > dstIP) ||
            (arg_pFilter->filterCriteria.dstAddrs.end < dstIP))
        {
            /* Filter did not match. */
            *arg_pFoundMatch = 0;
            return IX_SUCCESS;
        }
    }
    
    /* Classify by input port ID. */
    if(arg_pFilter->filterCriteria.flag & IX_CC_STKDRV_FILTER_INPORT_CLASS)
    {
        if((arg_pFilter->filterCriteria.inPorts.start > portID) ||
            (arg_pFilter->filterCriteria.inPorts.end < portID))
        {
            /* Filter did not match. */
            *arg_pFoundMatch = 0;
            return IX_SUCCESS;
        }
    }

    /* Found a match. */
    *arg_pFoundMatch = 1;
    return IX_SUCCESS;

}

/**
 * NAME: ix_cc_stkdrv_classify_pkt
 *
 * DESCRIPTION: This function classifies a packet according to the installed
 * packet filters.  Packets that do not match any classifiation will be
 * treated as local legacy packets.
 * 
 * @Param:  - IN ix_buffer_handle arg_hBuffer - handle to the packet to
 *          be classified.
 * @Param:  - IN ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl - pointer to the
 *          filter control structure.
 * @Param:  - OUT ix_cc_stkdrv_handler_id* arg_pHandlerID - pointer to the
 *          handler ID which is associated with the input packet's classification.
 *          The classification function tells us which communication handler the
 *          packet should be sent to.
 * @Param:  - OUT ix_cc_stkdrv_packet_type* arg_pPacketType - pointer to the
 *          packet type which is associated with the input packet's classification.
 *          The classification function tells us what type of packet this is
 *          (local or remote legacy stack, local or remote NPF stack).
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_INTERNAL
 *
 */
ix_error ix_cc_stkdrv_classify_pkt(
                                   ix_buffer_handle arg_hBuffer,
                                   ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl,
                                   ix_cc_stkdrv_handler_id* arg_pHandlerID,
                                   ix_cc_stkdrv_packet_type* arg_pPacketType
                                   )
{
    ix_cc_stkdrv_filter_index_node* pIndex;
    ix_cc_stkdrv_filter currFilter;
    ix_cc_stkdrv_filter_priority priority;

    ix_uint32 foundMatch = 0;

    /**
     * Check filters until there is a match.
     * Start with the high priority filters.
     */
    for(priority = IX_CC_STKDRV_FILTER_PRIORITY_HIGH;
        (priority < IX_CC_STKDRV_FILTER_PRIORITY_LAST) && (!foundMatch);
        priority++)
    {
        for(pIndex = arg_pFilterCtrl->pUsedFilterIndices[priority];
            pIndex != NULL;
            pIndex = pIndex->pNext)
        {
            currFilter = arg_pFilterCtrl->filterArray[pIndex->filterIndex];

            /* Classify the packet according to this filter. */
            if(arg_pFilterCtrl->aCheckFilter[currFilter.filterType] == NULL)
                return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("No function registered to check filter of type %d\n", currFilter.filterType));
            IX_ERROR_CRT(arg_pFilterCtrl->aCheckFilter[currFilter.filterType](
                arg_hBuffer, &currFilter, &foundMatch), IX_CC_ERROR_INTERNAL,
                IX_ERROR_LEVEL_WARNING);
            if(foundMatch)
                break;
        } /* end pIndex = arg_pFilterCtrl->pUsedFilterIndices[priority]... */
    } /* end for(priority = IX_CC_STKDRV_FILTER_PRIORITY_HIGH... */

    /* If we did not find a match, classify the packet as local legacy. */
    if(pIndex == NULL)
    {
        *arg_pHandlerID = IX_CC_STKDRV_HANDLER_ID_LOCAL_DRIVER;
        *arg_pPacketType = IX_CC_STKDRV_PACKET_TYPE_LOCAL_LEGACY;
        return IX_SUCCESS;
    }

    /* Return the handler ID and packet type from the matching filter. */
    *arg_pHandlerID = currFilter.handlerID;
    *arg_pPacketType = currFilter.packetType;
    return IX_SUCCESS;
}

/**
 * NAME: ix_cc_stkdrv_async_add_filter
 *
 * DESCRIPTION: This function is called to add a packet filter.
 * 
 * @Param:  - IN ix_cc_stkdrv_cb_filter_ops arg_pCallback - pointer to the
 *          client's callback function to be invoked once the message returns.
 * @Param:  - IN void* arg_pContext - pointer to client context.
 * @Param:  - IN ix_cc_stkdrv_filter* arg_pFilter - pointer to the filter to be added.
 * @Param:  - IN ix_cc_stkdrv_filter_priority arg_priority - determines
 *          whether this filter is of high, medium, or low priority. During
 *          classification higher priority filters will be checked first.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_OOM_SYSTEM
 */
ix_error ix_cc_stkdrv_async_add_filter(
                                       ix_cc_stkdrv_cb_filter_ops arg_pCallback,
                                       void* arg_pContext,
                                       ix_cc_stkdrv_filter* arg_pFilter,
                                       ix_cc_stkdrv_filter_priority arg_priority
                                       )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_filter_ops_context* pContext = NULL;
    _ix_cc_stkdrv_add_filter_msg* pMsg = NULL;

    /* Check for invalid callback. */
    if(arg_pCallback == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("NULL callback passed in"));

    /* Allocate memory for message structure. */
    pMsg = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_add_filter_msg));
    if(pMsg == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for message to add filter"));

    /* Allocate memory for message support context. */
    pContext = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_filter_ops_context));
    if(pContext == NULL)
    {
        err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for context to add filter"));
        goto label_1;
    }

    /* Fill in message support context. */
    pContext->pUserCallback = arg_pCallback;
    pContext->pUserContext = arg_pContext;

    /* Fill in add filter message. */
    pMsg->filter = *arg_pFilter;
    pMsg->priority = arg_priority;

    /* Send an asynchronous message to the Stack Driver to add a filter. */
    IX_ERROR_CG(ix_cc_msup_send_async_msg(IX_CC_STKDRV_MSG_INPUT,
                                        (ix_cc_msghlp_callback)_ix_cc_stkdrv_cb_return_filter_handle,
                                        pContext,
                                        IX_CC_STKDRV_MSG_ID_ADD_FILTER,
                                        pMsg,
                                        sizeof(_ix_cc_stkdrv_add_filter_msg)), err, label_2);

    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;

label_2:
    /* Free memory for message support context, only if an error has occurred. */
    ix_ossl_free(pContext);
    pContext = NULL;

label_1:
    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;
}

/**
 * NAME: _ix_cc_stkdrv_add_filter
 *
 * DESCRIPTION: This function adds a filter to the array of filters and also
 * places the filter index into the appropriate priority list.
 * 
 * @Param:  - IN ix_cc_stkdrv_filter* arg_pFilter - pointer to the filter to be added.
 * @Param:  - IN ix_cc_stkdrv_filter_priority arg_priority - determines
 *          whether this filter is of high, medium, or low priority. During
 *          classification higher priority filters will be checked first.
 * @Param:  - IN ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl - pointer to the
 *          filter control structure.
 * @Param:  - OUT ix_cc_stkdrv_filter_handle* arg_pFilterHandle - pointer to
 *          the handle of the added filter. The handle will be passed in to
 *          ix_cc_stkdrv_remove_filter to remove the filter.
 * @Return: IX_SUCCESS
 *          IX_CC_STKDRV_ERROR_OUT_OF_FILTERS
 *          IX_CC_ERROR_RANGE
 *          IX_CC_ERROR_INTERNAL
 */
ix_error _ix_cc_stkdrv_add_filter(
                                 ix_cc_stkdrv_filter* arg_pFilter,
                                 ix_cc_stkdrv_filter_priority arg_priority,
                                 ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl,
                                 ix_cc_stkdrv_filter_handle* arg_pFilterHandle
                                 )
{
    ix_cc_stkdrv_filter_index_node* pIndex;

    /* Check for valid filter type. */
    if(arg_pFilter->filterType != IX_CC_STKDRV_FILTER_TYPE_IPV4_DIP_INPORT)
    {
        /**
         * Right now we only support filtering on IPv4
         * destination IP and input portID.
         */
        return IX_ERROR_WARNING(IX_CC_ERROR_RANGE,
            ("Invalid filter type for adding filter"));
    }

    /* Check for valid priority. */
    if((arg_priority < IX_CC_STKDRV_FILTER_PRIORITY_FIRST) ||
        (arg_priority >= IX_CC_STKDRV_FILTER_PRIORITY_LAST))
    {
        return IX_ERROR_WARNING(IX_CC_ERROR_RANGE,
            ("Invalid priority for adding filter"));
    }

    /* Check for free filters. */
    pIndex = arg_pFilterCtrl->pFreeFilterIndices;
    if(pIndex == NULL)
        return IX_ERROR_WARNING(IX_CC_STKDRV_ERROR_OUT_OF_FILTERS,
            ("Out of filters for stack driver"));

    /* Remove this filter index from the list of free filter indices. */
    if(pIndex->pNext != NULL)
        pIndex->pNext->pPrev = NULL;
    arg_pFilterCtrl->pFreeFilterIndices = pIndex->pNext;

    /* Put this filter index into the appropriate priority list. */
    pIndex->pPrev = NULL;
    if(arg_pFilterCtrl->pUsedFilterIndices[arg_priority] != NULL)
    {
        pIndex->pNext = arg_pFilterCtrl->pUsedFilterIndices[arg_priority];
        arg_pFilterCtrl->pUsedFilterIndices[arg_priority]->pPrev = pIndex;
    }
    else
    {
        pIndex->pNext = NULL;
        arg_pFilterCtrl->pUsedFilterIndices[arg_priority] = pIndex;
    }

    /* Copy filter information into the filter indexed here. */
    arg_pFilterCtrl->filterArray[pIndex->filterIndex] = *arg_pFilter;

    /* Increment filter count. */
    arg_pFilterCtrl->filterCount++;

    /* Return handle to added filter. */
    *arg_pFilterHandle = (ix_cc_stkdrv_filter_handle)pIndex->filterIndex;

    return IX_SUCCESS;
}

/**
 * NAME: _ix_cc_stkdrv_cb_return_filter_handle
 *
 * DESCRIPTION: This is the internal callback function for adding or modifying
 *              filters asynchronously.
 * 
 * @Param:  - IN ix_error arg_Result - error code returned from
 *          the asynchronous filter operation.
 * @Param:  - IN void* arg_pContext - pointer to reply message
 *          context, of the type _ix_cc_stkdrv_filter_ops_context.
 * @Param:  - IN void* arg_pMsg - pointer to reply message.  This memory
 *          is only valid for the duration of this routine - the user
 *          callback will have to copy its data into its own memory.
 * @Param:  - IN ix_uint32 arg_MsgLen - length of reply message.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 */
ix_error _ix_cc_stkdrv_cb_return_filter_handle(
                                     ix_error arg_Result,
                                     void* arg_pContext,
                                     void* arg_pMsg,
                                     ix_uint32 arg_MsgLen
                                     )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_filter_ops_context* pContext =
        (_ix_cc_stkdrv_filter_ops_context*)arg_pContext;

    /* Check for valid context. */
    if(pContext == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Internal callback invoked with NULL context"));

    /* Call user's callback function. */
    IX_ERROR_C((pContext->pUserCallback)(arg_Result,
        pContext->pUserContext, (ix_cc_stkdrv_filter_handle*)arg_pMsg), err);

    return err;
}

/**
 * NAME: ix_cc_stkdrv_async_remove_filter
 *
 * DESCRIPTION: This function is called to remove a packet filter.
 * 
 * @Param:  - IN ix_cc_stkdrv_cb_filter_ops arg_pCallback - pointer to the
 *          client's callback function to be invoked once the message returns.
 * @Param:  - IN void* arg_pContext - pointer to client context.
 * @Param:  - IN ix_cc_stkdrv_filter_handle arg_filterHandle - handle to the
 *          filter to be removed.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_OOM_SYSTEM
 */
ix_error ix_cc_stkdrv_async_remove_filter(
                                          ix_cc_stkdrv_cb_filter_ops arg_pCallback,
                                          void* arg_pContext,
                                          ix_cc_stkdrv_filter_handle arg_filterHandle
                                          )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_filter_ops_context* pContext = NULL;
    _ix_cc_stkdrv_remove_filter_msg* pMsg = NULL;

    /* Check for invalid callback. */
    if(arg_pCallback == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("NULL callback passed in"));

    /* Allocate memory for message structure. */
    pMsg = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_remove_filter_msg));
    if(pMsg == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for message to add filter"));

    /* Allocate memory for message support context. */
    pContext = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_filter_ops_context));
    if(pContext == NULL)
    {
        err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for context to add filter"));
        goto label_1;
    }

    /* Fill in message support context. */
    pContext->pUserCallback = arg_pCallback;
    pContext->pUserContext = arg_pContext;

    /* Fill in remove filter message. */
    pMsg->filterHandle = arg_filterHandle;

    /* Send an asynchronous message to the Stack Driver to remove a filter. */
    IX_ERROR_CG(ix_cc_msup_send_async_msg(IX_CC_STKDRV_MSG_INPUT,
                                        (ix_cc_msghlp_callback)_ix_cc_stkdrv_cb_generic_filter_op,
                                        pContext,
                                        IX_CC_STKDRV_MSG_ID_REMOVE_FILTER,
                                        pMsg,
                                        sizeof(_ix_cc_stkdrv_remove_filter_msg)), err, label_2);

    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;

label_2:
    /* Free memory for message support context, only if an error has occurred. */
    ix_ossl_free(pContext);
    pContext = NULL;

label_1:
    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;
}

/**
 * NAME: _ix_cc_stkdrv_remove_filter
 *
 * DESCRIPTION: This function removes a filter from the array of filters and
 * the appropriate priority list.
 * 
 * @Param:  - IN ix_cc_stkdrv_filter_handle arg_filterHandle - opaque handle
 *          to the filter to be removed.
 * @Param:  - IN ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl - pointer to the
 *          filter control structure.
 * @Return: IX_SUCCESS
 *          IX_CC_STKDRV_ERROR_INVALID_FILTER
 *          IX_CC_ERROR_INTERNAL
 *
 */
ix_error _ix_cc_stkdrv_remove_filter(
                                    ix_cc_stkdrv_filter_handle arg_filterHandle,
                                    ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl
                                    )
{
    ix_cc_stkdrv_filter_priority priority;
    ix_uint32 filterIndex = (ix_uint32)arg_filterHandle;
    ix_cc_stkdrv_filter_index_node* pIndex;


    /**
     * Check filters until there is a match.
     * Start with the high priority filters.
     */
    for(priority = IX_CC_STKDRV_FILTER_PRIORITY_HIGH;
        priority < IX_CC_STKDRV_FILTER_PRIORITY_LAST;
        priority++)
    {
        
        for(pIndex = arg_pFilterCtrl->pUsedFilterIndices[priority];
            pIndex != NULL;
            pIndex = pIndex->pNext)
        {
            /* Check if this filter is in use for this priority. */
            if(pIndex->filterIndex == filterIndex)
            {
                /* Remove this filter from this priority list. */
                if((pIndex == arg_pFilterCtrl->pUsedFilterIndices[priority]) &&
                    (pIndex->pPrev == NULL) &&
                    (pIndex->pNext == NULL))
                {
                    arg_pFilterCtrl->pUsedFilterIndices[priority] = NULL;
                }
                if(pIndex->pPrev != NULL)
                    pIndex->pPrev->pNext = pIndex->pNext;
                if(pIndex->pNext != NULL)
                    pIndex->pNext->pPrev = pIndex->pPrev;

                /* Put this filter back onto the free list. */
                pIndex->pNext = arg_pFilterCtrl->pFreeFilterIndices;
                pIndex->pPrev = NULL;
                arg_pFilterCtrl->pFreeFilterIndices->pPrev = pIndex;
                arg_pFilterCtrl->pFreeFilterIndices = pIndex;

                /* Decrement filter count. */
                arg_pFilterCtrl->filterCount--;

                return IX_SUCCESS;
            }
        }
    } /* end for(priority = IX_CC_STKDRV_FILTER_PRIORITY_HIGH... */

    return IX_ERROR_WARNING(IX_CC_STKDRV_ERROR_INVALID_FILTER,
        ("Tried to remove a filter not in use"));
}


/**
 * NAME: _ix_cc_stkdrv_cb_generic_filter_op
 *
 * DESCRIPTION: This is the internal callback function for removing
 *              filters asynchronously.
 * 
 * @Param:  - IN ix_error arg_Result - error code returned from
 *          the asynchronous filter operation.
 * @Param:  - IN void* arg_pContext - pointer to reply message
 *          context, of the type _ix_cc_stkdrv_filter_ops_context.
 * @Param:  - IN void* arg_pMsg - pointer to reply message.  This memory
 *          is only valid for the duration of this routine - the user
 *          callback will have to copy its data into its own memory.
 * @Param:  - IN ix_uint32 arg_MsgLen - length of reply message.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 */
ix_error _ix_cc_stkdrv_cb_generic_filter_op(
                                            ix_error arg_Result,
                                            void* arg_pContext,
                                            void* arg_pMsg,
                                            ix_uint32 arg_MsgLen
                                            )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_filter_ops_context* pContext =
        (_ix_cc_stkdrv_filter_ops_context*)arg_pContext;

    /* Check for valid context. */
    if(pContext == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("Internal callback invoked with NULL context"));

    /* Call user's callback function. */
    IX_ERROR_C((pContext->pUserCallback)(arg_Result,
        pContext->pUserContext, NULL), err);

    /* Free the msg memory, only if the msg support library is stubbed. */
#if defined(IX_CC_MSUP_STUB)
    if(arg_pMsg != NULL)
    {
        ix_ossl_free(arg_pMsg);
        arg_pMsg = NULL;
    }
#endif /* defined(IX_CC_MSUP_STUB) */

    return err;
}


/**
 * NAME: ix_cc_stkdrv_async_remove_all_filters
 *
 * DESCRIPTION: This function is called to remove all packet filters.
 * 
 * @Param:  - IN ix_cc_stkdrv_cb_filter_ops arg_pCallback - pointer to the
 *          client's callback function to be invoked once the message returns.
 * @Param:  - IN void* arg_pContext - pointer to client context.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_OOM_SYSTEM
 */
ix_error ix_cc_stkdrv_async_remove_all_filters(
                                               ix_cc_stkdrv_cb_filter_ops arg_pCallback,
                                               void* arg_pContext
                                               )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_filter_ops_context* pContext = NULL;

    /* Check for invalid callback. */
    if(arg_pCallback == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("NULL callback passed in"));

    /* Allocate memory for message support context. */
    pContext = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_filter_ops_context));
    if(pContext == NULL)
    {
        return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for context to add filter"));
    }

    /* Fill in message support context. */
    pContext->pUserCallback = arg_pCallback;
    pContext->pUserContext = arg_pContext;

    /* Send an asynchronous message to the Stack Driver to remove all filters. */
    IX_ERROR_CG(ix_cc_msup_send_async_msg(IX_CC_STKDRV_MSG_INPUT,
                                        (ix_cc_msghlp_callback)_ix_cc_stkdrv_cb_generic_filter_op,
                                        pContext,
                                        IX_CC_STKDRV_MSG_ID_REMOVE_ALL_FILTERS,
                                        NULL,
                                        0), err, label_1);

    return err;

label_1:
    /* Free memory for message support context, only if an error has occurred. */
    ix_ossl_free(pContext);
    pContext = NULL;

    return err;
}

/**
 * NAME: _ix_cc_stkdrv_remove_all_filters
 *
 * DESCRIPTION: This function removes all filters.
 * 
 * @Param:  - INOUT ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl - pointer to the
 * filter control structure.
 * @Return: IX_SUCCESS
 *
 */
ix_error _ix_cc_stkdrv_remove_all_filters(
                                         ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl
                                         )
{
    ix_cc_stkdrv_filter_priority priority;
    ix_cc_stkdrv_filter_index_node* pIndex;
    ix_cc_stkdrv_filter_index_node* pNextIndex = NULL;

    /* Put all filters back onto the free list of filters. */
    for(priority = IX_CC_STKDRV_FILTER_PRIORITY_HIGH;
        priority < IX_CC_STKDRV_FILTER_PRIORITY_LAST;
        priority++)
    {
        pIndex = arg_pFilterCtrl->pUsedFilterIndices[priority];
        while(pIndex != NULL)
        {
            /* Store pointer to next index in list. */
            pNextIndex = pIndex->pNext;

            /* Remove this filter from this priority list. */
            if(pIndex->pPrev != NULL)
                pIndex->pPrev->pNext = pIndex->pNext;
            if(pIndex->pNext != NULL)
                pIndex->pNext->pPrev = pIndex->pPrev;

            /* Put this filter back onto the free list. */
            pIndex->pNext = arg_pFilterCtrl->pFreeFilterIndices;
            pIndex->pPrev = NULL;
            arg_pFilterCtrl->pFreeFilterIndices->pPrev = pIndex;
            arg_pFilterCtrl->pFreeFilterIndices = pIndex;

            /* Decrement filter count. */
            arg_pFilterCtrl->filterCount--;

            /* Get next index in list. */
            pIndex = pNextIndex;
        }
        /* Reset head of each priority list to NULL. */
        arg_pFilterCtrl->pUsedFilterIndices[priority] = NULL;
    }

    /* Reset filter array entries to 0. */
    ix_ossl_memset(arg_pFilterCtrl->filterArray, 0,
        sizeof(ix_cc_stkdrv_filter) * IX_CC_STKDRV_NUM_FILTERS);

    return IX_SUCCESS;

}


/**
 * NAME: ix_cc_stkdrv_async_modify_filter
 *
 * DESCRIPTION: This function is called to modify a packet filter.
 * 
 * @Param:  - IN ix_cc_stkdrv_cb_filter_ops arg_pCallback - pointer to the
 *          client's callback function to be invoked once the message returns.
 * @Param:  - IN void* arg_pContext - pointer to client context.
 * @Param:  - IN ix_cc_stkdrv_filter* arg_pFilter - pointer to the new filter info.
 * @Param:  - IN ix_cc_stkdrv_filter_priority arg_priority - determines
 *          whether this filter is of high, medium, or low priority. During
 *          classification higher priority filters will be checked first.
 * @Param:  - IN ix_cc_stkdrv_filter_handle arg_filterHandle - handle to the
 *          filter to be modified.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_OOM_SYSTEM
 */
ix_error ix_cc_stkdrv_async_modify_filter(
                                          ix_cc_stkdrv_cb_filter_ops arg_pCallback,
                                          void* arg_pContext,
                                          ix_cc_stkdrv_filter* arg_pFilter,
                                          ix_cc_stkdrv_filter_priority arg_priority,
                                          ix_cc_stkdrv_filter_handle arg_filterHandle
                                          )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_filter_ops_context* pContext = NULL;
    _ix_cc_stkdrv_modify_filter_msg* pMsg = NULL;

    /* Check for invalid callback. */
    if(arg_pCallback == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("NULL callback passed in"));

    /* Allocate memory for message structure. */
    pMsg = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_modify_filter_msg));
    if(pMsg == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for message to modify filter"));

    /* Allocate memory for message support context. */
    pContext = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_filter_ops_context));
    if(pContext == NULL)
    {
        err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for context to modify filter"));
        goto label_1;
    }

    /* Fill in message support context. */
    pContext->pUserCallback = arg_pCallback;
    pContext->pUserContext = arg_pContext;

    /* Fill in modify filter message. */
    pMsg->filter = *arg_pFilter;
    pMsg->priority = arg_priority;
    pMsg->filterHandle = arg_filterHandle;

    /* Send an asynchronous message to the Stack Driver to modify a filter. */
    IX_ERROR_CG(ix_cc_msup_send_async_msg(IX_CC_STKDRV_MSG_INPUT,
                                        (ix_cc_msghlp_callback)_ix_cc_stkdrv_cb_return_filter_handle,
                                        pContext,
                                        IX_CC_STKDRV_MSG_ID_MODIFY_FILTER,
                                        pMsg,
                                        sizeof(_ix_cc_stkdrv_modify_filter_msg)), err, label_2);

    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;

label_2:
    /* Free memory for message support context, only if an error has occurred. */
    ix_ossl_free(pContext);
    pContext = NULL;

label_1:
    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;
}

/**
 * NAME: _ix_cc_stkdrv_modify_filter
 *
 * DESCRIPTION: This function modifies a filter with new input filter
 * criteria and data.
 * 
 * @Param:  - IN ix_cc_stkdrv_filter* arg_pFilter - pointer to new filter data
 *          to be used for the modified filter.
 * @Param:  - IN ix_cc_stkdrv_filter_priority arg_priority - determines whether
 *          this filter is of high, medium, or low priority. During
 *          classification higher priority filters will be checked first.
 * @Param:  - IN ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl - pointer to the
 *          filter control structure.
 * @Param:  - INOUT ix_cc_stkdrv_filter_handle* arg_pFilterHandle - pointer to
 *          the handle of the filter to be modified.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_RANGE
 *          IX_CC_ERROR_INTERNAL
 *          IX_CC_STKDRV_ERROR_INVALID_FILTER
 *          IX_CC_STKDRV_ERROR_OUT_OF_FILTERS
 *
 */
ix_error _ix_cc_stkdrv_modify_filter(
                                    ix_cc_stkdrv_filter* arg_pFilter,
                                    ix_cc_stkdrv_filter_priority arg_priority,
                                    ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl, 
                                    ix_cc_stkdrv_filter_handle* arg_pFilterHandle
                                    )
{
    IX_ERROR_CR(_ix_cc_stkdrv_remove_filter(*arg_pFilterHandle, arg_pFilterCtrl));
    IX_ERROR_CR(_ix_cc_stkdrv_add_filter(arg_pFilter, arg_priority, arg_pFilterCtrl, arg_pFilterHandle));

    return IX_SUCCESS;
}


/**
 * NAME: ix_cc_stkdrv_async_register_filter_type
 *
 * DESCRIPTION: This function is called to register a callback function for a
 *              specific filter type asynchronously.
 * 
 * @Param:  - IN ix_cc_stkdrv_cb_filter_ops arg_pCallback - pointer to the
 *          client's callback function to be invoked once the message returns.
 * @Param:  - IN void* arg_pContext - pointer to client context.
 * @Param:  - IN ix_cc_stkdrv_filter_type arg_filterType - type of the filter
 *          being registered.
 * @Param:  - IN ix_cc_stkdrv_cb_check_filter arg_checkFilterCB - callback
 *          function to check a filter of a specific type.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_RANGE
 *          IX_CC_ERROR_OOM_SYSTEM
 */
ix_error ix_cc_stkdrv_async_register_filter_type(
                                                 ix_cc_stkdrv_cb_filter_ops arg_pCallback,
                                                 void* arg_pContext,
                                                 ix_cc_stkdrv_filter_type arg_filterType,
                                                 ix_cc_stkdrv_cb_check_filter arg_checkFilterCB
                                                 )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_filter_ops_context* pContext = NULL;
    _ix_cc_stkdrv_register_filter_type_msg* pMsg = NULL;

    /* Check for invalid callback. */
    if((arg_pCallback == NULL) || (arg_checkFilterCB == NULL))
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("NULL callback passed in"));

    /* Check for invalid filter type. */
    if((arg_filterType < IX_CC_STKDRV_FILTER_TYPE_FIRST) ||
        (arg_filterType >= IX_CC_STKDRV_FILTER_TYPE_LAST))
        return IX_ERROR_WARNING(IX_CC_ERROR_RANGE, ("Filter type %d not valid\n", arg_filterType));

    /* Allocate memory for message structure. */
    pMsg = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_register_filter_type_msg));
    if(pMsg == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for message to register filter type"));

    /* Allocate memory for message support context. */
    pContext = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_filter_ops_context));
    if(pContext == NULL)
    {
        err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for context to register filter type"));
        goto label_1;
    }

    /* Fill in message support context. */
    pContext->pUserCallback = arg_pCallback;
    pContext->pUserContext = arg_pContext;

    /* Fill in register filter message. */
    pMsg->filterType = arg_filterType;
    pMsg->checkFilterCB = arg_checkFilterCB;

    /* Send an asynchronous message to the Stack Driver to modify a filter. */
    IX_ERROR_CG(ix_cc_msup_send_async_msg(IX_CC_STKDRV_MSG_INPUT,
                                        (ix_cc_msghlp_callback)_ix_cc_stkdrv_cb_generic_filter_op,
                                        pContext,
                                        IX_CC_STKDRV_MSG_ID_REGISTER_FILTER_TYPE,
                                        pMsg,
                                        sizeof(_ix_cc_stkdrv_register_filter_type_msg)), err, label_2);

    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;

label_2:
    /* Free memory for message support context, only if an error has occurred. */
    ix_ossl_free(pContext);
    pContext = NULL;

label_1:
    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;
}


/**
 * NAME: _ix_cc_stkdrv_register_filter_type
 *
 * DESCRIPTION: This function registers a callback for a specific filter type.
 * 
 * @Param:  - IN ix_cc_stkdrv_filter_type arg_filterType - type of the filter
 *          being registered.
 * @Param:  - IN ix_cc_stkdrv_cb_check_filter arg_checkFilterCB - callback
 *          function to check a filter of a specific type.
 * @Param:  - IN ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl - pointer to the
 *          filter control structure.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_DUPLICATE_ENTRY
 *
 */
ix_error _ix_cc_stkdrv_register_filter_type(
                                            ix_cc_stkdrv_filter_type arg_filterType,
                                            ix_cc_stkdrv_cb_check_filter arg_checkFilterCB,
                                            ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl
                                            )
{

    /* Ensure that the filter type is not already registered. */
    if(arg_pFilterCtrl->aCheckFilter[arg_filterType] != NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_DUPLICATE_ENTRY, ("Filter type %d already registered\n", arg_filterType));

    /* Set the callback for this filter type. */
    arg_pFilterCtrl->aCheckFilter[arg_filterType] = arg_checkFilterCB;

    return IX_SUCCESS;
}


/**
 * NAME: ix_cc_stkdrv_async_unregister_filter_type
 *
 * DESCRIPTION: This function is called to unregister a callback function for a
 *              specific filter type asynchronously.
 * 
 * @Param:  - IN ix_cc_stkdrv_cb_filter_ops arg_pCallback - pointer to the
 *          client's callback function to be invoked once the message returns.
 * @Param:  - IN void* arg_pContext - pointer to client context.
 * @Param:  - IN ix_cc_stkdrv_filter_type arg_filterType - type of the filter
 *          being registered.
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_RANGE
 *          IX_CC_ERROR_OOM_SYSTEM
 */
ix_error ix_cc_stkdrv_async_unregister_filter_type(
                                                   ix_cc_stkdrv_cb_filter_ops arg_pCallback,
                                                   void* arg_pContext,
                                                   ix_cc_stkdrv_filter_type arg_filterType
                                                   )
{
    ix_error err = IX_SUCCESS;

    _ix_cc_stkdrv_filter_ops_context* pContext = NULL;
    _ix_cc_stkdrv_unregister_filter_type_msg* pMsg = NULL;

    /* Check for invalid callback. */
    if(arg_pCallback == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_NULL, ("NULL callback passed in"));

    /* Check for invalid filter type. */
    if((arg_filterType < IX_CC_STKDRV_FILTER_TYPE_FIRST) ||
        (arg_filterType >= IX_CC_STKDRV_FILTER_TYPE_LAST))
        return IX_ERROR_WARNING(IX_CC_ERROR_RANGE, ("Filter type %d not valid\n", arg_filterType));

    /* Allocate memory for message structure. */
    pMsg = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_unregister_filter_type_msg));
    if(pMsg == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for message to unregister filter type"));

    /* Allocate memory for message support context. */
    pContext = ix_ossl_malloc(sizeof(_ix_cc_stkdrv_filter_ops_context));
    if(pContext == NULL)
    {
        err = IX_ERROR_WARNING(IX_CC_ERROR_OOM_SYSTEM, ("Could not allocate memory for context to unregister filter type"));
        goto label_1;
    }

    /* Fill in message support context. */
    pContext->pUserCallback = arg_pCallback;
    pContext->pUserContext = arg_pContext;

    /* Fill in unregister filter message. */
    pMsg->filterType = arg_filterType;

    /* Send an asynchronous message to the Stack Driver to modify a filter. */
    IX_ERROR_CG(ix_cc_msup_send_async_msg(IX_CC_STKDRV_MSG_INPUT,
                                        (ix_cc_msghlp_callback)_ix_cc_stkdrv_cb_generic_filter_op,
                                        pContext,
                                        IX_CC_STKDRV_MSG_ID_UNREGISTER_FILTER_TYPE,
                                        pMsg,
                                        sizeof(_ix_cc_stkdrv_unregister_filter_type_msg)), err, label_2);

    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;

label_2:
    /* Free memory for message support context, only if an error has occurred. */
    ix_ossl_free(pContext);
    pContext = NULL;

label_1:
    /* Free memory allocated to message structure. */
    ix_ossl_free(pMsg);
    pMsg = NULL;

    return err;
}


/**
 * NAME: _ix_cc_stkdrv_unregister_filter_type
 *
 * DESCRIPTION: This function unregisters a callback for a specific filter type.
 * 
 * @Param:  - IN ix_cc_stkdrv_filter_type arg_filterType - type of the filter
 *          being unregistered.
 * @Param:  - IN ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl - pointer to the
 *          filter control structure.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_ENTRY_NOT_FOUND
 *
 */
ix_error _ix_cc_stkdrv_unregister_filter_type(
                                              ix_cc_stkdrv_filter_type arg_filterType,
                                              ix_cc_stkdrv_filter_ctrl* arg_pFilterCtrl
                                              )
{

    /* Ensure that the filter type is already registered. */
    if(arg_pFilterCtrl->aCheckFilter[arg_filterType] == NULL)
        return IX_ERROR_WARNING(IX_CC_ERROR_ENTRY_NOT_FOUND, ("Filter type %d not registered\n", arg_filterType));

    /* Set the callback for this filter type. */
    arg_pFilterCtrl->aCheckFilter[arg_filterType] = NULL;

    return IX_SUCCESS;
}



#if (_IX_OS_TYPE_ == _IX_OS_LINUX_KERNEL_)

/**
 * NAME: ix_cc_stkdrv_init_og_filters
 *
 * DESCRIPTION: This function initliazes the outgoing filter control structure
 * 
 * @Param:  - OUT ix_cc_stkdrv_og_filter_ctrl **arg_ppFilterCtrl - location of
 *          pointer to outgoing filter control structure.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_NULL
 *          IX_CC_ERROR_OOM_SYSTEM
 *
 */
ix_error ix_cc_stkdrv_init_og_filters(
                  ix_cc_stkdrv_og_filter_ctrl **arg_ppFilterCtrl)
{
    ix_error                    err = IX_SUCCESS;
    ix_cc_stkdrv_og_filter_ctrl *pFilterCtrl = NULL;
    ix_uint32                   loop;

    if(arg_ppFilterCtrl == NULL)
    {
       return IX_ERROR_LOCAL(IX_CC_ERROR_NULL, 
                       ("Init OG Filter called with NULL filter ctrl"));
    }

    /* Allocate memory for the filter control */    
    pFilterCtrl = ix_ossl_malloc(sizeof(ix_cc_stkdrv_og_filter_ctrl));
    if(pFilterCtrl == NULL)
    {
        return IX_ERROR_LOCAL(IX_CC_ERROR_OOM_SYSTEM, 
                       ("Could not allocate memory for OG Filter Control"));
    }
    ix_ossl_memset(pFilterCtrl, 0, sizeof(ix_cc_stkdrv_og_filter_ctrl));

    /* Initialze the outgoing filter control structure */    
    pFilterCtrl->filterCount = 0;
    
    /* Initialize all filters */
    for(loop=0; loop<IX_CC_STKDRV_OG_FILTER_LAST; loop++)
    {
        pFilterCtrl->aFilterArray[loop].filterType = 
                                IX_CC_STKDRV_OG_FILTER_TYPE_UNDEFINED;
        pFilterCtrl->aFilterArray[loop].portRange.start = 0;
        pFilterCtrl->aFilterArray[loop].portRange.end = 0;
        pFilterCtrl->aFilterArray[loop].pCritereon = NULL;
        pFilterCtrl->aFilterArray[loop].aOgChkFunc = NULL;
    }

    /* 
     * Initialize the Packet Type filter, since this is the only filter which 
     * is currently defined. 
     */
    loop = pFilterCtrl->filterCount;
    pFilterCtrl->aFilterArray[loop].filterType = IX_CC_STKDRV_OG_FILTER_TYPE_PKT;
    pFilterCtrl->aFilterArray[loop].portRange.start = 0;
    pFilterCtrl->aFilterArray[loop].portRange.end = (IX_CC_STKDRV_NUM_PHYS_PORTS - 1);
    pFilterCtrl->aFilterArray[loop].pCritereon = NULL;
    pFilterCtrl->aFilterArray[loop].aOgChkFunc = 
                        _ix_cc_stkdrv_og_chk_pkt_type_filter;
    pFilterCtrl->filterCount++;

     /* 
     * Initilizing one user defined filter here. This is currently a 
     * place holder as teh filtering function is currently empty
     */
    loop = pFilterCtrl->filterCount;
    pFilterCtrl->aFilterArray[loop].filterType = 
                       IX_CC_STKDRV_OG_FILTER_TYPE_USR_DEFINED_0;
    pFilterCtrl->aFilterArray[loop].portRange.start = 0xFF; /* TBC */
    pFilterCtrl->aFilterArray[loop].portRange.end = 0xFF; /* TBC */
    pFilterCtrl->aFilterArray[loop].pCritereon = NULL;
    pFilterCtrl->aFilterArray[loop].aOgChkFunc = 
                    _ix_cc_stkdrv_og_chk_usr_def0_filter;
    pFilterCtrl->filterCount++;

     /* 
     * If any additional user defined filter is to be added , 
     * initialize them here
     */


    /* Initialize commId/CC map */
    loop = IX_CC_STKDRV_OG_CC_TYPE_IPV4_FWDER;
    pFilterCtrl->aMap[loop].ccType = IX_CC_STKDRV_OG_CC_TYPE_IPV4_FWDER;
    pFilterCtrl->aMap[loop].commId = IX_CC_STKDRV_IPV4_OUTPUT; 

    loop = IX_CC_STKDRV_OG_CC_TYPE_IPV6_FWDER;
    pFilterCtrl->aMap[loop].ccType = IX_CC_STKDRV_OG_CC_TYPE_IPV6_FWDER;
    pFilterCtrl->aMap[loop].commId = IX_CC_STKDRV_IPV6_OUTPUT;

    loop = IX_CC_STKDRV_OG_CC_TYPE_Q_MGR;
    pFilterCtrl->aMap[loop].ccType = IX_CC_STKDRV_OG_CC_TYPE_Q_MGR;
    pFilterCtrl->aMap[loop].commId = IX_CC_STKDRV_MULTICAST_PKT_OUTPUT;

    /* Set the outbound param */    
    *arg_ppFilterCtrl = pFilterCtrl;

    return err;
}

/**
 * NAME: ix_cc_stkdrv_fini_og_filters
 *
 * DESCRIPTION: This function de-initliazes the outgoing filter 
 * control structure
 * 
 * @Param:  - INOUT ix_cc_stkdrv_og_filter_ctrl **arg_ppFilterCtrl - location of
 *          pointer to outgoing filter control structure.
 *
 * @Return: IX_SUCCESS
 *
 */
ix_error ix_cc_stkdrv_fini_og_filters(
                  ix_cc_stkdrv_og_filter_ctrl **arg_ppFilterCtrl)
{
    /* Free memory for filter control structure. */
    ix_ossl_free(*arg_ppFilterCtrl);
    *arg_ppFilterCtrl = NULL;


    return IX_SUCCESS;
}

/**
 * NAME: ix_cc_stkdrv_classify_output_id
 *
 * DESCRIPTION: This is the entry point function for outgoing packet 
 * classification
 * 
 * @Param:  - IN ix_buffer_handle arg_hBuffer - Handle to Buffer
 *          - IN ix_uint8 arg_pktType - Packet Type (V4 vs V6)
 *          - OUT ix_communication_id *arg_pCommId - Outgoing 
 *            Communication Id
 *          - OUT ix_cc_stkdrv_og_filter_ctrl **arg_ppFilterCtrl - location of
 *          pointer to outgoing filter control structure.
 *          - OUT void **arg_ppData - Can be used to return any other desired context
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_INTERNAL
 *
 */
ix_error _ix_cc_stkdrv_classify_output_id(
                  ix_buffer_handle              arg_hBuffer,
                  ix_uint8                      arg_pktType, 
                  ix_communication_id           *arg_pCommId,
                  ix_cc_stkdrv_physical_if_node *arg_pPhysicalIf,
                  void                          **arg_ppData)
{
    ix_cc_stkdrv_og_filter_ctrl *pFilterCtrl;
    ix_cc_stkdrv_og_filter      *pFilter = NULL;
    ix_uint32                   loop;
    ix_uint32                   ogPortId;
    ix_cc_stkdrv_og_cc_type     ccType = IX_CC_STKDRV_OG_CC_TYPE_UNDEFINED;
    ix_error                    err = IX_SUCCESS;

    /* Perform validity checks */         
    if(arg_pCommId == NULL)
    {
       return IX_ERROR_LOCAL(IX_CC_ERROR_NULL, 
                       ("Classify Output ID called with NULL CommId pointer"));
    }
    if(arg_pPhysicalIf == NULL)
    {
       return IX_ERROR_LOCAL(IX_CC_ERROR_NULL, 
                      ("Classify Output ID called with NULL physicalIf pnter"));
    }

    /* Initialize the local variables */
    ogPortId = arg_pPhysicalIf->pPhysicalIfInfo->portId;
    pFilterCtrl = g_pFilterCtrl;

    /* Obtain the applicable filter to be used */
    for(loop=0; loop<IX_CC_STKDRV_OG_FILTER_LAST; loop++)
    {
        if( (ogPortId >= pFilterCtrl->aFilterArray[loop].portRange.start) &&
            (ogPortId <= pFilterCtrl->aFilterArray[loop].portRange.end) )
        {
           pFilter = &(pFilterCtrl->aFilterArray[loop]);
           break;
        }
    }

    if(pFilter->filterType == IX_CC_STKDRV_OG_FILTER_TYPE_UNDEFINED)
    {
       err = IX_CC_ERROR_INTERNAL;
    }
    else
    {
       /* Invoke the asscoiated filtering function */
       if(pFilter->aOgChkFunc != NULL)
       {
          err = (pFilter->aOgChkFunc)(arg_hBuffer, arg_pktType, 
                           arg_pPhysicalIf, pFilter, &ccType, arg_ppData);
       }
       else
       {
          return IX_ERROR_WARNING(IX_CC_ERROR_NULL, 
                      ("No function registered to check filter of type %d\n", 
                       pFilter->filterType));
       }
    }

    /* Obtain the communication ID using the ccType returned */
    if(err == IX_SUCCESS)
    {
       for(loop=0; loop<IX_CC_STKDRV_OG_CC_TYPE_LAST; loop++)
       {
          if(pFilterCtrl->aMap[loop].ccType == ccType)
          {
             *arg_pCommId = pFilterCtrl->aMap[loop].commId;
             break;
          }
       }
    }

    return err;
}


/**
 * NAME: _ix_cc_stkdrv_og_chk_pkt_type_filter
 *
 * DESCRIPTION: This is the outgoing filtering function for packet based 
 * classifications
 * 
 * @Param:  - IN ix_buffer_handle arg_hBuffer - Handle to Buffer
 *          - IN ix_uint8 arg_pktType - Packet Type (V4 vs V6)
 *          - IN ix_cc_stkdrv_physical_if_node *arg_pPhysicalIf - Pointer to 
 *                                    physical interface
 *          - IN ix_cc_stkdrv_og_filter *pFilter - Pointer to outgoing filter
 *          - OUT ix_communication_id *arg_pCcType - Outgoing Core Component
 *          - OUT void **arg_ppData - Can be used to return any other desired context
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_INTERNAL
 *
 */
ix_error _ix_cc_stkdrv_og_chk_pkt_type_filter(              
                        ix_buffer_handle              arg_hBuffer,
                        ix_uint8                      arg_pktType,
                        ix_cc_stkdrv_physical_if_node *arg_pPhysicalIf,
                        ix_cc_stkdrv_og_filter        *pFilter,
                        ix_cc_stkdrv_og_cc_type       *arg_pCcType,
                        void                          **arg_ppData
                         )
{
   void* pData;
   ix_uint32 dstIP;
   ix_uint32 classD;
   ix_error err = IX_SUCCESS;

   switch(arg_pktType)
   {
      case IX_CC_STKDRV_OG_PKT_TYPE_IPV4:
         *arg_pCcType = IX_CC_STKDRV_OG_CC_TYPE_IPV4_FWDER;

         /* Check if this is a MCAST packet */         
         IX_ERROR_CR(ix_cc_hw_get_packet_data(arg_hBuffer, &pData));
    
         /* Get destination IP address in network-byte order. */
         dstIP = IX_NTOH32(*(((ix_uint32*)pData) + 
                                  IX_CC_STKDRV_IPV4_DST_IP_OFFSET));

         classD = (dstIP >> 28);

         if(classD == 0xE)
         {
            *arg_pCcType = IX_CC_STKDRV_OG_CC_TYPE_Q_MGR;
            *arg_ppData = pData;
         }
         break;

      case IX_CC_STKDRV_OG_PKT_TYPE_IPV6:
         *arg_pCcType = IX_CC_STKDRV_OG_CC_TYPE_IPV6_FWDER;
         break;

      case IX_CC_STKDRV_OG_PKT_TYPE_UNAVAILABLE:
         err = IX_CC_ERROR_INTERNAL;
         break;

      case IX_CC_STKDRV_OG_PKT_TYPE_UNDEFINED:
      default:
         err = IX_CC_ERROR_INTERNAL;
         break;
   }
   return err;
}


/**
 * NAME: ix_cc_stkdrv_og_chk_usr_def0_filter 
 *
 * DESCRIPTION: This is the place holder function for a user defined 
 * classification. This may be used at a later point when additional 
 * classification is added, which is not based on pakcet type alone.
 * 
 * @Param:  - IN ix_buffer_handle arg_hBuffer - Handle to Buffer
 *          - IN ix_uint8 arg_pktType - Packet Type (V4 vs V6)
 *          - IN ix_cc_stkdrv_physical_if_node *arg_pPhysicalIf - Pointer to 
 *                                    physical interface
 *          - IN ix_cc_stkdrv_og_filter *pFilter - Pointer to outgoing filter
 *          - OUT ix_communication_id *arg_pCcType - Outgoing Core Component
 *          - OUT void **arg_ppData - Can be used to return any other desired context
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_INTERNAL
 *
 */
ix_error _ix_cc_stkdrv_og_chk_usr_def0_filter (              
                        ix_buffer_handle              arg_hBuffer,
                        ix_uint8                      arg_pktType,
                        ix_cc_stkdrv_physical_if_node *arg_pPhysicalIf,
                        ix_cc_stkdrv_og_filter        *pFilter,
                        ix_cc_stkdrv_og_cc_type       *arg_pCcType,
                        void                          **arg_ppData
                         )
{
   ix_error err = IX_SUCCESS;

   ix_ossl_message_log("ERROR: <ix_cc_stkdrv_physical_if_node> invoked. Currently undefined\n");

   err = IX_CC_ERROR_INTERNAL;

   return err;
}     


/*
 * TYPENAME: _ix_cc_stkdrv_check_ipv6_dip_inport_filter
 * 
 * DESCRIPTION: Check a filter by destination IPv6 and input port ID.
 *
 * @Param:  - IN ix_buffer_handle arg_hBuffer - handle to the buffer to be classified.
 * @Param:  - IN ix_cc_stkdrv_filter* arg_pFilter - pointer to the filter to be checked.
 * @Param:  - OUT ix_uint32* arg_pFoundMatch - pointer to integer indicating whether
 *          a filter matched.
 *
 * @Return: IX_SUCCESS
 *          IX_CC_ERROR_INTERNAL
 *
 */
ix_error _ix_cc_stkdrv_check_ipv6_dip_inport_filter(
                                          ix_buffer_handle arg_hBuffer,
                                          ix_cc_stkdrv_filter* arg_pFilter,
                                          ix_uint32* arg_pFoundMatch
                                                    )
{
    ix_hw_buffer_meta *pMeta;
    void              *pData;
    ix_uint8          hdrType;
    ix_uint16         portID;
    ix_uint128        dstIp;
    ix_uint32         aAddr[4];
    ix_uint8          gtSt;
    ix_uint8          lsSt;


    /* Get meta-data */
    IX_ERROR_CRT(ix_rm_buffer_get_meta(arg_hBuffer,(void**)&pMeta),
        IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);

    /* 
     * Get hdr type from the packet. If this is not IPv6 pkt, 
     * return from here itself  
     */
    hdrType = IX_RM_HW_META_GET_HEADER_TYPE(pMeta->m_BufferInfo);
    //if(!(hdrType & ETHER_IPV6_TYPE)) chk the value
    if(!(hdrType & IPV4_TYPE))
    {
        *arg_pFoundMatch = 0;
        return IX_SUCCESS;
    }

    /* Get port ID from the packet. */
    portID = IX_NTOH16(IX_RM_MEM_UINT16_READ(&pMeta->m_InputPort));

    /* Get destination IP address from the packet. */
    IX_ERROR_CRT(ix_cc_hw_get_packet_data(arg_hBuffer,&pData),
        IX_CC_ERROR_INTERNAL, IX_ERROR_LEVEL_WARNING);

    /* Get V6 address here into a array */
    aAddr[0] = IX_NTOH32(*(((ix_uint32*)pData) + IX_CC_STKDRV_IPV6_DST_IP_OFFSET));
    aAddr[1] = IX_NTOH32(*(((ix_uint32*)pData) + (IX_CC_STKDRV_IPV6_DST_IP_OFFSET + 1)));
    aAddr[2] = IX_NTOH32(*(((ix_uint32*)pData) + (IX_CC_STKDRV_IPV6_DST_IP_OFFSET + 2)));
    aAddr[3] = IX_NTOH32(*(((ix_uint32*)pData) + (IX_CC_STKDRV_IPV6_DST_IP_OFFSET + 3)));
    IX_128_BIT_BINARY_OPERATION(dstIp, 32_BIT_ASSIGN_TO, aAddr);
    
    /* Classify by destination IP */
    if(arg_pFilter->v6FilterCriteria.flag & IX_CC_STKDRV_FILTER_DIP_CLASS)
    {
        _ix_128_bit_chk_grtr_than(
                &(arg_pFilter->v6FilterCriteria.dstAddrs.start), 
                &(dstIp), &(gtSt));


        _ix_128_bit_chk_less_than(
                &(arg_pFilter->v6FilterCriteria.dstAddrs.end), 
                &(dstIp), &(lsSt));

        if( (gtSt) || (lsSt))
        {
            /* Filter did not match. */
            *arg_pFoundMatch = 0;
            return IX_SUCCESS;
        }
    }
    
    /* Classify by input port ID. */
    if(arg_pFilter->v6FilterCriteria.flag & IX_CC_STKDRV_FILTER_INPORT_CLASS)
    {
        if((arg_pFilter->v6FilterCriteria.inPorts.start > portID) ||
            (arg_pFilter->v6FilterCriteria.inPorts.end < portID))
        {
            /* Filter did not match. */
            *arg_pFoundMatch = 0;
            return IX_SUCCESS;
        }
    }

    /* Found a match. */
    *arg_pFoundMatch = 1;

    return IX_SUCCESS;
}

/*
 * TYPENAME: _ix_128_bit_chk_grtr_than
 * 
 * DESCRIPTION: Check if arg1 is greater than arg2 for 128 bit arguments
 *
 * @Param:  - IN ix_uint128 *arg1 - Argument to be checked if greater
 * @Param:  - IN ix_uint128 *arg2 - Argument with which comparison to be made
 * @Param:  - OUT ix_uint8 *arg_status - Status of comparison
 *
 * @Return: None
 *
 */
void _ix_128_bit_chk_grtr_than(ix_uint128 *arg1, ix_uint128 *arg2, ix_uint8 *arg_status)
{
    *arg_status = 0;
    if(arg1->aUint32[0] > arg2->aUint32[0])
    {
      *arg_status = 1;
    }
    else if(arg1->aUint32[0] == arg2->aUint32[0])
    {
       if(arg1->aUint32[1] > arg2->aUint32[1])
       {
          *arg_status = 1;
       }
       else if(arg1->aUint32[1] == arg2->aUint32[1])
       {
          if(arg1->aUint32[2] > arg2->aUint32[2])
          {
             *arg_status = 1;
          }
          else if(arg1->aUint32[2] == arg2->aUint32[2])
          {
             if(arg1->aUint32[3] > arg2->aUint32[3])
                 *arg_status = 1;
          }
       }
    }
    return;
}


/*
 * TYPENAME: _ix_128_bit_chk_less_than
 * 
 * DESCRIPTION: Check if arg1 is less than arg2 for 128 bit arguments
 *
 * @Param:  - IN ix_uint128 *arg1 - Argument to be checked if lesser
 * @Param:  - IN ix_uint128 *arg2 - Argument with which comparison to be made
 * @Param:  - OUT ix_uint8 *arg_status - Status of comparison
 *
 * @Return: None
 *
 */
void _ix_128_bit_chk_less_than(ix_uint128 *arg1, ix_uint128 *arg2, ix_uint8 *arg_status)
{
    *arg_status = 0;
    if(arg1->aUint32[0] < arg2->aUint32[0])
    {
      *arg_status = 1;
    }
    else if(arg1->aUint32[0] == arg2->aUint32[0])
    {
       if(arg1->aUint32[1] < arg2->aUint32[1])
       {
          *arg_status = 1;
       }
       else if(arg1->aUint32[1] == arg2->aUint32[1])
       {
          if(arg1->aUint32[2] < arg2->aUint32[2])
          {
             *arg_status = 1;
          }
          else if(arg1->aUint32[2] == arg2->aUint32[2])
          {
             if(arg1->aUint32[3] < arg2->aUint32[3])
                 *arg_status = 1;
          }
       }
    }
    return;
}

#endif /* _IX_OS_LINUX_KERNEL_ */

#endif /* defined(IX_CC_STKDRV_PKT_CLASS) */
