diff --git a/src/xdp/program.c b/src/xdp/program.c
index 310612e6..57cd8c0f 100644
--- a/src/xdp/program.c
+++ b/src/xdp/program.c
@@ -223,12 +223,18 @@ XdpInvokeEbpf(
ASSERT((FragmentRing == NULL) || (FragmentExtension != NULL));
//
- // Fragmented frames are currently not supported by eBPF.
+ // Fragmented frames require special handling for eBPF programs using direct
+ // packet access. On Linux, the program must be loaded with a specific flag
+ // in order to inspect discontiguous packets. On Windows, discontiguous
+ // frames are always inspected by default, at least until a program flag API
+ // is supported by eBPF-for-Windows.
+ //
+ // https://github.com/microsoft/ebpf-for-windows/issues/3576
+ // https://github.com/microsoft/xdp-for-windows/issues/517
//
if (FragmentRing != NULL &&
XdpGetFragmentExtension(Frame, FragmentExtension)->FragmentBufferCount != 0) {
- RxAction = XDP_RX_ACTION_DROP;
- goto Exit;
+ STAT_INC(RxQueueStats, InspectFramesDiscontiguous);
}
Buffer = &Frame->Buffer;
diff --git a/src/xdppcw/inc/xdppcw.h b/src/xdppcw/inc/xdppcw.h
index 3236f049..14aa884c 100644
--- a/src/xdppcw/inc/xdppcw.h
+++ b/src/xdppcw/inc/xdppcw.h
@@ -19,6 +19,7 @@ typedef struct _XDP_PCW_RX_QUEUE {
UINT64 InspectFramesDropped;
UINT64 InspectFramesRedirected;
UINT64 InspectFramesForwarded;
+ UINT64 InspectFramesDiscontiguous;
} XDP_PCW_RX_QUEUE;
typedef struct _XDP_PCW_LWF_RX_QUEUE {
diff --git a/src/xdppcw/xdppcw.man b/src/xdppcw/xdppcw.man
index 2db6ac56..ea47696b 100644
--- a/src/xdppcw/xdppcw.man
+++ b/src/xdppcw/xdppcw.man
@@ -145,6 +145,19 @@
detailLevel="standard"
defaultScale="1"
/>
+
Mask(sizeof(Payload) - Backfill - Trailer, 0xFF);
- auto LwfFilter = LwfRxFilter(FnLwf, Payload + Backfill, &Mask[0], sizeof(Payload) - Backfill - Trailer);
-
- TEST_HRESULT(MpRxEnqueueFrame(GenericMp, &Frame));
- MpRxFlush(GenericMp);
-
//
- // We currently do not support fragments with eBPF, so this packet should
- // be dropped.
+ // XDP-for-Windows has limited eBPF support for fragments: the first buffer
+ // is visible to eBPF programs, and the remaining fragments (if any) are
+ // inaccessible.
+ //
+ // Actions apply to the entire frame, not just to the first fragement.
//
+ std::vector Mask((SIZE_T)Buffers[0].DataLength + Buffers[1].DataLength, 0xFF);
+ MpTxFilter(GenericMp, Payload + Backfill, &Mask[0], (ULONG)Mask.size());
- Sleep(TEST_TIMEOUT_ASYNC_MS);
+ RX_FRAME Frame;
+ RxInitializeFrame(&Frame, If.GetQueueId(), Buffers, RTL_NUMBER_OF(Buffers));
+ TEST_HRESULT(MpRxEnqueueFrame(GenericMp, &Frame));
+ MpRxFlush(GenericMp);
- UINT32 FrameLength = 0;
- TEST_EQUAL(
- HRESULT_FROM_WIN32(ERROR_NOT_FOUND),
- LwfRxGetFrame(FnLwf, If.GetQueueId(), &FrameLength, NULL));
+ MpTxAllocateAndGetFrame(GenericMp, If.GetQueueId());
+ MpTxDequeueFrame(GenericMp, If.GetQueueId());
+ MpTxFlush(GenericMp);
}
VOID