Skip to content

Commit

Permalink
multipeer: support for SET_PEER
Browse files Browse the repository at this point in the history
Add support for IOCTL_MP_SET_PEER. This is used
to set keepalive internal, keepalive timeout
and MSS for a specific peer.

Note that proper userspace notification for
expired peer is stil missing.

GitHub: OpenVPN#85

Signed-off-by: Lev Stipakov <[email protected]>
  • Loading branch information
lstipakov committed Oct 18, 2024
1 parent 70f7155 commit b737eb9
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 29 deletions.
7 changes: 7 additions & 0 deletions Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ OvpnDeviceCheckMode(OVPN_MODE mode, ULONG code)
// those IOCTLs are for MP mode
case OVPN_IOCTL_MP_START_VPN:
case OVPN_IOCTL_MP_NEW_PEER:
case OVPN_IOCTL_MP_SET_PEER:
return FALSE;
}
}
Expand Down Expand Up @@ -503,6 +504,12 @@ OvpnEvtIoDeviceControl(WDFQUEUE queue, WDFREQUEST request, size_t outputBufferLe
status = OvpnMPPeerNew(device, request);
break;

case OVPN_IOCTL_MP_SET_PEER:
kirql = ExAcquireSpinLockExclusive(&device->SpinLock);
status = OvpnMPPeerSet(device, request);
ExReleaseSpinLockExclusive(&device->SpinLock, kirql);
break;

default:
LOG_WARN("Unknown <ioControlCode>", TraceLoggingValue(ioControlCode, "ioControlCode"));
status = STATUS_INVALID_DEVICE_REQUEST;
Expand Down
4 changes: 0 additions & 4 deletions Driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,6 @@ struct OVPN_DEVICE {
BCRYPT_ALG_HANDLE AesAlgHandle;
BCRYPT_ALG_HANDLE ChachaAlgHandle;

// set from the userspace, defines TCP Maximum Segment Size
_Guarded_by_(SpinLock)
UINT16 MSS;

_Guarded_by_(SpinLock)
OvpnSocket Socket;

Expand Down
65 changes: 53 additions & 12 deletions peer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,13 @@ OvpnMPPeerNew(POVPN_DEVICE device, WDFREQUEST request)

peerCtx->PeerId = peer->PeerId;

// create peer-specific timer
LOG_IF_NOT_NT_SUCCESS(status = OvpnTimerCreate(device->WdfDevice, peerCtx, &peerCtx->Timer));
if (status != STATUS_SUCCESS) {
OvpnPeerCtxFree(peerCtx);
goto done;
}

kirql = ExAcquireSpinLockExclusive(&device->SpinLock);
LOG_IF_NOT_NT_SUCCESS(status = OvpnAddPeer(device, peerCtx));
if (status == STATUS_SUCCESS) {
Expand All @@ -304,6 +311,27 @@ OvpnMPPeerNew(POVPN_DEVICE device, WDFREQUEST request)
return status;
}

VOID OvpnPeerSetDoWork(OvpnPeerContext *peer, LONG keepaliveInterval, LONG keepaliveTimeout, LONG mss)
{
if (mss != -1) {
peer->MSS = (UINT16)mss;
}

if (keepaliveInterval != -1) {
peer->KeepaliveInterval = keepaliveInterval;

// keepalive xmit timer, sends ping packets
OvpnTimerSetXmitInterval(peer->Timer, peer->KeepaliveInterval);
}

if (keepaliveTimeout != -1) {
peer->KeepaliveTimeout = keepaliveTimeout;

// keepalive recv timer, detects keepalive timeout
OvpnTimerSetRecvTimeout(peer->Timer, peer->KeepaliveTimeout);
}
}

_Use_decl_annotations_
NTSTATUS OvpnPeerSet(POVPN_DEVICE device, WDFREQUEST request)
{
Expand All @@ -326,24 +354,37 @@ NTSTATUS OvpnPeerSet(POVPN_DEVICE device, WDFREQUEST request)
TraceLoggingValue(set_peer->KeepaliveTimeout, "timeout"),
TraceLoggingValue(set_peer->MSS, "MSS"));

if (set_peer->MSS != -1) {
device->MSS = (UINT16)set_peer->MSS;
}
OvpnPeerSetDoWork(peer, set_peer->KeepaliveInterval, set_peer->KeepaliveTimeout, set_peer->MSS);

if (set_peer->KeepaliveInterval != -1) {
peer->KeepaliveInterval = set_peer->KeepaliveInterval;
done:
LOG_EXIT();
return status;
}

// keepalive xmit timer, sends ping packets
OvpnTimerSetXmitInterval(peer->Timer, peer->KeepaliveInterval);
}
_Use_decl_annotations_
NTSTATUS OvpnMPPeerSet(POVPN_DEVICE device, WDFREQUEST request)
{
LOG_ENTER();

NTSTATUS status = STATUS_SUCCESS;

if (peer->KeepaliveTimeout != -1) {
peer->KeepaliveTimeout = set_peer->KeepaliveTimeout;
POVPN_MP_SET_PEER set_peer = NULL;
GOTO_IF_NOT_NT_SUCCESS(done, status, WdfRequestRetrieveInputBuffer(request, sizeof(OVPN_MP_SET_PEER), (PVOID*)&set_peer, nullptr));

// keepalive recv timer, detects keepalive timeout
OvpnTimerSetRecvTimeout(peer->Timer, peer->KeepaliveTimeout);
LOG_INFO("MP Set peer", TraceLoggingValue(set_peer->PeerId, "peer-id"),
TraceLoggingValue(set_peer->KeepaliveInterval, "interval"),
TraceLoggingValue(set_peer->KeepaliveTimeout, "timeout"),
TraceLoggingValue(set_peer->MSS, "MSS"));

OvpnPeerContext* peer = OvpnFindPeer(device, set_peer->PeerId);
if (peer == NULL) {
LOG_ERROR("Peer not found", TraceLoggingValue(set_peer->PeerId, "peer-id"));
status = STATUS_INVALID_DEVICE_REQUEST;
goto done;
}

OvpnPeerSetDoWork(peer, set_peer->KeepaliveInterval, set_peer->KeepaliveTimeout, set_peer->MSS);

done:
LOG_EXIT();
return status;
Expand Down
7 changes: 7 additions & 0 deletions peer.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ struct OvpnPeerContext
// 1-sec timer which handles ping intervals and keepalive timeouts
WDFTIMER Timer;

UINT16 MSS;

struct {
IN_ADDR IPv4;
IN6_ADDR IPv6;
Expand Down Expand Up @@ -91,6 +93,11 @@ _Requires_exclusive_lock_held_(device->SpinLock)
NTSTATUS
OvpnPeerSet(_In_ POVPN_DEVICE device, WDFREQUEST request);

_Must_inspect_result_
_Requires_exclusive_lock_held_(device->SpinLock)
NTSTATUS
OvpnMPPeerSet(_In_ POVPN_DEVICE device, WDFREQUEST request);

_Must_inspect_result_
NTSTATUS
_Requires_shared_lock_held_(device->SpinLock)
Expand Down
6 changes: 3 additions & 3 deletions socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,16 +254,16 @@ VOID OvpnSocketDataPacketReceived(_In_ POVPN_DEVICE device, UCHAR op, UINT32 pee

// ping packet?
if (OvpnTimerIsKeepaliveMessage(buffer->Data, buffer->Len)) {
LOG_INFO("Ping received");
LOG_INFO("Ping received", TraceLoggingValue(peer->PeerId, "peer-id"));

// no need to inject ping packet into OS, return buffer to the pool
OvpnRxBufferPoolPut(buffer);
}
else {
if (OvpnMssIsIPv4(buffer->Data, buffer->Len)) {
OvpnMssDoIPv4(buffer->Data, buffer->Len, device->MSS);
OvpnMssDoIPv4(buffer->Data, buffer->Len, peer->MSS);
} else if (OvpnMssIsIPv6(buffer->Data, buffer->Len)) {
OvpnMssDoIPv6(buffer->Data, buffer->Len, device->MSS);
OvpnMssDoIPv6(buffer->Data, buffer->Len, peer->MSS);
}

// enqueue plaintext buffer, it will be dequeued by NetAdapter RX datapath
Expand Down
5 changes: 3 additions & 2 deletions timer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,10 @@ static VOID OvpnTimerXmit(WDFTIMER timer)

if (NT_SUCCESS(status)) {
// start async send, completion handler will return ciphertext buffer to the pool
LOG_IF_NOT_NT_SUCCESS(status = OvpnSocketSend(&device->Socket, buffer, NULL));
SOCKADDR* sa = (SOCKADDR*)&(peer->TransportAddrs.Remote);
LOG_IF_NOT_NT_SUCCESS(status = OvpnSocketSend(&device->Socket, buffer, sa));
if (NT_SUCCESS(status)) {
LOG_INFO("Ping sent");
LOG_INFO("Ping sent", TraceLoggingValue(peer->PeerId, "peer-id"));
}
}
else {
Expand Down
16 changes: 11 additions & 5 deletions txqueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,24 @@ OvpnTxProcessPacket(_In_ POVPN_DEVICE device, _In_ POVPN_TXQUEUE queue, _In_ NET
}

OvpnPeerContext* peer = NULL;

if (OvpnMssIsIPv4(buffer->Data, buffer->Len)) {
OvpnMssDoIPv4(buffer->Data, buffer->Len, device->MSS);
peer = OvpnFindPeerVPN4(device, ((IPV4_HEADER*)buffer->Data)->DestinationAddress);
auto addr = ((IPV4_HEADER*)buffer->Data)->DestinationAddress;
peer = OvpnFindPeerVPN4(device, addr);
if (peer != NULL) {
OvpnMssDoIPv4(buffer->Data, buffer->Len, peer->MSS);
}
} else if (OvpnMssIsIPv6(buffer->Data, buffer->Len)) {
OvpnMssDoIPv6(buffer->Data, buffer->Len, device->MSS);
peer = OvpnFindPeerVPN6(device, ((IPV6_HEADER*)buffer->Data)->DestinationAddress);
auto addr = ((IPV6_HEADER*)buffer->Data)->DestinationAddress;
peer = OvpnFindPeerVPN6(device, addr);
if (peer != NULL) {
OvpnMssDoIPv6(buffer->Data, buffer->Len, peer->MSS);
}
}

if (peer == NULL) {
status = STATUS_ADDRESS_NOT_ASSOCIATED;
OvpnTxBufferPoolPut(buffer);
LOG_WARN("No peer");
goto out;
}

Expand Down
14 changes: 11 additions & 3 deletions uapi/ovpn-dco.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,17 @@ typedef struct _OVPN_CRYPTO_DATA_V2 {
UINT32 CryptoOptions;
} OVPN_CRYPTO_DATA_V2, * POVPN_CRYPTO_DATA_V2;

typedef struct _OVPN_MP_SET_PEER {
int PeerId;
LONG KeepaliveInterval;
LONG KeepaliveTimeout;
LONG MSS;
} OVPN_MP_SET_PEER, * POVPN_MP_SET_PEER;

typedef struct _OVPN_SET_PEER {
LONG KeepaliveInterval;
LONG KeepaliveTimeout;
LONG MSS;
LONG KeepaliveInterval;
LONG KeepaliveTimeout;
LONG MSS;
} OVPN_SET_PEER, * POVPN_SET_PEER;

typedef struct _OVPN_VERSION {
Expand Down Expand Up @@ -160,3 +167,4 @@ typedef struct _OVPN_MP_START_VPN {

#define OVPN_IOCTL_MP_START_VPN CTL_CODE(FILE_DEVICE_UNKNOWN, 11, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define OVPN_IOCTL_MP_NEW_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 12, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define OVPN_IOCTL_MP_SET_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 13, METHOD_BUFFERED, FILE_ANY_ACCESS)

0 comments on commit b737eb9

Please sign in to comment.