/*++

Copyright (c) 1999 Microsoft Corporation


Module Name:

    txspecific.c

Abstract:

    This module contains all of the code to drive the
    specific transport filter list management of IPSecSPD
    Service.

Author:

    abhisheV    29-October-1999

Environment

    User Level: Win32

Revision History:


--*/


#include "precomp.h"


DWORD
ApplyTxTransform(
    PINITXFILTER pFilter,
    MATCHING_ADDR * pMatchingAddresses,
    DWORD dwAddrCnt,
    PINITXSFILTER * ppSpecificFilters
    )
/*++

Routine Description:

    This function expands a generic transport filter into its
    corresponding specific filters.

Arguments:

    pFilter - Generic filter to expand.

    pMatchingAddresses - List of local ip addresses whose interface
                         type matches that of the filter.

    dwAddrCnt - Number of local ip addresses in the list.

    ppSpecificFilters - List of specific filters expanded for the
                        given generic filter.

Return Value:

    ERROR_SUCCESS - Success.

    Win32 Error - Failure.

--*/
{
    DWORD dwError = 0;
    PINITXSFILTER pSpecificFilters = NULL;
    PINITXSFILTER pOutboundSpecificFilters = NULL;
    PINITXSFILTER pInboundSpecificFilters = NULL;

    PADDR pOutSrcAddrList = NULL;
    DWORD dwOutSrcAddrCnt = 0;
    PADDR pInSrcAddrList = NULL;
    DWORD dwInSrcAddrCnt = 0;

    PADDR pOutDesAddrList = NULL;
    DWORD dwOutDesAddrCnt = 0;
    PADDR pInDesAddrList = NULL;
    DWORD dwInDesAddrCnt = 0;


    // 
    // Form the outbound and inbound source and destination
    // address lists.
    // 

    dwError = FormTxOutboundInboundAddresses(
                  pFilter,
                  pMatchingAddresses,
                  dwAddrCnt,
                  &pOutSrcAddrList,
                  &dwOutSrcAddrCnt,
                  &pInSrcAddrList,
                  &dwInSrcAddrCnt,
                  &pOutDesAddrList,
                  &dwOutDesAddrCnt,
                  &pInDesAddrList,
                  &dwInDesAddrCnt
                  );
    BAIL_ON_WIN32_ERROR(dwError);


    //
    // Form outbound specific filters.
    //

    dwError = FormSpecificTxFilters(
                  pFilter,
                  pOutSrcAddrList,
                  dwOutSrcAddrCnt,
                  pOutDesAddrList,
                  dwOutDesAddrCnt,
                  FILTER_DIRECTION_OUTBOUND,
                  &pOutboundSpecificFilters
                  );
    BAIL_ON_WIN32_ERROR(dwError);


    //
    // Form inbound specific filters.
    //

    dwError = FormSpecificTxFilters(
                  pFilter,
                  pInSrcAddrList,
                  dwInSrcAddrCnt,
                  pInDesAddrList,
                  dwInDesAddrCnt,
                  FILTER_DIRECTION_INBOUND,
                  &pInboundSpecificFilters
                  );
    BAIL_ON_WIN32_ERROR(dwError);


    pSpecificFilters = pOutboundSpecificFilters;

    AddToSpecificTxList(
        &pSpecificFilters,
        pInboundSpecificFilters
        );


    *ppSpecificFilters = pSpecificFilters;

cleanup:

    if (pOutSrcAddrList) {
        FreeSPDMemory(pOutSrcAddrList);
    }

    if (pInSrcAddrList) {
        FreeSPDMemory(pInSrcAddrList);
    }

    if (pOutDesAddrList) {
        FreeSPDMemory(pOutDesAddrList);
    }

    if (pInDesAddrList) {
        FreeSPDMemory(pInDesAddrList);
    }

    return (dwError);

error:

    if (pOutboundSpecificFilters) {
        FreeIniTxSFilterList(pOutboundSpecificFilters);
    }

    if (pInboundSpecificFilters) {
        FreeIniTxSFilterList(pInboundSpecificFilters);
    }


    *ppSpecificFilters = NULL;
    goto cleanup;
}


DWORD
FormTxOutboundInboundAddresses(
    PINITXFILTER pFilter,
    MATCHING_ADDR * pMatchingAddresses,
    DWORD dwAddrCnt,
    PADDR * ppOutSrcAddrList,
    PDWORD pdwOutSrcAddrCnt,
    PADDR * ppInSrcAddrList,
    PDWORD pdwInSrcAddrCnt,
    PADDR * ppOutDesAddrList,
    PDWORD pdwOutDesAddrCnt,
    PADDR * ppInDesAddrList,
    PDWORD pdwInDesAddrCnt
    )
