Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion network/netadaptercx/netvadapter/build.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ set PLAT=%~2
if "%CONFIG%"=="" set CONFIG=Debug
if "%PLAT%"=="" set PLAT=x64

msbuild "%~dp0netvadapter.sln" /t:Build /p:Configuration=%CONFIG% /p:Platform=%PLAT% /p:UMDF_VERSION_MINOR=33 /p:UMDF_MINIMUM_VERSION_REQUIRED=33 /m
msbuild "%~dp0netvadapter.sln" /t:Rebuild /p:Configuration=%CONFIG% /p:Platform=%PLAT% /p:UMDF_VERSION_MINOR=33 /p:UMDF_MINIMUM_VERSION_REQUIRED=33 /m
endlocal
23 changes: 22 additions & 1 deletion network/netadaptercx/netvadapterlibrary/code/adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,11 @@ EvtSetReceiveFilter(
)
{
NetvAdapter* adapter = NetvAdapterGetContextFromWDFObject(NetAdapter);

#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,
"ENL EvtSetReceiveFilter port=%u filter=0x%x\n",
adapter->EnlPortIndex, (ULONG)NetReceiveFilterGetPacketFilter(Handle));
#endif
adapter->PacketFilter = NetReceiveFilterGetPacketFilter(Handle);

adapter->NumMulticastAddresses = (ULONG)NetReceiveFilterGetMulticastAddressCount(Handle);
Expand Down Expand Up @@ -507,6 +511,11 @@ EvtTxQueueStart(
NETPACKETQUEUE Queue
)
{
#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,
"ENL EvtTxQueueStart port=%u\n",
NetvTxQueueGetContext(Queue)->m_adapter.EnlPortIndex);
#endif
NetvTxQueueGetContext(Queue)->Start();
}

Expand All @@ -525,6 +534,13 @@ EvtTxQueueAdvance(
NETPACKETQUEUE Queue
)
{
#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
static LONG txAdvCount = 0;
if (InterlockedIncrement(&txAdvCount) <= 3)
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,
"ENL EvtTxQueueAdvance port=%u\n",
NetvTxQueueGetContext(Queue)->m_adapter.EnlPortIndex);
#endif
NetvTxQueueGetContext(Queue)->Advance();
}

Expand Down Expand Up @@ -553,6 +569,11 @@ EvtRxQueueStart(
NETPACKETQUEUE Queue
)
{
#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,
"ENL EvtRxQueueStart port=%u\n",
NetvRxQueueGetContext(Queue)->m_adapter.EnlPortIndex);
#endif
NetvRxQueueGetContext(Queue)->Start();
}

Expand Down
54 changes: 15 additions & 39 deletions network/netadaptercx/netvadapterlibrary/code/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ NETVADAPTER_ADVANCED_PROPERTY NetvSupportedProperties[] =
// reg value name - Offset in NetvAdapter - Field size - Default Value - Min - Max

