Skip to content

Commit

Permalink
Add built-in filter rule condition XDP_MATCH_IP_NEXT_HEADER (#749)
Browse files Browse the repository at this point in the history
  • Loading branch information
mtfriesen authored Nov 21, 2024
1 parent 300d18c commit 306fad5
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 10 deletions.
4 changes: 4 additions & 0 deletions docs/api/XDP_MATCH_PATTERN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
```

Expand Down
5 changes: 5 additions & 0 deletions docs/api/XDP_MATCH_TYPE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
```

Expand Down
2 changes: 2 additions & 0 deletions published/external/xdp/program.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions src/xdp/program.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 14 additions & 2 deletions src/xdp/programinspect.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
47 changes: 40 additions & 7 deletions test/functional/lib/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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};
Expand All @@ -4610,6 +4635,7 @@ GenericRxHeaderMultipleFragments(
Params.SplitCount = RTL_NUMBER_OF(SplitIndexes) - i;
Params.IsTxInspect = IsTxInspect;
Params.LowResources = IsLowResources;
Params.UseIpNextHeaderMatch = UseIpNextHeaderMatch;
GenericRxFragmentBuffer(Af, &Params);
}
}
Expand All @@ -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};
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion test/functional/lib/tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions test/functional/taef/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
8 changes: 8 additions & 0 deletions test/spinxsk/spinxsk.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 306fad5

Please sign in to comment.