Driver: Prepare ACK packets deferring for speed limit.

This commit is contained in:
Nodir Temirkhodjaev 2018-02-16 17:52:19 +05:00
parent f6c955e94b
commit dd358a047a
4 changed files with 529 additions and 69 deletions

View File

@ -245,10 +245,7 @@ fort_prov_flow_register (HANDLE transEngine, BOOL filter_transport)
|| (status = FwpmFilterAdd0(engine, &dfilter4, NULL, NULL))
|| (filter_transport
&& ((status = FwpmFilterAdd0(engine, &itfilter4, NULL, NULL))
#if 0
|| (status = FwpmFilterAdd0(engine, &otfilter4, NULL, NULL))
#endif
))
|| (status = FwpmFilterAdd0(engine, &otfilter4, NULL, NULL))))
) {
fort_prov_trans_abort(engine);
}

View File

@ -21,40 +21,10 @@
#include "../common/fortprov.c"
#include "forttds.c"
#include "fortbuf.c"
#include "fortpkt.c"
#include "fortstat.c"
#include "forttmr.c"
#define HTONL(l) _byteswap_ulong(l)
#define NTOHL(l) HTONL(l)
#define HTONS(s) _byteswap_ushort(s)
#define NTOHS(s) HTONS(s)
#define TCP_HEADER_FLAG_FIN 0x0001
#define TCP_HEADER_FLAG_SYN 0x0002
#define TCP_HEADER_FLAG_RST 0x0004
#define TCP_HEADER_FLAG_PSH 0x0008
#define TCP_HEADER_FLAG_ACK 0x0010
#define TCP_HEADER_FLAG_URG 0x0020
#define TCP_HEADER_FLAG_ECE 0x0040
#define TCP_HEADER_FLAG_CWR 0x0080
typedef struct tcp_header {
UINT16 source; // Source Port
UINT16 dest; // Destination Port
UINT32 seq; // Sequence number
UINT32 ack_seq; // Acknowledgement number
UCHAR res1 : 4; // Unused
UCHAR doff : 4; // Data offset
UCHAR flags; // Flags
UINT16 window; // Window size
UINT16 csum; // Checksum
UINT16 urg_ptr; // Urgent Pointer
} TCP_HEADER, *PTCP_HEADER;
typedef struct fort_conf_ref {
UINT32 volatile refcount;
@ -65,6 +35,7 @@ typedef struct fort_device {
UINT32 prov_boot : 1;
UINT32 is_opened : 1;
UINT32 was_conf : 1;
UINT32 power_off : 1;
UINT32 connect4_id;
UINT32 accept4_id;
@ -73,8 +44,12 @@ typedef struct fort_device {
PFORT_CONF_REF volatile conf_ref;
KSPIN_LOCK conf_lock;
PCALLBACK_OBJECT power_cb_obj;
PVOID power_cb_reg;
FORT_BUFFER buffer;
FORT_STAT stat;
FORT_DEFER defer;
FORT_TIMER timer;
} FORT_DEVICE, *PFORT_DEVICE;
@ -304,7 +279,7 @@ fort_callout_classify_v4 (const FWPS_INCOMING_VALUES0 *inFixedValues,
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Classify v4: Flow assoc. error: %d\n", status);
"FORT: Classify v4: Flow assoc. error: %x\n", status);
} else if (is_new_proc) {
fort_buffer_proc_new_write(&g_device->buffer,
process_id, path_len, path, &irp, &info);
@ -388,6 +363,7 @@ fort_callout_flow_classify_v4 (const FWPS_INCOMING_METADATA_VALUES0 *inMetaValue
if (fort_stat_flow_classify(&g_device->stat, flowContext,
headerSize + dataSize, inbound)) {
fort_callout_classify_drop(classifyOut);
} else {
fort_callout_classify_permit(filter, classifyOut);
@ -443,10 +419,18 @@ fort_callout_flow_delete_v4 (UINT16 layerId, UINT32 calloutId, UINT64 flowContex
fort_stat_flow_delete(&g_device->stat, flowContext);
}
static void
fort_transport_inject_complete (PFORT_PACKET pkt,
PNET_BUFFER_LIST clonedNetBufList,
BOOLEAN dispatchLevel)
{
fort_defer_free(&g_device->defer, pkt, clonedNetBufList, dispatchLevel);
}
static void
fort_callout_transport_classify_v4 (const FWPS_INCOMING_VALUES0 *inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues,
const PNET_BUFFER_LIST netBufList,
PNET_BUFFER_LIST netBufList,
const FWPS_FILTER0 *filter,
UINT64 flowContext,
FWPS_CLASSIFY_OUT0 *classifyOut,
@ -456,35 +440,72 @@ fort_callout_transport_classify_v4 (const FWPS_INCOMING_VALUES0 *inFixedValues,
ipProtoField].value.uint8;
const BOOL is_udp = (ip_proto == IPPROTO_UDP);
if (is_udp) goto permit;
if (!(is_udp || FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues,
FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED))
&& netBufList != NULL) {
/* Position in the packet data:
* FWPS_LAYER_INBOUND_TRANSPORT_V4: The beginning of the data.
* FWPS_LAYER_OUTBOUND_TRANSPORT_V4: The beginning of the transport header.
*/
PFORT_STAT_FLOW flow = (PFORT_STAT_FLOW) flowContext;
if (inbound && g_device->conf_flags.ignore_tcp_rst) {
const PNET_BUFFER netBuf = NET_BUFFER_LIST_FIRST_NB(netBufList);
TCP_HEADER buf;
PTCP_HEADER tcpHeader;
BOOL blocked = FALSE;
const BOOL ignore_tcp_rst = inbound && g_device->conf_flags.ignore_tcp_rst;
const BOOL defer_flow = flow->opt.speed_limit && !g_device->power_off
&& (inbound ? flow->opt.defer_in : flow->opt.defer_out);
NdisRetreatNetBufferDataStart(netBuf, sizeof(TCP_HEADER), 0, NULL);
if (ignore_tcp_rst || defer_flow) {
const PNET_BUFFER netBuf = NET_BUFFER_LIST_FIRST_NB(netBufList);
TCP_HEADER buf;
PTCP_HEADER tcpHeader;
UINT32 tcpFlags;
tcpHeader = NdisGetDataBuffer(netBuf, sizeof(TCP_HEADER), &buf, 1, 0);
if (netBuf == NULL)
goto permit;
blocked = (tcpHeader->flags & TCP_HEADER_FLAG_RST);
/* Position in the packet data:
* FWPS_LAYER_INBOUND_TRANSPORT_V4: The beginning of the data.
* FWPS_LAYER_OUTBOUND_TRANSPORT_V4: The beginning of the transport header.
*/
const UINT32 headerOffset = inbound ? 0 : sizeof(TCP_HEADER);
NdisAdvanceNetBufferDataStart(netBuf, sizeof(TCP_HEADER), FALSE, NULL);
if (headerOffset != 0) {
NdisRetreatNetBufferDataStart(netBuf, headerOffset, 0, NULL);
}
if (blocked) {
fort_callout_classify_drop(classifyOut);
return;
tcpHeader = NdisGetDataBuffer(netBuf, sizeof(TCP_HEADER), &buf, 1, 0);
tcpFlags = tcpHeader ? tcpHeader->flags : 0;
if (headerOffset != 0) {
NdisAdvanceNetBufferDataStart(netBuf, headerOffset, FALSE, NULL);
}
if (tcpHeader == NULL)
goto permit;
if (ignore_tcp_rst && (tcpFlags & TCP_FLAG_RST))
goto block;
if (defer_flow //&& (tcpFlags & TCP_FLAG_ACK)
&& NET_BUFFER_DATA_LENGTH(netBuf) == headerOffset) {
NTSTATUS status;
status = fort_defer_add(&g_device->defer, inFixedValues, inMetaValues,
netBufList, inbound);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Transport Classify: Defer error: %x\n", status);
}
goto block;
}
}
}
permit:
fort_callout_classify_permit(filter, classifyOut);
return;
block:
fort_callout_classify_drop(classifyOut);
return;
}
static void
@ -540,7 +561,7 @@ fort_callout_install (PDEVICE_OBJECT device)
status = FwpsCalloutRegister0(device, &c, &g_device->connect4_id);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Register Connect V4: Error: %d\n", status);
"FORT: Register Connect V4: Error: %x\n", status);
return status;
}
@ -551,7 +572,7 @@ fort_callout_install (PDEVICE_OBJECT device)
status = FwpsCalloutRegister0(device, &c, &g_device->accept4_id);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Register Accept V4: Error: %d\n", status);
"FORT: Register Accept V4: Error: %x\n", status);
return status;
}
@ -565,7 +586,7 @@ fort_callout_install (PDEVICE_OBJECT device)
status = FwpsCalloutRegister0(device, &c, &g_device->stat.stream4_id);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Register Stream V4: Error: %d\n", status);
"FORT: Register Stream V4: Error: %x\n", status);
return status;
}
@ -578,7 +599,7 @@ fort_callout_install (PDEVICE_OBJECT device)
status = FwpsCalloutRegister0(device, &c, &g_device->stat.datagram4_id);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Register Datagram V4: Error: %d\n", status);
"FORT: Register Datagram V4: Error: %x\n", status);
return status;
}
@ -589,7 +610,7 @@ fort_callout_install (PDEVICE_OBJECT device)
status = FwpsCalloutRegister0(device, &c, &g_device->stat.in_transport4_id);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Register Inbound Transport V4: Error: %d\n", status);
"FORT: Register Inbound Transport V4: Error: %x\n", status);
return status;
}
@ -600,7 +621,7 @@ fort_callout_install (PDEVICE_OBJECT device)
status = FwpsCalloutRegister0(device, &c, &g_device->stat.out_transport4_id);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Register Outbound Transport V4: Error: %d\n", status);
"FORT: Register Outbound Transport V4: Error: %x\n", status);
return status;
}
@ -667,7 +688,7 @@ fort_callout_force_reauth (PDEVICE_OBJECT device,
if ((status = fort_prov_register(engine, conf_flags.prov_boot)))
goto cleanup;
goto stat;
goto stat_prov;
}
/* Check flow filter */
@ -676,10 +697,10 @@ fort_callout_force_reauth (PDEVICE_OBJECT device,
fort_prov_flow_unregister(engine);
}
stat:
stat_prov:
if (conf_flags.log_stat) {
if ((status = fort_prov_flow_register(engine,
(conf_flags.ignore_tcp_rst != 0))))
(conf_flags.ignore_tcp_rst || stat->limit_bits))))
goto cleanup;
}
}
@ -703,7 +724,7 @@ fort_callout_force_reauth (PDEVICE_OBJECT device,
end:
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Callout Reauth: Error: %d\n", status);
"FORT: Callout Reauth: Error: %x\n", status);
}
return status;
@ -715,8 +736,8 @@ fort_callout_timer (void)
PFORT_BUFFER buf = &g_device->buffer;
PFORT_STAT stat = &g_device->stat;
KLOCK_QUEUE_HANDLE stat_lock_queue;
KLOCK_QUEUE_HANDLE buf_lock_queue;
KLOCK_QUEUE_HANDLE stat_lock_queue;
PIRP irp = NULL;
ULONG_PTR info;
@ -755,6 +776,19 @@ fort_callout_timer (void)
if (irp != NULL) {
fort_request_complete_info(irp, STATUS_SUCCESS, info);
}
/* Flush deferred packets */
fort_defer_dpc_flush(&g_device->defer, fort_transport_inject_complete);
}
static void
fort_callout_timer_force (void)
{
KLOCK_QUEUE_HANDLE lock_queue;
KeAcquireInStackQueuedSpinLock(&g_device->conf_lock, &lock_queue);
fort_callout_timer(); // Should be called from DISPATCH_LEVEL!
KeReleaseInStackQueuedSpinLock(&lock_queue);
}
static NTSTATUS
@ -903,7 +937,7 @@ fort_device_control (PDEVICE_OBJECT device, PIRP irp)
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Device Control: Error: %d\n", status);
"FORT: Device Control: Error: %x\n", status);
}
fort_request_complete_info(irp, status, info);
@ -911,16 +945,73 @@ fort_device_control (PDEVICE_OBJECT device, PIRP irp)
return status;
}
static void
fort_power_callback (PVOID context, PVOID event, PVOID specifics)
{
BOOL power_off;
UNUSED(context);
if (event != (PVOID) PO_CB_SYSTEM_STATE_LOCK)
return;
power_off = (specifics == NULL);
g_device->power_off = power_off;
if (power_off) {
fort_callout_timer_force();
}
}
static NTSTATUS
fort_power_callback_register (void)
{
OBJECT_ATTRIBUTES obj_attr;
UNICODE_STRING obj_name;
NTSTATUS status;
RtlInitUnicodeString(&obj_name, L"\\Callback\\PowerState");
InitializeObjectAttributes(&obj_attr, &obj_name,
OBJ_CASE_INSENSITIVE, NULL, NULL);
status = ExCreateCallback(&g_device->power_cb_obj, &obj_attr, FALSE, TRUE);
if (NT_SUCCESS(status)) {
g_device->power_cb_reg = ExRegisterCallback(g_device->power_cb_obj,
fort_power_callback, NULL);
}
return status;
}
static void
fort_power_callback_unregister (void)
{
if (g_device->power_cb_reg != NULL) {
ExUnregisterCallback(g_device->power_cb_reg);
}
if (g_device->power_cb_obj != NULL) {
ObDereferenceObject(g_device->power_cb_obj);
}
}
static void
fort_driver_unload (PDRIVER_OBJECT driver)
{
UNICODE_STRING device_link;
if (g_device != NULL) {
fort_callout_timer_force();
fort_timer_close(&g_device->timer);
fort_defer_close(&g_device->defer);
fort_stat_close(&g_device->stat);
fort_buffer_close(&g_device->buffer);
fort_power_callback_unregister();
if (!g_device->prov_boot) {
fort_prov_unregister(0);
}
@ -993,6 +1084,7 @@ DriverEntry (PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
fort_buffer_open(&g_device->buffer);
fort_stat_open(&g_device->stat);
fort_defer_open(&g_device->defer);
fort_timer_open(&g_device->timer, &fort_callout_timer);
KeInitializeSpinLock(&g_device->conf_lock);
@ -1011,12 +1103,17 @@ DriverEntry (PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
if (NT_SUCCESS(status)) {
status = fort_prov_register(0, g_device->prov_boot);
}
/* Register power state change callback */
if (NT_SUCCESS(status)) {
status = fort_power_callback_register();
}
}
}
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Entry: Error: %d\n", status);
"FORT: Entry: Error: %x\n", status);
fort_driver_unload(driver);
}