// Standard Keywords
{ CONSTANT_UNICODE_STRING(L"MACLastByte"), NETV_OFFSET(MACLastByte), NETV_SIZE(MACLastByte), 0, 0, 254 },
{ CONSTANT_UNICODE_STRING(L"MACLastByte"), NETV_OFFSET(MACLastByte), NETV_SIZE(MACLastByte), 1, 1, 254 },
{ CONSTANT_UNICODE_STRING(L"LinkProcIndex"), NETV_OFFSET(LinkProcIndex), NETV_SIZE(LinkProcIndex), 1000, 0, 1023 },
{ CONSTANT_UNICODE_STRING(L"S0Idle"), NETV_OFFSET(S0Idle), NETV_SIZE(S0Idle), 0, 0, 1 },
{ CONSTANT_UNICODE_STRING(L"EnableUsoUro"), NETV_OFFSET(EnableUsoUro), NETV_SIZE(EnableUsoUro), 0, 0, 1 },
Expand All @@ -42,57 +42,33 @@ NetvAdapterReadConfiguration(
RETURN_IF_NOT_STATUS_SUCCESS(
NetDeviceOpenConfiguration(Device, WDF_NO_OBJECT_ATTRIBUTES, &configuration));

// read all the registry values
// A missing or out-of-range registry value must not fail initialization;
// fall back to the property default instead. Under NT the parameters are
// all read in as DWORDs.
for (auto &property : NetvSupportedProperties)
{
// Driver should NOT fail the initialization only because it can not
// read the registry
auto pointer = (PUCHAR)Adapter + property.FieldOffset;

// Get the configuration value for a specific parameter. Under NT the
// parameters are all read in as DWORDs.
ULONG value = 0;

status = NetConfigurationQueryUlong(
configuration,
NET_CONFIGURATION_QUERY_ULONG_NO_FLAGS,
&property.RegName,
&value);

// Store the value in the adapter structure.
switch (property.FieldSize)
if (! NT_SUCCESS(status) ||
value < property.Min ||
value > property.Max)
{
case 1:
*((PUCHAR)pointer) = (UCHAR)value;
break;

case 2:
*((PUSHORT)pointer) = (USHORT)value;
break;

case 4:
*((PULONG)pointer) = (ULONG)value;
break;

default:
break;
value = property.Default;
status = STATUS_SUCCESS;
}

// If the parameter was present, then check its value for validity.
if (NT_SUCCESS(status))
{
// Check that param value is not too small or too large

if (value < property.Min ||
value > property.Max)
{
value = property.Default;
}
}
else
auto pointer = (PUCHAR)Adapter + property.FieldOffset;
switch (property.FieldSize)
{
value = property.Default;
status = STATUS_SUCCESS;
case 1: *((PUCHAR)pointer) = (UCHAR)value; break;
case 2: *((PUSHORT)pointer) = (USHORT)value; break;
case 4: *((PULONG)pointer) = (ULONG)value; break;
default: break;
}
}

Expand Down
89 changes: 89 additions & 0 deletions network/netadaptercx/netvadapterlibrary/code/enl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@

ENL_MLINK NetvEnlMLink[MAX_ADAPTER_COUNT / 2];

#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
// Forward-logging budgets, reset when the link goes idle so each tx burst
// starts fresh. g_rxAdvDbg is defined in rxqueue.cpp.
static LONG g_enlFwdDbg = 0;
extern LONG g_rxAdvDbg;
#endif

/*++
The iteration routine performs one full pass over all input queues and
any internal queues (that might be holding previous items waiting to be
Expand Down Expand Up @@ -52,6 +59,59 @@ CopyTxPacketDataToBuffer(
return bytesCopied;
}

//
// Emulate the AP relay for Native 802.11 frames.
//
// Both sample stations associate (in software) to the same hard-coded AP
// BSSID, and the ENL link is the wireless medium. nwifi builds a
// station-to-peer frame as a "to-DS" frame whose Address1/RA is the AP BSSID,
// expecting the (nonexistent) AP to relay it. Copied verbatim into the peer's
// receive ring, that RA is not the peer's address, so nwifi on the peer drops
// it before it reaches TCP/IP. Rewrite the header in place into the "from-DS"
// frame the peer accepts, exactly as a real AP would when forwarding:
// Addr1 (RA) <- old Addr3 (final destination = peer MAC)
// Addr2 (TA) <- old Addr1 (BSSID)
// Addr3 (SA) <- old Addr2 (original sender)
// FrameControl: clear ToDS, set FromDS
// The address offsets are identical for data and QoS-data frames, and the
// guard below makes this a no-op for non-802.11 (802.3 netvmini) traffic.
static
VOID
EnlpRelayWiFiFrame(
_Inout_updates_bytes_(Length) PUCHAR Frame,
_In_ SIZE_T Length)
{
// FrameControl[2] Duration[2] Addr1[6] Addr2[6] Addr3[6] SeqCtrl[2] ...
SIZE_T const k80211HeaderLength = 24;
if (Length < k80211HeaderLength)
{
return;
}

// Relay only 802.11 data-type (Type == 10b) frames headed to the DS.
UCHAR const type = Frame[0] & 0x0C;
UCHAR const dsBits = Frame[1] & 0x03;
if (type != 0x08 || dsBits != 0x01)
{
return;
}

PUCHAR const addr1 = Frame + 4;
PUCHAR const addr2 = Frame + 10;
PUCHAR const addr3 = Frame + 16;

UCHAR ra[6], ta[6], sa[6];
RtlCopyMemory(ra, addr3, 6);
RtlCopyMemory(ta, addr1, 6);
RtlCopyMemory(sa, addr2, 6);

RtlCopyMemory(addr1, ra, 6);
RtlCopyMemory(addr2, ta, 6);
RtlCopyMemory(addr3, sa, 6);

Frame[1] = (Frame[1] & ~0x01) | 0x02; // ToDS -> FromDS
}

VOID
EnlpAffinitizeThread(
_In_ ULONG ProcIndex,
Expand Down Expand Up @@ -329,10 +389,18 @@ EnlpIterationRoutine(
EnlDisarmWake(enlLink);
}

#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
int dbgRxState = -1;
int dbgHasFrag = -1;
int dbgHasPkt = -1;
#endif
if (rxport->RxQueueCount > 0)
{
//TODO: Currently does 1:1 mapping between Tx and Rx. Need to set up indirection table
auto rxq = &rxport->RxQueue[ci];
#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
dbgRxState = rxq->State;
#endif

if (rxq->State == Started)
{
Expand All @@ -341,6 +409,10 @@ EnlpIterationRoutine(
};

auto rxPi = NetRingGetPostPackets(rxq->Queue->m_rings);
#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
dbgHasFrag = NetFragmentIteratorHasAny(&rxFi) ? 1 : 0;
dbgHasPkt = NetPacketIteratorHasAny(&rxPi) ? 1 : 0;
#endif

if (NetFragmentIteratorHasAny(&rxFi) && NetPacketIteratorHasAny(&rxPi))
{
Expand All @@ -364,6 +436,11 @@ EnlpIterationRoutine(
&txq->TxQueue->VirtualAddressExtension,
static_cast<SIZE_T>(fragment->Capacity));

// Relay the frame as an AP would (see function).
EnlpRelayWiFiFrame(
fragmentBuffer,
static_cast<SIZE_T>(fragment->ValidLength));

auto rxPacket = NetPacketIteratorGetPacket(&rxPi);
rxPacket->FragmentIndex = NetFragmentIteratorGetIndex(&rxFi);
rxPacket->FragmentCount = 1;
Expand Down Expand Up @@ -453,6 +530,13 @@ EnlpIterationRoutine(
}
}

#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
if (InterlockedIncrement(&g_enlFwdDbg) <= 30)
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,
"ENL fwd tx-port=%zu rx-port=%zu rxQCount=%lu rxState=%d hasFrag=%d hasPkt=%d drop=%d\n",
pi, pi ^ 0x1, (ULONG)rxport->RxQueueCount,
dbgRxState, dbgHasFrag, dbgHasPkt, rxDrop ? 1 : 0);
#endif
if (rxDrop)
{
// TODO - add rxdrop stat
Expand Down Expand Up @@ -480,6 +564,11 @@ EnlpIterationRoutine(

if (emptyTx)
{
#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
// Link idle: give the next tx burst a fresh logging budget.
InterlockedExchange(&g_enlFwdDbg, 0);
InterlockedExchange(&g_rxAdvDbg, 0);
#endif
if (enlLink->Poll == FALSE)
{
enlpArmAndWait(enlLink);
Expand Down
32 changes: 32 additions & 0 deletions network/netadaptercx/netvadapterlibrary/code/rxqueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
#include "rxqueue.h"
#include "memory.h"

#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
// Rx-indication logging budget. Non-static so enl.cpp can reset it when the
// link goes idle (see g_enlFwdDbg).
LONG g_rxAdvDbg = 0;
#endif

static
void
CheckForWakeFrame(
Expand Down Expand Up @@ -117,11 +123,25 @@ NetvRxQueue::Advance(

// Ideally this would run in EvtQueueStart, but at that point the receive buffers are not
// attached to the fragment yet
#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
bool const ranWake = ! CheckedWakeFrame;
#endif
if (! CheckedWakeFrame)
{
CheckForWakeFrame(this);
}

#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
ULONG const dbgFBegin = fr->BeginIndex;
ULONG const dbgFEnd = fr->EndIndex;
int const dbgHaveFrag = NetFragmentIteratorHasAny(&fi) ? 1 : 0;
int const dbgHavePkt = NetPacketIteratorHasAny(&pi) ? 1 : 0;
int dbgFirstScratch = -1;
if (dbgHaveFrag)
dbgFirstScratch = (int)NetFragmentIteratorGetFragment(&fi)->Scratch;
ULONG dbgDrained = 0;
#endif

// Move begin index forward for all fragments with Scratch == 1, thus returning them to the OS since we're done processing them.
for (; NetFragmentIteratorHasAny(&fi) && NetPacketIteratorHasAny(&pi); NetPacketIteratorAdvance(&pi), NetFragmentIteratorAdvance(&fi))
{
Expand All @@ -130,11 +150,23 @@ NetvRxQueue::Advance(
{
break;
}
#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
dbgDrained++;
#endif
}

NetFragmentIteratorSet(&fi);
NetPacketIteratorSet(&pi);
EnlRingDoorBell(EnlQueueHandle, fr->EndIndex);

#if defined(_KERNEL_MODE) && defined(NETV_DATAPATH_DEBUG)
if ((dbgDrained > 0 || ranWake || dbgFirstScratch == 1) &&
InterlockedIncrement(&g_rxAdvDbg) <= 40)
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,
"RX adv port=%u fBegin=%lu fEnd=%lu haveFrag=%d havePkt=%d firstScr=%d drained=%lu wake=%d\n",
(ULONG)m_adapter.EnlPortIndex, dbgFBegin, dbgFEnd,
dbgHaveFrag, dbgHavePkt, dbgFirstScratch, dbgDrained, ranWake ? 1 : 0);
#endif
}

_Use_decl_annotations_
Expand Down
2 changes: 1 addition & 1 deletion network/wlan/wificx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ The sample interacts with three OS components:
`netvxxxx` files and classes implement the **data path**, managing network buffers and synchronizing with the control path for transfer start/stop operations.

## Data Buffers from Firmware
The control path uses hardcoded data since this sample does not target real hardware. The data path uses **Emulated Network Link (ENL)**, which connects two virtual network adapters directly. Packets sent over one adapter are delivered to the other and vice versa.
The control path uses hardcoded data since this sample does not target real hardware. The data path uses **Emulated Network Link (ENL)**, which connects two virtual network adapters directly. Packets sent over one adapter are delivered to the other and vice versa. Because both instances associate to the same emulated access point, the ENL also performs the access point's relay role, rewriting each forwarded 802.11 frame so the receiving station accepts it.

## Supported Scenarios
- [x] Scan access points and report BSS entries to the Windows UI.
Expand Down
Loading