/********************************************************************/
/**                     Microsoft LAN Manager                      **/
/**               Copyright(c) Microsoft Corp., 1990-1993          **/
/********************************************************************/
/* :ts=4 */

//** SECFLTR.C - Security Filter Support
//
//
// Security filters provide a mechanism by which the transport protocol
// traffic accepted on IP interfaces may be controlled. Security filtering
// is globally enabled or disabled for all IP interfaces and transports.
// If filtering is enabled, incoming traffic is filtered based on registered
// {interface, protocol, transport value} tuples. The tuples specify
// permissible traffic. All other values will be rejected. For UDP datagrams
// and TCP connections, the transport value is the port number. For RawIP
// datagrams, the transport value is the IP protocol number. An entry exists
// in the filter database for all active interfaces and protocols in the
// system.
//
// The initial status of security filtering - enabled or disabled, is
// controlled by the registry parameter
//
//         Services\Tcpip\Parameters\EnableSecurityFilters
//
// If the parameter is not found, filtering is disabled.
//
// The list of permissible values for each protocol is stored in the registry
// under the <Adaptername>\Parameters\Tcpip key in MULTI_SZ parameters.
// The parameter names are TCPAllowedPorts, UDPAllowedPorts and
// RawIPAllowedProtocols. If no parameter is found for a particular protocol,
// all values are permissible. If a parameter is found, the string identifies
// the permissible values. If the string is empty, no values are permissible.
//
// Filter Operation (Filtering Enabled):
//
//     IF ( Match(interface, protocol) AND ( AllValuesPermitted(Protocol) OR
//                                       Match(Value) ))
//     THEN operation permitted.
//     ELSE operation rejected.
//
// Database Implementation:
//
// The filter database is implemented as a three-level structure. The top
// level is a list of interface entries. Each interface entry points to
// a list of protocol entries. Each protocol entry contains a bucket hash
// table used to store transport value entries.
//

// The following calls may be used to access the security filter database:
//
//     InitializeSecurityFilters
//     CleanupSecurityFilters
//     IsSecurityFilteringEnabled
//     ControlSecurityFiltering
//     AddProtocolSecurityFilter
//     DeleteProtocolSecurityFilter
//     AddValueSecurityFilter
//     DeleteValueSecurityFilter
//     EnumerateSecurityFilters
//     IsPermittedSecurityFilter
//

#include "precomp.h"

#include "addr.h"
#include "tlcommon.h"
#include "udp.h"
#include "tcp.h"
#include "raw.h"
#include "tcpcfg.h"
#include "tcpinfo.h"
#include "secfltr.h"

//
// All of the init code can be discarded.
//

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, InitializeSecurityFilters)
#endif

//
// The following routines must be supplied by each platform which implements
// security filters.
//
extern TDI_STATUS
 GetSecurityFilterList(
                       IN NDIS_HANDLE ConfigHandle,
                       IN ulong Protocol,
                       IN OUT PNDIS_STRING FilterList
                       );

extern uint
 EnumSecurityFilterValue(
                         IN PNDIS_STRING FilterList,
                         IN ulong Index,
                         OUT ulong * FilterValue
                         );

//
// Constants
//

#define DHCP_CLIENT_PORT 68

//
// Modification Opcodes
//
#define ADD_VALUE_SECURITY_FILTER     0
#define DELETE_VALUE_SECURITY_FILTER  1

//
// Types
//

//
// Structure for a transport value entry.
//
struct value_entry {
    struct Queue ve_link;
    ulong ve_value;
};

typedef struct value_entry VALUE_ENTRY, *PVALUE_ENTRY;

#define VALUE_ENTRY_HASH_SIZE  16
#define VALUE_ENTRY_HASH(value)   (value % VALUE_ENTRY_HASH_SIZE)

//
// Structure for a protocol entry.
//
struct protocol_entry {
    struct Queue pe_link;
    ulong pe_protocol;
    ULONG pe_accept_all;        // TRUE if all values are accepted.

    struct Queue pe_entries[VALUE_ENTRY_HASH_SIZE];
};

typedef struct protocol_entry PROTOCOL_ENTRY, *PPROTOCOL_ENTRY;

//
// Structure for an interface entry.
//
struct interface_entry {
    struct Queue ie_link;
    IPAddr ie_address;
    struct Queue ie_protocol_list;    // list of protocols to filter

};

typedef struct interface_entry INTERFACE_ENTRY, *PINTERFACE_ENTRY;

//
// Global Data
//

//
// This list of interface entries is the root of the filter database.
//
struct Queue InterfaceEntryList;

//
// The filter operations are synchronized using the AddrObjTableLock.
//
extern IPInfo LocalNetInfo;

//
// Filter Database Helper Functions
//

