Skip to content

Commit

Permalink
v2.10.14
Browse files Browse the repository at this point in the history
  • Loading branch information
bernerdad committed Jun 25, 2024
1 parent a5f9d0c commit d0edfca
Show file tree
Hide file tree
Showing 14 changed files with 424 additions and 276 deletions.
302 changes: 208 additions & 94 deletions backend/windows/windscribe_service/split_tunneling/callout_filter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@
#include "../utils.h"

DEFINE_GUID(
WINDSCRIBE_CALLOUT_GUID,
WINDSCRIBE_BIND_CALLOUT_GUID,
0x5DF29179,
0x344E,
0x4F9C,
0xA4, 0x5D, 0xC3, 0x0F, 0x95, 0x9B, 0x01, 0x2D
);

DEFINE_GUID(
WINDSCRIBE_TCP_CALLOUT_GUID,
0xB53C4ADE,
0x7A35,
0x4A63,
0xB6, 0x90, 0xD9, 0x6B, 0xD4, 0x26, 0x21, 0x03
0xB6, 0x90, 0xD9, 0x6B, 0xD4, 0x26, 0x23, 0x03
);

DEFINE_GUID(
Expand All @@ -29,24 +37,14 @@ DEFINE_GUID(
0xBC, 0x77, 0x1F, 0xA4, 0x6E, 0x99, 0x4A, 0x4B
);

#pragma pack(push,1)

typedef struct WINDSCRIBE_CALLOUT_DATA_
{
UINT32 localIp;
UINT32 vpnIp;
UINT8 isExclude;

// this is a list of addresses represented by pairs of addreses and masks:
// excludeAddresses[0] -> Address_0; excludeAddresses[1] -> Mask_0
// .....
// excludeAddresses[cntExcludeAddresses - 2] -> Address_last; excludeAddresses[cntExcludeAddresses - 1] -> Mask_last
UINT16 cntExcludeAddresses;
UINT32 excludeAddresses[1];

} WINDSCRIBE_CALLOUT_DATA;

#pragma pack(pop)


CalloutFilter::CalloutFilter(FwpmWrapper &fwmpWrapper): fwmpWrapper_(fwmpWrapper), isEnabled_(false), prevLocalIp_(0), prevVpnIp_(0)
{
Expand Down Expand Up @@ -124,42 +122,12 @@ void CalloutFilter::disable()

bool CalloutFilter::addProviderContext(HANDLE engineHandle, const GUID &guid, UINT32 localIp, UINT32 vpnIp, bool isExclude)
{
const std::vector<std::string> lanRanges = {
"10.0.0.0/9", // this and the following 10.x.x.x ranges represent the range 10.0.0.0 - 10.254.255.255,
"10.128.0.0/10", // specifically excluding the reserved range of 10.255.255.0/24
"10.192.0.0/11",
"10.224.0.0/12",
"10.240.0.0/13",
"10.248.0.0/14",
"10.252.0.0/15",
"10.254.0.0/16",
"127.0.0.0/8",
"169.254.0.0/16",
"172.16.0.0/12",
"192.168.0.0/16",
"224.0.0.0/4",
};

bool bRet = true;
WINDSCRIBE_CALLOUT_DATA proxyData;
FWP_BYTE_BLOB byteBlob = { 0 };

// fill callout data struct
// here is some C style code needed to fill the structure and pass it to the driver
std::vector<unsigned char> data;
data.resize(sizeof(WINDSCRIBE_CALLOUT_DATA) - sizeof(WINDSCRIBE_CALLOUT_DATA().excludeAddresses) + lanRanges.size() * 2 * sizeof(UINT32));
WINDSCRIBE_CALLOUT_DATA *proxyData = reinterpret_cast<WINDSCRIBE_CALLOUT_DATA *>(data.data());

proxyData->localIp = localIp;
proxyData->vpnIp = vpnIp;
proxyData->isExclude = isExclude;
proxyData->cntExcludeAddresses = lanRanges.size() * 2;

int ind = 0;
for (const auto &it: lanRanges) {
Ip4AddressAndMask addr(it.c_str());
proxyData->excludeAddresses[ind++] = addr.ipHostOrder();
proxyData->excludeAddresses[ind++] = addr.maskHostOrder();
}
proxyData.localIp = localIp;
proxyData.vpnIp = vpnIp;
proxyData.isExclude = isExclude;

FWPM_PROVIDER_CONTEXT1 providerContext = { 0 };
UINT64 providerContextId;
Expand All @@ -169,8 +137,8 @@ bool CalloutFilter::addProviderContext(HANDLE engineHandle, const GUID &guid, UI
providerContext.type = FWPM_GENERAL_CONTEXT;

providerContext.dataBuffer = &byteBlob;
providerContext.dataBuffer->size = data.size();
providerContext.dataBuffer->data = (UINT8*)data.data();
providerContext.dataBuffer->size = sizeof(proxyData);
providerContext.dataBuffer->data = (UINT8*)&proxyData;

DWORD ret = FwpmProviderContextAdd1(engineHandle, &providerContext, 0, &providerContextId);
if (ret != ERROR_SUCCESS) {
Expand All @@ -183,16 +151,34 @@ bool CalloutFilter::addProviderContext(HANDLE engineHandle, const GUID &guid, UI

bool CalloutFilter::addCallouts(HANDLE engineHandle)
{
FWPM_CALLOUT0 callout = { 0 };
UINT32 calloutId;
callout.calloutKey = WINDSCRIBE_CALLOUT_GUID;
callout.displayData.name = (wchar_t*)L"Windscribe split tunnel callout";
callout.applicableLayer = FWPM_LAYER_ALE_CONNECT_REDIRECT_V4;
callout.flags |= FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT;
DWORD ret = FwpmCalloutAdd(engineHandle, &callout, NULL, &calloutId);
if (ret != ERROR_SUCCESS) {
Logger::instance().out(L"CalloutFilter::addCallouts(), FwpmCalloutAdd failed");
return false;
// Callout for non-TCP traffic
{
FWPM_CALLOUT0 callout = { 0 };
UINT32 calloutId;
callout.calloutKey = WINDSCRIBE_BIND_CALLOUT_GUID;
callout.displayData.name = (wchar_t*)L"Windscribe split tunnel non-TCP callout";
callout.applicableLayer = FWPM_LAYER_ALE_BIND_REDIRECT_V4;
callout.flags |= FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT;
DWORD ret = FwpmCalloutAdd(engineHandle, &callout, NULL, &calloutId);
if (ret != ERROR_SUCCESS) {
Logger::instance().out(L"CalloutFilter::addCallouts(), FwpmCalloutAdd (non-TCP) failed");
return false;
}
}

// Callout for TCP traffic
{
FWPM_CALLOUT0 callout = { 0 };
UINT32 calloutId;
callout.calloutKey = WINDSCRIBE_TCP_CALLOUT_GUID;
callout.displayData.name = (wchar_t*)L"Windscribe split tunnel TCP callout";
callout.applicableLayer = FWPM_LAYER_ALE_CONNECT_REDIRECT_V4;
callout.flags |= FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT;
DWORD ret = FwpmCalloutAdd(engineHandle, &callout, NULL, &calloutId);
if (ret != ERROR_SUCCESS) {
Logger::instance().out(L"CalloutFilter::addCallouts(), FwpmCalloutAdd (TCP) failed");
return false;
}
}
return true;
}
Expand All @@ -215,45 +201,172 @@ bool CalloutFilter::addSubLayer(HANDLE engineHandle)
bool CalloutFilter::addFilters(HANDLE engineHandle, bool withTcpFilters, const AppsIds &appsIds)
{
bool retValue = true;
const std::vector<std::string> lanRanges = {
"10.0.0.0/9", // this and the following 10.x.x.x ranges represent the range 10.0.0.0 - 10.254.255.255,
"10.128.0.0/10", // specifically excluding the reserved range of 10.255.255.0/24
"10.192.0.0/11",
"10.224.0.0/12",
"10.240.0.0/13",
"10.248.0.0/14",
"10.252.0.0/15",
"10.254.0.0/16",
"127.0.0.0/8",
"169.254.0.0/16",
"172.16.0.0/12",
"192.168.0.0/16",
"224.0.0.0/4",
};
std::vector<FWP_V4_ADDR_AND_MASK> addrMasks(lanRanges.size());
std::vector<FWP_V4_ADDR_AND_MASK> localAddrMasks;

if (appsIds.count() == 0) {
return true;
}

// All traffic for apps goes to ALE_CONNECT_REDIRECT_V4 filter
std::vector<FWPM_FILTER_CONDITION> conditions;
conditions.reserve(appsIds.count());

// Must match one of the apps
for (size_t i = 0; i < appsIds.count(); ++i) {
FWPM_FILTER_CONDITION condition;
condition.fieldKey = FWPM_CONDITION_ALE_APP_ID;
condition.matchType = FWP_MATCH_EQUAL;
condition.conditionValue.type = FWP_BYTE_BLOB_TYPE;
condition.conditionValue.byteBlob = (FWP_BYTE_BLOB *)appsIds.getAppId(i);
conditions.push_back(condition);
// All other traffic for apps goes to BIND filter
{
std::vector<FWPM_FILTER_CONDITION> conditions;
conditions.reserve(appsIds.count());
for (size_t i = 0; i < appsIds.count(); ++i) {
FWPM_FILTER_CONDITION condition;
condition.fieldKey = FWPM_CONDITION_ALE_APP_ID;
condition.matchType = FWP_MATCH_EQUAL;
condition.conditionValue.type = FWP_BYTE_BLOB_TYPE;
condition.conditionValue.byteBlob = (FWP_BYTE_BLOB *)appsIds.getAppId(i);
conditions.push_back(condition);
}

FWPM_FILTER filter = { 0 };
filter.subLayerKey = SUBLAYER_CALLOUT_GUID;
filter.layerKey = FWPM_LAYER_ALE_BIND_REDIRECT_V4;
filter.displayData.name = (wchar_t *)L"Windscribe bind filter for callout driver";
filter.weight.type = FWP_UINT8;
filter.weight.uint8 = 0x00;
filter.providerContextKey = CALLOUT_PROVIDER_CONTEXT_IP_GUID;
filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
filter.numFilterConditions = static_cast<UINT32>(conditions.size());
filter.filterCondition = &conditions[0];
filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN;
filter.action.calloutKey = WINDSCRIBE_BIND_CALLOUT_GUID;

UINT64 filterId;
DWORD ret = FwpmFilterAdd(engineHandle, &filter, NULL, &filterId);
retValue = (ret == ERROR_SUCCESS);
if (!retValue) {
Logger::instance().out(L"CalloutFilter::addFilter(), bind filter failed: %u", ret);
return retValue;
}
}


FWPM_FILTER filter = { 0 };
filter.subLayerKey = SUBLAYER_CALLOUT_GUID;
filter.layerKey = FWPM_LAYER_ALE_CONNECT_REDIRECT_V4;
filter.displayData.name = (wchar_t *)L"Windscribe ALE_CONNECT_REDIRECT_V4 filter for callout driver";
filter.weight.type = FWP_UINT8;
filter.weight.uint8 = 0x00;
filter.providerContextKey = CALLOUT_PROVIDER_CONTEXT_IP_GUID;
filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
filter.numFilterConditions = static_cast<UINT32>(conditions.size());
filter.filterCondition = &conditions[0];
filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN;
filter.action.calloutKey = WINDSCRIBE_CALLOUT_GUID;

UINT64 filterId;
DWORD ret = FwpmFilterAdd(engineHandle, &filter, NULL, &filterId);
retValue = (ret == ERROR_SUCCESS);
if (!retValue) {
Logger::instance().out(L"CalloutFilter::addFilter(), ALE_CONNECT_REDIRECT_V4 filter failed: %u", ret);
return retValue;
// LAN TCP traffic goes to CONNECT filter
if (withTcpFilters) {
// first, we create a filter that explicitly allows traffic to the private VPN IPs, which
// on IKEv2/OpenVPN reside on a LAN range.
{
std::vector<FWPM_FILTER_CONDITION> conditions;
AdaptersInfo ai;
std::vector<NET_IFINDEX> taps = ai.getTAPAdapters();

for (int i = 0; i < taps.size(); i++) {
std::vector<std::string> addrs = ai.getAdapterAddresses(taps[i]);

for (int j = 0; j < addrs.size(); j++) {
FWPM_FILTER_CONDITION condition;
FWP_V4_ADDR_AND_MASK addrMask;
condition.fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
condition.matchType = FWP_MATCH_EQUAL;
condition.conditionValue.type = FWP_V4_ADDR_MASK;
Ip4AddressAndMask ipAddress(addrs[j].c_str());
addrMask.addr = ipAddress.ipHostOrder();
addrMask.mask = 0xffffffff;
localAddrMasks.push_back(addrMask);
condition.conditionValue.v4AddrMask = &localAddrMasks.back();
conditions.push_back(condition);
}
}

if (!conditions.empty()) {
FWPM_FILTER filter = { 0 };
filter.subLayerKey = SUBLAYER_CALLOUT_GUID;
filter.layerKey = FWPM_LAYER_ALE_CONNECT_REDIRECT_V4;
filter.displayData.name = (wchar_t *)L"Windscribe TCP exception filter for callout driver";
filter.weight.type = FWP_UINT8;
filter.weight.uint8 = 0x01;
filter.numFilterConditions = static_cast<UINT32>(conditions.size());
filter.filterCondition = &conditions[0];
filter.action.type = FWP_ACTION_PERMIT;

UINT64 filterId;
DWORD ret = FwpmFilterAdd(engineHandle, &filter, NULL, &filterId);
retValue = (ret == ERROR_SUCCESS);
if (!retValue) {
Logger::instance().out(L"CalloutFilter::addFilter(), TCP exception filter failed: %u", ret);
return retValue;
}
}
}

// now, we create the filter which allows other LAN traffic to be routed back to the primary interface
{
std::vector<FWPM_FILTER_CONDITION> conditions;

// Must match one of the apps
for (size_t i = 0; i < appsIds.count(); ++i) {
FWPM_FILTER_CONDITION condition;
condition.fieldKey = FWPM_CONDITION_ALE_APP_ID;
condition.matchType = FWP_MATCH_EQUAL;
condition.conditionValue.type = FWP_BYTE_BLOB_TYPE;
condition.conditionValue.byteBlob = (FWP_BYTE_BLOB *)appsIds.getAppId(i);
conditions.push_back(condition);
}

// must match TCP
{
FWPM_FILTER_CONDITION condition;
condition.fieldKey = FWPM_CONDITION_IP_PROTOCOL;
condition.matchType = FWP_MATCH_EQUAL;
condition.conditionValue.type = FWP_UINT8;
condition.conditionValue.uint8 = IPPROTO_TCP;
conditions.push_back(condition);
}

// must match a LAN range
{
for (int i = 0; i < lanRanges.size(); i++) {
FWPM_FILTER_CONDITION condition;
condition.fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
condition.matchType = FWP_MATCH_EQUAL;
condition.conditionValue.type = FWP_V4_ADDR_MASK;
condition.conditionValue.v4AddrMask = &addrMasks[i];
Ip4AddressAndMask ipAddress(lanRanges[i].c_str());
addrMasks[i].addr = ipAddress.ipHostOrder();
addrMasks[i].mask = ipAddress.maskHostOrder();
addrMasks.push_back(addrMasks[i]);
conditions.push_back(condition);
}
}

FWPM_FILTER filter = { 0 };
filter.subLayerKey = SUBLAYER_CALLOUT_GUID;
filter.layerKey = FWPM_LAYER_ALE_CONNECT_REDIRECT_V4;
filter.displayData.name = (wchar_t *)L"Windscribe TCP filter for callout driver";
filter.weight.type = FWP_UINT8;
filter.weight.uint8 = 0x00;
filter.providerContextKey = CALLOUT_PROVIDER_CONTEXT_IP_GUID;
filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;
filter.numFilterConditions = static_cast<UINT32>(conditions.size());
filter.filterCondition = &conditions[0];
filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN;
filter.action.calloutKey = WINDSCRIBE_TCP_CALLOUT_GUID;

UINT64 filterId;
DWORD ret = FwpmFilterAdd(engineHandle, &filter, NULL, &filterId);
retValue = (ret == ERROR_SUCCESS);
if (!retValue) {
Logger::instance().out(L"CalloutFilter::addFilter(), TCP filter failed: %u", ret);
return retValue;
}
}
}

return retValue;
Expand All @@ -273,10 +386,11 @@ bool CalloutFilter::removeAllFilters(FwpmWrapper &fwmpWrapper)
HANDLE hEngine = fwmpWrapper.getHandleAndLock();
fwmpWrapper.beginTransaction();

deleteSublayer(hEngine);
DWORD ret = deleteSublayer(hEngine);

FwpmCalloutDeleteByKey(hEngine, &WINDSCRIBE_CALLOUT_GUID);
FwpmProviderContextDeleteByKey(hEngine, &CALLOUT_PROVIDER_CONTEXT_IP_GUID);
ret = FwpmCalloutDeleteByKey(hEngine, &WINDSCRIBE_BIND_CALLOUT_GUID);
ret = FwpmCalloutDeleteByKey(hEngine, &WINDSCRIBE_TCP_CALLOUT_GUID);
ret = FwpmProviderContextDeleteByKey(hEngine, &CALLOUT_PROVIDER_CONTEXT_IP_GUID);

fwmpWrapper.endTransaction();
fwmpWrapper.unlock();
Expand Down
Loading

0 comments on commit d0edfca

Please sign in to comment.