From 306fad54d0709ca0b90c84ba30fbb662b3931a06 Mon Sep 17 00:00:00 2001 From: Michael Friesen <3517159+mtfriesen@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:32:10 -0500 Subject: [PATCH] Add built-in filter rule condition XDP_MATCH_IP_NEXT_HEADER (#749) --- docs/api/XDP_MATCH_PATTERN.md | 4 +++ docs/api/XDP_MATCH_TYPE.md | 5 ++++ published/external/xdp/program.h | 2 ++ src/xdp/program.c | 6 ++++ src/xdp/programinspect.c | 16 +++++++++-- test/functional/lib/tests.cpp | 47 +++++++++++++++++++++++++++----- test/functional/lib/tests.h | 3 +- test/functional/taef/tests.cpp | 8 ++++++ test/spinxsk/spinxsk.c | 8 ++++++ 9 files changed, 89 insertions(+), 10 deletions(-) diff --git a/docs/api/XDP_MATCH_PATTERN.md b/docs/api/XDP_MATCH_PATTERN.md index a4f58aa7..9f8ab591 100644 --- a/docs/api/XDP_MATCH_PATTERN.md +++ b/docs/api/XDP_MATCH_PATTERN.md @@ -74,6 +74,10 @@ typedef union _XDP_MATCH_PATTERN { // Match on destination IP address and port. // XDP_IP_PORT_SET IpPortSet; + // + // Match on destination IPv4 Protocol / IPv6 NextHeader field. + // + UINT8 NextHeader; } XDP_MATCH_PATTERN; ``` diff --git a/docs/api/XDP_MATCH_TYPE.md b/docs/api/XDP_MATCH_TYPE.md index 74f84601..7bda0409 100644 --- a/docs/api/XDP_MATCH_TYPE.md +++ b/docs/api/XDP_MATCH_TYPE.md @@ -97,6 +97,11 @@ typedef enum _XDP_MATCH_TYPE { // Port in XDP_MATCH_PATTERN. // XDP_MATCH_TCP_CONTROL_DST, + // + // Match frames with a specific IPv4 Protocol / IPv6 NextHeader field. + // The port number is specified by field NextHeader in XDP_MATCH_PATTERN. + // + XDP_MATCH_IP_NEXT_HEADER, } XDP_MATCH_TYPE; ``` diff --git a/published/external/xdp/program.h b/published/external/xdp/program.h index 09cc004e..18d659e1 100644 --- a/published/external/xdp/program.h +++ b/published/external/xdp/program.h @@ -35,6 +35,7 @@ typedef enum _XDP_MATCH_TYPE { XDP_MATCH_TCP_QUIC_FLOW_SRC_CID, XDP_MATCH_TCP_QUIC_FLOW_DST_CID, XDP_MATCH_TCP_CONTROL_DST, + XDP_MATCH_IP_NEXT_HEADER, } XDP_MATCH_TYPE; typedef union _XDP_INET_ADDR { @@ -82,6 +83,7 @@ typedef union _XDP_MATCH_PATTERN { XDP_QUIC_FLOW QuicFlow; XDP_PORT_SET PortSet; XDP_IP_PORT_SET IpPortSet; + UINT8 NextHeader; } XDP_MATCH_PATTERN; typedef enum _XDP_RULE_ACTION { diff --git a/src/xdp/program.c b/src/xdp/program.c index e08a92e4..2cd2409a 100644 --- a/src/xdp/program.c +++ b/src/xdp/program.c @@ -566,6 +566,12 @@ XdpProgramTrace( Program, i, ntohs(Rule->Pattern.Port)); break; + case XDP_MATCH_IP_NEXT_HEADER: + TraceInfo( + TRACE_CORE, "Program=%p Rule[%u]=XDP_MATCH_IP_NEXT_HEADER NextHeader=%u", + Program, i, ntohs(Rule->Pattern.NextHeader)); + break; + default: ASSERT(FALSE); break; diff --git a/src/xdp/programinspect.c b/src/xdp/programinspect.c index b07e7282..08a6fab9 100644 --- a/src/xdp/programinspect.c +++ b/src/xdp/programinspect.c @@ -391,7 +391,7 @@ XdpParseFrame( UINT32 Offset = 0; // - // This routine always attempts to parse Ethernet through UDP headers. + // This routine always attempts to parse Ethernet through TCP/UDP headers. // Cache->EthCached = TRUE; Cache->Ip4Cached = TRUE; @@ -1004,6 +1004,18 @@ XdpInspect( } break; + case XDP_MATCH_IP_NEXT_HEADER: + if (!(FrameCache.Ip4Cached || FrameCache.Ip6Cached)) { + XdpParseFrame( + Frame, FragmentRing, FragmentExtension, FragmentIndex, VirtualAddressExtension, + &FrameCache, &Program->FrameStorage); + } + if ((FrameCache.Ip4Valid && FrameCache.Ip4Hdr->Protocol == Rule->Pattern.NextHeader) || + (FrameCache.Ip6Valid && FrameCache.Ip6Hdr->NextHeader == Rule->Pattern.NextHeader)) { + Matched = TRUE; + } + break; + default: ASSERT(FALSE); break; @@ -1154,7 +1166,7 @@ XdpProgramValidateRule( // RtlZeroMemory(ValidatedRule, sizeof(*ValidatedRule)); - if (UserRule->Match < XDP_MATCH_ALL || UserRule->Match > XDP_MATCH_TCP_CONTROL_DST) { + if (UserRule->Match < XDP_MATCH_ALL || UserRule->Match > XDP_MATCH_IP_NEXT_HEADER) { Status = STATUS_INVALID_PARAMETER; goto Exit; } diff --git a/test/functional/lib/tests.cpp b/test/functional/lib/tests.cpp index c0bd0f8d..d26fb556 100644 --- a/test/functional/lib/tests.cpp +++ b/test/functional/lib/tests.cpp @@ -4008,6 +4008,7 @@ typedef struct _GENERIC_RX_FRAGMENT_PARAMS { _In_ BOOLEAN LowResources; _In_ UINT16 GroSegCount; _In_opt_ UINT32 IfMtu; + _In_ BOOLEAN UseIpNextHeaderMatch; _In_ XDP_RULE_ACTION Action; _In_opt_ const NDIS_OFFLOAD_PARAMETERS *OffloadParams; _In_opt_ const FN_OFFLOAD_OPTIONS *OffloadOptions; @@ -4348,6 +4349,7 @@ GenericRxFragmentBuffer( unique_fnmp_mtu_handle MtuReset; const UINT8 ThFlags = Params->TcpFlags != 0 ? Params->TcpFlags : TH_ACK; auto If = FnMpIf; + static const UINT8 TestNextHeaderValue = 0xFD; // Reserved for testing in RFC 3692. LocalPort = htons(1234); RemotePort = htons(4321); @@ -4379,8 +4381,13 @@ GenericRxFragmentBuffer( } XDP_RULE Rule; - Rule.Match = Params->IsUdp ? XDP_MATCH_UDP_DST : XDP_MATCH_TCP_DST; - Rule.Pattern.Port = LocalPort; + if (Params->UseIpNextHeaderMatch) { + Rule.Match = XDP_MATCH_IP_NEXT_HEADER; + Rule.Pattern.NextHeader = TestNextHeaderValue; + } else { + Rule.Match = Params->IsUdp ? XDP_MATCH_UDP_DST : XDP_MATCH_TCP_DST; + Rule.Pattern.Port = LocalPort; + } Rule.Action = Params->Action; if (Params->Action == XDP_PROGRAM_ACTION_REDIRECT) { @@ -4423,6 +4430,23 @@ GenericRxFragmentBuffer( 0x567890fe, ThFlags, 65535, &LocalHw, &RemoteHw, Af, &LocalIp, &RemoteIp, LocalPort, RemotePort)); } + if (Params->UseIpNextHeaderMatch) { + VOID *IpHeader = + RTL_PTR_ADD(PacketBuffer.data() + Params->Backfill, sizeof(ETHERNET_HEADER)); + + // + // Rewrite the IP next header field to be a unique value unused by the + // local stack. Do not fix up checksums. Not compatible with offloads. + // + if (Af == AF_INET) { + IPV4_HEADER *Ipv4 = (IPV4_HEADER *)IpHeader; + Ipv4->Protocol = TestNextHeaderValue; + } else { + TEST_EQUAL(AF_INET6, Af); + IPV6_HEADER *Ipv6 = (IPV6_HEADER *)IpHeader; + Ipv6->NextHeader = TestNextHeaderValue; + } + } ActualPacketLength += Params->DataTrailer; @@ -4589,7 +4613,8 @@ GenericRxHeaderMultipleFragments( _In_ XDP_RULE_ACTION ProgramAction, _In_ BOOLEAN IsUdp, _In_ BOOLEAN IsTxInspect, - _In_ BOOLEAN IsLowResources + _In_ BOOLEAN IsLowResources, + _In_ BOOLEAN UseIpNextHeaderMatch ) { GENERIC_RX_FRAGMENT_PARAMS Params = {0}; @@ -4610,6 +4635,7 @@ GenericRxHeaderMultipleFragments( Params.SplitCount = RTL_NUMBER_OF(SplitIndexes) - i; Params.IsTxInspect = IsTxInspect; Params.LowResources = IsLowResources; + Params.UseIpNextHeaderMatch = UseIpNextHeaderMatch; GenericRxFragmentBuffer(Af, &Params); } } @@ -4620,7 +4646,8 @@ GenericRxHeaderFragments( _In_ XDP_RULE_ACTION ProgramAction, _In_ BOOLEAN IsUdp, _In_ BOOLEAN IsTxInspect, - _In_ BOOLEAN IsLowResources + _In_ BOOLEAN IsLowResources, + _In_ BOOLEAN UseIpNextHeaderMatch ) { GENERIC_RX_FRAGMENT_PARAMS Params = {0}; @@ -4629,20 +4656,26 @@ GenericRxHeaderFragments( Params.PayloadLength = 43; Params.Backfill = 13; Params.Trailer = 17; + Params.IsTxInspect = IsTxInspect; + Params.LowResources = IsLowResources; + Params.UseIpNextHeaderMatch = UseIpNextHeaderMatch; UINT16 HeadersLength = sizeof(ETHERNET_HEADER) + ((Af == AF_INET) ? sizeof(IPV4_HEADER) : sizeof(IPV6_HEADER)) + (IsUdp ? sizeof(UDP_HDR) : sizeof(TCP_HDR)); - GenericRxHeaderMultipleFragments(Af, ProgramAction, IsUdp, IsTxInspect, IsLowResources); + GenericRxHeaderMultipleFragments( + Af, ProgramAction, IsUdp, IsTxInspect, IsLowResources, UseIpNextHeaderMatch); for (UINT32 i = 1; i < HeadersLength; i++) { Params.SplitIndexes = &i; Params.SplitCount = 1; - Params.IsTxInspect = IsTxInspect; - Params.LowResources = IsLowResources; GenericRxFragmentBuffer(Af, &Params); } + + Params.SplitIndexes = NULL; + Params.SplitCount = 0; + GenericRxFragmentBuffer(Af, &Params); } VOID diff --git a/test/functional/lib/tests.h b/test/functional/lib/tests.h index cfc67062..f393de1e 100644 --- a/test/functional/lib/tests.h +++ b/test/functional/lib/tests.h @@ -86,7 +86,8 @@ GenericRxHeaderFragments( _In_ XDP_RULE_ACTION ProgramAction, _In_ BOOLEAN IsUdp, _In_ BOOLEAN IsTxInspect = FALSE, - _In_ BOOLEAN IsLowResources = FALSE + _In_ BOOLEAN IsLowResources = FALSE, + _In_ BOOLEAN UseIpNextHeaderMatch = FALSE ); VOID diff --git a/test/functional/taef/tests.cpp b/test/functional/taef/tests.cpp index e986d678..026dbdd9 100644 --- a/test/functional/taef/tests.cpp +++ b/test/functional/taef/tests.cpp @@ -403,6 +403,14 @@ TEST_CLASS(xdpfunctionaltests) GenericRxHeaderFragments(AF_INET, XDP_PROGRAM_ACTION_L2FWD, TRUE, TRUE); } + TEST_METHOD(GenericRxIpNextHeaderV4) { + GenericRxHeaderFragments(AF_INET, XDP_PROGRAM_ACTION_REDIRECT, TRUE, FALSE, FALSE, TRUE); + } + + TEST_METHOD(GenericRxIpNextHeaderV6) { + GenericRxHeaderFragments(AF_INET6, XDP_PROGRAM_ACTION_REDIRECT, TRUE, FALSE, FALSE, TRUE); + } + TEST_METHOD(GenericRxFromTxInspectV4) { GenericRxFromTxInspect(AF_INET); } diff --git a/test/spinxsk/spinxsk.c b/test/spinxsk/spinxsk.c index 31fddfa5..f2460dee 100644 --- a/test/spinxsk/spinxsk.c +++ b/test/spinxsk/spinxsk.c @@ -683,6 +683,14 @@ AttachXdpProgram( } else { rule.Match = XDP_MATCH_ALL; + if (!(RandUlong() % 2)) { + rule.Match = RandUlong() % (XDP_MATCH_IP_NEXT_HEADER + 1); + } + + if (!(RandUlong() % 128)) { + rule.Match = (XDP_MATCH_TYPE)RandUlong(); + } + switch (RandUlong() % 4) { case 0: rule.Action = XDP_PROGRAM_ACTION_REDIRECT;