//* FindInterfaceEntry - Search for an interface entry.
//
//  This utility routine searches the security filter database
//  for the specified interface entry.
//
//
//  Input:  InterfaceAddress  - The address of the interface to search for.
//
//
//  Returns: A pointer to the database entry for the Interface,
//               or NULL if no match was found.
//
//
PINTERFACE_ENTRY
FindInterfaceEntry(ULONG InterfaceAddress)
{
    PINTERFACE_ENTRY ientry;
    struct Queue *qentry;

    for (qentry = InterfaceEntryList.q_next;
         qentry != &InterfaceEntryList;
         qentry = qentry->q_next
         ) {
        ientry = STRUCT_OF(INTERFACE_ENTRY, qentry, ie_link);

        if (ientry->ie_address == InterfaceAddress) {
            return (ientry);
        }
    }

    return (NULL);
}

//* FindProtocolEntry - Search for a protocol associated with an interface.
//
//  This utility routine searches the security filter database
//  for the specified protocol registered under the specified interface.
//
//
//  Input:  InterfaceEntry    - A pointer to an interface entry to search under.
//          Protocol          - The protocol value to search for.
//
//
//  Returns: A pointer to the database entry for the <Address, Protocol>,
//               or NULL if no match was found.
//
//
PPROTOCOL_ENTRY
FindProtocolEntry(PINTERFACE_ENTRY InterfaceEntry, ULONG Protocol)
{
    PPROTOCOL_ENTRY pentry;
    struct Queue *qentry;

    for (qentry = InterfaceEntry->ie_protocol_list.q_next;
         qentry != &(InterfaceEntry->ie_protocol_list);
         qentry = qentry->q_next
         ) {
        pentry = STRUCT_OF(PROTOCOL_ENTRY, qentry, pe_link);

        if (pentry->pe_protocol == Protocol) {
            return (pentry);
        }
    }

    return (NULL);
}

//* FindValueEntry - Search for a value on a particular protocol.
//
//  This utility routine searches the security filter database
//  for the specified value registered under the specified protocol.
//
//
//  Input:  ProtocolEntry   - A pointer to the database structure for the
//                                Protocol to search.
//          FilterValue     - The value to search for.
//
//
//  Returns: A pointer to the database entry for the <Protocol, Value>,
//               or NULL if no match was found.
//
//
PVALUE_ENTRY
FindValueEntry(PPROTOCOL_ENTRY ProtocolEntry, ULONG FilterValue)
{
    PVALUE_ENTRY ventry;
    ulong hash_value = VALUE_ENTRY_HASH(FilterValue);
    struct Queue *qentry;

    for (qentry = ProtocolEntry->pe_entries[hash_value].q_next;
         qentry != &(ProtocolEntry->pe_entries[hash_value]);
         qentry = qentry->q_next
         ) {
        ventry = STRUCT_OF(VALUE_ENTRY, qentry, ve_link);

        if (ventry->ve_value == FilterValue) {
            return (ventry);
        }
    }

    return (NULL);
}

//* DeleteProtocolValueEntries
//
//  This utility routine deletes all the value entries associated with
//  a protocol filter entry.
//
//
//  Input:  ProtocolEntry - The protocol filter entry for which to
//                          delete the value entries.
//
//
//  Returns: Nothing
//
void
DeleteProtocolValueEntries(PPROTOCOL_ENTRY ProtocolEntry)
{
    ulong i;
    PVALUE_ENTRY entry;

    for (i = 0; i < VALUE_ENTRY_HASH_SIZE; i++) {
        while (!EMPTYQ(&(ProtocolEntry->pe_entries[i]))) {

            DEQUEUE(&(ProtocolEntry->pe_entries[i]), entry, VALUE_ENTRY, ve_link);
            CTEFreeMem(entry);
        }
    }

    return;
}

