From f26079edf2550d9c9776d576d4ea6beff2875dc8 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Thu, 12 Jan 2023 10:39:48 +0100 Subject: [PATCH] [proxy] implement dynamic channel filter * Allow modules to configure intercept channels * Allow modules to rewrite packets --- include/freerdp/server/proxy/proxy_context.h | 19 +- .../freerdp/server/proxy/proxy_modules_api.h | 227 ++++++++++-------- include/freerdp/server/proxy/proxy_types.h | 48 ++++ server/proxy/channels/pf_channel_drdynvc.c | 54 ++++- server/proxy/pf_context.c | 11 +- server/proxy/pf_modules.c | 12 + server/proxy/proxy_modules.h | 3 + 7 files changed, 254 insertions(+), 120 deletions(-) create mode 100644 include/freerdp/server/proxy/proxy_types.h diff --git a/include/freerdp/server/proxy/proxy_context.h b/include/freerdp/server/proxy/proxy_context.h index 6e377dff4..0caa4ef8f 100644 --- a/include/freerdp/server/proxy/proxy_context.h +++ b/include/freerdp/server/proxy/proxy_context.h @@ -26,6 +26,7 @@ #include #include +#include #define PROXY_SESSION_ID_LENGTH 32 @@ -46,24 +47,6 @@ extern "C" /* All proxy interception channels derive from this base struct * and set their cleanup function accordingly. */ FREERDP_API void intercept_context_entry_free(void* obj); - - /** @brief how is handled a channel */ - typedef enum - { - PF_UTILS_CHANNEL_NOT_HANDLED, /*!< channel not handled */ - PF_UTILS_CHANNEL_BLOCK, /*!< block and drop traffic on this channel */ - PF_UTILS_CHANNEL_PASSTHROUGH, /*!< pass traffic from this channel */ - PF_UTILS_CHANNEL_INTERCEPT, /*!< inspect traffic from this channel */ - } pf_utils_channel_mode; - - /** @brief result of a channel treatment */ - typedef enum - { - PF_CHANNEL_RESULT_PASS, /*!< pass the packet as is */ - PF_CHANNEL_RESULT_DROP, /*!< drop the packet */ - PF_CHANNEL_RESULT_ERROR /*!< error during packet treatment */ - } PfChannelResult; - typedef PfChannelResult (*proxyChannelDataFn)(proxyData* pdata, const pServerStaticChannelContext* channel, const BYTE* xdata, size_t xsize, UINT32 flags, diff --git a/include/freerdp/server/proxy/proxy_modules_api.h b/include/freerdp/server/proxy/proxy_modules_api.h index 0a4466552..1887f9091 100644 --- a/include/freerdp/server/proxy/proxy_modules_api.h +++ b/include/freerdp/server/proxy/proxy_modules_api.h @@ -6,6 +6,8 @@ * Copyright 2019 Idan Freiberg * Copyright 2021 Armin Novak * Copyright 2021 Thincast Technologies GmbH + * Copyright 2023 Armin Novak + * Copyright 2023 Thincast Technologies GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,114 +25,130 @@ #ifndef FREERDP_SERVER_PROXY_MODULES_API_H #define FREERDP_SERVER_PROXY_MODULES_API_H -#include #include +#include #include +#include + #define MODULE_TAG(module) "proxy.modules." module -typedef struct proxy_data proxyData; -typedef struct proxy_module proxyModule; -typedef struct proxy_plugin proxyPlugin; -typedef struct proxy_plugins_manager proxyPluginsManager; - -/* hook callback. should return TRUE on success or FALSE on error. */ -typedef BOOL (*proxyHookFn)(proxyPlugin*, proxyData*, void*); - -/* - * Filter callback: - * It MUST return TRUE if the related event should be proxied, - * or FALSE if it should be ignored. - */ -typedef BOOL (*proxyFilterFn)(proxyPlugin*, proxyData*, void*); - -/* describes a plugin: name, description and callbacks to execute. - * - * This is public API, so always add new fields at the end of the struct to keep - * some backward compatibility. - */ -struct proxy_plugin +#ifdef __cplusplus +extern "C" { - const char* name; /* 0: unique module name */ - const char* description; /* 1: module description */ +#endif - UINT64 reserved1[32 - 2]; /* 2-32 */ + typedef struct proxy_data proxyData; + typedef struct proxy_module proxyModule; + typedef struct proxy_plugin proxyPlugin; + typedef struct proxy_plugins_manager proxyPluginsManager; - BOOL (*PluginUnload)(proxyPlugin* plugin); /* 33 */ - UINT64 reserved2[66 - 34]; /* 34 - 65 */ + /* hook callback. should return TRUE on success or FALSE on error. */ + typedef BOOL (*proxyHookFn)(proxyPlugin*, proxyData*, void*); - /* proxy hooks. a module can set these function pointers to register hooks */ - proxyHookFn ClientInitConnect; /* 66 custom=rdpContext* */ - proxyHookFn ClientUninitConnect; /* 67 custom=rdpContext* */ - proxyHookFn ClientPreConnect; /* 68 custom=rdpContext* */ - proxyHookFn ClientPostConnect; /* 69 custom=rdpContext* */ - proxyHookFn ClientPostDisconnect; /* 70 custom=rdpContext* */ - proxyHookFn ClientX509Certificate; /* 71 custom=rdpContext* */ - proxyHookFn ClientLoginFailure; /* 72 custom=rdpContext* */ - proxyHookFn ClientEndPaint; /* 73 custom=rdpContext* */ - proxyHookFn ClientRedirect; /* 74 custom=rdpContext* */ - proxyHookFn ClientLoadChannels; /* 75 custom=rdpContext* */ - UINT64 reserved3[96 - 76]; /* 76-95 */ + /* + * Filter callback: + * It MUST return TRUE if the related event should be proxied, + * or FALSE if it should be ignored. + */ + typedef BOOL (*proxyFilterFn)(proxyPlugin*, proxyData*, void*); - proxyHookFn ServerPostConnect; /* 96 custom=freerdp_peer* */ - proxyHookFn ServerPeerActivate; /* 97 custom=freerdp_peer* */ - proxyHookFn ServerChannelsInit; /* 98 custom=freerdp_peer* */ - proxyHookFn ServerChannelsFree; /* 99 custom=freerdp_peer* */ - proxyHookFn ServerSessionEnd; /* 100 custom=freerdp_peer* */ - proxyHookFn ServerSessionInitialize; /* 101 custom=freerdp_peer* */ - proxyHookFn ServerSessionStarted; /* 102 custom=freerdp_peer* */ + /* describes a plugin: name, description and callbacks to execute. + * + * This is public API, so always add new fields at the end of the struct to keep + * some backward compatibility. + */ + struct proxy_plugin + { + const char* name; /* 0: unique module name */ + const char* description; /* 1: module description */ - UINT64 reserved4[128 - 103]; /* 103 - 127 */ + UINT64 reserved1[32 - 2]; /* 2-32 */ - /* proxy filters. a module can set these function pointers to register filters */ - proxyFilterFn KeyboardEvent; /* 128 */ - proxyFilterFn MouseEvent; /* 129 */ - proxyFilterFn ClientChannelData; /* 130 passthrough channels data */ - proxyFilterFn ServerChannelData; /* 131 passthrough channels data */ - proxyFilterFn DynamicChannelCreate; /* 132 passthrough drdynvc channel create data */ - proxyFilterFn ServerFetchTargetAddr; /* 133 */ - proxyFilterFn ServerPeerLogon; /* 134 */ - proxyFilterFn ChannelCreate; /* 135 passthrough drdynvc channel create data */ - proxyFilterFn UnicodeEvent; /* 136 */ - proxyFilterFn MouseExEvent; /* 137 */ - UINT64 reserved5[160 - 138]; /* 138-159 */ + BOOL (*PluginUnload)(proxyPlugin* plugin); /* 33 */ + UINT64 reserved2[66 - 34]; /* 34 - 65 */ - /* Runtime data fields */ - proxyPluginsManager* mgr; /* 160 */ /** Set during plugin registration */ - void* userdata; /* 161 */ /** Custom data provided with RegisterPlugin, memory managed - outside of plugin. */ - void* custom; /* 162 */ /** Custom configuration data, must be allocated in RegisterPlugin and - freed in PluginUnload */ + /* proxy hooks. a module can set these function pointers to register hooks */ + proxyHookFn ClientInitConnect; /* 66 custom=rdpContext* */ + proxyHookFn ClientUninitConnect; /* 67 custom=rdpContext* */ + proxyHookFn ClientPreConnect; /* 68 custom=rdpContext* */ + proxyHookFn ClientPostConnect; /* 69 custom=rdpContext* */ + proxyHookFn ClientPostDisconnect; /* 70 custom=rdpContext* */ + proxyHookFn ClientX509Certificate; /* 71 custom=rdpContext* */ + proxyHookFn ClientLoginFailure; /* 72 custom=rdpContext* */ + proxyHookFn ClientEndPaint; /* 73 custom=rdpContext* */ + proxyHookFn ClientRedirect; /* 74 custom=rdpContext* */ + proxyHookFn ClientLoadChannels; /* 75 custom=rdpContext* */ + UINT64 reserved3[96 - 76]; /* 76-95 */ - UINT64 reserved6[192 - 163]; /* 163-191 Add some filler data to allow for new callbacks or - * fields without breaking API */ -}; + proxyHookFn ServerPostConnect; /* 96 custom=freerdp_peer* */ + proxyHookFn ServerPeerActivate; /* 97 custom=freerdp_peer* */ + proxyHookFn ServerChannelsInit; /* 98 custom=freerdp_peer* */ + proxyHookFn ServerChannelsFree; /* 99 custom=freerdp_peer* */ + proxyHookFn ServerSessionEnd; /* 100 custom=freerdp_peer* */ + proxyHookFn ServerSessionInitialize; /* 101 custom=freerdp_peer* */ + proxyHookFn ServerSessionStarted; /* 102 custom=freerdp_peer* */ -/* - * Main API for use by external modules. - * Supports: - * - Registering a plugin. - * - Setting/getting plugin's per-session specific data. - * - Aborting a session. - */ -struct proxy_plugins_manager -{ - /* 0 used for registering a fresh new proxy plugin. */ - BOOL (*RegisterPlugin)(struct proxy_plugins_manager* mgr, const proxyPlugin* plugin); + UINT64 reserved4[128 - 103]; /* 103 - 127 */ - /* 1 used for setting plugin's per-session info. */ - BOOL (*SetPluginData)(struct proxy_plugins_manager* mgr, const char*, proxyData*, void*); + /* proxy filters. a module can set these function pointers to register filters */ + proxyFilterFn KeyboardEvent; /* 128 */ + proxyFilterFn MouseEvent; /* 129 */ + proxyFilterFn ClientChannelData; /* 130 passthrough channels data */ + proxyFilterFn ServerChannelData; /* 131 passthrough channels data */ + proxyFilterFn DynamicChannelCreate; /* 132 passthrough drdynvc channel create data */ + proxyFilterFn ServerFetchTargetAddr; /* 133 */ + proxyFilterFn ServerPeerLogon; /* 134 */ + proxyFilterFn ChannelCreate; /* 135 passthrough drdynvc channel create data */ + proxyFilterFn UnicodeEvent; /* 136 */ + proxyFilterFn MouseExEvent; /* 137 */ - /* 2 used for getting plugin's per-session info. */ - void* (*GetPluginData)(struct proxy_plugins_manager* mgr, const char*, proxyData*); + /* proxy dynamic channel filters: + * + * - a function that returns the list of channels to intercept + * - a function to call with the data received + */ + proxyFilterFn DynChannelToIntercept; /* 138 */ + proxyFilterFn DynChannelIntercept; /* 139 */ + proxyFilterFn StaticChannelToIntercept; /* 140 */ + UINT64 reserved5[160 - 141]; /* 141-159 */ - /* 3 used for aborting a session. */ - void (*AbortConnect)(struct proxy_plugins_manager* mgr, proxyData*); + /* Runtime data fields */ + proxyPluginsManager* mgr; /* 160 */ /** Set during plugin registration */ + void* userdata; /* 161 */ /** Custom data provided with RegisterPlugin, memory managed + outside of plugin. */ + void* custom; /* 162 */ /** Custom configuration data, must be allocated in RegisterPlugin + and freed in PluginUnload */ - UINT64 reserved[128 - 4]; /* 4-127 reserved fields */ -}; + UINT64 reserved6[192 - 163]; /* 163-191 Add some filler data to allow for new callbacks or + * fields without breaking API */ + }; -typedef BOOL (*proxyModuleEntryPoint)(proxyPluginsManager* plugins_manager, void* userdata); + /* + * Main API for use by external modules. + * Supports: + * - Registering a plugin. + * - Setting/getting plugin's per-session specific data. + * - Aborting a session. + */ + struct proxy_plugins_manager + { + /* 0 used for registering a fresh new proxy plugin. */ + BOOL (*RegisterPlugin)(struct proxy_plugins_manager* mgr, const proxyPlugin* plugin); + + /* 1 used for setting plugin's per-session info. */ + BOOL (*SetPluginData)(struct proxy_plugins_manager* mgr, const char*, proxyData*, void*); + + /* 2 used for getting plugin's per-session info. */ + void* (*GetPluginData)(struct proxy_plugins_manager* mgr, const char*, proxyData*); + + /* 3 used for aborting a session. */ + void (*AbortConnect)(struct proxy_plugins_manager* mgr, proxyData*); + + UINT64 reserved[128 - 4]; /* 4-127 reserved fields */ + }; + + typedef BOOL (*proxyModuleEntryPoint)(proxyPluginsManager* plugins_manager, void* userdata); /* filter events parameters */ #define WINPR_PACK_PUSH @@ -174,14 +192,6 @@ typedef struct UINT32 flags; } proxyChannelDataEventInfo; -typedef enum -{ - PROXY_FETCH_TARGET_METHOD_DEFAULT, - PROXY_FETCH_TARGET_METHOD_CONFIG, - PROXY_FETCH_TARGET_METHOD_LOAD_BALANCE_INFO, - PROXY_FETCH_TARGET_USE_CUSTOM_ADDR -} ProxyFetchTargetMethod; - typedef struct { /* out values */ @@ -200,7 +210,32 @@ typedef struct server_peer_logon const SEC_WINNT_AUTH_IDENTITY* identity; BOOL automatic; } proxyServerPeerLogon; + +typedef struct dyn_channel_intercept_data +{ + const char* name; + UINT32 channelId; + wStream* data; + BOOL isBackData; + BOOL first; + BOOL last; + BOOL rewritten; + size_t packetSize; + PfChannelResult result; +} proxyDynChannelInterceptData; + +typedef struct dyn_channel_to_intercept_data +{ + const char* name; + UINT32 channelId; + BOOL intercept; +} proxyChannelToInterceptData; + #define WINPR_PACK_POP #include +#ifdef __cplusplus +} +#endif + #endif /* FREERDP_SERVER_PROXY_MODULES_API_H */ diff --git a/include/freerdp/server/proxy/proxy_types.h b/include/freerdp/server/proxy/proxy_types.h new file mode 100644 index 000000000..4dbba1b62 --- /dev/null +++ b/include/freerdp/server/proxy/proxy_types.h @@ -0,0 +1,48 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Proxy enum types + * + * Copyright 2023 Armin Novak + * Copyright 2023 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_SERVER_PROXY_TYPES_H +#define FREERDP_SERVER_PROXY_TYPES_H + +/** @brief how is handled a channel */ +typedef enum +{ + PF_UTILS_CHANNEL_NOT_HANDLED, /*!< channel not handled */ + PF_UTILS_CHANNEL_BLOCK, /*!< block and drop traffic on this channel */ + PF_UTILS_CHANNEL_PASSTHROUGH, /*!< pass traffic from this channel */ + PF_UTILS_CHANNEL_INTERCEPT, /*!< inspect traffic from this channel */ +} pf_utils_channel_mode; + +/** @brief result of a channel treatment */ +typedef enum +{ + PF_CHANNEL_RESULT_PASS, /*!< pass the packet as is */ + PF_CHANNEL_RESULT_DROP, /*!< drop the packet */ + PF_CHANNEL_RESULT_ERROR /*!< error during packet treatment */ +} PfChannelResult; +typedef enum +{ + PROXY_FETCH_TARGET_METHOD_DEFAULT, + PROXY_FETCH_TARGET_METHOD_CONFIG, + PROXY_FETCH_TARGET_METHOD_LOAD_BALANCE_INFO, + PROXY_FETCH_TARGET_USE_CUSTOM_ADDR +} ProxyFetchTargetMethod; + +#endif /* FREERDP_SERVER_PROXY_TYPES_H */ diff --git a/server/proxy/channels/pf_channel_drdynvc.c b/server/proxy/channels/pf_channel_drdynvc.c index 326020e1f..727d42648 100644 --- a/server/proxy/channels/pf_channel_drdynvc.c +++ b/server/proxy/channels/pf_channel_drdynvc.c @@ -87,6 +87,35 @@ typedef enum DYNCVC_READ_INCOMPLETE /*!< missing bytes to read the complete packet */ } DynvcReadResult; +static PfChannelResult data_cb(pServerContext* ps, pServerDynamicChannelContext* channel, + BOOL isBackData, ChannelStateTracker* tracker, BOOL firstPacket, + BOOL lastPacket) +{ + WINPR_ASSERT(ps); + WINPR_ASSERT(channel); + WINPR_ASSERT(tracker); + WINPR_ASSERT(ps->pdata); + + wStream* currentPacket = channelTracker_getCurrentPacket(tracker); + proxyDynChannelInterceptData dyn = { .name = channel->channelName, + .channelId = channel->channelId, + .data = currentPacket, + .isBackData = isBackData, + .first = firstPacket, + .last = lastPacket, + .rewritten = FALSE, + .packetSize = channelTracker_getCurrentPacketSize(tracker), + .result = PF_CHANNEL_RESULT_ERROR }; + Stream_SealLength(dyn.data); + if (!pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_INTERCEPT_CHANNEL, ps->pdata, &dyn)) + return PF_CHANNEL_RESULT_ERROR; + + channelTracker_setCurrentPacketSize(tracker, dyn.packetSize); + if (dyn.rewritten) + return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData); + return dyn.result; +} + static pServerDynamicChannelContext* DynamicChannelContext_new(pServerContext* ps, const char* name, UINT32 id) { @@ -106,15 +135,24 @@ static pServerDynamicChannelContext* DynamicChannelContext_new(pServerContext* p return NULL; } - ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name); + ret->frontTracker.dataCallback = data_cb; + ret->backTracker.dataCallback = data_cb; + + proxyChannelToInterceptData dyn = { .name = name, .channelId = id, .intercept = FALSE }; + if (pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_DYN_INTERCEPT_LIST, ps->pdata, &dyn) && + dyn.intercept) + ret->channelMode = PF_UTILS_CHANNEL_INTERCEPT; + else + ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name); ret->openStatus = CHANNEL_OPENSTATE_OPENED; ret->packetReassembly = (ret->channelMode == PF_UTILS_CHANNEL_INTERCEPT); return ret; } -static void DynamicChannelContext_free(pServerDynamicChannelContext* c) +static void DynamicChannelContext_free(void* ptr) { + pServerDynamicChannelContext* c = (pServerDynamicChannelContext*)ptr; if (!c) return; @@ -137,8 +175,10 @@ static UINT32 ChannelId_Hash(const void* key) return *v; } -static BOOL ChannelId_Compare(const UINT32* v1, const UINT32* v2) +static BOOL ChannelId_Compare(const void* objA, const void* objB) { + const UINT32* v1 = objA; + const UINT32* v2 = objB; return (*v1 == *v2); } @@ -557,10 +597,10 @@ static DynChannelContext* DynChannelContext_new(proxyData* pdata, goto fail; obj = HashTable_KeyObject(dyn->channels); - obj->fnObjectEquals = (OBJECT_EQUALS_FN)ChannelId_Compare; + obj->fnObjectEquals = ChannelId_Compare; obj = HashTable_ValueObject(dyn->channels); - obj->fnObjectFree = (OBJECT_FREE_FN)DynamicChannelContext_free; + obj->fnObjectFree = DynamicChannelContext_free; return dyn; @@ -575,9 +615,11 @@ static PfChannelResult pf_dynvc_back_data(proxyData* pdata, size_t totalSize) { WINPR_ASSERT(channel); + DynChannelContext* dyn = (DynChannelContext*)channel->context; WINPR_UNUSED(pdata); WINPR_ASSERT(dyn); + return channelTracker_update(dyn->backTracker, xdata, xsize, flags, totalSize); } @@ -587,9 +629,11 @@ static PfChannelResult pf_dynvc_front_data(proxyData* pdata, size_t totalSize) { WINPR_ASSERT(channel); + DynChannelContext* dyn = (DynChannelContext*)channel->context; WINPR_UNUSED(pdata); WINPR_ASSERT(dyn); + return channelTracker_update(dyn->frontTracker, xdata, xsize, flags, totalSize); } diff --git a/server/proxy/pf_context.c b/server/proxy/pf_context.c index 7dab6ce17..d5eac6a80 100644 --- a/server/proxy/pf_context.c +++ b/server/proxy/pf_context.c @@ -29,6 +29,8 @@ #include "pf_client.h" #include "pf_utils.h" +#include "proxy_modules.h" + #include #include "channels/pf_channel_rdpdr.h" @@ -65,7 +67,14 @@ pServerStaticChannelContext* StaticChannelContext_new(pServerContext* ps, const return NULL; } - ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name); + proxyChannelToInterceptData channel = { .name = name, .channelId = id, .intercept = FALSE }; + + if (pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_STATIC_INTERCEPT_LIST, ps->pdata, + &channel) && + channel.intercept) + ret->channelMode = PF_UTILS_CHANNEL_INTERCEPT; + else + ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name); return ret; } diff --git a/server/proxy/pf_modules.c b/server/proxy/pf_modules.c index 4fc45cfe8..323cc92cd 100644 --- a/server/proxy/pf_modules.c +++ b/server/proxy/pf_modules.c @@ -292,6 +292,18 @@ static BOOL pf_modules_ArrayList_ForEachFkt(void* data, size_t index, va_list ap result = IFCALLRESULT(TRUE, plugin->ServerPeerLogon, plugin, pdata, param); break; + case FILTER_TYPE_INTERCEPT_CHANNEL: + result = IFCALLRESULT(TRUE, plugin->DynChannelIntercept, plugin, pdata, param); + break; + + case FILTER_TYPE_DYN_INTERCEPT_LIST: + result = IFCALLRESULT(TRUE, plugin->DynChannelToIntercept, plugin, pdata, param); + break; + + case FILTER_TYPE_STATIC_INTERCEPT_LIST: + result = IFCALLRESULT(TRUE, plugin->StaticChannelToIntercept, plugin, pdata, param); + break; + case FILTER_LAST: default: WLog_ERR(TAG, "invalid filter called"); diff --git a/server/proxy/proxy_modules.h b/server/proxy/proxy_modules.h index 3f46b0d79..d5883a5c2 100644 --- a/server/proxy/proxy_modules.h +++ b/server/proxy/proxy_modules.h @@ -39,6 +39,9 @@ typedef enum FILTER_TYPE_SERVER_PEER_LOGON, /* proxyServerPeerLogon */ FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE, /* proxyChannelDataEventInfo */ + FILTER_TYPE_STATIC_INTERCEPT_LIST, /* proxyChannelToInterceptData */ + FILTER_TYPE_DYN_INTERCEPT_LIST, /* proxyChannelToInterceptData */ + FILTER_TYPE_INTERCEPT_CHANNEL, /* proxyDynChannelInterceptData */ FILTER_LAST } PF_FILTER_TYPE;