/*++

Routine Description:

    This function forms the outbound and inbound source and
    destination address sets for a generic filter.

Arguments:

    pFilter - Generic filter under consideration.

    pMatchingAddresses - List of local ip addresses whose interface
                         type matches that of the filter.

    dwAddrCnt - Number of local ip addresses in the list.

    ppOutSrcAddrList - List of outbound source addresses.

    pdwOutSrcAddrCnt - Number of addresses in the outbound
                       source address list.

    ppInSrcAddrList - List of inbound source addresses.

    pdwInSrcAddrCnt - Number of addresses in the inbound
                      source address list.

    ppOutDesAddrList - List of outbound destination addresses.

    pdwOutDesAddrCnt - Number of addresses in the outbound
                       destination address list.

    ppInDesAddrList - List of inbound destination addresses.

    pdwInDesAddrCnt - Number of addresses in the inbound
                      destination address list.

Return Value:

    ERROR_SUCCESS - Success.

    Win32 Error - Failure.

--*/
{
    DWORD dwError = 0;

    PADDR pSrcAddrList = NULL;
    DWORD dwSrcAddrCnt = 0;
    PADDR pDesAddrList = NULL;
    DWORD dwDesAddrCnt = 0;

    PADDR pOutSrcAddrList = NULL;
    DWORD dwOutSrcAddrCnt = 0;
    PADDR pInSrcAddrList = NULL;
    DWORD dwInSrcAddrCnt = 0;

    PADDR pOutDesAddrList = NULL;
    DWORD dwOutDesAddrCnt = 0;
    PADDR pInDesAddrList = NULL;
    DWORD dwInDesAddrCnt = 0;


    //
    // Replace wild card information to generate the new source
    // address list.
    //

    dwError = FormAddressList(
                  pFilter->SrcAddr,
                  pMatchingAddresses,
                  dwAddrCnt,
                  &pSrcAddrList,
                  &dwSrcAddrCnt
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    //
    // Replace wild card information to generate the new destination
    // address list.
    //

    dwError = FormAddressList(
                  pFilter->DesAddr,
                  pMatchingAddresses,
                  dwAddrCnt,
                  &pDesAddrList,
                  &dwDesAddrCnt
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    //
    // Separate the source address list into outbound and inbound 
    // source address sets based on the local machine's ip addresses.
    //

    dwError = SeparateAddrList(
                  pFilter->SrcAddr.AddrType,
                  pSrcAddrList,
                  dwSrcAddrCnt,
                  pMatchingAddresses,
                  dwAddrCnt,
                  &pOutSrcAddrList,
                  &dwOutSrcAddrCnt,
                  &pInSrcAddrList,
                  &dwInSrcAddrCnt
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    //
    // Separate the destination address list into outbound and inbound
    // destination address sets based on the local machine's ip 
    // addresses.
    //

    dwError = SeparateAddrList(
                  pFilter->DesAddr.AddrType,
                  pDesAddrList,
                  dwDesAddrCnt,
                  pMatchingAddresses,
                  dwAddrCnt,
                  &pInDesAddrList,
                  &dwInDesAddrCnt,
                  &pOutDesAddrList,
                  &dwOutDesAddrCnt
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    *ppOutSrcAddrList = pOutSrcAddrList;
    *pdwOutSrcAddrCnt = dwOutSrcAddrCnt;
    *ppInSrcAddrList = pInSrcAddrList;
    *pdwInSrcAddrCnt = dwInSrcAddrCnt;

    *ppOutDesAddrList = pOutDesAddrList;
    *pdwOutDesAddrCnt = dwOutDesAddrCnt;
    *ppInDesAddrList = pInDesAddrList;
    *pdwInDesAddrCnt = dwInDesAddrCnt;

cleanup:

    if (pSrcAddrList) {
        FreeSPDMemory(pSrcAddrList);
    }

    if (pDesAddrList) {
        FreeSPDMemory(pDesAddrList);
    }

    return (dwError);

error:

    if (pOutSrcAddrList) {
        FreeSPDMemory(pOutSrcAddrList);
    }

    if (pInSrcAddrList) {
        FreeSPDMemory(pInSrcAddrList);
    }

    if (pOutDesAddrList) {
        FreeSPDMemory(pOutDesAddrList);
    }

    if (pInDesAddrList) {
        FreeSPDMemory(pInDesAddrList);
    }

    *ppOutSrcAddrList = NULL;
    *pdwOutSrcAddrCnt = 0;
    *ppInSrcAddrList = NULL;
    *pdwInSrcAddrCnt = 0;

    *ppOutDesAddrList = NULL;
    *pdwOutDesAddrCnt = 0;
    *ppInDesAddrList = NULL;
    *pdwInDesAddrCnt = 0;

    goto cleanup;
}


DWORD
FormAddressList(
    ADDR InAddr,
    MATCHING_ADDR * pMatchingAddresses,
    DWORD dwAddrCnt,
    PADDR * ppOutAddr,
    PDWORD pdwOutAddrCnt
    )
/*++

Routine Description:

    This function forms the address list for a generic
    address.

Arguments:

    InAddr - Generic address to expand.

    pMatchingAddresses - List of local ip addresses whose interface
                         type matches that of the filter.

    dwAddrCnt - Number of local ip addresses in the list.

    ppOutAddr - Expanded address list for the generic address.

    pdwOutAddrCnt - Number of addresses in the expanded list.

Return Value:

    ERROR_SUCCESS - Success.

    Win32 Error - Failure.

--*/
{
    DWORD dwError = 0;
    PADDR pOutAddr = NULL;
    DWORD dwOutAddrCnt = 0;
    DWORD i = 0, j = 0;

    switch(InAddr.AddrType) {

    case IP_ADDR_UNIQUE:

        if (InAddr.uIpAddr == IP_ADDRESS_ME) {

            dwError = AllocateSPDMemory(
                          sizeof(ADDR) * dwAddrCnt,
                          &pOutAddr
                          );
            BAIL_ON_WIN32_ERROR(dwError);

            for (i = 0; i < dwAddrCnt; i++) {
                pOutAddr[i].AddrType = InAddr.AddrType;
                pOutAddr[i].uIpAddr = pMatchingAddresses[i].uIpAddr;
                pOutAddr[i].uSubNetMask = InAddr.uSubNetMask;
                memcpy(
                    &pOutAddr[i].gInterfaceID,
                    &InAddr.gInterfaceID,
                    sizeof(GUID)
                    );
            }
            dwOutAddrCnt = dwAddrCnt;

        }
        else {

            dwError = AllocateSPDMemory(
                          sizeof(ADDR),
                          &pOutAddr
                          );
            BAIL_ON_WIN32_ERROR(dwError);

            memcpy(pOutAddr, &InAddr, sizeof(ADDR));
            dwOutAddrCnt = 1;

        }

        break;

    case IP_ADDR_SUBNET:

        dwError = AllocateSPDMemory(
                      sizeof(ADDR),
                      &pOutAddr
                      );
        BAIL_ON_WIN32_ERROR(dwError);

        memcpy(pOutAddr, &InAddr, sizeof(ADDR));
        dwOutAddrCnt = 1;

        break;

    case IP_ADDR_INTERFACE:

        for (i = 0; i < dwAddrCnt; i++) {
            if (!memcmp(
                    &pMatchingAddresses[i].gInterfaceID,
                    &InAddr.gInterfaceID,
                    sizeof(GUID))) {
                dwOutAddrCnt++;
            } 
        }

        if (dwOutAddrCnt) {

            dwError = AllocateSPDMemory(
                          sizeof(ADDR) * dwOutAddrCnt,
                          &pOutAddr
                          );
            BAIL_ON_WIN32_ERROR(dwError);

            for (i = 0; i < dwAddrCnt; i++) {

                if (!memcmp(
                    &pMatchingAddresses[i].gInterfaceID,
                    &InAddr.gInterfaceID,
                    sizeof(GUID))) {
                    pOutAddr[j].AddrType = InAddr.AddrType;
                    pOutAddr[j].uIpAddr = pMatchingAddresses[i].uIpAddr;
                    pOutAddr[j].uSubNetMask = InAddr.uSubNetMask;
                    memcpy(
                        &pOutAddr[j].gInterfaceID,
                        &InAddr.gInterfaceID,
                        sizeof(GUID)
                        );
                    j++;
                }

            }

        }

        break; 

    }

    *ppOutAddr = pOutAddr;
    *pdwOutAddrCnt = dwOutAddrCnt;
    return (dwError);

error:

    *ppOutAddr = NULL;
    *pdwOutAddrCnt = 0;
    return (dwError);
}


DWORD
SeparateAddrList(
    ADDR_TYPE AddrType,
    PADDR pAddrList,
    DWORD dwAddrCnt,
    MATCHING_ADDR * pMatchingAddresses,
    DWORD dwLocalAddrCnt,
    PADDR * ppOutAddrList,
    PDWORD pdwOutAddrCnt,
    PADDR * ppInAddrList,
    PDWORD pdwInAddrCnt
    )
/*++

Routine Description:

    This function separates the address list into
    two mutually exclusive outbound and inbound
    address sets.

Arguments:

    AddrType - Type of address under consideration.

    pAddrList - List of addresses to separate.

    dwAddrCnt - Number of addresses in the list.

    pMatchingAddresses - List of local ip addresses whose interface
                         type matches that of the filter.

    dwLocalAddrCnt - Number of local ip addresses in the list.

    ppOutAddrList - List of outbound addresses.

    pdwOutAddrCnt - Number of addresses in the outbound
                    address list.

    ppInAddrList - List of inbound addresses.

    pdwInAddrCnt - Number of addresses in the inbound
                   address list.

Return Value:

    ERROR_SUCCESS - Success.

    Win32 Error - Failure.

--*/
{
    DWORD dwError = 0;

    switch(AddrType) {

    case IP_ADDR_UNIQUE:

        dwError = SeparateUniqueAddresses(
                      pAddrList,
                      dwAddrCnt,
                      pMatchingAddresses,
                      dwLocalAddrCnt,
                      ppOutAddrList,
                      pdwOutAddrCnt,
                      ppInAddrList,
                      pdwInAddrCnt
                      );
        BAIL_ON_WIN32_ERROR(dwError);
        break;

    case IP_ADDR_SUBNET:

        dwError = SeparateSubNetAddresses(
                      pAddrList,
                      dwAddrCnt,
                      pMatchingAddresses,
                      dwLocalAddrCnt,
                      ppOutAddrList,
                      pdwOutAddrCnt,
                      ppInAddrList,
                      pdwInAddrCnt
                      );
        BAIL_ON_WIN32_ERROR(dwError);
        break;

    case IP_ADDR_INTERFACE:

        dwError = SeparateInterfaceAddresses(
                      pAddrList,
                      dwAddrCnt,
                      pMatchingAddresses,
                      dwLocalAddrCnt,
                      ppOutAddrList,
                      pdwOutAddrCnt,
                      ppInAddrList,
                      pdwInAddrCnt
                      );
         BAIL_ON_WIN32_ERROR(dwError);
         break;

    }

error:

    return (dwError);
}

    
DWORD
FormSpecificTxFilters(
    PINITXFILTER pFilter,
    PADDR pSrcAddrList,
    DWORD dwSrcAddrCnt,
    PADDR pDesAddrList,
    DWORD dwDesAddrCnt,
    DWORD dwDirection,
    PINITXSFILTER * ppSpecificFilters
    )
/*++

Routine Description:

    This function forms the specific transport filters
    for the given generic filter and the source and 
    destination address sets.

Arguments:

    pFilter - Generic filter for which specific filters
              are to be created.

    pSrcAddrList - List of source addresses.

    dwSrcAddrCnt - Number of addresses in the source
                   address list.

    pDesAddrList - List of destination addresses.

    dwDesAddrCnt - Number of addresses in the destination
                   address list.

    ppSpecificFilters - Specific filters created for the given
                        generic filter and the given addresses.

Return Value:

    ERROR_SUCCESS - Success.

    Win32 Error - Failure.

--*/
{
    DWORD dwError = 0;
    PINITXSFILTER pSpecificFilters = NULL;
    DWORD i = 0, j = 0;
    PINITXSFILTER pSpecificFilter = NULL;



    for (i = 0; i < dwSrcAddrCnt; i++) {

        for (j = 0; j < dwDesAddrCnt; j++) {

            dwError = CreateSpecificTxFilter(
                          pFilter,
                          pSrcAddrList[i],
                          pDesAddrList[j],
                          &pSpecificFilter
                          );
            BAIL_ON_WIN32_ERROR(dwError);

            //
            // Set the direction of the filter.
            //

            pSpecificFilter->dwDirection = dwDirection;

            AssignTxFilterWeight(pSpecificFilter);

            AddToSpecificTxList(
                &pSpecificFilters,
                pSpecificFilter
                );

        }

    }

    *ppSpecificFilters = pSpecificFilters;
    return (dwError);

error:

    if (pSpecificFilters) {
        FreeIniTxSFilterList(pSpecificFilters);
    }

    *ppSpecificFilters = NULL;
    return (dwError);
}


DWORD
SeparateUniqueAddresses(
    PADDR pAddrList,
    DWORD dwAddrCnt,
    MATCHING_ADDR * pMatchingAddresses,
    DWORD dwLocalAddrCnt,
    PADDR * ppIsMeAddrList,
    PDWORD pdwIsMeAddrCnt,
    PADDR * ppIsNotMeAddrList,
    PDWORD pdwIsNotMeAddrCnt
    )
/*++

Routine Description:

    This function separates a list of unique ip addresses into
    two mutually exclusive local and non-local address sets.

Arguments:

    pAddrList - List of unique ip addresses to separate.

    dwAddrCnt - Number of unique ip addresses in the list.

    pMatchingAddresses - List of local ip addresses whose interface
                         type matches that of the filter.

    dwAddrCnt - Number of local ip addresses in the list.

    ppIsMeAddrList - List of machine's ip addresses separated from
                     the given list.

    pdwIsMeAddrCnt - Number of machine's ip addresses in the list.

    ppIsNotMeAddrList - List of not machine's ip addresses separated from
                        the given list.

    pdwIsNotMeAddrCnt - Number of not machine's ip addresses in the list.

Return Value:

    ERROR_SUCCESS - Success.

    Win32 Error - Failure.

--*/
{
    DWORD dwError = 0;
    PADDR pIsMeAddrList = NULL;
    DWORD dwIsMeAddrCnt = 0;
    PADDR pIsNotMeAddrList = NULL;
    DWORD dwIsNotMeAddrCnt = 0;
    DWORD i = 0, j = 0, k = 0;
    BOOL bEqual = FALSE;
    BOOL bIsClassD = FALSE;


    for (i = 0; i < dwAddrCnt; i++) {

        bIsClassD = IN_CLASSD(ntohl(pAddrList[i].uIpAddr));

        switch (bIsClassD) {

        case TRUE:

            dwIsMeAddrCnt++;
            dwIsNotMeAddrCnt++;

            break;

        case FALSE:

            //
            // Check if the address is one of the matching interfaces' address.
            //

            bEqual = InterfaceAddrIsLocal(
                         pAddrList[i].uIpAddr,
                         pAddrList[i].uSubNetMask,
                         pMatchingAddresses,
                         dwLocalAddrCnt
                         );
            if (bEqual) {
                dwIsMeAddrCnt++;
            }
            else {
                //
                // Check if the address is one of the machine's ip address.
                //
                bEqual = IsMyAddress(
                             pAddrList[i].uIpAddr,
                             pAddrList[i].uSubNetMask,
                             gpInterfaceList
                             );
                if (!bEqual) {
                    dwIsNotMeAddrCnt++;
                }
            }

            break;

        }

    }

    if (dwIsMeAddrCnt) {
        dwError = AllocateSPDMemory(
                      sizeof(ADDR) * dwIsMeAddrCnt,
                      &pIsMeAddrList
                      );
        BAIL_ON_WIN32_ERROR(dwError);
    }

    if (dwIsNotMeAddrCnt) {
        dwError = AllocateSPDMemory(
                      sizeof(ADDR) * dwIsNotMeAddrCnt,
                      &pIsNotMeAddrList
                      );
        BAIL_ON_WIN32_ERROR(dwError);
    }

    for (i = 0; i < dwAddrCnt; i++) {

        bIsClassD = IN_CLASSD(ntohl(pAddrList[i].uIpAddr));

        switch (bIsClassD) {

        case TRUE:

            memcpy(
                &(pIsMeAddrList[j]),
                &(pAddrList[i]),
                sizeof(ADDR)
                );
            j++;

            memcpy(
                &pIsNotMeAddrList[k],
                &pAddrList[i],
                sizeof(ADDR)
                );
            k++;

            break;

        case FALSE:

            bEqual = InterfaceAddrIsLocal(
                         pAddrList[i].uIpAddr,
                         pAddrList[i].uSubNetMask,
                         pMatchingAddresses,
                         dwLocalAddrCnt
                         );
            if (bEqual) {
                memcpy(
                    &(pIsMeAddrList[j]),
                    &(pAddrList[i]),
                    sizeof(ADDR)
                    );
                j++;
            }
            else {
                bEqual = IsMyAddress(
                             pAddrList[i].uIpAddr,
                             pAddrList[i].uSubNetMask,
                             gpInterfaceList
                             );
                if (!bEqual) {
                    memcpy(
                        &pIsNotMeAddrList[k],
                        &pAddrList[i],
                        sizeof(ADDR)
                        );
                    k++;
                }
            }

            break;

        }

    }

    *ppIsMeAddrList = pIsMeAddrList;
    *pdwIsMeAddrCnt = dwIsMeAddrCnt;
    *ppIsNotMeAddrList = pIsNotMeAddrList;
    *pdwIsNotMeAddrCnt = dwIsNotMeAddrCnt;

    return (dwError);

error:

    if (pIsMeAddrList) {
        FreeSPDMemory(pIsMeAddrList);
    }

    if (pIsNotMeAddrList) {
        FreeSPDMemory(pIsNotMeAddrList);
    }

    *ppIsMeAddrList = NULL;
    *pdwIsMeAddrCnt = 0;
    *ppIsNotMeAddrList = NULL;
    *pdwIsNotMeAddrCnt = 0;

    return (dwError);
}


DWORD
SeparateSubNetAddresses(
    PADDR pAddrList,
    DWORD dwAddrCnt,
    MATCHING_ADDR * pMatchingAddresses,
    DWORD dwLocalAddrCnt,
    PADDR * ppIsMeAddrList,
    PDWORD pdwIsMeAddrCnt,
    PADDR * ppIsNotMeAddrList,
    PDWORD pdwIsNotMeAddrCnt
    )
/*++

Routine Description:

    This function separates a list of subnet addresses into
    two non-mutually exclusive local and non-local address sets.

Arguments:

    pAddrList - List of subnet addresses to separate.

    dwAddrCnt - Number of subnet addresses in the list.

    pMatchingAddresses - List of local ip addresses whose interface
                         type matches that of the filter.

    dwAddrCnt - Number of local ip addresses in the list.

    ppIsMeAddrList - List of subnet addresses that contain atleast
                     one of the machine's ip address.

    pdwIsMeAddrCnt - Number of subnet addresses containing atleast
                     one of the machine's ip address.

    ppIsNotMeAddrList - List of subnet addresses as in the input list.

    pdwIsNotMeAddrCnt - Number of subnet addresses as in the input list.

Return Value:

    ERROR_SUCCESS - Success.

    Win32 Error - Failure.

--*/
{
    DWORD dwError = 0;
    PADDR pIsMeAddrList = NULL;
    DWORD dwIsMeAddrCnt = 0;
    PADDR pIsNotMeAddrList = NULL;
    DWORD dwIsNotMeAddrCnt = 0;
    DWORD i = 0, j = 0, k = 0;
    BOOL bEqual = FALSE;
    BOOL bIsClassD = FALSE;


    for (i = 0; i < dwAddrCnt; i++) {

        bIsClassD = IN_CLASSD(ntohl(pAddrList[i].uIpAddr));

        switch (bIsClassD) {

        case TRUE:

            dwIsMeAddrCnt++;
            break;

        case FALSE:

            //
            // Check if one of the matching interfaces' address belongs to
            // the subnet.
            //

            bEqual = InterfaceAddrIsLocal(
                         pAddrList[i].uIpAddr,
                         pAddrList[i].uSubNetMask,
                         pMatchingAddresses,
                         dwLocalAddrCnt
                         );
            if (bEqual) {
                dwIsMeAddrCnt++;
            }
            break;

        }

        //
        // The subnet will have addresses that don't belong to the local
        // machine.
        //

        dwIsNotMeAddrCnt++;

    }

    if (dwIsMeAddrCnt) {
        dwError = AllocateSPDMemory(
                      sizeof(ADDR) * dwIsMeAddrCnt,
                      &pIsMeAddrList
                      );
        BAIL_ON_WIN32_ERROR(dwError);
    }

    if (dwIsNotMeAddrCnt) {
        dwError = AllocateSPDMemory(
                      sizeof(ADDR) * dwIsNotMeAddrCnt,
                      &pIsNotMeAddrList
                      );
        BAIL_ON_WIN32_ERROR(dwError);
    }

    for (i = 0; i < dwAddrCnt; i++) {

        bIsClassD = IN_CLASSD(ntohl(pAddrList[i].uIpAddr));

        switch (bIsClassD) {

        case TRUE:

            memcpy(
                &(pIsMeAddrList[j]),
                &(pAddrList[i]),
                sizeof(ADDR)
                );
            j++;
            break;

        case FALSE:

            bEqual = InterfaceAddrIsLocal(
                         pAddrList[i].uIpAddr,
                         pAddrList[i].uSubNetMask,
                         pMatchingAddresses,
                         dwLocalAddrCnt
                         );
            if (bEqual) {
                memcpy(
                    &(pIsMeAddrList[j]),
                    &(pAddrList[i]),
                    sizeof(ADDR)
                    );
                j++;
            }
            break;

        }

        memcpy(
            &pIsNotMeAddrList[k],
            &pAddrList[i],
            sizeof(ADDR)
            );
        k++;

    }

    *ppIsMeAddrList = pIsMeAddrList;
    *pdwIsMeAddrCnt = dwIsMeAddrCnt;
    *ppIsNotMeAddrList = pIsNotMeAddrList;
    *pdwIsNotMeAddrCnt = dwIsNotMeAddrCnt;

    return (dwError);

error:

    if (pIsMeAddrList) {
        FreeSPDMemory(pIsMeAddrList);
    }

    if (pIsNotMeAddrList) {
        FreeSPDMemory(pIsNotMeAddrList);
    }

    *ppIsMeAddrList = NULL;
    *pdwIsMeAddrCnt = 0;
    *ppIsNotMeAddrList = NULL;
    *pdwIsNotMeAddrCnt = 0;

    return (dwError);
}


DWORD
CreateSpecificTxFilter(
    PINITXFILTER pGenericFilter,
    ADDR SrcAddr,
    ADDR DesAddr,
    PINITXSFILTER * ppSpecificFilter
    )
{
    DWORD dwError = 0; 
    PINITXSFILTER pSpecificFilter = NULL;


    dwError = AllocateSPDMemory(
                    sizeof(INITXSFILTER),
                    &pSpecificFilter
                    );
    BAIL_ON_WIN32_ERROR(dwError);

    pSpecificFilter->cRef = 0;

    CopyGuid(pGenericFilter->gFilterID, &(pSpecificFilter->gParentID));

    dwError = AllocateSPDString(
                  pGenericFilter->pszFilterName,
                  &(pSpecificFilter->pszFilterName)
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    pSpecificFilter->InterfaceType = pGenericFilter->InterfaceType;

    pSpecificFilter->dwFlags = pGenericFilter->dwFlags;

    CopyAddresses(SrcAddr, &(pSpecificFilter->SrcAddr));

    CopyAddresses(DesAddr, &(pSpecificFilter->DesAddr));

    CopyPorts(pGenericFilter->SrcPort, &(pSpecificFilter->SrcPort));

    CopyPorts(pGenericFilter->DesPort, &(pSpecificFilter->DesPort));

    CopyProtocols(pGenericFilter->Protocol, &(pSpecificFilter->Protocol));

    pSpecificFilter->InboundFilterFlag = pGenericFilter->InboundFilterFlag;

    pSpecificFilter->OutboundFilterFlag = pGenericFilter->OutboundFilterFlag;

    //
    // Direction must be set in the calling routine.
    //

    pSpecificFilter->dwDirection = 0;

    //
    // Weight must be set in the calling routine.
    //

    pSpecificFilter->dwWeight = 0;

    CopyGuid(pGenericFilter->gPolicyID, &(pSpecificFilter->gPolicyID));

    pSpecificFilter->pIniQMPolicy = NULL;

    pSpecificFilter->pNext = NULL;

    *ppSpecificFilter = pSpecificFilter;
    return (dwError);

error:

    if (pSpecificFilter) {
        FreeIniTxSFilter(pSpecificFilter);
    }

    *ppSpecificFilter = NULL;
    return (dwError);

}


VOID
AssignTxFilterWeight(
    PINITXSFILTER pSpecificFilter
    )
/*++

Routine Description:

    Computes and assigns the weight to a specific transport filter.

    The transport filter weight consists of the following:

    31         16       12           8        0
    +-----------+--------+-----------+--------+
    |AddrMaskWgt|Reserved|ProtocolWgt|PortWgts|
    +-----------+--------+-----------+--------+

Arguments:

    pSpecificFilter - Specific transport filter to which the weight 
                      is to be assigned.

Return Value:

    None.

--*/
{
    DWORD dwWeight = 0;
    ULONG SrcMask = 0;
    ULONG DesMask = 0;
    DWORD dwMaskWeight = 0;
    DWORD i = 0;


    //
    // Weight Rule:
    // A field with a more specific value gets a higher weight than
    // the same field with a lesser specific value.
    //

    //
    // If the protocol is specific then assign the specific protocol
    // weight else the weight is zero.
    // All the specific filters that have a specific protocol and
    // differ only in the protocol field will have the same weight.
    //

    if (pSpecificFilter->Protocol.dwProtocol != 0) {
        dwWeight |= WEIGHT_SPECIFIC_PROTOCOL;
    }

    //
    // If the source port is specific then assign the specific source
    // port weight else the weight is zero.
    // All the specific filters that have a specific source port and 
    // differ only in the source port field will have the same weight.
    //

    if (pSpecificFilter->SrcPort.wPort != 0) {
        dwWeight |= WEIGHT_SPECIFIC_SOURCE_PORT;
    }

    //
    // If the destination port is specific then assign the specific
    // destination port weight else the weight is zero. 
    // All the specific filters that have a specific destination port
    // and differ only in the destination port field will have the
    // same weight.
    //

    if (pSpecificFilter->DesPort.wPort != 0) {
        dwWeight |= WEIGHT_SPECIFIC_DESTINATION_PORT;
    }

    //
    // IP addresses get the weight values based on their mask values.
    // In the address case, the weight is computed as a sum of the 
    // bit positions starting from the position that contains the 
    // first least significant non-zero bit to the most significant
    // bit position of the mask. 
    // All unique ip addresses have a mask of 0xFFFFFFFF and thus get
    // the same weight, which is 1 + 2 + .... + 32.
    // A subnet address has a mask with atleast the least significant
    // bit zero and thus gets weight in the range (2 + .. + 32) to 0.
    //
  
    DesMask = ntohl(pSpecificFilter->DesAddr.uSubNetMask);

    for (i = 0; i < sizeof(ULONG) * 8; i++) {

         //
         // If the bit position contains a non-zero bit, add the bit
         // position to the sum.
         //

         if ((DesMask & 0x1) == 0x1) {
             dwMaskWeight += (i+1);
         }

         //
         // Move to the next bit position.
         //

         DesMask = DesMask >> 1;

    }


    SrcMask = ntohl(pSpecificFilter->SrcAddr.uSubNetMask);

    for (i = 0; i < sizeof(ULONG) * 8; i++) {

         //
         // If the bit position contains a non-zero bit, add the bit
         // position to the sum.
         //

         if ((SrcMask & 0x1) == 0x1) {
             dwMaskWeight += (i+1);
         }

         //
         // Move to the next bit position.
         //

         SrcMask = SrcMask >> 1;

    }

    //
    // Move the mask weight to the set of bits in the overall weight
    // that it occupies.
    //

    dwMaskWeight = dwMaskWeight << 16;

    dwWeight += dwMaskWeight;

    pSpecificFilter->dwWeight = dwWeight;
}


VOID
AddToSpecificTxList(
    PINITXSFILTER * ppSpecificTxFilterList,
    PINITXSFILTER pSpecificTxFilters
    )
{
    PINITXSFILTER pListOne = NULL;
    PINITXSFILTER pListTwo = NULL;
    PINITXSFILTER pListMerge = NULL;
    PINITXSFILTER pLast = NULL;

    if (!(*ppSpecificTxFilterList) && !pSpecificTxFilters) {
        return;
    }

    if (!(*ppSpecificTxFilterList)) {
        *ppSpecificTxFilterList = pSpecificTxFilters;
        return;
    }

    if (!pSpecificTxFilters) {
        return;
    }

    pListOne = *ppSpecificTxFilterList;
    pListTwo = pSpecificTxFilters;

    while (pListOne && pListTwo) {

        if ((pListOne->dwWeight) > (pListTwo->dwWeight)) {

            if (!pListMerge) {
                pListMerge = pListOne;
                pLast = pListOne;
                pListOne = pListOne->pNext;
            }
            else {
                pLast->pNext = pListOne;
                pListOne = pListOne->pNext;
                pLast = pLast->pNext;
            }

        }
        else {

            if (!pListMerge) {
                pListMerge = pListTwo;
                pLast = pListTwo;
                pListTwo = pListTwo->pNext;
            }
            else {
                pLast->pNext = pListTwo;
                pListTwo = pListTwo->pNext;
                pLast = pLast->pNext;
            }

        }

    }

    if (pListOne) {
        pLast->pNext = pListOne;
    }
    else {
        pLast->pNext = pListTwo;
    }

    *ppSpecificTxFilterList = pListMerge;
    return;
}


VOID
FreeIniTxSFilterList(
    PINITXSFILTER pIniTxSFilterList
    )
{
    PINITXSFILTER pFilter = NULL;
    PINITXSFILTER pTempFilter = NULL;

    pFilter = pIniTxSFilterList;

    while (pFilter) {
        pTempFilter = pFilter;
        pFilter = pFilter->pNext;
        FreeIniTxSFilter(pTempFilter);
    }
}


VOID
FreeIniTxSFilter(
    PINITXSFILTER pIniTxSFilter
    )
{
    if (pIniTxSFilter) {
        if (pIniTxSFilter->pszFilterName) {
            FreeSPDString(pIniTxSFilter->pszFilterName);
        }

        //
        // Must not ever free pIniTxSFilter->pIniQMPolicy.
        //

        FreeSPDMemory(pIniTxSFilter);
    }
}


VOID
LinkTxSpecificFilters(
    PINIQMPOLICY pIniQMPolicy,
    PINITXSFILTER pIniTxSFilters
    )
{
    PINITXSFILTER pTemp = NULL;

    pTemp = pIniTxSFilters;

    while (pTemp) {
        pTemp->pIniQMPolicy = pIniQMPolicy;
        pTemp = pTemp->pNext;
    }

    return;
}


VOID
RemoveIniTxSFilter(
    PINITXSFILTER pIniTxSFilter
    )
{
    PINITXSFILTER * ppTemp = NULL;

    ppTemp = &gpIniTxSFilter;

    while (*ppTemp) {

        if (*ppTemp == pIniTxSFilter) {
            break;
        }
        ppTemp = &((*ppTemp)->pNext);
    }

    if (*ppTemp) {
        *ppTemp = pIniTxSFilter->pNext;
    }

    return;
}


DWORD
EnumSpecificTxFilters(
    PINITXSFILTER pIniTxSFilterList,
    DWORD dwResumeHandle,
    DWORD dwPreferredNumEntries,
    PTRANSPORT_FILTER * ppTxFilters,
    PDWORD pdwNumTxFilters
    )
/*++

Routine Description:

    This function creates enumerated specific filters.

Arguments:

    pIniTxSFilterList - List of specific filters to enumerate.

    dwResumeHandle - Location in the specific filter list from which
                     to resume enumeration.

    dwPreferredNumEntries - Preferred number of enumeration entries.

    ppTxFilters - Enumerated filters returned to the caller.

    pdwNumTxFilters - Number of filters actually enumerated.

Return Value:

    ERROR_SUCCESS - Success.

    Win32 Error - Failure.

--*/
{
    DWORD dwError = 0;
    DWORD dwNumToEnum = 0;
    PINITXSFILTER pIniTxSFilter = NULL;
    DWORD i = 0;
    PINITXSFILTER pTemp = NULL;
    DWORD dwNumTxFilters = 0;
    PTRANSPORT_FILTER pTxFilters = 0;
    PTRANSPORT_FILTER pTxFilter = 0;


    if (!dwPreferredNumEntries || 
        (dwPreferredNumEntries > MAX_TRANSPORTFILTER_ENUM_COUNT)) {
        dwNumToEnum = MAX_TRANSPORTFILTER_ENUM_COUNT;
    }
    else {
        dwNumToEnum = dwPreferredNumEntries;
    }

    pIniTxSFilter = pIniTxSFilterList;

    for (i = 0; (i < dwResumeHandle) && (pIniTxSFilter != NULL); i++) {
        pIniTxSFilter = pIniTxSFilter->pNext;
    }

    if (!pIniTxSFilter) {
        dwError = ERROR_NO_DATA;
        BAIL_ON_WIN32_ERROR(dwError);
    }

    pTemp = pIniTxSFilter;

    while (pTemp && (dwNumTxFilters < dwNumToEnum)) {
        dwNumTxFilters++;
        pTemp = pTemp->pNext;
    }

    dwError = SPDApiBufferAllocate(
                  sizeof(TRANSPORT_FILTER)*dwNumTxFilters,
                  &pTxFilters
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    pTemp = pIniTxSFilter;
    pTxFilter = pTxFilters;

    for (i = 0; i < dwNumTxFilters; i++) {

        dwError = CopyTxSFilter(
                      pTemp,
                      pTxFilter
                      );
        BAIL_ON_WIN32_ERROR(dwError);

        pTemp = pTemp->pNext;
        pTxFilter++;

    }

    *ppTxFilters = pTxFilters;
    *pdwNumTxFilters = dwNumTxFilters;
    return (dwError);

error:

    if (pTxFilters) {
        FreeTxFilters(
            i,
            pTxFilters
            );
    }

    *ppTxFilters = NULL;
    *pdwNumTxFilters = 0;

    return (dwError);
}


DWORD
CopyTxSFilter(
    PINITXSFILTER pIniTxSFilter,
    PTRANSPORT_FILTER pTxFilter
    )
/*++

Routine Description:

    This function copies an internal filter into an external filter
    container.

Arguments:

    pIniTxSFilter - Internal filter to copy.

    pTxFilter - External filter container in which to copy.

Return Value:

    ERROR_SUCCESS - Success.

    Win32 Error - Failure.

--*/
{
    DWORD dwError = 0;

    CopyGuid(pIniTxSFilter->gParentID, &(pTxFilter->gFilterID));

    dwError = CopyName(
                  pIniTxSFilter->pszFilterName,
                  &(pTxFilter->pszFilterName)
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    pTxFilter->InterfaceType = pIniTxSFilter->InterfaceType;

    pTxFilter->bCreateMirror = FALSE;

    pTxFilter->dwFlags = pIniTxSFilter->dwFlags;

    CopyAddresses(pIniTxSFilter->SrcAddr, &(pTxFilter->SrcAddr));

    CopyAddresses(pIniTxSFilter->DesAddr, &(pTxFilter->DesAddr));

    CopyProtocols(pIniTxSFilter->Protocol, &(pTxFilter->Protocol));

    CopyPorts(pIniTxSFilter->SrcPort, &(pTxFilter->SrcPort));

    CopyPorts(pIniTxSFilter->DesPort, &(pTxFilter->DesPort));

    pTxFilter->InboundFilterFlag = pIniTxSFilter->InboundFilterFlag;

    pTxFilter->OutboundFilterFlag = pIniTxSFilter->OutboundFilterFlag;

    pTxFilter->dwDirection = pIniTxSFilter->dwDirection;

    pTxFilter->dwWeight = pIniTxSFilter->dwWeight;

    CopyGuid(pIniTxSFilter->gPolicyID, &(pTxFilter->gPolicyID));

error:

    return (dwError);
}


DWORD
EnumSelectSpecificTxFilters(
    PINITXFILTER pIniTxFilter,
    DWORD dwResumeHandle,
    DWORD dwPreferredNumEntries,
    PTRANSPORT_FILTER * ppTxFilters,
    PDWORD pdwNumTxFilters
    )
/*++

Routine Description:

    This function creates enumerated specific filters for
    the given generic filter.

Arguments:

    pIniTxFilter - Generic filter for which specific filters
                   are to be enumerated.

    dwResumeHandle - Location in the specific filter list for the
                     given generic filter from which to resume
                     enumeration.

    dwPreferredNumEntries - Preferred number of enumeration entries.

    ppTxFilters - Enumerated filters returned to the caller.

    pdwNumTxFilters - Number of filters actually enumerated.

Return Value:

    ERROR_SUCCESS - Success.

    Win32 Error - Failure.

--*/
{
    DWORD dwError = 0;
    DWORD dwNumToEnum = 0;
    DWORD dwNumTxSFilters = 0; 
    PINITXSFILTER * ppIniTxSFilters = NULL;
    DWORD i = 0;
    DWORD dwNumTxFilters = 0;
    PTRANSPORT_FILTER pTxFilters = 0;
    PTRANSPORT_FILTER pTxFilter = 0;


    if (!dwPreferredNumEntries || 
        (dwPreferredNumEntries > MAX_TRANSPORTFILTER_ENUM_COUNT)) {
        dwNumToEnum = MAX_TRANSPORTFILTER_ENUM_COUNT;
    }
    else {
        dwNumToEnum = dwPreferredNumEntries;
    }

    dwNumTxSFilters = pIniTxFilter->dwNumTxSFilters;
    ppIniTxSFilters = pIniTxFilter->ppIniTxSFilters;

    if (!dwNumTxSFilters || (dwNumTxSFilters <= dwResumeHandle)) {
        dwError = ERROR_NO_DATA;
        BAIL_ON_WIN32_ERROR(dwError);
    }

    dwNumTxFilters = min((dwNumTxSFilters-dwResumeHandle),
                         dwNumToEnum);
 
    dwError = SPDApiBufferAllocate(
                  sizeof(TRANSPORT_FILTER)*dwNumTxFilters,
                  &pTxFilters
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    pTxFilter = pTxFilters;

    for (i = 0; i < dwNumTxFilters; i++) {

        dwError = CopyTxSFilter(
                      *(ppIniTxSFilters + (dwResumeHandle + i)),
                      pTxFilter
                      );
        BAIL_ON_WIN32_ERROR(dwError);
        pTxFilter++;

    }

    *ppTxFilters = pTxFilters;
    *pdwNumTxFilters = dwNumTxFilters;
    return (dwError);

error:

    if (pTxFilters) {
        FreeTxFilters(
            i,
            pTxFilters
            );
    }

    *ppTxFilters = NULL;
    *pdwNumTxFilters = 0;

    return (dwError);
}


DWORD
MatchTransportFilter(
    LPWSTR pServerName,
    PTRANSPORT_FILTER pTxFilter,
    DWORD dwFlags,
    PTRANSPORT_FILTER * ppMatchedTxFilters,
    PIPSEC_QM_POLICY * ppMatchedQMPolicies,
    DWORD dwPreferredNumEntries,
    LPDWORD pdwNumMatches,
    LPDWORD pdwResumeHandle
    )
/*++

Routine Description:

    This function finds the matching transport filters for the 
    given transport filter template. The matched filters can not
    be more specific than the given filter template.

Arguments:

    pServerName - Server on which a filter template is to be matched.

    pTxFilter - Filter template to match.

    dwFlags - Flags.

    ppMatchedTxFilters - Matched transport filters returned to the
                         caller.

    ppMatchedQMPolicies - Quick mode policies corresponding to the 
                          matched transport filters returned to the
                          caller.

    dwPreferredNumEntries - Preferred number of matched entries.

    pdwNumMatches - Number of filters actually matched.

    pdwResumeHandle - Handle to the location in the matched filter 
                      list from which to resume enumeration.

Return Value:

    ERROR_SUCCESS - Success.

    Win32 Error - Failure.

--*/
{
    DWORD dwError = 0;
    DWORD dwResumeHandle = 0;
    DWORD dwNumToMatch = 0;
    PINITXSFILTER pIniTxSFilter = NULL;
    DWORD i = 0;
    BOOL bMatches = FALSE;
    PINITXSFILTER pTemp = NULL;
    DWORD dwNumMatches = 0;
    PINITXSFILTER pLastMatchedFilter = NULL;
    PTRANSPORT_FILTER pMatchedTxFilters = NULL;
    PIPSEC_QM_POLICY pMatchedQMPolicies = NULL;
    DWORD dwNumFilters = 0;
    DWORD dwNumPolicies = 0;
    PTRANSPORT_FILTER pMatchedTxFilter = NULL;
    PIPSEC_QM_POLICY pMatchedQMPolicy = NULL;


    dwError = ValidateTxFilterTemplate(
                  pTxFilter
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    dwResumeHandle = *pdwResumeHandle;

    if (!dwPreferredNumEntries) {
        dwNumToMatch = 1;
    }
    else if (dwPreferredNumEntries > MAX_TRANSPORTFILTER_ENUM_COUNT) {
        dwNumToMatch = MAX_TRANSPORTFILTER_ENUM_COUNT;
    }
    else {
        dwNumToMatch = dwPreferredNumEntries;
    }

    ENTER_SPD_SECTION();

    dwError = ValidateTxSecurity(
                  SPD_OBJECT_SERVER,
                  SERVER_ACCESS_ADMINISTER,
                  NULL,
                  NULL
                  );
    BAIL_ON_LOCK_ERROR(dwError);

    pIniTxSFilter = gpIniTxSFilter;

    while ((i < dwResumeHandle) && (pIniTxSFilter != NULL)) {
        bMatches = MatchIniTxSFilter(
                       pIniTxSFilter,
                       pTxFilter
                       );
        if (bMatches) {
            i++;
        }
        pIniTxSFilter = pIniTxSFilter->pNext;
    }

    if (!pIniTxSFilter) {
        if (!(dwFlags & RETURN_DEFAULTS_ON_NO_MATCH)) {
            dwError = ERROR_NO_DATA;
            BAIL_ON_LOCK_ERROR(dwError);
        }
        else {
            dwError = CopyTxMatchDefaults(
                          &pMatchedTxFilters,
                          &pMatchedQMPolicies,
                          &dwNumMatches
                          );
            BAIL_ON_LOCK_ERROR(dwError);
            BAIL_ON_LOCK_SUCCESS(dwError);
        }
    }

    pTemp = pIniTxSFilter;

    while (pTemp && (dwNumMatches < dwNumToMatch)) {
        bMatches = MatchIniTxSFilter(
                       pTemp,
                       pTxFilter
                       );
        if (bMatches) {
            pLastMatchedFilter = pTemp;
            dwNumMatches++;
        }
        pTemp = pTemp->pNext;
    }

    if (!dwNumMatches) {
        if (!(dwFlags & RETURN_DEFAULTS_ON_NO_MATCH)) {
            dwError = ERROR_NO_DATA;
            BAIL_ON_LOCK_ERROR(dwError);
        }
        else {
            dwError = CopyTxMatchDefaults(
                          &pMatchedTxFilters,
                          &pMatchedQMPolicies,
                          &dwNumMatches
                          );
            BAIL_ON_LOCK_ERROR(dwError);
            BAIL_ON_LOCK_SUCCESS(dwError);
        }
    }

    dwError = SPDApiBufferAllocate(
                  sizeof(TRANSPORT_FILTER)*dwNumMatches,
                  &pMatchedTxFilters
                  );
    BAIL_ON_LOCK_ERROR(dwError);

    dwError = SPDApiBufferAllocate(
                  sizeof(IPSEC_QM_POLICY)*dwNumMatches,
                  &pMatchedQMPolicies
                  );
    BAIL_ON_LOCK_ERROR(dwError);


    if (dwNumMatches == 1) {

        dwError = CopyTxSFilter(
                      pLastMatchedFilter,
                      pMatchedTxFilters
                      );
        BAIL_ON_LOCK_ERROR(dwError);
        dwNumFilters++;

        if (pLastMatchedFilter->pIniQMPolicy) {
            dwError = CopyQMPolicy(
                          pLastMatchedFilter->pIniQMPolicy,
                          pMatchedQMPolicies
                          );
            BAIL_ON_LOCK_ERROR(dwError);
        }
        else {
            memset(pMatchedQMPolicies, 0, sizeof(IPSEC_QM_POLICY));
        }
        dwNumPolicies++;

    }
    else {

        pTemp = pIniTxSFilter;
        pMatchedTxFilter = pMatchedTxFilters;
        pMatchedQMPolicy = pMatchedQMPolicies;
        i = 0;

        while (i < dwNumMatches) {

            bMatches = MatchIniTxSFilter(
                           pTemp,
                           pTxFilter
                           );
            if (bMatches) {

                dwError = CopyTxSFilter(
                              pTemp,
                              pMatchedTxFilter
                              );
                BAIL_ON_LOCK_ERROR(dwError);
                pMatchedTxFilter++;
                dwNumFilters++;

                if (pTemp->pIniQMPolicy) {
                    dwError = CopyQMPolicy(
                                  pTemp->pIniQMPolicy,
                                  pMatchedQMPolicy
                                  );
                    BAIL_ON_LOCK_ERROR(dwError);
                }
                else {
                    memset(pMatchedQMPolicy, 0, sizeof(IPSEC_QM_POLICY));
                }
                pMatchedQMPolicy++;
                dwNumPolicies++;

                i++;

            }

            pTemp = pTemp->pNext;

        }

    }

lock_success:

    LEAVE_SPD_SECTION();

    *ppMatchedTxFilters = pMatchedTxFilters;
    *ppMatchedQMPolicies = pMatchedQMPolicies;
    *pdwNumMatches = dwNumMatches;
    *pdwResumeHandle = dwResumeHandle + dwNumMatches;

    return (dwError);

lock:

    LEAVE_SPD_SECTION();

error:

    if (pMatchedTxFilters) {
        FreeTxFilters(
            dwNumFilters,
            pMatchedTxFilters
            );
    }

    if (pMatchedQMPolicies) {
        FreeQMPolicies(
            dwNumPolicies,
            pMatchedQMPolicies
            );
    }

    *ppMatchedTxFilters = NULL;
    *ppMatchedQMPolicies = NULL;
    *pdwNumMatches = 0;
    *pdwResumeHandle = dwResumeHandle;

    return (dwError);
}


DWORD
ValidateTxFilterTemplate(
    PTRANSPORT_FILTER pTxFilter
    )
{
    DWORD dwError = 0;
    BOOL bConflicts = FALSE;


    if (!pTxFilter) {
        dwError = ERROR_INVALID_PARAMETER;
        BAIL_ON_WIN32_ERROR(dwError);
    }

    dwError = VerifyAddresses(pTxFilter->SrcAddr, TRUE, FALSE);
    BAIL_ON_WIN32_ERROR(dwError);

    dwError = VerifyAddresses(pTxFilter->DesAddr, TRUE, TRUE);
    BAIL_ON_WIN32_ERROR(dwError);

    bConflicts = AddressesConflict(
                     pTxFilter->SrcAddr,
                     pTxFilter->DesAddr
                     );
    if (bConflicts) {
        dwError = ERROR_INVALID_PARAMETER;
        BAIL_ON_WIN32_ERROR(dwError);
    }

    dwError = VerifyProtocols(pTxFilter->Protocol);
    BAIL_ON_WIN32_ERROR(dwError);

    dwError = VerifyPortsForProtocol(
                  pTxFilter->SrcPort,
                  pTxFilter->Protocol
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    dwError = VerifyPortsForProtocol(
                  pTxFilter->DesPort,
                  pTxFilter->Protocol
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    if (pTxFilter->dwDirection) {
        if ((pTxFilter->dwDirection != FILTER_DIRECTION_INBOUND) &&
            (pTxFilter->dwDirection != FILTER_DIRECTION_OUTBOUND)) {
            dwError = ERROR_INVALID_PARAMETER;
            BAIL_ON_WIN32_ERROR(dwError);
        }
    }

error:

    return (dwError);
}


BOOL
MatchIniTxSFilter(
    PINITXSFILTER pIniTxSFilter,
    PTRANSPORT_FILTER pTxFilter
    )
{
    BOOL bMatches = FALSE;

    if (pTxFilter->dwDirection) {
        if (pTxFilter->dwDirection != pIniTxSFilter->dwDirection) {
            return (FALSE);
        }
    }

    if ((pIniTxSFilter->InboundFilterFlag != NEGOTIATE_SECURITY) &&
        (pIniTxSFilter->OutboundFilterFlag != NEGOTIATE_SECURITY)) {
        return (FALSE);
    }

    bMatches = MatchAddresses(
                   pIniTxSFilter->SrcAddr,
                   pTxFilter->SrcAddr
                   );
    if (!bMatches) {
        return (FALSE);
    }

    bMatches = MatchAddresses(
                   pIniTxSFilter->DesAddr,
                   pTxFilter->DesAddr
                   );
    if (!bMatches) {
        return (FALSE);
    }

    bMatches = MatchPorts(
                   pIniTxSFilter->SrcPort,
                   pTxFilter->SrcPort
                   );
    if (!bMatches) {
        return (FALSE);
    }

    bMatches = MatchPorts(
                   pIniTxSFilter->DesPort,
                   pTxFilter->DesPort
                   );
    if (!bMatches) {
        return (FALSE);
    }

    bMatches = MatchProtocols(
                   pIniTxSFilter->Protocol,
                   pTxFilter->Protocol
                   );
    if (!bMatches) {
        return (FALSE);
    }

    return (TRUE);
}


DWORD
CopyTxMatchDefaults(
    PTRANSPORT_FILTER * ppTxFilters,
    PIPSEC_QM_POLICY * ppQMPolicies,
    PDWORD pdwNumMatches
    )
{
    DWORD dwError = 0;
    PTRANSPORT_FILTER pTxFilters = NULL;
    PIPSEC_QM_POLICY pQMPolicies = NULL;
    DWORD dwNumFilters = 0;
    DWORD dwNumPolicies = 0;


    if (!gpIniDefaultQMPolicy) {
        dwError = ERROR_IPSEC_DEFAULT_QM_POLICY_NOT_FOUND;
        BAIL_ON_WIN32_ERROR(dwError);
    }

    dwError = SPDApiBufferAllocate(
                  sizeof(TRANSPORT_FILTER),
                  &pTxFilters
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    dwError = SPDApiBufferAllocate(
                  sizeof(IPSEC_QM_POLICY),
                  &pQMPolicies
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    dwError = CopyDefaultTxFilter(
                  pTxFilters,
                  gpIniDefaultQMPolicy
                  );
    BAIL_ON_WIN32_ERROR(dwError);
    dwNumFilters++;

    dwError = CopyQMPolicy(
                  gpIniDefaultQMPolicy,
                  pQMPolicies
                  );
    BAIL_ON_WIN32_ERROR(dwError);
    pQMPolicies->dwFlags |= IPSEC_QM_POLICY_ON_NO_MATCH;
    dwNumPolicies++;

    *ppTxFilters = pTxFilters;
    *ppQMPolicies = pQMPolicies;
    *pdwNumMatches = 1;

    return (dwError);

error:

    if (pTxFilters) {
        FreeTxFilters(
            dwNumFilters,
            pTxFilters
            );
    }

    if (pQMPolicies) {
        FreeQMPolicies(
            dwNumPolicies,
            pQMPolicies
            );
    }

    *ppTxFilters = NULL;
    *ppQMPolicies = NULL;
    *pdwNumMatches = 0;

    return (dwError);
}


DWORD
CopyDefaultTxFilter(
    PTRANSPORT_FILTER pTxFilter,
    PINIQMPOLICY pIniQMPolicy
    )
{
    DWORD dwError = 0;


    UuidCreate(&(pTxFilter->gFilterID));

    dwError = CopyName(
                  L"0",
                  &(pTxFilter->pszFilterName)
                  );
    BAIL_ON_WIN32_ERROR(dwError);

    pTxFilter->InterfaceType = INTERFACE_TYPE_ALL;

    pTxFilter->bCreateMirror = TRUE;

    pTxFilter->dwFlags = 0;
    pTxFilter->dwFlags |= IPSEC_QM_POLICY_DEFAULT_POLICY;

    pTxFilter->SrcAddr.AddrType = IP_ADDR_SUBNET;
    pTxFilter->SrcAddr.uIpAddr = SUBNET_ADDRESS_ANY;
    pTxFilter->SrcAddr.uSubNetMask = SUBNET_MASK_ANY;

    pTxFilter->DesAddr.AddrType = IP_ADDR_SUBNET;
    pTxFilter->DesAddr.uIpAddr = SUBNET_ADDRESS_ANY;
    pTxFilter->DesAddr.uSubNetMask = SUBNET_MASK_ANY;

    pTxFilter->Protocol.ProtocolType = PROTOCOL_UNIQUE;
    pTxFilter->Protocol.dwProtocol = 0;

    pTxFilter->SrcPort.PortType = PORT_UNIQUE;
    pTxFilter->SrcPort.wPort = 0;

    pTxFilter->DesPort.PortType = PORT_UNIQUE;
    pTxFilter->DesPort.wPort = 0;

    pTxFilter->InboundFilterFlag = NEGOTIATE_SECURITY;

    pTxFilter->OutboundFilterFlag = NEGOTIATE_SECURITY;

    pTxFilter->dwDirection = 0;

    pTxFilter->dwWeight = 0;

    CopyGuid(pIniQMPolicy->gPolicyID, &(pTxFilter->gPolicyID));

error:

    return (dwError);
}


DWORD
SeparateInterfaceAddresses(
    PADDR pAddrList,
    DWORD dwAddrCnt,
    MATCHING_ADDR * pMatchingAddresses,
    DWORD dwLocalAddrCnt,
    PADDR * ppIsMeAddrList,
    PDWORD pdwIsMeAddrCnt,
    PADDR * ppIsNotMeAddrList,
    PDWORD pdwIsNotMeAddrCnt
    )
{
    DWORD dwError = 0;
    PADDR pIsMeAddrList = NULL;
    DWORD i = 0;


    if (dwAddrCnt) {
        dwError = AllocateSPDMemory(
                      sizeof(ADDR) * dwAddrCnt,
                      &pIsMeAddrList
                      );
        BAIL_ON_WIN32_ERROR(dwError);
    }

    for (i = 0; i < dwAddrCnt; i++) {

        memcpy(
            &(pIsMeAddrList[i]),
            &(pAddrList[i]),
            sizeof(ADDR)
            );

    }

    *ppIsMeAddrList = pIsMeAddrList;
    *pdwIsMeAddrCnt = dwAddrCnt;
    *ppIsNotMeAddrList = NULL;
    *pdwIsNotMeAddrCnt = 0;

    return (dwError);

error:

    if (pIsMeAddrList) {
        FreeSPDMemory(pIsMeAddrList);
    }

    *ppIsMeAddrList = NULL;
    *pdwIsMeAddrCnt = 0;
    *ppIsNotMeAddrList = NULL;
    *pdwIsNotMeAddrCnt = 0;

    return (dwError);
}