OpenVPN / tap-windows6

Windows TAP driver (NDIS 6)
Other
785 stars 237 forks source link

Problem with 802.1Q marked frames with OpenVPN Client #116

Closed stepman0 closed 3 years ago

stepman0 commented 4 years ago

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

My setup:

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: https://github.com/OpenVPN/tap-windows6/pull/81

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

svkers commented 4 years ago

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.

selvanair commented 4 years ago

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?

svkers commented 4 years ago

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);
cron2 commented 4 years ago

Hi,

On Tue, Jul 21, 2020 at 10:05:34AM -0700, Sven Kerschbaum wrote:

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:

Thanks. This makes sense. (I assume that sgstair just assumed that NdisMoveMemory() would handle this correctly... - this 802.1q handling was rewritten/added for the test suite, that's why it works on older versions)

We'll have to wait for Samuli to return from vacation to roll and sign an updated tapv9 driver.

Well, and for me to actually turn the issue into a PR and submit that as PR :-) (tapv9 does PRs for patch submission).

gert

-- "If was one thing all people took for granted, was conviction that if you feed honest figures into a computer, honest figures come out. Never doubted it myself till I met a computer with a sense of humor." Robert A. Heinlein, The Moon is a Harsh Mistress

Gert Doering - Munich, Germany gert@greenie.muc.de

selvanair commented 4 years ago

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

svkers commented 4 years ago

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

cron2 commented 4 years ago

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

selvanair commented 4 years ago

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.

cron2 commented 3 years ago

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

svkers commented 3 years ago

@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.

cron2 commented 3 years ago

Great, thanks for testing & feedback.