Skip to content

Commit

Permalink
multipeer: support for iroute
Browse files Browse the repository at this point in the history
This introduces support for iroute functionality,
enabling per-peer routing of specific subnets.

 - Added a trie-based routing table structure (IPTrie) to
 efficiently store and manage IPv4 and IPv6 routes.

 - Implemented InsertRoute, FindRoute, and RemoveRoute methods to
 add, look up, and delete routes.

 - Integrated OvpnPeerContext management with reference counting to
 ensure proper resource cleanup during route deletions.

 - Added thread-safe synchronization using read/write spinlocks to
 allow concurrent route lookups while protecting writes.

Signed-off-by: Lev Stipakov <[email protected]>
  • Loading branch information
lstipakov committed Jan 13, 2025
1 parent 53d805b commit cd87f21
Show file tree
Hide file tree
Showing 12 changed files with 459 additions and 11 deletions.
76 changes: 76 additions & 0 deletions Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ OvpnDeviceCheckMode(OVPN_MODE mode, ULONG code)
case OVPN_IOCTL_MP_SET_PEER:
case OVPN_IOCTL_MP_DEL_PEER:
case OVPN_IOCTL_MP_SWAP_KEYS:
case OVPN_IOCTL_MP_ADD_IROUTE:
case OVPN_IOCTL_MP_DEL_IROUTE:
return FALSE;
}
}
Expand Down Expand Up @@ -372,6 +374,9 @@ OvpnStopVPN(_In_ POVPN_DEVICE device)

device->PendingNotificationsQueue.FlushEvents();

device->IRoutesIPV4.Cleanup();
device->IRoutesIPV6.Cleanup();

LOG_EXIT();

return STATUS_SUCCESS;
Expand Down Expand Up @@ -463,6 +468,67 @@ OvpnNotifyEvent(POVPN_DEVICE device, WDFREQUEST request, _Out_ ULONG_PTR* bytesR
return status;
}

NTSTATUS
OvpnMPAddIRoute(POVPN_DEVICE device, WDFREQUEST request) {
NTSTATUS status = STATUS_SUCCESS;

POVPN_MP_IROUTE iroute = NULL;
GOTO_IF_NOT_NT_SUCCESS(done, status, WdfRequestRetrieveInputBuffer(request, sizeof(OVPN_MP_IROUTE), (PVOID*)&iroute, NULL));

auto peer = OvpnFindPeer(device, iroute->PeerId);
if (peer == nullptr) {
LOG_ERROR("Peer not found", TraceLoggingValue(iroute->PeerId, "peer-id"));
status = STATUS_INVALID_DEVICE_REQUEST;
goto done;
}

if (iroute->IPv6) {
LOG_INFO("Add IPV6 iroute", TraceLoggingValue(iroute->PeerId, "peer-id"),
TraceLoggingIPv6Address(&iroute->Addr.Addr6, "network"),
TraceLoggingValue(iroute->Netbits, "netbits"));

status = device->IRoutesIPV6.Insert(reinterpret_cast<UCHAR*>(&iroute->Addr.Addr6), iroute->Netbits, peer);
}
else {
LOG_INFO("Add IPV4 iroute", TraceLoggingValue(iroute->PeerId, "peer-id"),
TraceLoggingIPv4Address(iroute->Addr.Addr4.S_un.S_addr, "network"),
TraceLoggingValue(iroute->Netbits, "netbits"));

status = device->IRoutesIPV4.Insert(reinterpret_cast<UCHAR*>(&iroute->Addr.Addr4), iroute->Netbits, peer);
}

if (peer != nullptr) {
OvpnPeerCtxRelease(peer);
}

done:
return status;
}

NTSTATUS
OvpnMPDelIRoute(POVPN_DEVICE device, WDFREQUEST request) {
NTSTATUS status = STATUS_SUCCESS;

POVPN_MP_IROUTE iroute = NULL;
GOTO_IF_NOT_NT_SUCCESS(done, status, WdfRequestRetrieveInputBuffer(request, sizeof(OVPN_MP_IROUTE), (PVOID*)&iroute, NULL));

if (iroute->IPv6) {
LOG_INFO("Delete IPV6 iroute", TraceLoggingIPv6Address(&iroute->Addr.Addr6, "network"),
TraceLoggingValue(iroute->Netbits, "netbits"));

status = device->IRoutesIPV6.Remove(reinterpret_cast<UCHAR*>(&iroute->Addr.Addr6), iroute->Netbits);
}
else {
LOG_INFO("Delete IPV4 iroute", TraceLoggingIPv4Address(iroute->Addr.Addr4.S_un.S_addr, "network"),
TraceLoggingValue(iroute->Netbits, "netbits"));

status = device->IRoutesIPV4.Remove(reinterpret_cast<UCHAR*>(&iroute->Addr.Addr4), iroute->Netbits);
}

done:
return status;
}

EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL OvpnEvtIoDeviceControl;