353
src/driver/fortpkt.c Normal file
View File

@ -0,0 +1,353 @@
/* Fort Firewall ACK Packets Deferring & Re-injection */
#define HTONL(l) _byteswap_ulong(l)
#define NTOHL(l) HTONL(l)
#define HTONS(s) _byteswap_ushort(s)
#define NTOHS(s) HTONS(s)
#define TCP_FLAG_FIN 0x0001
#define TCP_FLAG_SYN 0x0002
#define TCP_FLAG_RST 0x0004
#define TCP_FLAG_PSH 0x0008
#define TCP_FLAG_ACK 0x0010
#define TCP_FLAG_URG 0x0020
#define TCP_FLAG_ECE 0x0040
#define TCP_FLAG_CWR 0x0080
typedef struct tcp_header {
UINT16 source; // Source Port
UINT16 dest; // Destination Port
UINT32 seq; // Sequence number
UINT32 ack_seq; // Acknowledgement number
UCHAR res1 : 4; // Unused
UCHAR doff : 4; // Data offset
UCHAR flags; // Flags
UINT16 window; // Window size
UINT16 csum; // Checksum
UINT16 urg_ptr; // Urgent Pointer
} TCP_HEADER, *PTCP_HEADER;
typedef struct fort_packet_in {
IF_INDEX interfaceIndex;
IF_INDEX subInterfaceIndex;
UINT16 ipHeaderSize;
UINT16 transportHeaderSize;
UINT16 nblOffset;
} FORT_PACKET_IN, *PFORT_PACKET_IN;
typedef struct fort_packet_out {
UINT32 remoteAddr4;
SCOPE_ID remoteScopeId;
UINT64 endpointHandle;
} FORT_PACKET_OUT, *PFORT_PACKET_OUT;
typedef struct fort_packet {
BOOL inbound;
COMPARTMENT_ID compartmentId;
PNET_BUFFER_LIST netBufList;
struct fort_packet *next;
union {
FORT_PACKET_IN in;
FORT_PACKET_OUT out;
};
} FORT_PACKET, *PFORT_PACKET;
typedef struct fort_defer {
HANDLE injection4_id;
PFORT_PACKET packet_head;
PFORT_PACKET packet_tail;
PFORT_PACKET packet_free;
tommy_arrayof packets;
KSPIN_LOCK lock;
} FORT_DEFER, *PFORT_DEFER;
typedef void (*FORT_INJECT_COMPLETE_FUNC) (PFORT_PACKET, PNET_BUFFER_LIST, BOOLEAN);
static void
fort_defer_open (PFORT_DEFER defer)
{
NTSTATUS status;
status = FwpsInjectionHandleCreate0(AF_INET, FWPS_INJECTION_TYPE_TRANSPORT,
&defer->injection4_id);
if (!NT_SUCCESS(status)) {
defer->injection4_id = INVALID_HANDLE_VALUE;
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Defer: Injection init error: %x\n", status);
}
tommy_arrayof_init(&defer->packets, sizeof(FORT_PACKET));
KeInitializeSpinLock(&defer->lock);
}
static void
fort_defer_close (PFORT_DEFER defer)
{
FwpsInjectionHandleDestroy0(defer->injection4_id);
tommy_arrayof_done(&defer->packets);
}
static NTSTATUS
fort_defer_add (PFORT_DEFER defer,
const FWPS_INCOMING_VALUES0 *inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues,
PNET_BUFFER_LIST netBufList,
BOOL inbound)
{
KLOCK_QUEUE_HANDLE lock_queue;
PFORT_PACKET pkt;
NTSTATUS status;
if (defer->injection4_id == INVALID_HANDLE_VALUE)
return STATUS_FWP_TCPIP_NOT_READY;
/* Skip IpSec protected packet */
if (inbound) {
FWPS_PACKET_LIST_INFORMATION info;
status = FwpsGetPacketListSecurityInformation0(netBufList,
FWPS_PACKET_LIST_INFORMATION_QUERY_IPSEC
| FWPS_PACKET_LIST_INFORMATION_QUERY_INBOUND, &info);
if (!NT_SUCCESS(status))
return status;
if (info.ipsecInformation.inbound.isSecure)
return STATUS_UNSUCCESSFUL;
}
KeAcquireInStackQueuedSpinLock(&defer->lock, &lock_queue);
if (defer->packet_free != NULL) {
pkt = defer->packet_free;
defer->packet_free = pkt->next;
} else {
const tommy_count_t size = tommy_arrayof_size(&defer->packets);
// TODO: tommy_arrayof_grow(): check calloc()'s result for NULL
if (tommy_arrayof_grow(&defer->packets, size + 1), 0) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
pkt = tommy_arrayof_ref(&defer->packets, size);
}
if (defer->packet_tail == NULL) {
defer->packet_head = defer->packet_tail = pkt;
} else {
defer->packet_tail->next = pkt;
}
pkt->inbound = inbound;
pkt->compartmentId = inMetaValues->compartmentId;
pkt->netBufList = netBufList;
pkt->next = NULL;
if (inbound) {
PFORT_PACKET_IN pkt_in = &pkt->in;
const int interfaceField = inbound
? FWPS_FIELD_INBOUND_TRANSPORT_V4_INTERFACE_INDEX
: FWPS_FIELD_OUTBOUND_TRANSPORT_V4_INTERFACE_INDEX;
const int subInterfaceField = inbound
? FWPS_FIELD_INBOUND_TRANSPORT_V4_SUB_INTERFACE_INDEX
: FWPS_FIELD_OUTBOUND_TRANSPORT_V4_SUB_INTERFACE_INDEX;
pkt_in->interfaceIndex = inFixedValues->incomingValue[interfaceField].value.uint32;
pkt_in->subInterfaceIndex = inFixedValues->incomingValue[subInterfaceField].value.uint32;
pkt_in->ipHeaderSize = (UINT16) inMetaValues->ipHeaderSize;
pkt_in->transportHeaderSize = (UINT16) inMetaValues->transportHeaderSize;
pkt_in->nblOffset = (UINT16) NET_BUFFER_DATA_OFFSET(
NET_BUFFER_LIST_FIRST_NB(netBufList));
} else {
PFORT_PACKET_OUT pkt_out = &pkt->out;
const int remoteAddrField = inbound
? FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS
: FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS;
/* host-order -> network-order conversion */
pkt_out->remoteAddr4 = HTONL(
inFixedValues->incomingValue[remoteAddrField].value.uint32);
pkt_out->remoteScopeId = inMetaValues->remoteScopeId;
pkt_out->endpointHandle = inMetaValues->transportEndpointHandle;
}
FwpsReferenceNetBufferList0(netBufList, TRUE);
status = STATUS_SUCCESS;
end:
KeReleaseInStackQueuedSpinLock(&lock_queue);
return status;
}
static void
fort_defer_free (PFORT_DEFER defer, PFORT_PACKET pkt,
PNET_BUFFER_LIST clonedNetBufList,
BOOL dispatchLevel)
{
KLOCK_QUEUE_HANDLE lock_queue;
if (clonedNetBufList != NULL) {
const NTSTATUS status = clonedNetBufList->Status;
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Defer: Injection error: %x\n", status);
}
FwpsFreeCloneNetBufferList0(clonedNetBufList, 0);
}
FwpsDereferenceNetBufferList0(pkt->netBufList, FALSE);
/* Add to free chain */
if (dispatchLevel) {
KeAcquireInStackQueuedSpinLockAtDpcLevel(&defer->lock, &lock_queue);
} else {
KeAcquireInStackQueuedSpinLock(&defer->lock, &lock_queue);
}
pkt->next = defer->packet_free;
defer->packet_free = pkt;
if (dispatchLevel) {
KeReleaseInStackQueuedSpinLockFromDpcLevel(&lock_queue);
} else {
KeReleaseInStackQueuedSpinLock(&lock_queue);
}
}
static NTSTATUS
fort_defer_inject_in (PFORT_DEFER defer, PFORT_PACKET pkt,
PNET_BUFFER_LIST *clonedNetBufList,
FORT_INJECT_COMPLETE_FUNC complete_func)
{
PFORT_PACKET_IN pkt_in = &pkt->in;
PNET_BUFFER_LIST netBufList = pkt->netBufList;
PNET_BUFFER netBuffer = NET_BUFFER_LIST_FIRST_NB(netBufList);
NTSTATUS status;
/* The TCP/IP stack could have retreated the net buffer list by the
* transportHeaderSize amount; detect the condition here to avoid
* retreating twice.
*/
if (pkt_in->nblOffset != NET_BUFFER_DATA_OFFSET(netBuffer)) {
pkt_in->transportHeaderSize = 0;
}
/* Adjust the net buffer list offset to the start of the IP header. */
NdisRetreatNetBufferDataStart(netBuffer,
pkt_in->ipHeaderSize + pkt_in->transportHeaderSize, 0, NULL);
status = FwpsAllocateCloneNetBufferList0(netBufList,
NULL, NULL, 0, clonedNetBufList);
/* Undo the adjustment on the original net buffer list. */
NdisAdvanceNetBufferDataStart(netBuffer,
pkt_in->ipHeaderSize + pkt_in->transportHeaderSize, FALSE, NULL);
if (NT_SUCCESS(status)) {
status = FwpsInjectTransportReceiveAsync0(
defer->injection4_id, NULL, NULL, 0,
AF_INET, pkt->compartmentId,
pkt_in->interfaceIndex, pkt_in->subInterfaceIndex,
*clonedNetBufList, complete_func, pkt);
}
return status;
}
static NTSTATUS
fort_defer_inject_out (PFORT_DEFER defer, PFORT_PACKET pkt,
PNET_BUFFER_LIST *clonedNetBufList,
FORT_INJECT_COMPLETE_FUNC complete_func)
{
PFORT_PACKET_OUT pkt_out = &pkt->out;
PNET_BUFFER_LIST netBufList = pkt->netBufList;
NTSTATUS status;
status = FwpsAllocateCloneNetBufferList0(netBufList,
NULL, NULL, 0, clonedNetBufList);
if (NT_SUCCESS(status)) {
FWPS_TRANSPORT_SEND_PARAMS0 send_args;
RtlZeroMemory(&send_args, sizeof(FWPS_TRANSPORT_SEND_PARAMS0));
send_args.remoteAddress = (UCHAR *) &pkt_out->remoteAddr4;
send_args.remoteScopeId = pkt_out->remoteScopeId;
status = FwpsInjectTransportSendAsync0(
defer->injection4_id, NULL,
pkt_out->endpointHandle, 0,
&send_args, AF_INET, pkt->compartmentId,
*clonedNetBufList, complete_func, pkt);
}
return status;
}
static void
fort_defer_dpc_flush (PFORT_DEFER defer, FORT_INJECT_COMPLETE_FUNC complete_func)
{
KLOCK_QUEUE_HANDLE lock_queue;
PFORT_PACKET pkt;
KeAcquireInStackQueuedSpinLockAtDpcLevel(&defer->lock, &lock_queue);
pkt = defer->packet_head;
if (pkt != NULL) {
defer->packet_head = defer->packet_tail = NULL;
}
KeReleaseInStackQueuedSpinLockFromDpcLevel(&lock_queue);
while (pkt != NULL) {
PFORT_PACKET pkt_next = pkt->next;
PNET_BUFFER_LIST clonedNetBufList = NULL;
NTSTATUS status;
status = pkt->inbound
? fort_defer_inject_in(defer, pkt, &clonedNetBufList, complete_func)
: fort_defer_inject_out(defer, pkt, &clonedNetBufList, complete_func);
if (!NT_SUCCESS(status)) {
if (clonedNetBufList != NULL) {
clonedNetBufList->Status = status;
} else {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Defer: Injection prepare error: %x\n", status);
}
fort_defer_free(defer, pkt, clonedNetBufList, TRUE);
}
pkt = pkt_next;
}
}

