Skip to content

Commit b51b267

Browse files
committed
Fix routing loops when default route is lost in full-tunnel VPN
Added checks to detect and drop OpenVPN’s own encrypted traffic when it gets mistakenly routed back through the VPN tunnel. This happens when the default route is removed or the network adapter is disabled while a full-tunnel VPN is active. Without a proper route to the internet, OpenVPN’s outgoing traffic can end up going through the tunnel itself, creating a loop that breaks connectivity. Now, we compare the destination IP and port against the peer’s transport address to catch and drop these packets before they cause issues. #102 Bump to 2.5.6. Signed-off-by: Lev Stipakov <[email protected]>
1 parent fe36a34 commit b51b267

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

PropertySheet.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<PropertyGroup Label="UserMacros">
55
<OVPN_DCO_VERSION_MAJOR>2</OVPN_DCO_VERSION_MAJOR>
66
<OVPN_DCO_VERSION_MINOR>5</OVPN_DCO_VERSION_MINOR>
7-
<OVPN_DCO_VERSION_PATCH>5</OVPN_DCO_VERSION_PATCH>
7+
<OVPN_DCO_VERSION_PATCH>6</OVPN_DCO_VERSION_PATCH>
88
</PropertyGroup>
99
<PropertyGroup />
1010
<ItemDefinitionGroup>

txqueue.cpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,91 @@ OvpnTxAreSockaddrEqual(const SOCKADDR* addr1, const SOCKADDR* addr2) {
6464
return 0;
6565
}
6666