_Use_decl_annotations_
Expand Down Expand Up @@ -548,6 +614,14 @@ OvpnEvtIoDeviceControl(WDFQUEUE queue, WDFREQUEST request, size_t outputBufferLe
status = OvpnMPPeerSwapKeys(device, request);
break;

case OVPN_IOCTL_MP_ADD_IROUTE:
status = OvpnMPAddIRoute(device, request);
break;

case OVPN_IOCTL_MP_DEL_IROUTE:
status = OvpnMPDelIRoute(device, request);
break;

default:
LOG_WARN("Unknown <ioControlCode>", TraceLoggingValue(ioControlCode, "ioControlCode"));
status = STATUS_INVALID_DEVICE_REQUEST;
Expand Down Expand Up @@ -757,6 +831,8 @@ OvpnEvtDeviceAdd(WDFDRIVER wdfDriver, PWDFDEVICE_INIT deviceInit) {

// constructors are not called for the members of WDF object context, so we use Init() method
device->PendingNotificationsQueue.Init();
device->IRoutesIPV4.Init(FALSE);
device->IRoutesIPV6.Init(TRUE);

GOTO_IF_NOT_NT_SUCCESS(done, status, OvpnCryptoInitAlgHandles(&device->AesAlgHandle, &device->ChachaAlgHandle));

Expand Down
4 changes: 4 additions & 0 deletions Driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "crypto.h"
#include "notifyqueue.h"
#include "socket.h"
#include "trie.h"
#include "uapi\ovpn-dco.h"

extern "C" {
Expand Down Expand Up @@ -102,6 +103,9 @@ struct OVPN_DEVICE {
RTL_GENERIC_TABLE PeersByVpn6;

OVPN_MODE Mode;

IPTrie IRoutesIPV4;
IPTrie IRoutesIPV6;
};

typedef OVPN_DEVICE * POVPN_DEVICE;
Expand Down
2 changes: 2 additions & 0 deletions ovpn-dco-win.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
<ClCompile Include="pktid.cpp" />
<ClCompile Include="rxqueue.cpp" />
<ClCompile Include="timer.cpp" />
<ClCompile Include="trie.cpp" />
<ClCompile Include="txqueue.cpp" />
<ClCompile Include="socket.cpp" />
</ItemGroup>
Expand All @@ -87,6 +88,7 @@
<ClInclude Include="control.h" />
<ClInclude Include="crypto.h" />
<ClInclude Include="driver.h" />
<ClInclude Include="trie.h" />
<ClInclude Include="mss.h" />
<ClInclude Include="netringiterator.h" />
<ClInclude Include="notifyqueue.h" />
Expand Down
6 changes: 6 additions & 0 deletions ovpn-dco-win.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@
<ClInclude Include="notifyqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="trie.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="driver.cpp">
Expand Down Expand Up @@ -114,6 +117,9 @@
<ClCompile Include="notifyqueue.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="trie.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Inf Include="ovpn-dco.inf">
Expand Down
13 changes: 4 additions & 9 deletions peer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,14 @@
#include "timer.h"
#include "socket.h"

#ifndef TraceLoggingIPv4Address
#define TraceLoggingIPv4Address(value, ...) _tlgArgScalarVal(UINT32, value, TlgInUINT32, (TlgOutIPV4), __VA_ARGS__)
#endif

#ifndef TraceLoggingIPv6Address
#define TraceLoggingIPv6Address(pValue, ...) _tlgArgBinary(void, pValue, 16u, TlgInBINARY, (TlgOutIPV6), __VA_ARGS__)
#endif

_Use_decl_annotations_
OvpnPeerContext*
OvpnPeerCtxAlloc()
{
OvpnPeerContext* peer = (OvpnPeerContext*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(OvpnPeerContext), 'ovpn');
if (peer != NULL) {
RtlZeroMemory(peer, sizeof(OvpnPeerContext));
InitializeListHead(&peer->ListEntry);
InterlockedIncrement(&peer->RefCounter);
}
return peer;
Expand Down Expand Up @@ -314,7 +307,6 @@ OvpnDeletePeerFromTable(POVPN_DEVICE device, RTL_GENERIC_TABLE* table, OvpnPeerC
}
}


static
VOID
OvpnPeerZeroStats(POVPN_STATS stats)
Expand Down Expand Up @@ -818,6 +810,9 @@ OvpnPeerDelete(POVPN_DEVICE device, INT32 peerId, OVPN_DEL_PEER_REASON reason)
OvpnDeletePeerFromTable(device, &device->PeersByVpn6, peer, "vpn6");
OvpnDeletePeerFromTable(device, &device->Peers, peer, "peers");

device->IRoutesIPV4.RemoveByPeerId(peer->PeerId);
device->IRoutesIPV6.RemoveByPeerId(peer->PeerId);

OvpnPeerCtxRelease(peer);

// notify userspace
Expand Down
2 changes: 2 additions & 0 deletions peer.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

struct OvpnPeerContext
{
LIST_ENTRY ListEntry; // used by iroute tries for deferred cleanup

EX_SPIN_LOCK SpinLock;

OvpnCryptoContext CryptoContext;
Expand Down
3 changes: 1 addition & 2 deletions socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,7 @@ VOID OvpnSocketDataPacketReceived(_In_ POVPN_DEVICE device, UCHAR op, UINT32 pee
OvpnBufferQueueEnqueue(device->DataRxBufferQueue, &buffer->QueueListEntry);

OvpnAdapterNotifyRx(device->Adapter);
}
else {
} else {
// packet is dropped dur to RPF, return buffer to the pool
OvpnRxBufferPoolPut(buffer);
}
Expand Down
8 changes: 8 additions & 0 deletions trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,11 @@ TRACELOGGING_DECLARE_PROVIDER(g_hOvpnEtwProvider);
goto Label; \
} \
} while(0,0)

#ifndef TraceLoggingIPv4Address
#define TraceLoggingIPv4Address(value, ...) _tlgArgScalarVal(UINT32, value, TlgInUINT32, (TlgOutIPV4), __VA_ARGS__)
#endif

#ifndef TraceLoggingIPv6Address
#define TraceLoggingIPv6Address(pValue, ...) _tlgArgBinary(void, pValue, 16u, TlgInBINARY, (TlgOutIPV6), __VA_ARGS__)
#endif
Loading

0 comments on commit cd87f21

Please sign in to comment.