//* ModifyProtocolEntry
//
//  This utility routine modifies one or more filter values associated
//  with a protocol.
//
//
//  Input:  Operation     - The operation to perform (add or delete)
//
//          ProtocolEntry - A pointer to the protocol entry structure on
//                              which to operate.
//
//          FilterValue   - The value to add or delete.
//
//
//  Returns: TDI_STATUS code
//
TDI_STATUS
ModifyProtocolEntry(ulong Operation, PPROTOCOL_ENTRY ProtocolEntry,
                    ulong FilterValue)
{
    TDI_STATUS status = TDI_SUCCESS;

    if (FilterValue == 0) {
        if (Operation == ADD_VALUE_SECURITY_FILTER) {
            //
            // Accept all values for the protocol
            //
            ProtocolEntry->pe_accept_all = TRUE;
        } else {
            //
            // Reject all values for the protocol
            //
            ProtocolEntry->pe_accept_all = FALSE;
        }

        DeleteProtocolValueEntries(ProtocolEntry);
    } else {
        PVALUE_ENTRY ventry;
        ulong hash_value;

        //
        // This request modifies an individual entry.
        //
        ventry = FindValueEntry(ProtocolEntry, FilterValue);

        if (Operation == ADD_VALUE_SECURITY_FILTER) {

            if (ventry == NULL) {

                ventry = CTEAllocMem(sizeof(VALUE_ENTRY));

                if (ventry != NULL) {
                    ventry->ve_value = FilterValue;
                    hash_value = VALUE_ENTRY_HASH(FilterValue);

                    ENQUEUE(&(ProtocolEntry->pe_entries[hash_value]),
                            &(ventry->ve_link));

                    ProtocolEntry->pe_accept_all = FALSE;
                } else {
                    status = TDI_NO_RESOURCES;
                }
            }
        } else {
            if (ventry != NULL) {
                REMOVEQ(&(ventry->ve_link));
                CTEFreeMem(ventry);
            }
        }
    }

    return (status);
}

//* ModifyInterfaceEntry
//
//  This utility routine modifies the value entries of one or more protocol
//  entries associated with an interface.
//
//
//  Input:  Operation     - The operation to perform (add or delete)
//
//          ProtocolEntry - A pointer to the interface entry structure on
//                              which to operate.
//
//          Protocol      - The protocol on which to operate.
//
//          FilterValue   - The value to add or delete.
//
//
//  Returns: TDI_STATUS code
//
TDI_STATUS
ModifyInterfaceEntry(ulong Operation, PINTERFACE_ENTRY InterfaceEntry,
                     ulong Protocol, ulong FilterValue)
{
    PPROTOCOL_ENTRY pentry;
    TDI_STATUS status;
    TDI_STATUS returnStatus = TDI_SUCCESS;

    if (Protocol == 0) {
        struct Queue *qentry;

        //
        // Modify all protocols on the interface
        //
        for (qentry = InterfaceEntry->ie_protocol_list.q_next;
             qentry != &(InterfaceEntry->ie_protocol_list);
             qentry = qentry->q_next
             ) {
            pentry = STRUCT_OF(PROTOCOL_ENTRY, qentry, pe_link);
            status = ModifyProtocolEntry(Operation, pentry, FilterValue);

            if (status != TDI_SUCCESS) {
                returnStatus = status;
            }
        }
    } else {
        //
        // Modify a specific protocol on the interface
        //
        pentry = FindProtocolEntry(InterfaceEntry, Protocol);

        if (pentry != NULL) {
            returnStatus = ModifyProtocolEntry(Operation, pentry, FilterValue);
        } else {
            returnStatus = TDI_INVALID_PARAMETER;
        }
    }

    return (returnStatus);
}

//* ModifySecurityFilter - Add or delete an entry.
//
//  This routine adds or deletes an entry to/from the security filter database.
//
//
//  Input:  Operation         - The operation to perform (Add or Delete)
//          InterfaceAddress  - The interface address to modify.
//          Protocol          - The protocol to modify.
//          FilterValue       - The transport value to add/delete.
//
//  Returns: A TDI status code:
//               TDI_INVALID_PARAMETER if the protocol is not in the database.
//               TDI_ADDR_INVALID if the interface is not in the database.
//               TDI_NO_RESOURCES if memory could not be allocated.
//               TDI_SUCCESS otherwise
//
//  NOTES:
//
TDI_STATUS
ModifySecurityFilter(ulong Operation, IPAddr InterfaceAddress, ulong Protocol,
                     ulong FilterValue)
{
    PINTERFACE_ENTRY ientry;
    TDI_STATUS status;
    TDI_STATUS returnStatus = TDI_SUCCESS;

    if (InterfaceAddress == 0) {
        struct Queue *qentry;

        //
        // Modify on all interfaces
        //
        for (qentry = InterfaceEntryList.q_next;
             qentry != &InterfaceEntryList;
             qentry = qentry->q_next
             ) {
            ientry = STRUCT_OF(INTERFACE_ENTRY, qentry, ie_link);
            status = ModifyInterfaceEntry(Operation, ientry, Protocol,
                                          FilterValue);

            if (status != TDI_SUCCESS) {
                returnStatus = status;
            }
        }
    } else {
        ientry = FindInterfaceEntry(InterfaceAddress);

        if (ientry != NULL) {
            returnStatus = ModifyInterfaceEntry(Operation, ientry, Protocol,
                                                FilterValue);
        } else {
            returnStatus = TDI_ADDR_INVALID;
        }
    }

    return (returnStatus);
}

