Add ability to fragment first TCP packet.

This commit is contained in:
Nodir Temirkhodjaev 2019-02-18 22:43:45 +05:00
parent bb01a52daa
commit 9d707490bd
21 changed files with 682 additions and 305 deletions

View File

@ -20,6 +20,7 @@ typedef struct fort_conf_flags {
UINT32 app_allow_all : 1;
UINT32 log_blocked : 1;
UINT32 log_stat : 1;
UINT32 filter_transport : 1;
UINT32 group_bits : 16;
} FORT_CONF_FLAGS, *PFORT_CONF_FLAGS;
@ -45,6 +46,13 @@ typedef struct fort_traf {
};
} FORT_TRAF, *PFORT_TRAF;
typedef struct fort_conf_group {
UINT16 fragment_bits;
UINT32 limit_bits;
FORT_TRAF limits[FORT_CONF_GROUP_MAX]; /* Bytes per 0.5 sec. */
} FORT_CONF_GROUP, *PFORT_CONF_GROUP;
typedef struct fort_conf {
FORT_CONF_FLAGS flags;
@ -67,9 +75,7 @@ typedef struct fort_conf {
typedef struct fort_conf_io {
UINT16 driver_version;
UINT32 limit_bits;
FORT_TRAF limits[FORT_CONF_GROUP_MAX]; /* Bytes per 0.5 sec. */
FORT_CONF_GROUP conf_group;
FORT_CONF conf;
} FORT_CONF_IO, *PFORT_CONF_IO;

View File

@ -7,6 +7,6 @@
#define APP_UPDATES_URL "https://github.com/tnodir/fort/releases"
#define APP_UPDATES_API_URL "https://api.github.com/repos/tnodir/fort/releases/latest"
#define DRIVER_VERSION 9
#define DRIVER_VERSION 10
#endif // VERSION_H

View File

@ -4,6 +4,7 @@
#define NDIS630 1
#define WIN9X_COMPAT_SPINLOCK /* XXX: Support Windows 7: KeInitializeSpinLock() */
#define POOL_NX_OPTIN 1 /* Enhanced protection of NX pool */
#include <wdm.h>
#include <fwpmk.h>
@ -323,8 +324,8 @@ fort_callout_classify_v4 (const FWPS_INCOMING_VALUES0 *inFixedValues,
BOOL is_new_proc = FALSE;
NTSTATUS status;
status = fort_stat_flow_associate(&g_device->stat,
flowId, process_id, group_index, is_reauth, &is_new_proc);
status = fort_flow_associate(&g_device->stat, flowId, process_id,
group_index, is_reauth, &is_new_proc);
if (!NT_SUCCESS(status)) {
if (status == FORT_STATUS_FLOW_BLOCK)
@ -404,38 +405,85 @@ fort_callout_notify (FWPS_CALLOUT_NOTIFY_TYPE notifyType,
return STATUS_SUCCESS;
}
static void
fort_packet_inject_complete (PFORT_PACKET pkt,
PNET_BUFFER_LIST clonedNetBufList,
BOOLEAN dispatchLevel)
{
fort_defer_packet_free(&g_device->defer, pkt, clonedNetBufList, dispatchLevel);
}
static void
fort_callout_flow_classify_v4 (const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues,
const FWPS_FILTER0 *filter,
UINT64 flowContext,
FWPS_CLASSIFY_OUT0 *classifyOut,
UINT32 dataSize, BOOL inbound)
{
const UINT32 headerSize = inbound ? inMetaValues->transportHeaderSize : 0;
fort_stat_flow_classify(&g_device->stat, flowContext,
fort_flow_classify(&g_device->stat, flowContext,
headerSize + dataSize, inbound);
fort_callout_classify_continue(classifyOut);
}
static void
fort_callout_stream_classify_v4 (const FWPS_INCOMING_VALUES0 *inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues,
const FWPS_STREAM_CALLOUT_IO_PACKET0 *packet,
FWPS_STREAM_CALLOUT_IO_PACKET0 *packet,
const FWPS_FILTER0 *filter,
UINT64 flowContext,
FWPS_CLASSIFY_OUT0 *classifyOut)
{
const FWPS_STREAM_DATA0 *streamData = packet->streamData;
const UINT32 streamFlags = streamData->flags;
const UINT32 dataSize = (UINT32) streamData->dataLength;
const BOOL inbound = (streamData->flags & FWPS_STREAM_FLAG_RECEIVE) != 0;
const BOOL inbound = (streamFlags & FWPS_STREAM_FLAG_RECEIVE) != 0;
UNUSED(inFixedValues);
fort_callout_flow_classify_v4(inMetaValues, filter, flowContext,
fort_callout_flow_classify_v4(inMetaValues, flowContext,
classifyOut, dataSize, inbound);
/* Fragment first TCP packet */
if ((streamFlags & (FWPS_STREAM_FLAG_SEND
| FWPS_STREAM_FLAG_SEND_EXPEDITED
| FWPS_STREAM_FLAG_SEND_DISCONNECT))
== FWPS_STREAM_FLAG_SEND) {
PFORT_FLOW flow = (PFORT_FLOW) flowContext;
const UCHAR flow_flags = fort_flow_flags(flow);
const UCHAR fragment_flags = (flow_flags
& (FORT_FLOW_FRAGMENT | FORT_FLOW_FRAGMENT_DEFER | FORT_FLOW_FRAGMENTED));
if (fragment_flags != 0
&& !(fragment_flags & FORT_FLOW_FRAGMENTED)) {
const UCHAR fragment_size = 3;
if (fragment_flags & FORT_FLOW_FRAGMENT_DEFER) {
const NTSTATUS status = fort_defer_stream_add(&g_device->defer,
inFixedValues, inMetaValues, streamData, filter, inbound);
if (NT_SUCCESS(status))
goto drop;
fort_flow_flags_set(flow, FORT_FLOW_FRAGMENTED);
}
else if (dataSize > fragment_size) {
packet->countBytesEnforced = fragment_size;
fort_flow_flags_set(flow, FORT_FLOW_FRAGMENT_DEFER);
}
}
}
/* permit: */
fort_callout_classify_permit(filter, classifyOut);
return;
drop:
fort_callout_classify_drop(classifyOut);
return;
}
static void
@ -454,9 +502,12 @@ fort_callout_datagram_classify_v4 (const FWPS_INCOMING_VALUES0 *inFixedValues,
const BOOL inbound = (direction == FWP_DIRECTION_INBOUND);
UNUSED(inFixedValues);
UNUSED(filter);
fort_callout_flow_classify_v4(inMetaValues, filter, flowContext,
fort_callout_flow_classify_v4(inMetaValues, flowContext,
classifyOut, dataSize, inbound);
fort_callout_classify_continue(classifyOut);
}
static void
@ -465,15 +516,7 @@ fort_callout_flow_delete_v4 (UINT16 layerId, UINT32 calloutId, UINT64 flowContex
UNUSED(layerId);
UNUSED(calloutId);
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);
fort_flow_delete(&g_device->stat, flowContext);
}
static void
@ -494,17 +537,23 @@ fort_callout_transport_classify_v4 (const FWPS_INCOMING_VALUES0 *inFixedValues,
FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED))
&& netBufList != NULL
&& (netBuf = NET_BUFFER_LIST_FIRST_NB(netBufList)) != NULL) {
PFORT_STAT_FLOW flow = (PFORT_STAT_FLOW) flowContext;
PFORT_FLOW flow = (PFORT_FLOW) flowContext;
const UCHAR flow_flags = fort_flow_flags(flow);
const UCHAR defer_flag = inbound
? FORT_STAT_FLOW_DEFER_IN : FORT_STAT_FLOW_DEFER_OUT;
? FORT_FLOW_DEFER_IN : FORT_FLOW_DEFER_OUT;
const UCHAR speed_limit = inbound
? FORT_STAT_FLOW_SPEED_LIMIT_OUT : FORT_STAT_FLOW_SPEED_LIMIT_IN;
? FORT_FLOW_SPEED_LIMIT_OUT : FORT_FLOW_SPEED_LIMIT_IN;
const UCHAR flow_flags = speed_limit | defer_flag;
const BOOL defer_flow = (fort_stat_flow_flags(flow) & flow_flags) == flow_flags
const UCHAR speed_defer_flags = speed_limit | defer_flag;
const BOOL defer_flow = (flow_flags & speed_defer_flags) == speed_defer_flags
&& !g_device->power_off;
const BOOL fragment_packet = !inbound && (flow_flags
& (FORT_FLOW_FRAGMENT_DEFER | FORT_FLOW_FRAGMENTED))
== FORT_FLOW_FRAGMENT_DEFER;
/* 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.
@ -541,25 +590,35 @@ fort_callout_transport_classify_v4 (const FWPS_INCOMING_VALUES0 *inFixedValues,
/* Defer TCP Pure (zero length) ACK-packets */
if (defer_flow && NET_BUFFER_DATA_LENGTH(netBuf) == headerOffset) {
const NTSTATUS status = fort_defer_add(&g_device->defer,
const NTSTATUS status = fort_defer_packet_add(&g_device->defer,
inFixedValues, inMetaValues, netBufList, inbound,
flow->opt.group_index);
if (NT_SUCCESS(status))
goto block;
goto drop;
if (status == STATUS_CANT_TERMINATE_SELF) {
/* Clear ACK deferring */
fort_stat_flow_flags_clear(flow, defer_flag);
fort_flow_flags_clear(flow, defer_flag);
}
goto permit;
}
/* Fragment first TCP packet */
if (fragment_packet) {
fort_defer_stream_flush(&g_device->defer, fort_packet_inject_complete,
flow->flow_id, FALSE);
fort_flow_flags_set(flow, FORT_FLOW_FRAGMENTED);
}
}
/* permit: */
permit:
fort_callout_classify_permit(filter, classifyOut);
return;
block:
drop:
fort_callout_classify_drop(classifyOut);
return;
}
@ -721,10 +780,19 @@ fort_callout_remove (void)
static void
fort_callout_defer_flush (UINT32 list_bits, BOOL dispatchLevel)
{
fort_defer_flush(&g_device->defer, fort_transport_inject_complete,
fort_defer_packet_flush(&g_device->defer, fort_packet_inject_complete,
list_bits, dispatchLevel);
}
static void
fort_callout_defer_flush_all (void)
{
fort_callout_defer_flush(FORT_DEFER_FLUSH_ALL, FALSE);
fort_defer_stream_flush(&g_device->defer, fort_packet_inject_complete,
FORT_DEFER_STREAM_ALL, FALSE);
}
static NTSTATUS
fort_callout_force_reauth (const FORT_CONF_FLAGS old_conf_flags,
const FORT_CONF_FLAGS conf_flags,
@ -766,15 +834,16 @@ fort_callout_force_reauth (const FORT_CONF_FLAGS old_conf_flags,
}
/* Check flow filter */
if (old_conf_flags.log_stat != conf_flags.log_stat) {
if (old_conf_flags.log_stat != conf_flags.log_stat
|| old_conf_flags.filter_transport != conf_flags.filter_transport) {
if (old_conf_flags.log_stat) {
fort_prov_flow_unregister(engine);
}
stat_prov:
if (conf_flags.log_stat) {
const BOOL is_speed_limit = (stat->limit_bits != 0);
if ((status = fort_prov_flow_register(engine, is_speed_limit)))
if ((status = fort_prov_flow_register(engine,
conf_flags.filter_transport)))
goto cleanup;
}
}
@ -989,13 +1058,15 @@ fort_device_control (PDEVICE_OBJECT device, PIRP irp)
if (conf_ref == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
PFORT_STAT stat = &g_device->stat;
const FORT_CONF_FLAGS old_conf_flags = fort_conf_ref_set(conf_ref);
const FORT_CONF_FLAGS conf_flags = conf_ref->conf.flags;
const UINT32 defer_flush_bits =
(g_device->stat.limit_bits ^ conf_io->limit_bits);
(stat->conf_group.limit_bits ^ conf_io->conf_group.limit_bits);
fort_stat_update_limits(&g_device->stat, conf_io);
fort_stat_conf_update(stat, conf_io);
status = fort_callout_force_reauth(old_conf_flags, conf_flags,
defer_flush_bits);
@ -1064,7 +1135,7 @@ fort_power_callback (PVOID context, PVOID event, PVOID specifics)
g_device->power_off = power_off;
if (power_off) {
fort_callout_defer_flush(FORT_DEFER_FLUSH_ALL, FALSE);
fort_callout_defer_flush_all();
}
}
@ -1154,7 +1225,7 @@ fort_driver_unload (PDRIVER_OBJECT driver)
UNICODE_STRING device_link;
if (g_device != NULL) {
fort_callout_defer_flush(FORT_DEFER_FLUSH_ALL, FALSE);
fort_callout_defer_flush_all();
fort_timer_close(&g_device->app_timer);
fort_timer_close(&g_device->log_timer);
@ -1206,6 +1277,9 @@ DriverEntry (PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
UNUSED(reg_path);
// Use NX Non-Paged Pool
ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
/* Wait for BFE to start */
status = fort_bfe_wait();
if (!NT_SUCCESS(status)) {

View File

@ -3,6 +3,8 @@
#define FORT_DEFER_FLUSH_ALL 0xFFFFFFFF
#define FORT_DEFER_LIST_MAX (FORT_CONF_GROUP_MAX * 2)
#define FORT_DEFER_STREAM_ALL ((UINT64) ((INT64) -1))
#define HTONL(l) _byteswap_ulong(l)
#define NTOHL(l) HTONL(l)
#define HTONS(s) _byteswap_ushort(s)
@ -35,26 +37,38 @@ typedef struct tcp_header {
} TCP_HEADER, *PTCP_HEADER;
typedef struct fort_packet_in {
COMPARTMENT_ID compartmentId;
IF_INDEX interfaceIndex;
IF_INDEX subInterfaceIndex;
UINT16 ipHeaderSize;
UINT16 transportHeaderSize;
UINT16 nblOffset;
} FORT_PACKET_IN, *PFORT_PACKET_IN;
typedef struct fort_packet_out {
COMPARTMENT_ID compartmentId;
UINT32 remoteAddr4;
SCOPE_ID remoteScopeId;
UINT64 endpointHandle;
} FORT_PACKET_OUT, *PFORT_PACKET_OUT;
typedef struct fort_packet {
BOOL inbound;
typedef struct fort_packet_stream {
UINT16 layerId;
COMPARTMENT_ID compartmentId;
UINT32 streamFlags;
UINT32 calloutId;
UINT64 flowId;
} FORT_PACKET_STREAM, *PFORT_PACKET_STREAM;
typedef struct fort_packet {
UINT32 inbound : 1;
UINT32 is_stream : 1;
UINT32 dataOffset : 12;
UINT32 dataSize : 18;
PNET_BUFFER_LIST netBufList;
@ -63,6 +77,7 @@ typedef struct fort_packet {
union {
FORT_PACKET_IN in;
FORT_PACKET_OUT out;
FORT_PACKET_STREAM stream;
};
} FORT_PACKET, *PFORT_PACKET;
@ -74,33 +89,187 @@ typedef struct fort_defer_list {
typedef struct fort_defer {
UINT32 list_bits;
HANDLE injection4_id;
HANDLE transport_injection4_id;
HANDLE stream_injection4_id;
PFORT_PACKET packet_free;
tommy_arrayof packets;
FORT_DEFER_LIST stream_list;
FORT_DEFER_LIST lists[FORT_DEFER_LIST_MAX]; /* in/out-bounds */
KSPIN_LOCK lock;
} FORT_DEFER, *PFORT_DEFER;
typedef void (*FORT_INJECT_COMPLETE_FUNC) (PFORT_PACKET, PNET_BUFFER_LIST, BOOLEAN);
typedef NTSTATUS (*FORT_INJECT_FUNC) (PFORT_DEFER, PFORT_PACKET, PNET_BUFFER_LIST *, FORT_INJECT_COMPLETE_FUNC);
static NTSTATUS
fort_packet_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->dataOffset != 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->transport_injection4_id, NULL, NULL, 0,
AF_INET, pkt_in->compartmentId,
pkt_in->interfaceIndex, pkt_in->subInterfaceIndex,
*clonedNetBufList, complete_func, pkt);
}
return status;
}
static NTSTATUS
fort_packet_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->transport_injection4_id, NULL,
pkt_out->endpointHandle, 0,
&send_args, AF_INET, pkt_out->compartmentId,
*clonedNetBufList, complete_func, pkt);
}
return status;
}
static NTSTATUS
fort_packet_inject_stream (PFORT_DEFER defer,
PFORT_PACKET pkt,
PNET_BUFFER_LIST *clonedNetBufList,
FORT_INJECT_COMPLETE_FUNC complete_func)
{
PFORT_PACKET_STREAM pkt_stream = &pkt->stream;
PNET_BUFFER_LIST netBufList = pkt->netBufList;
PNET_BUFFER netBuffer = NET_BUFFER_LIST_FIRST_NB(netBufList);
NTSTATUS status;
/* Adjust the net buffer list offset to the start of the actual data. */
NdisAdvanceNetBufferDataStart(netBuffer, pkt->dataOffset, FALSE, NULL);
status = FwpsAllocateCloneNetBufferList0(netBufList,
NULL, NULL, 0, clonedNetBufList);
/* Undo the adjustment on the original net buffer list. */
NdisRetreatNetBufferDataStart(netBuffer, pkt->dataOffset, 0, NULL);
if (NT_SUCCESS(status)) {
status = FwpsStreamInjectAsync0(
defer->stream_injection4_id, NULL, 0,
pkt_stream->flowId, pkt_stream->calloutId,
pkt_stream->layerId, pkt_stream->streamFlags,
*clonedNetBufList, pkt->dataSize,
complete_func, pkt);
}
return status;
}
static BOOL
fort_packet_injected_by_self (PFORT_DEFER defer,
PNET_BUFFER_LIST netBufList)
{
const FWPS_PACKET_INJECTION_STATE state = FwpsQueryPacketInjectionState0(
defer->transport_injection4_id, netBufList, NULL);
return (state == FWPS_PACKET_INJECTED_BY_SELF
|| state == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF);
}
static PFORT_PACKET
fort_defer_packet_get (PFORT_DEFER defer)
{
PFORT_PACKET pkt = NULL;
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 */
tommy_arrayof_grow(&defer->packets, size + 1);
pkt = tommy_arrayof_ref(&defer->packets, size);
}
return pkt;
}
static void
fort_defer_packet_put (PFORT_DEFER defer, PFORT_PACKET pkt)
{
pkt->next = defer->packet_free;
defer->packet_free = pkt;
}
static void
fort_defer_open (PFORT_DEFER defer)
{
NTSTATUS status;
status = FwpsInjectionHandleCreate0(AF_INET, FWPS_INJECTION_TYPE_TRANSPORT,
&defer->injection4_id);
&defer->transport_injection4_id);
if (!NT_SUCCESS(status)) {
defer->injection4_id = INVALID_HANDLE_VALUE;
defer->transport_injection4_id = INVALID_HANDLE_VALUE;
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Defer: Injection init error: %x\n", status);
"FORT: Defer: Transport injection init error: %x\n", status);
}
status = FwpsInjectionHandleCreate0(AF_INET, FWPS_INJECTION_TYPE_STREAM,
&defer->stream_injection4_id);
if (!NT_SUCCESS(status)) {
defer->stream_injection4_id = INVALID_HANDLE_VALUE;
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Defer: Stream injection init error: %x\n", status);
}
tommy_arrayof_init(&defer->packets, sizeof(FORT_PACKET));
@ -111,13 +280,14 @@ fort_defer_open (PFORT_DEFER defer)
static void
fort_defer_close (PFORT_DEFER defer)
{
FwpsInjectionHandleDestroy0(defer->injection4_id);
FwpsInjectionHandleDestroy0(defer->transport_injection4_id);
FwpsInjectionHandleDestroy0(defer->stream_injection4_id);
tommy_arrayof_done(&defer->packets);
}
static NTSTATUS
fort_defer_add (PFORT_DEFER defer,
fort_defer_packet_add (PFORT_DEFER defer,
const FWPS_INCOMING_VALUES0 *inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues,
PNET_BUFFER_LIST netBufList,
@ -129,18 +299,12 @@ fort_defer_add (PFORT_DEFER defer,
UINT16 list_index;
NTSTATUS status;
if (defer->injection4_id == INVALID_HANDLE_VALUE)
if (defer->transport_injection4_id == INVALID_HANDLE_VALUE)
return STATUS_FWP_TCPIP_NOT_READY;
/* Skip self injected packet */
{
const FWPS_PACKET_INJECTION_STATE state = FwpsQueryPacketInjectionState0(
defer->injection4_id, netBufList, NULL);
if (state == FWPS_PACKET_INJECTED_BY_SELF
|| state == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)
if (fort_packet_injected_by_self(defer, netBufList))
return STATUS_CANT_TERMINATE_SELF;
}
/* Skip IpSec protected packet */
if (inbound) {
@ -163,21 +327,12 @@ fort_defer_add (PFORT_DEFER defer,
defer_list = &defer->lists[list_index];
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) {
pkt = fort_defer_packet_get(defer);
if (pkt == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
pkt = tommy_arrayof_ref(&defer->packets, size);
}
if (defer_list->packet_tail == NULL) {
defer_list->packet_head = defer_list->packet_tail = pkt;
} else {
@ -186,7 +341,9 @@ fort_defer_add (PFORT_DEFER defer,
}
pkt->inbound = inbound;
pkt->compartmentId = inMetaValues->compartmentId;
pkt->is_stream = FALSE;
pkt->dataOffset = 0;
pkt->dataSize = 0; /* not used */
pkt->netBufList = netBufList;
pkt->next = NULL;
@ -201,13 +358,15 @@ fort_defer_add (PFORT_DEFER defer,
? FWPS_FIELD_INBOUND_TRANSPORT_V4_SUB_INTERFACE_INDEX
: FWPS_FIELD_OUTBOUND_TRANSPORT_V4_SUB_INTERFACE_INDEX;
pkt_in->compartmentId = inMetaValues->compartmentId;
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(
pkt->dataOffset = (UINT16) NET_BUFFER_DATA_OFFSET(
NET_BUFFER_LIST_FIRST_NB(netBufList));
} else {
PFORT_PACKET_OUT pkt_out = &pkt->out;
@ -216,6 +375,8 @@ fort_defer_add (PFORT_DEFER defer,
? FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS
: FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS;
pkt_out->compartmentId = inMetaValues->compartmentId;
/* host-order -> network-order conversion */
pkt_out->remoteAddr4 = HTONL(
inFixedValues->incomingValue[remoteAddrField].value.uint32);
@ -224,7 +385,7 @@ fort_defer_add (PFORT_DEFER defer,
pkt_out->endpointHandle = inMetaValues->transportEndpointHandle;
}
FwpsReferenceNetBufferList0(netBufList, TRUE);
FwpsReferenceNetBufferList0(pkt->netBufList, TRUE);
/* Set to be flushed bit */
defer->list_bits |= (1 << list_index);
@ -237,8 +398,76 @@ fort_defer_add (PFORT_DEFER defer,
return status;
}
static NTSTATUS
fort_defer_stream_add (PFORT_DEFER defer,
const FWPS_INCOMING_VALUES0 *inFixedValues,
const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues,
const FWPS_STREAM_DATA0 *streamData,
const FWPS_FILTER0 *filter,
BOOL inbound)
{
KLOCK_QUEUE_HANDLE lock_queue;
PFORT_PACKET pkt;
NTSTATUS status;
if (defer->stream_injection4_id == INVALID_HANDLE_VALUE)
return STATUS_FWP_TCPIP_NOT_READY;
/* Check data offset compatibility */
{
const PNET_BUFFER netBuf = NET_BUFFER_LIST_FIRST_NB(streamData->netBufferListChain);
const FWPS_STREAM_DATA_OFFSET0 *dataOffset = &streamData->dataOffset;
if (dataOffset->streamDataOffset != 0
&& (dataOffset->netBuffer != netBuf
|| dataOffset->mdl != NET_BUFFER_CURRENT_MDL(netBuf)))
return STATUS_INVALID_PARAMETER;
}
KeAcquireInStackQueuedSpinLock(&defer->lock, &lock_queue);
pkt = fort_defer_packet_get(defer);
if (pkt == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto end;
}
if (defer->stream_list.packet_tail == NULL) {
defer->stream_list.packet_head = defer->stream_list.packet_tail = pkt;
} else {
defer->stream_list.packet_tail->next = pkt;
defer->stream_list.packet_tail = pkt;
}
pkt->inbound = inbound;
pkt->is_stream = TRUE;
pkt->dataOffset = (UINT32) streamData->dataOffset.streamDataOffset;
pkt->dataSize = (UINT32) streamData->dataLength;
pkt->netBufList = streamData->netBufferListChain;
pkt->next = NULL;
{
PFORT_PACKET_STREAM pkt_stream = &pkt->stream;
pkt_stream->layerId = inFixedValues->layerId;
pkt_stream->streamFlags = streamData->flags;
pkt_stream->calloutId = filter->action.calloutId;
pkt_stream->flowId = inMetaValues->flowHandle;
}
FwpsReferenceNetBufferList0(pkt->netBufList, TRUE);
status = STATUS_SUCCESS;
end:
KeReleaseInStackQueuedSpinLock(&lock_queue);
return status;
}
static void
fort_defer_free (PFORT_DEFER defer, PFORT_PACKET pkt,
fort_defer_packet_free (PFORT_DEFER defer,
PFORT_PACKET pkt,
PNET_BUFFER_LIST clonedNetBufList,
BOOL dispatchLevel)
{
@ -264,8 +493,7 @@ fort_defer_free (PFORT_DEFER defer, PFORT_PACKET pkt,
KeAcquireInStackQueuedSpinLock(&defer->lock, &lock_queue);
}
pkt->next = defer->packet_free;
defer->packet_free = pkt;
fort_defer_packet_put(defer, pkt);
if (dispatchLevel) {
KeReleaseInStackQueuedSpinLockFromDpcLevel(&lock_queue);
@ -274,91 +502,54 @@ fort_defer_free (PFORT_DEFER defer, PFORT_PACKET pkt,
}
}
static NTSTATUS
fort_defer_inject_in (PFORT_DEFER defer, PFORT_PACKET pkt,
PNET_BUFFER_LIST *clonedNetBufList,
FORT_INJECT_COMPLETE_FUNC complete_func)
static void
fort_defer_packet_inject (PFORT_DEFER defer,
PFORT_PACKET pkt,
FORT_INJECT_COMPLETE_FUNC complete_func,
BOOL dispatchLevel)
{
PFORT_PACKET_IN pkt_in = &pkt->in;
PNET_BUFFER_LIST netBufList = pkt->netBufList;
PNET_BUFFER netBuffer = NET_BUFFER_LIST_FIRST_NB(netBufList);
while (pkt != NULL) {
PFORT_PACKET pkt_next = pkt->next;
PNET_BUFFER_LIST clonedNetBufList = NULL;
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;
FORT_INJECT_FUNC inject_func = pkt->is_stream
? &fort_packet_inject_stream
: (pkt->inbound ? &fort_packet_inject_in : &fort_packet_inject_out);
status = inject_func(defer, pkt, &clonedNetBufList, complete_func);
if (!NT_SUCCESS(status)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Defer: Injection prepare error: %x\n", status);
if (clonedNetBufList != NULL) {
clonedNetBufList->Status = STATUS_SUCCESS;
}
/* 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);
fort_defer_packet_free(defer, pkt, clonedNetBufList, dispatchLevel);
}
return status;
pkt = pkt_next;
}
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_flush (PFORT_DEFER defer,
fort_defer_packet_flush (PFORT_DEFER defer,
FORT_INJECT_COMPLETE_FUNC complete_func,
UINT32 list_bits,
BOOL dispatchLevel)
{
KLOCK_QUEUE_HANDLE lock_queue;
PFORT_DEFER_LIST defer_list;
PFORT_PACKET pkt;
PFORT_PACKET pkt_chain;
int i;
list_bits &= defer->list_bits;
if (list_bits == 0)
return;
pkt = NULL;
pkt_chain = NULL;
if (dispatchLevel) {
KeAcquireInStackQueuedSpinLockAtDpcLevel(&defer->lock, &lock_queue);
@ -376,8 +567,8 @@ fort_defer_flush (PFORT_DEFER defer,
defer_list = &defer->lists[i];
if (defer_list->packet_head != NULL) {
defer_list->packet_tail->next = pkt;
pkt = defer_list->packet_head;
defer_list->packet_tail->next = pkt_chain;
pkt_chain = defer_list->packet_head;
defer_list->packet_head = defer_list->packet_tail = NULL;
}
@ -392,26 +583,61 @@ fort_defer_flush (PFORT_DEFER defer,
KeReleaseInStackQueuedSpinLock(&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)) {
DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
"FORT: Defer: Injection prepare error: %x\n", status);
if (clonedNetBufList != NULL) {
clonedNetBufList->Status = STATUS_SUCCESS;
fort_defer_packet_inject(defer, pkt_chain, complete_func, dispatchLevel);
}
fort_defer_free(defer, pkt, clonedNetBufList, TRUE);
static void
fort_defer_stream_flush (PFORT_DEFER defer,
FORT_INJECT_COMPLETE_FUNC complete_func,
UINT64 flowId,
BOOL dispatchLevel)
{
KLOCK_QUEUE_HANDLE lock_queue;
PFORT_PACKET pkt_chain = NULL;
if (dispatchLevel) {
KeAcquireInStackQueuedSpinLockAtDpcLevel(&defer->lock, &lock_queue);
} else {
KeAcquireInStackQueuedSpinLock(&defer->lock, &lock_queue);
}
if (flowId == FORT_DEFER_STREAM_ALL) {
pkt_chain = defer->stream_list.packet_head;
defer->stream_list.packet_head = defer->stream_list.packet_tail = NULL;
}
else if (defer->stream_list.packet_head != NULL) {
PFORT_PACKET pkt = defer->stream_list.packet_head;
do {
PFORT_PACKET pkt_next = pkt->next;
if (flowId == pkt->stream.flowId) {
if (pkt_chain != NULL) {
pkt_chain->next = pkt;
}
pkt->next = NULL;
pkt_chain = pkt;
if (pkt == defer->stream_list.packet_head) {
defer->stream_list.packet_head = pkt_next;
}
if (pkt == defer->stream_list.packet_tail) {
defer->stream_list.packet_tail = NULL;
}
}
pkt = pkt_next;
} while (pkt != NULL);
}
if (dispatchLevel) {
KeReleaseInStackQueuedSpinLockFromDpcLevel(&lock_queue);
} else {
KeReleaseInStackQueuedSpinLock(&lock_queue);
}
fort_defer_packet_inject(defer, pkt_chain, complete_func, dispatchLevel);
}

View File

@ -41,28 +41,32 @@ typedef struct fort_stat_proc {
struct fort_stat_proc *next_active;
} FORT_STAT_PROC, *PFORT_STAT_PROC;
#define FORT_STAT_FLOW_SPEED_LIMIT_IN 0x01
#define FORT_STAT_FLOW_SPEED_LIMIT_OUT 0x02
#define FORT_STAT_FLOW_SPEED_LIMIT (FORT_STAT_FLOW_SPEED_LIMIT_IN | FORT_STAT_FLOW_SPEED_LIMIT_OUT)
#define FORT_STAT_FLOW_DEFER_IN 0x04
#define FORT_STAT_FLOW_DEFER_OUT 0x08
#define FORT_FLOW_SPEED_LIMIT_IN 0x01
#define FORT_FLOW_SPEED_LIMIT_OUT 0x02
#define FORT_FLOW_SPEED_LIMIT (FORT_FLOW_SPEED_LIMIT_IN | FORT_FLOW_SPEED_LIMIT_OUT)
#define FORT_FLOW_DEFER_IN 0x04
#define FORT_FLOW_DEFER_OUT 0x08
#define FORT_FLOW_FRAGMENT 0x10
#define FORT_FLOW_FRAGMENT_DEFER 0x20
#define FORT_FLOW_FRAGMENTED 0x40
#define FORT_FLOW_XFLAGS (FORT_FLOW_FRAGMENT_DEFER | FORT_FLOW_FRAGMENTED)
typedef struct fort_stat_flow_opt {
typedef struct fort_flow_opt {
UCHAR volatile flags;
UCHAR group_index;
UINT16 proc_index;
} FORT_STAT_FLOW_OPT, *PFORT_STAT_FLOW_OPT;
} FORT_FLOW_OPT, *PFORT_FLOW_OPT;
/* Synchronize with tommy_hashdyn_node! */
typedef struct fort_stat_flow {
struct fort_stat_flow *next;
struct fort_stat_flow *prev;
typedef struct fort_flow {
struct fort_flow *next;
struct fort_flow *prev;
union {
#if defined(_WIN64)
UINT64 flow_id;
#else
FORT_STAT_FLOW_OPT opt;
FORT_FLOW_OPT opt;
#endif
void *data;
};
@ -70,11 +74,11 @@ typedef struct fort_stat_flow {
tommy_key_t flow_hash;
#if defined(_WIN64)
FORT_STAT_FLOW_OPT opt;
FORT_FLOW_OPT opt;
#else
UINT64 flow_id;
#endif
} FORT_STAT_FLOW, *PFORT_STAT_FLOW;
} FORT_FLOW, *PFORT_FLOW;
typedef struct fort_stat {
UCHAR volatile closed;
@ -83,7 +87,6 @@ typedef struct fort_stat {
UINT16 proc_active_count;
UINT32 limit_bits;
UINT32 group_flush_bits;
UINT32 stream4_id;
@ -94,7 +97,7 @@ typedef struct fort_stat {
PFORT_STAT_PROC proc_free;
PFORT_STAT_PROC proc_active;
PFORT_STAT_FLOW flow_free;
PFORT_FLOW flow_free;
tommy_arrayof procs;
tommy_hashdyn procs_map;
@ -102,17 +105,22 @@ typedef struct fort_stat {
tommy_arrayof flows;
tommy_hashdyn flows_map;
FORT_TRAF limits[FORT_CONF_GROUP_MAX];
FORT_CONF_GROUP conf_group;
FORT_STAT_GROUP groups[FORT_CONF_GROUP_MAX];
KSPIN_LOCK lock;
} FORT_STAT, *PFORT_STAT;
#define fort_stat_proc_hash(process_id) tommy_inthash_u32((UINT32) (process_id))
#define fort_stat_flow_hash(flow_id) tommy_inthash_u32((UINT32) (flow_id))
#define fort_flow_hash(flow_id) tommy_inthash_u32((UINT32) (flow_id))
#define fort_stat_group_fragment(stat, group_index) \
((((stat)->conf_group.fragment_bits >> (group_index)) & 1) != 0 \
? FORT_FLOW_FRAGMENT : 0)
#define fort_stat_group_speed_limit(stat, group_index) \
(((stat)->limit_bits >> ((group_index) * 2)) & 3)
(((stat)->conf_group.limit_bits >> ((group_index) * 2)) & 3)
static void
@ -215,25 +223,25 @@ fort_stat_proc_add (PFORT_STAT stat, UINT32 process_id)
}
static UCHAR
fort_stat_flow_flags (PFORT_STAT_FLOW flow)
fort_flow_flags_set (PFORT_FLOW flow, UCHAR flags)
{
return flow->opt.flags;
return InterlockedOr8(&flow->opt.flags, flags);
}
static UCHAR
fort_flow_flags_clear (PFORT_FLOW flow, UCHAR flags)
{
return InterlockedAnd8(&flow->opt.flags, ~flags);
}
static UCHAR
fort_flow_flags (PFORT_FLOW flow)
{
return fort_flow_flags_set(flow, 0);
}
static void
fort_stat_flow_flags_set (PFORT_STAT_FLOW flow, UCHAR flags)
{
flow->opt.flags |= flags;
}
static void
fort_stat_flow_flags_clear (PFORT_STAT_FLOW flow, UCHAR flags)
{
flow->opt.flags &= ~flags;
}
static void
fort_stat_flow_context_set (PFORT_STAT stat, PFORT_STAT_FLOW flow)
fort_flow_context_set (PFORT_STAT stat, PFORT_FLOW flow)
{
const UINT64 flow_id = flow->flow_id;
const UINT64 flowContext = (UINT64) flow;
@ -245,7 +253,7 @@ fort_stat_flow_context_set (PFORT_STAT stat, PFORT_STAT_FLOW flow)
}
static void
fort_stat_flow_context_remove (PFORT_STAT stat, PFORT_STAT_FLOW flow)
fort_flow_context_remove (PFORT_STAT stat, PFORT_FLOW flow)
{
const UINT64 flow_id = flow->flow_id;
@ -256,15 +264,15 @@ fort_stat_flow_context_remove (PFORT_STAT stat, PFORT_STAT_FLOW flow)
}
static void
fort_stat_flow_close (PFORT_STAT_FLOW flow)
fort_flow_close (PFORT_FLOW flow)
{
flow->opt.proc_index = FORT_PROC_BAD_INDEX;
}
static PFORT_STAT_FLOW
fort_stat_flow_get (PFORT_STAT stat, UINT64 flow_id, tommy_key_t flow_hash)
static PFORT_FLOW
fort_flow_get (PFORT_STAT stat, UINT64 flow_id, tommy_key_t flow_hash)
{
PFORT_STAT_FLOW flow = (PFORT_STAT_FLOW) tommy_hashdyn_bucket(
PFORT_FLOW flow = (PFORT_FLOW) tommy_hashdyn_bucket(
&stat->flows_map, flow_hash);
while (flow != NULL) {
@ -277,7 +285,7 @@ fort_stat_flow_get (PFORT_STAT stat, UINT64 flow_id, tommy_key_t flow_hash)
}
static void
fort_stat_flow_free (PFORT_STAT stat, PFORT_STAT_FLOW flow)
fort_flow_free (PFORT_STAT stat, PFORT_FLOW flow)
{
const UINT16 proc_index = flow->opt.proc_index;
@ -293,12 +301,12 @@ fort_stat_flow_free (PFORT_STAT stat, PFORT_STAT_FLOW flow)
}
static NTSTATUS
fort_stat_flow_add (PFORT_STAT stat, UINT64 flow_id,
fort_flow_add (PFORT_STAT stat, UINT64 flow_id,
UCHAR group_index, UINT16 proc_index,
UCHAR speed_limit, BOOL is_reauth)
UCHAR fragment, UCHAR speed_limit, BOOL is_reauth)
{
const tommy_key_t flow_hash = fort_stat_flow_hash(flow_id);
PFORT_STAT_FLOW flow = fort_stat_flow_get(stat, flow_id, flow_hash);
const tommy_key_t flow_hash = fort_flow_hash(flow_id);
PFORT_FLOW flow = fort_flow_get(stat, flow_id, flow_hash);
BOOL is_new_flow = FALSE;
if (flow == NULL) {
@ -323,7 +331,7 @@ fort_stat_flow_add (PFORT_STAT stat, UINT64 flow_id,
flow->flow_id = flow_id;
fort_stat_flow_context_set(stat, flow);
fort_flow_context_set(stat, flow);
is_new_flow = TRUE;
} else {
@ -334,7 +342,8 @@ fort_stat_flow_add (PFORT_STAT stat, UINT64 flow_id,
fort_stat_proc_inc(stat, proc_index);
}
flow->opt.flags = speed_limit;
flow->opt.flags = fragment | speed_limit
| (is_new_flow ? 0 : (flow->opt.flags & FORT_FLOW_XFLAGS));
flow->opt.group_index = group_index;
flow->opt.proc_index = proc_index;
@ -347,7 +356,7 @@ fort_stat_open (PFORT_STAT stat)
tommy_arrayof_init(&stat->procs, sizeof(FORT_STAT_PROC));
tommy_hashdyn_init(&stat->procs_map);
tommy_arrayof_init(&stat->flows, sizeof(FORT_STAT_FLOW));
tommy_arrayof_init(&stat->flows, sizeof(FORT_FLOW));
tommy_hashdyn_init(&stat->flows_map);
KeInitializeSpinLock(&stat->lock);
@ -363,7 +372,7 @@ fort_stat_close (PFORT_STAT stat)
stat->closed = TRUE;
tommy_hashdyn_foreach_node_arg(&stat->flows_map,
fort_stat_flow_context_remove, stat);
fort_flow_context_remove, stat);
tommy_arrayof_done(&stat->procs);
tommy_hashdyn_done(&stat->procs_map);
@ -380,7 +389,7 @@ fort_stat_clear (PFORT_STAT stat)
fort_stat_proc_active_clear(stat);
tommy_hashdyn_foreach_node_arg(&stat->procs_map, fort_stat_proc_free, stat);
tommy_hashdyn_foreach_node(&stat->flows_map, fort_stat_flow_close);
tommy_hashdyn_foreach_node(&stat->flows_map, fort_flow_close);
RtlZeroMemory(stat->groups, sizeof(stat->groups));
}
@ -402,32 +411,26 @@ fort_stat_update (PFORT_STAT stat, BOOL log_stat)
}
static void
fort_stat_update_limits (PFORT_STAT stat, PFORT_CONF_IO conf_io)
fort_stat_conf_update (PFORT_STAT stat, PFORT_CONF_IO conf_io)
{
KLOCK_QUEUE_HANDLE lock_queue;
KeAcquireInStackQueuedSpinLock(&stat->lock, &lock_queue);
{
const UINT32 limit_bits = conf_io->limit_bits;
stat->limit_bits = limit_bits;
if (limit_bits != 0) {
RtlCopyMemory(stat->limits, conf_io->limits, sizeof(stat->limits));
}
stat->conf_group = conf_io->conf_group;
}
KeReleaseInStackQueuedSpinLock(&lock_queue);
}
static NTSTATUS
fort_stat_flow_associate (PFORT_STAT stat, UINT64 flow_id,
fort_flow_associate (PFORT_STAT stat, UINT64 flow_id,
UINT32 process_id, UCHAR group_index,
BOOL is_reauth, BOOL *is_new_proc)
{
const tommy_key_t proc_hash = fort_stat_proc_hash(process_id);
KLOCK_QUEUE_HANDLE lock_queue;
PFORT_STAT_PROC proc;
UCHAR speed_limit;
UCHAR fragment, speed_limit;
NTSTATUS status;
KeAcquireInStackQueuedSpinLock(&stat->lock, &lock_queue);
@ -455,10 +458,12 @@ fort_stat_flow_associate (PFORT_STAT stat, UINT64 flow_id,
*is_new_proc = TRUE;
}
fragment = fort_stat_group_fragment(stat, group_index);
speed_limit = fort_stat_group_speed_limit(stat, group_index);
status = fort_stat_flow_add(stat, flow_id,
group_index, proc->proc_index, speed_limit, is_reauth);
status = fort_flow_add(stat, flow_id,
group_index, proc->proc_index,
fragment, speed_limit, is_reauth);
if (!NT_SUCCESS(status) && *is_new_proc) {
fort_stat_proc_free(stat, proc);
@ -471,27 +476,27 @@ fort_stat_flow_associate (PFORT_STAT stat, UINT64 flow_id,
}
static void
fort_stat_flow_delete (PFORT_STAT stat, UINT64 flowContext)
fort_flow_delete (PFORT_STAT stat, UINT64 flowContext)
{
KLOCK_QUEUE_HANDLE lock_queue;
PFORT_STAT_FLOW flow = (PFORT_STAT_FLOW) flowContext;
PFORT_FLOW flow = (PFORT_FLOW) flowContext;
if (stat->closed)
return; /* double check to avoid deadlock after remove-flow-context */
KeAcquireInStackQueuedSpinLock(&stat->lock, &lock_queue);
if (!stat->closed) {
fort_stat_flow_free(stat, flow);
fort_flow_free(stat, flow);
}
KeReleaseInStackQueuedSpinLock(&lock_queue);
}
static void
fort_stat_flow_classify (PFORT_STAT stat, UINT64 flowContext,
fort_flow_classify (PFORT_STAT stat, UINT64 flowContext,
UINT32 data_len, BOOL inbound)
{
KLOCK_QUEUE_HANDLE lock_queue;
PFORT_STAT_FLOW flow = (PFORT_STAT_FLOW) flowContext;
PFORT_FLOW flow = (PFORT_FLOW) flowContext;
KeAcquireInStackQueuedSpinLock(&stat->lock, &lock_queue);
@ -500,18 +505,20 @@ fort_stat_flow_classify (PFORT_STAT stat, UINT64 flowContext,
UINT32 *proc_bytes = inbound ? &proc->traf.in_bytes
: &proc->traf.out_bytes;
const UCHAR stat_flow_flags = fort_flow_flags(flow);
/* Add traffic to process */
*proc_bytes += data_len;
if (flow->opt.flags & (inbound ? FORT_STAT_FLOW_SPEED_LIMIT_IN
: FORT_STAT_FLOW_SPEED_LIMIT_OUT)) {
if (stat_flow_flags & (inbound ? FORT_FLOW_SPEED_LIMIT_IN
: FORT_FLOW_SPEED_LIMIT_OUT)) {
const UCHAR group_index = flow->opt.group_index;
PFORT_STAT_GROUP group = &stat->groups[group_index];
UINT32 *group_bytes = inbound ? &group->traf.in_bytes
: &group->traf.out_bytes;
const PFORT_TRAF group_limit = &stat->limits[group_index];
const PFORT_TRAF group_limit = &stat->conf_group.limits[group_index];
const UINT32 limit_bytes = inbound ? group_limit->in_bytes
: group_limit->out_bytes;
@ -526,12 +533,12 @@ fort_stat_flow_classify (PFORT_STAT stat, UINT64 flowContext,
/* Defer ACK */
{
const UCHAR defer_flag = inbound
? FORT_STAT_FLOW_DEFER_OUT : FORT_STAT_FLOW_DEFER_IN;
? FORT_FLOW_DEFER_OUT : FORT_FLOW_DEFER_IN;
if (defer_flow)
fort_stat_flow_flags_set(flow, defer_flag);
fort_flow_flags_set(flow, defer_flag);
else
fort_stat_flow_flags_clear(flow, defer_flag);
fort_flow_flags_clear(flow, defer_flag);
}
stat->group_flush_bits |= (1 << list_index);
@ -614,7 +621,7 @@ fort_stat_dpc_group_flush (PFORT_STAT stat)
continue;
group = &stat->groups[i];
group_limit = &stat->limits[i];
group_limit = &stat->conf_group.limits[i];
traf = group->traf;

View File

@ -3,7 +3,7 @@
#define FORT_WORKER_REAUTH 0x01
typedef struct fort_worker {
LONG volatile id_bits;
UCHAR volatile id_bits;
PIO_WORKITEM item;
} FORT_WORKER, *PFORT_WORKER;
@ -27,7 +27,7 @@ static void
fort_worker_callback (PVOID device, PVOID context, PIO_WORKITEM item)
{
PFORT_WORKER worker = (PFORT_WORKER) context;
const LONG id_bits = InterlockedAnd(&worker->id_bits, 0);
const UCHAR id_bits = InterlockedAnd8(&worker->id_bits, 0);
UNUSED(device);
UNUSED(item);
@ -38,9 +38,9 @@ fort_worker_callback (PVOID device, PVOID context, PIO_WORKITEM item)
}
static void
fort_worker_queue (PFORT_WORKER worker, int work_id)
fort_worker_queue (PFORT_WORKER worker, UCHAR work_id)
{
const LONG id_bits = InterlockedOr(&worker->id_bits, work_id);
const UCHAR id_bits = InterlockedOr8(&worker->id_bits, work_id);
if (id_bits == 0) {
IoQueueWorkItemEx(worker->item, &fort_worker_callback,

View File

@ -6,6 +6,7 @@
AppGroup::AppGroup(QObject *parent) :
QObject(parent),
m_enabled(true),
m_fragmentPacket(false),
m_periodEnabled(false),
m_periodFrom(0),
m_periodTo(0),
@ -24,6 +25,14 @@ void AppGroup::setEnabled(bool enabled)
}
}
void AppGroup::setFragmentPacket(bool enabled)
{
if (bool(m_fragmentPacket) != enabled) {
m_fragmentPacket = enabled;
emit fragmentPacketChanged();
}
}
void AppGroup::setPeriodEnabled(bool periodEnabled)
{
if (bool(m_periodEnabled) != periodEnabled) {
@ -108,15 +117,20 @@ QString AppGroup::label() const
{
QString text = name();
if (limitInEnabled() && speedLimitIn() != 0) {
if (fragmentPacket()) {
text += QLatin1Char(' ')
+ QChar(0x2207) // ∇
+ QChar(0x00F7); // ÷
}
if (enabledSpeedLimitIn() != 0) {
text += QLatin1Char(' ')
+ QChar(0x25BC) // ▼
+ NetUtil::formatSpeed(speedLimitIn() * 1024);
}
if (limitOutEnabled() && speedLimitOut() != 0) {
if (enabledSpeedLimitOut() != 0) {
text += QLatin1Char(' ')
+ QChar(0x2206) // ∆
+ QChar(0x25B2) // ▲
+ NetUtil::formatSpeed(speedLimitOut() * 1024);
}
@ -132,6 +146,8 @@ QVariant AppGroup::toVariant() const
{
QVariantMap map;
map["fragmentPacket"] = fragmentPacket();
map["periodEnabled"] = periodEnabled();
map["periodFrom"] = periodFrom();
map["periodTo"] = periodTo();
@ -152,6 +168,8 @@ void AppGroup::fromVariant(const QVariant &v)
{
const QVariantMap map = v.toMap();
m_fragmentPacket = map["fragmentPacket"].toBool();
m_periodEnabled = map["periodEnabled"].toBool();
m_periodFrom = map["periodFrom"].toInt();
m_periodTo = map["periodTo"].toInt();

View File

@ -8,6 +8,7 @@ class AppGroup : public QObject
{
Q_OBJECT
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
Q_PROPERTY(bool fragmentPacket READ fragmentPacket WRITE setFragmentPacket NOTIFY fragmentPacketChanged)
Q_PROPERTY(bool periodEnabled READ periodEnabled WRITE setPeriodEnabled NOTIFY periodEnabledChanged)
Q_PROPERTY(int periodFrom READ periodFrom WRITE setPeriodFrom NOTIFY periodFromChanged)
Q_PROPERTY(int periodTo READ periodTo WRITE setPeriodTo NOTIFY periodToChanged)
@ -25,6 +26,9 @@ public:
bool enabled() const { return m_enabled; }
void setEnabled(bool enabled);
bool fragmentPacket() const { return m_fragmentPacket; }
void setFragmentPacket(bool enabled);
bool periodEnabled() const { return m_periodEnabled; }
void setPeriodEnabled(bool periodEnabled);
@ -46,6 +50,13 @@ public:
quint32 speedLimitOut() const { return m_speedLimitOut; }
void setSpeedLimitOut(quint32 limit);
quint32 enabledSpeedLimitIn() const {
return limitInEnabled() ? speedLimitIn() : 0;
}
quint32 enabledSpeedLimitOut() const {
return limitOutEnabled() ? speedLimitOut() : 0;
}
QString name() const { return m_name; }
void setName(const QString &name);
@ -62,6 +73,7 @@ public:
signals:
void enabledChanged();
void fragmentPacketChanged();
void periodEnabledChanged();
void periodFromChanged();
void periodToChanged();
@ -78,6 +90,8 @@ public slots:
private:
uint m_enabled : 1;
uint m_fragmentPacket : 1;
uint m_periodEnabled : 1;
uint m_periodFrom : 5;
uint m_periodTo : 5;

View File

@ -7,6 +7,7 @@
<file>images/application_double.png</file>
<file>images/application_edit.png</file>
<file>images/application_error.png</file>
<file>images/application_key.png</file>
<file>images/arrow_refresh.png</file>
<file>images/bin_empty.png</file>
<file>images/cancel.png</file>
@ -17,8 +18,6 @@
<file>images/cross.png</file>
<file>images/cut.png</file>
<file>images/database_save.png</file>
<file>images/flag_green.png</file>
<file>images/flag_yellow.png</file>
<file>images/link.png</file>
<file>images/page_copy.png</file>
<file>images/page_paste.png</file>

View File

@ -32,7 +32,7 @@
<file>qml/pages/addresses/AddressGroupRow.qml</file>
<file>qml/pages/apps/AppsColumn.qml</file>
<file>qml/pages/apps/AppsTextColumn.qml</file>
<file>qml/pages/apps/SpeedLimitButton.qml</file>
<file>qml/pages/apps/GroupOptionsButton.qml</file>
<file>qml/pages/log/AppListView.qml</file>
<file>qml/pages/log/GraphButton.qml</file>
<file>qml/pages/log/IpListView.qml</file>

View File

@ -271,10 +271,10 @@ void GraphWindow::updateWindowTitleSpeed()
const auto outBytes = m_graphOut->data()->isEmpty()
? 0 : (m_graphOut->data()->constEnd() - 1)->mainValue();
setWindowTitle(QChar(0x2207) // ∇
setWindowTitle(QChar(0x25BC) // ▼
+ NetUtil::formatSpeed(quint32(inBytes))
+ QLatin1Char(' ')
+ QChar(0x2206) // ∆
+ QChar(0x25B2) // ▲
+ NetUtil::formatSpeed(quint32(outBytes))
);
}

Binary file not shown.

View File

@ -315,22 +315,22 @@
<translation>Сдвинуть направо</translation>
</message>
<message>
<location filename="../qml/pages/apps/AppsColumn.qml" line="47"/>
<location filename="../qml/pages/apps/AppsColumn.qml" line="49"/>
<source>Enabled</source>
<translation>Включено</translation>
</message>
<message>
<location filename="../qml/pages/apps/AppsColumn.qml" line="62"/>
<location filename="../qml/pages/apps/AppsColumn.qml" line="64"/>
<source>period, hours:</source>
<translation>период, часы</translation>
</message>
<message>
<location filename="../qml/pages/apps/AppsColumn.qml" line="101"/>
<location filename="../qml/pages/apps/AppsColumn.qml" line="103"/>
<source>Block</source>
<translation>Блокировать</translation>
</message>
<message>
<location filename="../qml/pages/apps/AppsColumn.qml" line="121"/>
<location filename="../qml/pages/apps/AppsColumn.qml" line="123"/>
<source>Allow</source>
<translation>Разрешить</translation>
</message>
@ -486,10 +486,16 @@
<translation>Ограничение скорости выгрузки, KiB/s:</translation>
</message>
<message>
<location filename="../qml/pages/apps/GroupOptionsButton.qml" line="11"/>
<location filename="../qml/pages/log/TrafOptionsButton.qml" line="11"/>
<source>Options</source>
<translation>Настройки</translation>
</message>
<message>
<location filename="../qml/pages/apps/GroupOptionsButton.qml" line="16"/>
<source>Fragment first TCP packet</source>
<translation>Фрагментировать первый TCP пакет</translation>
</message>
<message>
<location filename="../qml/pages/log/TrafOptionsButton.qml" line="21"/>
<location filename="../qml/pages/log/TrafOptionsButton.qml" line="36"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 671 B

View File

@ -50,7 +50,7 @@ Page {
: fortManager.applyConf(confFlagsOnly);
}
if (confSaved) {
if (confSaved && othersEdited) {
mainPage.saved();
}

View File

@ -33,7 +33,7 @@ ColumnLayout {
Layout.preferredWidth: 10
}
SpeedLimitButton {
GroupOptionsButton {
enabled: firewallConf.logStat
}

View File

@ -6,13 +6,9 @@ import com.fortfirewall 1.0
ButtonPopup {
icon.source: !((appGroup.limitInEnabled && appGroup.speedLimitIn)
|| (appGroup.limitOutEnabled && appGroup.speedLimitOut))
? "qrc:/images/flag_green.png"
: "qrc:/images/flag_yellow.png"
text: (translationManager.trTrigger
&& qsTranslate("qml", "Speed Limit: "))
+ speedLimitsText
icon.source: "qrc:/images/application_key.png"
text: translationManager.trTrigger
&& qsTranslate("qml", "Options…")
readonly property var speedLimitValues: [
10, 0, 20, 30, 50, 75, 100, 150, 200, 300, 500, 900,
@ -32,28 +28,6 @@ ButtonPopup {
return list;
}
readonly property var speedLimitDisabledText: speedLimitNames[1]
readonly property string speedLimitsText: {
const limitIn = appGroup.limitInEnabled
? appGroup.speedLimitIn : 0;
const limitOut = appGroup.limitOutEnabled
? appGroup.speedLimitOut : 0;
if (!(limitIn || limitOut))
return speedLimitDisabledText;
var text = "";
if (limitIn) {
text = "DL " + formatSpeed(limitIn);
}
if (limitOut) {
text += (text ? "; " : "")
+ "UL " + formatSpeed(limitOut);
}
return text;
}
function formatSpeed(kbytes) {
return netUtil.formatSpeed(kbytes * 1024);
}
@ -108,5 +82,18 @@ ButtonPopup {
}
}
}
HSeparator {}
CheckBox {
text: translationManager.trTrigger
&& qsTranslate("qml", "Fragment first TCP packet")
checked: appGroup.fragmentPacket
onToggled: {
appGroup.fragmentPacket = checked;
setConfEdited();
}
}
}
}

View File

@ -97,6 +97,7 @@ int ConfUtil::writeFlags(const FirewallConf &conf, QByteArray &buf)
confFlags->app_allow_all = conf.appAllowAll();
confFlags->log_blocked = conf.logBlocked();
confFlags->log_stat = conf.logStat();
confFlags->filter_transport = appGroupFilterTransport(conf);
confFlags->group_bits = conf.appGroupBits();
return flagsSize;
@ -313,8 +314,10 @@ void ConfUtil::writeData(char *output, const FirewallConf &conf,
drvConfIo->driver_version = DRIVER_VERSION;
drvConfIo->limit_bits = writeLimits(
drvConfIo->limits, conf.appGroupsList());
drvConfIo->conf_group.fragment_bits = appGroupFragmentBits(conf);
drvConfIo->conf_group.limit_bits = writeLimits(
drvConfIo->conf_group.limits, conf.appGroupsList());
drvConf->flags.prov_boot = conf.provBoot();
drvConf->flags.filter_enabled = conf.filterEnabled();
@ -328,6 +331,8 @@ void ConfUtil::writeData(char *output, const FirewallConf &conf,
drvConf->flags.log_blocked = conf.logBlocked();
drvConf->flags.log_stat = conf.logStat();
drvConf->flags.filter_transport = appGroupFilterTransport(conf);
drvConf->flags.group_bits = conf.appGroupBits();
FortCommon::confAppPermsMaskInit(drvConf);
@ -343,6 +348,37 @@ void ConfUtil::writeData(char *output, const FirewallConf &conf,
drvConf->apps_off = appPathsOff;
}
quint16 ConfUtil::appGroupFragmentBits(const FirewallConf &conf)
{
quint16 fragmentBits = 0;
int i = 0;
foreach (const AppGroup *appGroup, conf.appGroupsList()) {
if (appGroup->enabled() && appGroup->fragmentPacket()) {
fragmentBits |= (1 << i);
}
++i;
}
return fragmentBits;
}
bool ConfUtil::appGroupFilterTransport(const FirewallConf &conf)
{
foreach (const AppGroup *appGroup, conf.appGroupsList()) {
if (!appGroup->enabled())
continue;
// Fragment
if (appGroup->fragmentPacket())
return true;
// Speed limit
if (appGroup->enabledSpeedLimitIn() != 0
|| appGroup->enabledSpeedLimitOut() != 0)
return true;
}
return false;
}
quint32 ConfUtil::writeLimits(struct fort_traf *limits,
const QList<AppGroup *> &appGroups)
{
@ -353,10 +389,11 @@ quint32 ConfUtil::writeLimits(struct fort_traf *limits,
for (int i = 0; i < groupsCount; ++i, ++limit) {
const AppGroup *appGroup = appGroups.at(i);
limit->in_bytes = appGroup->enabled() && appGroup->limitInEnabled()
? appGroup->speedLimitIn() * 1024 / 2 : 0;
limit->out_bytes = appGroup->enabled() && appGroup->limitOutEnabled()
? appGroup->speedLimitOut() * 1024 / 2 : 0;
if (!appGroup->enabled())
continue;
limit->in_bytes = appGroup->enabledSpeedLimitIn() * 1024 / 2;
limit->out_bytes = appGroup->enabledSpeedLimitOut() * 1024 / 2;
if (limit->in_bytes) {
limitBits |= (1 << (i * 2));

View File

@ -73,6 +73,9 @@ private:
quint8 appPeriodsCount,
const appgroups_map_t &appGroupIndexes);
static quint16 appGroupFragmentBits(const FirewallConf &conf);
static bool appGroupFilterTransport(const FirewallConf &conf);
static quint32 writeLimits(struct fort_traf *limits,
const QList<AppGroup *> &appGroups);