Available since: Windows Packet Filter 3.6.1
Windows Packet Filter 3.6.1 introduced runtime dynamic static-filter management and driver-level flow caching. Prior to version 3.6.1, modifying static filters required allocating a full STATIC_FILTER_TABLE structure and calling SetPacketFilterTable, which completely replaced all existing active rules. With 3.6.1, developers can granularly add, insert, and remove individual static filters at runtime without interrupting existing network filtering.
Key Concepts
- Rule Evaluation Order: Static filters in
ndisrd.sysare evaluated sequentially from index 0 to N. The first filter matching a packet determines the action (FILTER_PACKET_PASS,FILTER_PACKET_DROP,FILTER_PACKET_REDIRECT,FILTER_PACKET_PASS_RDR, orFILTER_PACKET_DROP_RDR). - Prepend vs Append: Use AddStaticFilterFront to insert high-priority rules (such as emergency IP blocks) that must take immediate precedence over existing rules. Use AddStaticFilterBack for fallback or low-priority inspection policies.
- Arbitrary Insertion: Use InsertStaticFilter to place a rule at an exact zero-based position in the rule table using STATIC_FILTER_WITH_POSITION.
- Dynamic Removal: Use RemoveStaticFilter to delete a filter by index. Subsequent filters in the table automatically shift forward.
- Filter Decision Cache: When EnablePacketFilterCache is active, the driver maintains an internal 5-tuple flow lookup cache. Dynamic rule modifications automatically synchronize the driver’s flow cache.
Typical Workflow Example (C++)
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#include "ndisapi.h"
int main()
{
CNdisApi api;
if (!api.IsDriverLoaded()) {
std::cerr << "Driver not loaded." << std::endl;
return 1;
}
// 1. Enable driver flow-level filter cache for high throughput
api.EnablePacketFilterCache();
api.EnablePacketFragmentCache();
// 2. Prepare a static filter (e.g. block incoming TCP port 23 Telnet)
STATIC_FILTER filter;
ZeroMemory(&filter, sizeof(filter));
filter.m_Adapter.QuadPart = 0; // all adapters
filter.m_ValidFields = NETWORK_LAYER_VALID | TRANSPORT_LAYER_VALID;
filter.m_FilterAction = FILTER_PACKET_DROP;
filter.m_dwDirectionFlags = PACKET_FLAG_ON_RECEIVE;
// IPv4 rule: match TCP protocol
filter.m_NetworkFilter.m_dwUnionSelector = IPV4;
filter.m_NetworkFilter.m_IPv4.m_ValidFields = IP_V4_FILTER_PROTOCOL;
filter.m_NetworkFilter.m_IPv4.m_Protocol = IPPROTO_TCP;
// Transport TCP port 23
filter.m_TransportFilter.m_dwUnionSelector = TCPUDP;
filter.m_TransportFilter.m_TcpUdp.m_ValidFields = TCPUDP_DEST_PORT;
filter.m_TransportFilter.m_TcpUdp.m_DestPort.m_StartRange = 23;
filter.m_TransportFilter.m_TcpUdp.m_DestPort.m_EndRange = 23;
// 3. Prepend the block rule at the front of the filter table
if (api.AddStaticFilterFront(&filter)) {
std::cout << "Port 23 block filter successfully added to front." << std::endl;
}
// 4. Query current table size
DWORD tableSize = 0;
if (api.GetPacketFilterTableSize(&tableSize)) {
std::cout << "Active static filter rules: " << tableSize << std::endl;
}
// 5. Remove rule at index 0 when done
api.RemoveStaticFilter(0);
return 0;
}
See Also
AddStaticFilterFront, AddStaticFilterBack, InsertStaticFilter, RemoveStaticFilter, STATIC_FILTER