//* FillInEnumerationEntry
//
//  This utility routine fills in an enumeration entry for a particular
//  filter value entry.
//
//
//  Input:  InterfaceAddress  - The address of the associated interface.
//
//          Protocol          - The associated protocol number.
//
//          Value             - The enumerated value.
//
//          Buffer            - Pointer to the user's enumeration buffer.
//
//          BufferSize        - Pointer to the size of the enumeration buffer.
//
//          EntriesReturned   - Pointer to a running count of enumerated
//                              entries stored in Buffer.
//
//          EntriesAvailable  - Pointer to a running count of entries available
//                              for enumeration.
//
//  Returns: Nothing.
//
//  Note: Values written to enumeration entry are in host byte order.
//
void
FillInEnumerationEntry(IPAddr InterfaceAddress, ulong Protocol, ulong Value,
                       uchar ** Buffer, ulong * BufferSize,
                       ulong * EntriesReturned, ulong * EntriesAvailable)
{
    TCPSecurityFilterEntry *entry = (TCPSecurityFilterEntry *) * Buffer;

    if (*BufferSize >= sizeof(TCPSecurityFilterEntry)) {
        entry->tsf_address = net_long(InterfaceAddress);
        entry->tsf_protocol = Protocol;
        entry->tsf_value = Value;

        *Buffer += sizeof(TCPSecurityFilterEntry);
        *BufferSize -= sizeof(TCPSecurityFilterEntry);
        (*EntriesReturned)++;
    }
    (*EntriesAvailable)++;

    return;
}

//* EnumerateProtocolValues
//
//  This utility routine enumerates values associated with a
//  protocol on an interface.
//
//
//  Input:  InterfaceEntry    - Pointer to the associated interface entry.
//
//          ProtocolEntry     - Pointer to the protocol being enumerated.
//
//          Value             - The value to enumerate.
//
//          Buffer            - Pointer to the user's enumeration buffer.
//
//          BufferSize        - Pointer to the size of the enumeration buffer.
//
//          EntriesReturned   - Pointer to a running count of enumerated
//                              entries stored in Buffer.
//
//          EntriesAvailable  - Pointer to a running count of entries available
//                              for enumeration.
//
//  Returns: Nothing.
//
void
EnumerateProtocolValues(PINTERFACE_ENTRY InterfaceEntry,
                        PPROTOCOL_ENTRY ProtocolEntry, ulong Value,
                        uchar ** Buffer, ulong * BufferSize,
                        ulong * EntriesReturned, ulong * EntriesAvailable)
{
    struct Queue *qentry;
    TDI_STATUS status = TDI_SUCCESS;
    PVALUE_ENTRY ventry;
    ulong i;

    if (Value == 0) {
        //
        // Enumerate all values
        //
        if (ProtocolEntry->pe_accept_all == TRUE) {
            //
            // All values permitted.
            //
            FillInEnumerationEntry(
                                   InterfaceEntry->ie_address,
                                   ProtocolEntry->pe_protocol,
                                   0,
                                   Buffer,
                                   BufferSize,
                                   EntriesReturned,
                                   EntriesAvailable
                                   );
        } else {
            for (i = 0; i < VALUE_ENTRY_HASH_SIZE; i++) {
                for (qentry = ProtocolEntry->pe_entries[i].q_next;
                     qentry != &(ProtocolEntry->pe_entries[i]);
                     qentry = qentry->q_next
                     ) {
                    ventry = STRUCT_OF(VALUE_ENTRY, qentry, ve_link);

                    FillInEnumerationEntry(
                                           InterfaceEntry->ie_address,
                                           ProtocolEntry->pe_protocol,
                                           ventry->ve_value,
                                           Buffer,
                                           BufferSize,
                                           EntriesReturned,
                                           EntriesAvailable
                                           );
                }
            }
        }
    } else {
        //
        // Enumerate a specific value, if it is registered.
        //
        ventry = FindValueEntry(ProtocolEntry, Value);

        if (ventry != NULL) {
            FillInEnumerationEntry(
                                   InterfaceEntry->ie_address,
                                   ProtocolEntry->pe_protocol,
                                   ventry->ve_value,
                                   Buffer,
                                   BufferSize,
                                   EntriesReturned,
                                   EntriesAvailable
                                   );
        }
    }

    return;
}

