OpenVPN / tap-windows6

Windows TAP driver (NDIS 6)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Problem with 802.1Q marked frames with OpenVPN Client

stepman0 opened this issue · comments

Found an issue regarding the handling of 802.1Q (VLAN) tagged frames in the latest version of OpenVPN in Windows.

My setup:

  • A Linux-VM with OpenVPN server configured in Layer 2 bridge mode
  • A Windows VM with OpenVPN client configured in Layer 2 bridge mode

If the Linux send a frame with VLAN tags into the tunnel, this frame is corrupted (Source MAC is wrong) when the packet leaves the TAP interface in Windows. In detail, the last 4 bytes of the destination MAC (bytes 8..11 in frame) are identical to bytes 5..7 in the frame.

Maybe this is related to this merge request: #81

I observed the problem with OpenVPN 2.4.9, with OpenVPN 2.4.7 everything works fine.

Additional note: It seems that the bug is in the function TapStrip8021Q() in file src/rxpath.c line 376:

// Copy the first part of the ethernet header up and over the protocol and 802.1Q data
// Don't copy the ethernet header's protocol, leave the inner value from the 802.1Q header.
NdisMoveMemory(buffer+4, buffer, ETHERNET_HEADER_SIZE-2);

After copying/moving the first 4 bytes, the already overwritten bytes are read and copied again. Please see also the figure below illustrating the issue:
tap_8021q_bug

Quick fix:

for (int i = (ETHERNET_HEADER_SIZE-2)-1; i >= 0; i--)
{
    buffer[i+4] = buffer[i];
}

which is the same as:

buffer[15] = buffer[11];
buffer[14] = buffer[10];
buffer[13] = buffer[ 9];
buffer[12] = buffer[ 8];
buffer[11] = buffer[ 7];
buffer[10] = buffer[ 6];
buffer[ 9] = buffer[ 5];
buffer[ 8] = buffer[ 4];
buffer[ 7] = buffer[ 3];
buffer[ 6] = buffer[ 2];
buffer[ 5] = buffer[ 1];
buffer[ 4] = buffer[ 0];

Edit: The above code has not been tested.
Edit: According to MSDN NdisMoveMemory documentation the source and destination memory areas are not allowed to overlap:

The range specified by Source and Length cannot overlap the Destination range.

The use of NdisMoveMemory() for shifting the buffer by 4 is definitely buggy. Note that its essentially RtlCopyMemory() which works like memcpy() not memmove().

The proposed loop looks correct, but RtlMoveMemory() may be better?

Yes, indeed, RtlMoveMemory() is the better approach to avoid the for loop. It allows for overlapping source and destination memory blocks. Thus the fix looks as follows:

// Copy the first part of the ethernet header up and over the protocol and 802.1Q data
// Don't copy the ethernet header's protocol, leave the inner value from the 802.1Q header.
RtlMoveMemory(buffer+4, buffer, ETHERNET_HEADER_SIZE-2);

instead of

// Copy the first part of the ethernet header up and over the protocol and 802.1Q data
// Don't copy the ethernet header's protocol, leave the inner value from the 802.1Q header.
NdisMoveMemory(buffer+4, buffer, ETHERNET_HEADER_SIZE-2);

@svkers: you may want to submit a PR with this fix.

@selvanair Ok, I created the PR as you suggested. Thanks.

So the PR has been merged, and the fix should be seen in a signed driver "real soon now". Please test and report :-)

I tested the merged PR using
https://build.openvpn.net/downloads/releases/OpenVPN-2.5-beta2-I601-amd64.msi
The old tap driver 9.24.3.601 does mangle the destination source MAC while stripping VLAN id. The new one, 9.24.4.601 dated Aug 27, behaves correctly. I only checked the headers by capturing some packets, not the correct operation or performance of real traffic.

@stepman0 can you confirm that this is now fixed, and we can close the issue?

@cron2 and @stepman0 I can confirm that the merged PR works. I used the build https://build.openvpn.net/downloads/releases/OpenVPN-2.5-beta2-I601-amd64.msi.
Please close the issue.

Great, thanks for testing & feedback.