View File

@ -30,6 +30,8 @@ typedef struct fort_stat_proc {
typedef struct fort_stat_flow_opt {
UCHAR speed_limit : 1;
UCHAR defer_in : 1;
UCHAR defer_out : 1;
UCHAR group_index;
UINT16 proc_index;
} FORT_STAT_FLOW_OPT, *PFORT_STAT_FLOW_OPT;
@ -43,7 +45,7 @@ typedef struct fort_stat_flow {
#if defined(_WIN64)
UINT64 flow_id;
#else
FORT_STAT_FLOW_OPT opt;
FORT_STAT_FLOW_OPT volatile opt;
#endif
void *data;
};
@ -51,7 +53,7 @@ typedef struct fort_stat_flow {
tommy_key_t flow_hash;
#if defined(_WIN64)
FORT_STAT_FLOW_OPT opt;
FORT_STAT_FLOW_OPT volatile opt;
#else
UINT64 flow_id;
#endif
@ -345,6 +347,7 @@ fort_stat_flow_add (PFORT_STAT stat, UINT64 flow_id,
}
flow->opt.speed_limit = (UCHAR) speed_limit;
flow->opt.defer_in = flow->opt.defer_out = FALSE;
flow->opt.group_index = group_index;
flow->opt.proc_index = proc_index;
@ -525,13 +528,23 @@ fort_stat_flow_classify (PFORT_STAT stat, UINT64 flowContext,
PFORT_STAT_GROUP group = &stat->groups[group_index];
UINT32 *group_bytes = inbound ? &group->traf.in_bytes
: &group->traf.out_bytes;
UCHAR defer_flow = TRUE;
if (*group_bytes < limit_bytes) {
// Add traffic to app. group
*group_bytes += data_len;
defer_flow = (*group_bytes >= limit_bytes);
} else {
limited = TRUE;
}
// Defer ACK
if (inbound) {
flow->opt.defer_out = defer_flow;
} else {
flow->opt.defer_in = defer_flow;
}
}
}