//* EnumerateInterfaceProtocols
//
//  This utility routine enumerates protocols associated with
//  an interface.
//
//
//  Input:  InterfaceEntry    - Pointer to the associated interface entry.
//
//          Protocol          - Protocol number to enumerate.
//
//          Value             - The filter value to enumerate.
//
//          Buffer            - Pointer to the user's enumeration buffer.
//
//          BufferSize        - Pointer to the size of the enumeration buffer.
//
//          EntriesReturned   - Pointer to a running count of enumerated
//                              entries stored in Buffer.
//
//          EntriesAvailable  - Pointer to a running count of entries available
//                              for enumeration.
//
//  Returns: Nothing.
//
void
EnumerateInterfaceProtocols(PINTERFACE_ENTRY InterfaceEntry, ulong Protocol,
                            ulong Value, uchar ** Buffer, ulong * BufferSize,
                            ulong * EntriesReturned, ulong * EntriesAvailable)
{
    PPROTOCOL_ENTRY pentry;

    if (Protocol == 0) {
        struct Queue *qentry;

        //
        // Enumerate all protocols.
        //
        for (qentry = InterfaceEntry->ie_protocol_list.q_next;
             qentry != &(InterfaceEntry->ie_protocol_list);
             qentry = qentry->q_next
             ) {
            pentry = STRUCT_OF(PROTOCOL_ENTRY, qentry, pe_link);

            EnumerateProtocolValues(
                                    InterfaceEntry,
                                    pentry,
                                    Value,
                                    Buffer,
                                    BufferSize,
                                    EntriesReturned,
                                    EntriesAvailable
                                    );
        }
    } else {
        //
        // Enumerate a specific protocol
        //

        pentry = FindProtocolEntry(InterfaceEntry, Protocol);

        if (pentry != NULL) {
            EnumerateProtocolValues(
                                    InterfaceEntry,
                                    pentry,
                                    Value,
                                    Buffer,
                                    BufferSize,
                                    EntriesReturned,
                                    EntriesAvailable
                                    );
        }
    }

    return;
}

//
// Filter Database Public API.
//

//* InitializeSecurityFilters - Initializes the security filter database.
//
//  The routine performs the initialization necessary to enable the
//  security filter database for operation.
//
//  Input:  None.
//
//  Returns: Nothing.
//
//
void
InitializeSecurityFilters(void)
{
    INITQ(&InterfaceEntryList);

    return;
}

//* CleanupSecurityFilters - Deletes the entire security filter database.
//
//  This routine deletes all entries from the security filter database.
//
//
//  Input:  None.
//
//  Returns: Nothing.
//
//  NOTE: This routine acquires the AddrObjTableLock.
//
//
void
CleanupSecurityFilters(void)
{
    PPROTOCOL_ENTRY pentry;
    PINTERFACE_ENTRY ientry;
    CTELockHandle handle;

    CTEGetLock(&AddrObjTableLock.Lock, &handle);

    while (!EMPTYQ(&InterfaceEntryList)) {
        DEQUEUE(&InterfaceEntryList, ientry, INTERFACE_ENTRY, ie_link);

        while (!EMPTYQ(&(ientry->ie_protocol_list))) {
            DEQUEUE(&(ientry->ie_protocol_list), pentry, PROTOCOL_ENTRY,
                    pe_link);

            DeleteProtocolValueEntries(pentry);

            CTEFreeMem(pentry);
        }

        CTEFreeMem(ientry);
    }

    SecurityFilteringEnabled = FALSE;

    CTEFreeLock(&AddrObjTableLock.Lock, handle);

    return;
}

//* IsSecurityFilteringEnabled
//
//  This routine returns the current global status of security filtering.
//
//  Entry:  Nothing
//
//  Returns: 0 if filtering is disabled, !0 if filtering is enabled.
//
extern uint
IsSecurityFilteringEnabled(void)
{
    uint enabled;
    CTELockHandle handle;

    CTEGetLock(&AddrObjTableLock.Lock, &handle);

    enabled = SecurityFilteringEnabled;

    CTEFreeLock(&AddrObjTableLock.Lock, handle);

    return (enabled);
}

//* ControlSecurityFiltering
//
//  This routine globally enables/disables security filtering.
//
//  Entry:  IsEnabled  - 0 disabled filtering, !0 enables filtering.
//
//  Returns: Nothing
//
extern void
ControlSecurityFiltering(uint IsEnabled)
{
    CTELockHandle handle;

    CTEGetLock(&AddrObjTableLock.Lock, &handle);

    if (IsEnabled) {
        SecurityFilteringEnabled = TRUE;
    } else {
        SecurityFilteringEnabled = FALSE;
    }

    CTEFreeLock(&AddrObjTableLock.Lock, handle);

    return;
}