67+
BOOLEAN
68+
OvpnCheckRecursiveRoutingIPv4(SOCKADDR_IN* transportAdds, UCHAR* buffer, SIZE_T bufferLength, BOOLEAN tcp)
69+
{
70+
if (transportAdds->sin_family != AF_INET || bufferLength < sizeof(IPV4_HEADER))
71+
return FALSE; // Not an IPv4 peer or packet too short
72+
73+
// Extract pointers to avoid repeated FIELD_OFFSET calculations
74+
IN_ADDR* srcAddr = (IN_ADDR*)(buffer + FIELD_OFFSET(IPV4_HEADER, SourceAddress));
75+
IN_ADDR* dstAddr = (IN_ADDR*)(buffer + FIELD_OFFSET(IPV4_HEADER, DestinationAddress));
76+
UINT8 packetProtocol = buffer[FIELD_OFFSET(IPV4_HEADER, Protocol)];
77+
78+
// Validate the transport protocol
79+
if ((tcp && packetProtocol != IPPROTO_TCP) || (!tcp && packetProtocol != IPPROTO_UDP))
80+
return FALSE;
81+
82+
// Extract IP header length
83+
UINT8 ipHeaderLength = (buffer[0] & 0x0F) << 2;
84+
85+
// Ensure transport header is accessible
86+
if (bufferLength < ipHeaderLength + sizeof(UINT16) * 2)
87+
return FALSE;
88+
89+
// Read transport header efficiently
90+
UCHAR* transportHeader = buffer + ipHeaderLength;
91+
UINT16 packetSrcPort = RtlUshortByteSwap(*(UINT16*)(transportHeader));
92+
UINT16 packetDstPort = RtlUshortByteSwap(*(UINT16*)(transportHeader + 2));
93+
UINT16 peerPort = RtlUshortByteSwap(transportAdds->sin_port);
94+
95+
// Check for recursive routing
96+
if (packetDstPort == peerPort &&
97+
RtlCompareMemory(dstAddr, &transportAdds->sin_addr, sizeof(IN_ADDR)) == sizeof(IN_ADDR))
98+
{
99+
LOG_WARN("Recursive routing detected (IPv4), packet dropped",
100+
TraceLoggingIPv4Address(srcAddr->S_un.S_addr, "saddr"),
101+
TraceLoggingIPv4Address(dstAddr->S_un.S_addr, "daddr"),
102+
TraceLoggingUInt16(packetSrcPort, "sport"),
103+
TraceLoggingUInt16(packetDstPort, "dport"),
104+
TraceLoggingUInt8(packetProtocol, "protocol"));
105+
return TRUE;
106+
}
107+
108+
return FALSE;
109+
}
110+
111+
BOOLEAN
112+
OvpnCheckRecursiveRoutingIPv6(SOCKADDR_IN6* transportAddr, UCHAR* buffer, SIZE_T bufferLength, BOOLEAN tcp)
113+
{
114+
if (transportAddr->sin6_family != AF_INET6 || bufferLength < sizeof(IPV6_HEADER))
115+
return FALSE; // Not an IPv6 peer or packet too short
116+
117+
// Extract pointers to avoid repeated FIELD_OFFSET calculations
118+
IN6_ADDR* srcAddr = (IN6_ADDR*)(buffer + FIELD_OFFSET(IPV6_HEADER, SourceAddress));
119+
IN6_ADDR* dstAddr = (IN6_ADDR*)(buffer + FIELD_OFFSET(IPV6_HEADER, DestinationAddress));
120+
UINT8 packetProtocol = buffer[FIELD_OFFSET(IPV6_HEADER, NextHeader)];
121+
122+
// Validate the transport protocol
123+
if ((tcp && packetProtocol != IPPROTO_TCP) || (!tcp && packetProtocol != IPPROTO_UDP))
124+
return FALSE;
125+
126+
// Ensure transport header is accessible
127+
if (bufferLength < sizeof(IPV6_HEADER) + sizeof(UINT16) * 2)
128+
return FALSE;
129+
130+
// Read transport header efficiently
131+
UCHAR* transportHeader = buffer + sizeof(IPV6_HEADER);
132+
UINT16 packetSrcPort = RtlUshortByteSwap(*(UINT16*)(transportHeader));
133+
UINT16 packetDstPort = RtlUshortByteSwap(*(UINT16*)(transportHeader + 2));
134+
UINT16 peerPort = RtlUshortByteSwap(transportAddr->sin6_port);
135+
136+
// Check for recursive routing
137+
if (packetDstPort == peerPort &&
138+
RtlCompareMemory(dstAddr, &transportAddr->sin6_addr, sizeof(IN6_ADDR)) == sizeof(IN6_ADDR))
139+
{
140+
LOG_WARN("Recursive routing detected (IPv6), packet dropped",
141+
TraceLoggingIPv6Address(srcAddr->u.Byte, "saddr"),
142+
TraceLoggingIPv6Address(dstAddr->u.Byte, "daddr"),
143+
TraceLoggingUInt16(packetSrcPort, "sport"),
144+
TraceLoggingUInt16(packetDstPort, "dport"),
145+
TraceLoggingUInt8(packetProtocol, "protocol"));
146+
return TRUE;
147+
}
148+
149+
return FALSE;
150+
}
151+
67152
_Must_inspect_result_
68153
static
69154
NTSTATUS
@@ -114,6 +199,11 @@ OvpnTxProcessPacket(_In_ POVPN_DEVICE device, _In_ POVPN_TXQUEUE queue, _In_ NET
114199
peer = device->IRoutesIPV4.Find(reinterpret_cast<UCHAR*>(&addr));
115200
}
116201

202+
if ((device->Mode == OVPN_MODE_P2P) && (peer != nullptr) && (OvpnCheckRecursiveRoutingIPv4(&peer->TransportAddrs.Remote.IPv4, buffer->Data, buffer->Len, device->Socket.Tcp))) {
203+
OvpnPeerCtxRelease(peer);
204+
peer = nullptr;
205+
}
206+
117207
if (peer != nullptr) {
118208
OvpnMssDoIPv4(buffer->Data, buffer->Len, peer->MSS);
119209
}
@@ -125,6 +215,11 @@ OvpnTxProcessPacket(_In_ POVPN_DEVICE device, _In_ POVPN_TXQUEUE queue, _In_ NET
125215
peer = device->IRoutesIPV6.Find(reinterpret_cast<UCHAR*>(&addr));
126216
}
127217

218+
if ((device->Mode == OVPN_MODE_P2P) && (peer != nullptr) && (OvpnCheckRecursiveRoutingIPv6(&peer->TransportAddrs.Remote.IPv6, buffer->Data, buffer->Len, device->Socket.Tcp))) {
219+
OvpnPeerCtxRelease(peer);
220+
peer = nullptr;
221+
}
222+
128223
if (peer != nullptr) {
129224
OvpnMssDoIPv6(buffer->Data, buffer->Len, peer->MSS);
130225
}

0 commit comments

Comments
 (0)