//* AddProtocolSecurityFilter
//
//  This routine enables security filtering for a specified protocol
//  on a specified IP interface.
//
//  Entry:  InterfaceAddress - The interface on which to enable the protocol.
//                                 (in network byte order)
//          Protocol         - The protocol to enable.
//          ConfigName       - The configuration key from which to read
//                                 the filter value information.
//
//  Returns: Nothing
//
void
AddProtocolSecurityFilter(IPAddr InterfaceAddress, ulong Protocol,
                          NDIS_HANDLE ConfigHandle)
{
    NDIS_STRING filterList;
    ulong filterValue;
    ulong i;
    PINTERFACE_ENTRY ientry;
    PPROTOCOL_ENTRY pentry;
    PVOID temp;
    CTELockHandle handle;
    TDI_STATUS status;

    if (IP_ADDR_EQUAL(InterfaceAddress, NULL_IP_ADDR) ||
        IP_LOOPBACK_ADDR(InterfaceAddress)
        ) {
        return;
    }
    ASSERT((Protocol != 0) && (Protocol <= 0xFF));

    //
    // Read the protocol-specific filter value list from the registry.
    //
    filterList.MaximumLength = filterList.Length = 0;
    filterList.Buffer = NULL;

    if (ConfigHandle != NULL) {
        status = GetSecurityFilterList(ConfigHandle, Protocol, &filterList);
    }
    //
    // Preallocate interface & protocol structures. We abort on failure.
    // The interface & protocol will be protected by default.
    //
    ientry = CTEAllocMem(sizeof(INTERFACE_ENTRY));

    if (ientry == NULL) {
        return;
    }
    ientry->ie_address = InterfaceAddress;
    INITQ(&(ientry->ie_protocol_list));

    pentry = CTEAllocMem(sizeof(PROTOCOL_ENTRY));

    if (pentry == NULL) {
        CTEFreeMem(ientry);
        return;
    }
    pentry->pe_protocol = Protocol;
    pentry->pe_accept_all = FALSE;

    for (i = 0; i < VALUE_ENTRY_HASH_SIZE; i++) {
        INITQ(&(pentry->pe_entries[i]));
    }

    //
    // Now go set everything up. First create the interface and protocol
    // structures.
    //
    CTEGetLock(&AddrObjTableLock.Lock, &handle);

    temp = FindInterfaceEntry(InterfaceAddress);

    if (temp == NULL) {
        //
        // New interface & protocol.
        //
        ENQUEUE(&InterfaceEntryList, &(ientry->ie_link));
        ENQUEUE(&(ientry->ie_protocol_list), &(pentry->pe_link));
    } else {
        //
        // Existing interface
        //
        CTEFreeMem(ientry);
        ientry = temp;

        temp = FindProtocolEntry(ientry, Protocol);

        if (temp == NULL) {
            //
            // New protocol
            //
            ENQUEUE(&(ientry->ie_protocol_list), &(pentry->pe_link));
        } else {
            //
            // Existing protocol
            //
            CTEFreeMem(pentry);
        }
    }

    CTEFreeLock(&AddrObjTableLock.Lock, handle);

    //
    // At this point, the protocol entry is installed, but no values
    // are permitted. This is the safest default.
    //

    if (ConfigHandle != NULL) {
        //
        // Process the filter value list.
        //
        if (status == TDI_SUCCESS) {
            for (i = 0;
                 EnumSecurityFilterValue(&filterList, i, &filterValue);
                 i++
                 ) {
                AddValueSecurityFilter(InterfaceAddress, Protocol,
                                       filterValue);
            }
        } else if (status == TDI_ITEM_NOT_FOUND) {
            //
            // No filter registered, permit everything.
            //
            AddValueSecurityFilter(InterfaceAddress, Protocol, 0);
        }
    }
    if (filterList.Buffer != NULL) {
        CTEFreeMem(filterList.Buffer);
    }
    return;
}

//* DeleteProtocolSecurityFilter
//
//  This routine disables security filtering for a specified protocol
//  on a specified IP interface.
//
//  Entry:  InterfaceAddress - The interface on which to disable the protocol.
//                                 (in network byte order)
//          Protocol         - The protocol to disable.
//
//  Returns: Nothing
//
void
DeleteProtocolSecurityFilter(IPAddr InterfaceAddress, ulong Protocol)
{
    PINTERFACE_ENTRY ientry;
    PPROTOCOL_ENTRY pentry;
    CTELockHandle handle;
    BOOLEAN deleteInterface = FALSE;

    CTEGetLock(&AddrObjTableLock.Lock, &handle);

    ientry = FindInterfaceEntry(InterfaceAddress);

    if (ientry != NULL) {

        ASSERT(!EMPTYQ(&(ientry->ie_protocol_list)));

        pentry = FindProtocolEntry(ientry, Protocol);

        if (pentry != NULL) {
            REMOVEQ(&(pentry->pe_link));
        }
        if (EMPTYQ(&(ientry->ie_protocol_list))) {
            //
            // Last protocol, delete interface as well.
            //
            REMOVEQ(&(ientry->ie_link));
            deleteInterface = TRUE;
        }
        CTEFreeLock(&AddrObjTableLock.Lock, handle);

        if (pentry != NULL) {
            DeleteProtocolValueEntries(pentry);
            CTEFreeMem(pentry);
        }
        if (deleteInterface) {
            ASSERT(EMPTYQ(&(ientry->ie_protocol_list)));
            CTEFreeMem(ientry);
        }
    } else {
        CTEFreeLock(&AddrObjTableLock.Lock, handle);
    }

    return;
}

//* AddValueSecurityFilter - Add an entry.
//
//  This routine adds a value entry for a specified protocol on a specified
//  interface in the security filter database.
//
//
//  Input:  InterfaceAddress  - The interface address to which to add.
//                                  (in network byte order)
//          Protocol          - The protocol to which to add.
//          FilterValue       - The transport value to add.
//                                  (in host byte order)
//
//  Returns: A TDI status code:
//               TDI_INVALID_PARAMETER if the protocol is not in the database.
//               TDI_ADDR_INVALID if the interface is not in the database.
//               TDI_NO_RESOURCES if memory could not be allocated.
//               TDI_SUCCESS otherwise
//
//  NOTES:
//
//      This routine acquires AddrObjTableLock.
//
//      Zero is a wildcard value. Supplying a zero value for the
//      InterfaceAddress and/or Protocol causes the operation to be applied
//      to all interfaces and/or protocols, as appropriate. Supplying a
//      non-zero value causes the operation to be applied to only the
//      specified interface and/or protocol. Supplying a FilterValue parameter
//      of zero causes all values to be acceptable. Any previously
//      registered values are deleted from the database.
//
TDI_STATUS
AddValueSecurityFilter(IPAddr InterfaceAddress, ulong Protocol, ulong FilterValue)
{
    CTELockHandle handle;
    TDI_STATUS status;

    CTEGetLock(&AddrObjTableLock.Lock, &handle);

    status = ModifySecurityFilter(ADD_VALUE_SECURITY_FILTER, InterfaceAddress,
                                  Protocol, FilterValue);

    CTEFreeLock(&AddrObjTableLock.Lock, handle);

    return (status);
}

//* DeleteValueSecurityFilter - Delete an entry.
//
//  This routine deletes a value entry for a specified protocol on a specified
//  interface in the security filter database.
//
//
//  Input:  InterfaceAddress  - The interface address from which to delete.
//                                  (in network byte order)
//          Protocol          - The protocol from which to delete.
//          FilterValue       - The transport value to delete.
//                                  (in host byte order)
//
//  Returns: A TDI status code:
//               TDI_INVALID_PARAMETER if the protocol is not in the database.
//               TDI_ADDR_INVALID if the interface is not in the database.
//               TDI_NO_RESOURCES if memory could not be allocated.
//               TDI_SUCCESS otherwise
//
//  NOTES:
//
//      This routine acquires AddrObjTableLock.
//
//      Zero is a wildcard value. Supplying a zero value for the
//      InterfaceAddress and/or Protocol causes the operation to be applied
//      to all interfaces and/or protocols, as appropriate. Supplying a
//      non-zero value causes the operation to be applied to only the
//      specified interface and/or protocol. Supplying a FilterValue parameter
//      of zero causes all values to be rejected. Any previously
//      registered values are deleted from the database.
//
TDI_STATUS
DeleteValueSecurityFilter(IPAddr InterfaceAddress, ulong Protocol,
                          ulong FilterValue)
{
    CTELockHandle handle;
    TDI_STATUS status;

    CTEGetLock(&AddrObjTableLock.Lock, &handle);

    status = ModifySecurityFilter(DELETE_VALUE_SECURITY_FILTER,
                                  InterfaceAddress, Protocol, FilterValue);

    CTEFreeLock(&AddrObjTableLock.Lock, handle);

    return (status);
}

//* EnumerateSecurityFilters - Enumerate security filter database.
//
//  This routine enumerates the contents of the security filter database
//  for the specified protocol and IP interface.
//
//  Input:  InterfaceAddress  - The interface address to enumerate. A value
//                              of zero means enumerate all interfaces.
//                                  (in network byte order)
//
//          Protocol          - The protocol to enumerate. A value of zero
//                              means enumerate all protocols.
//
//          Value             - The Protocol value to enumerate. A value of
//                              zero means enumerate all protocol values.
//                                  (in host byte order)
//
//          Buffer            - A pointer to a buffer into which to put
//                              the returned filter entries.
//
//          BufferSize        - On input, the size in bytes of Buffer.
//                              On output, the number of bytes written.
//
//          EntriesAvailable  - On output, the total number of filter entries
//                              available in the database.
//
//  Returns: A TDI status code:
//
//           TDI_ADDR_INVALID if the address is not a valid IP interface.
//           TDI_SUCCESS otherwise.
//
//  NOTES:
//
//      This routine acquires AddrObjTableLock.
//
//      Entries written to output buffer are in host byte order.
//
void
EnumerateSecurityFilters(IPAddr InterfaceAddress, ulong Protocol,
                         ulong Value, uchar * Buffer, ulong BufferSize,
                         ulong * EntriesReturned, ulong * EntriesAvailable)
{
    PINTERFACE_ENTRY ientry;
    TDI_STATUS status = TDI_SUCCESS;
    CTELockHandle handle;

    *EntriesAvailable = *EntriesReturned = 0;

    CTEGetLock(&AddrObjTableLock.Lock, &handle);

    if (InterfaceAddress == 0) {
        struct Queue *qentry;

        //
        // Enumerate all interfaces.
        //
        for (qentry = InterfaceEntryList.q_next;
             qentry != &InterfaceEntryList;
             qentry = qentry->q_next
             ) {
            ientry = STRUCT_OF(INTERFACE_ENTRY, qentry, ie_link);

            EnumerateInterfaceProtocols(
                                        ientry,
                                        Protocol,
                                        Value,
                                        &Buffer,
                                        &BufferSize,
                                        EntriesReturned,
                                        EntriesAvailable
                                        );
        }
    } else {
        //
        // Enumerate a specific interface.
        //

        ientry = FindInterfaceEntry(InterfaceAddress);

        if (ientry != NULL) {
            EnumerateInterfaceProtocols(
                                        ientry,
                                        Protocol,
                                        Value,
                                        &Buffer,
                                        &BufferSize,
                                        EntriesReturned,
                                        EntriesAvailable
                                        );
        }
    }

    CTEFreeLock(&AddrObjTableLock.Lock, handle);

    return;
}

//* IsPermittedSecurityFilter
//
//  This routine determines if communications addressed to
//  {Protocol, InterfaceAddress, Value} are permitted by the security filters.
//  It looks up the tuple in the security filter database.
//
//  Input:  InterfaceAddress  - The IP interface address to check
//                                  (in network byte order)
//          IPContext         - The IPContext value passed to the transport
//          Protocol          - The protocol to check
//          Value             - The value to check (in host byte order)
//
//  Returns: A boolean indicating whether or not the communication is permitted.
//
//  NOTES:
//
//      This routine must be called with AddrObjTableLock held.
//
//
BOOLEAN
IsPermittedSecurityFilter(IPAddr InterfaceAddress, void *IPContext,
                          ulong Protocol, ulong FilterValue)
{
    PINTERFACE_ENTRY ientry;
    PPROTOCOL_ENTRY pentry;
    PVALUE_ENTRY ventry;
    ulong hash_value;
    struct Queue *qentry;

    ASSERT(Protocol <= 0xFF);

    for (qentry = InterfaceEntryList.q_next;
         qentry != &InterfaceEntryList;
         qentry = qentry->q_next
         ) {
        ientry = STRUCT_OF(INTERFACE_ENTRY, qentry, ie_link);

        if (ientry->ie_address == InterfaceAddress) {

            for (qentry = ientry->ie_protocol_list.q_next;
                 qentry != &(ientry->ie_protocol_list);
                 qentry = qentry->q_next
                 ) {
                pentry = STRUCT_OF(PROTOCOL_ENTRY, qentry, pe_link);

                if (pentry->pe_protocol == Protocol) {

                    if (pentry->pe_accept_all == TRUE) {
                        //
                        // All values accepted. Permit operation.
                        //
                        return (TRUE);
                    }
                    hash_value = VALUE_ENTRY_HASH(FilterValue);

                    for (qentry = pentry->pe_entries[hash_value].q_next;
                         qentry != &(pentry->pe_entries[hash_value]);
                         qentry = qentry->q_next
                         ) {
                        ventry = STRUCT_OF(VALUE_ENTRY, qentry, ve_link);

                        if (ventry->ve_value == FilterValue) {
                            //
                            // Found it. Operation is permitted.
                            //
                            return (TRUE);
                        }
                    }

                    //
                    // {Interface, Protocol} protected, but no value found.
                    // Reject operation.
                    //
                    return (FALSE);
                }
            }

            //
            // Protocol not registered. Reject operation
            //
            return (FALSE);
        }
    }

    //
    // If this packet is on the loopback interface, let it through.
    //
    if (IP_LOOPBACK_ADDR(InterfaceAddress)) {
        return (TRUE);
    }
    //
    // Special check to allow the DHCP client to receive its packets.
    // It is safe to make this check all the time because IP will
    // not permit a packet to get through on an NTE with a zero address
    // unless DHCP is in the process of configuring that NTE.
    //
    if ((Protocol == PROTOCOL_UDP) &&
        (FilterValue == DHCP_CLIENT_PORT) &&
        (*LocalNetInfo.ipi_isdhcpinterface) (IPContext)
        ) {
        return (TRUE);
    }
    //
    // Interface not registered. Deny operation.
    //
    return (FALSE);
}