Skip to content

Commit 078d24c

Browse files
update docs based on feedback; improve log level
1 parent 18832a4 commit 078d24c

8 files changed

Lines changed: 74 additions & 73 deletions

File tree

docs/CIBIR.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
## What is it
44

5-
See [XDP](./XDP.md) first to understand the context.
5+
See the [draft IETF](https://datatracker.ietf.org/doc/html/draft-banks-quic-cibir) for CIBIR.
66

7-
When CIBIR is used, rather than programming XDP to filter and demux packets based on on address and port number,
7+
When CIBIR is used, rather than programming [XDP](./XDP.md) to filter and demux packets based on on address and port number,
88
XDP with CIBIR will instead filter and de-mux packets based on address, port number, and QUIC connection ID.
99

1010
What CIBIR allows for is 2 or more separate server processes to share a single
@@ -17,10 +17,19 @@ port on the same machine, as long as their CIBIR ID is different.
1717
- The responsbility of book-keeping shared ports and ensuring robust protection for those shared ports is delegated to the application.
1818

1919

20-
## Port protection recommendation for shared ports
20+
## Port protection recommendations for shared ports
21+
22+
### Option 1: Persistent port reservations (Recommended)
2123

2224
MsQuic strongly recommends applications leverage the Windows [persistent port reservations API](https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-createpersistentudpportreservation) to secure shared CIBIR ports prior to serving multi-process CIBIR traffic on a shared port.
2325
- One time setup by a system admin to create the persistent reservation.
2426
> A good option for book-keeping persistent port reservations is via registry keys.
25-
- Persistent port reservations survive reboots, allowing for robust portection in the event of crashes.
26-
- Having a persistent reservation makes sure critical ports are taken out of the ephemeral port pool, so an unsuspecting application process won't get accidently assigned an ephemeral port that collides with a CIBIR port.
27+
- Persistent port reservations survive reboots, allowing for robust protection in the event of crashes.
28+
- Having a persistent reservation makes sure CIBIR ports are taken out of the ephemeral port pool and forbids sockets from binding to it unless it is associated with a persistent reservation token, which can only happen in an elevated process.
29+
- This way, an unsuspecting application process won't get accidently assigned an ephemeral port that collides with a CIBIR port.
30+
31+
### Option 2: WFP ALE (Application Layer Enforcement) filters
32+
33+
As an alternative, applications can use the [Windows Filtering Platform (WFP)](https://learn.microsoft.com/en-us/windows/win32/fwp/windows-filtering-platform-start-page) to create ALE filters that block unauthorized bind attempts to CIBIR ports.
34+
35+
ALE filters operate at the [bind and connect authorization layers](https://learn.microsoft.com/en-us/windows/win32/fwp/ale-layers) (`FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4/V6`, `FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4/V6`). A filter can be configured to block any process from binding to a specific UDP port unless it matches an allowed application path or security descriptor.

docs/XDP.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# MsQuic over XDP
22

3-
To avoid confusion, "XDP" refers to [XDP-for-windows](https://github.com/microsoft/xdp-for-windows). While Linux XDP has been experimented
4-
upon in the past and shown some promise for running MsQuic, it is NOT a stable datapath actively being maintained today.
3+
To avoid confusion, "XDP" refers to [XDP-for-windows](https://github.com/microsoft/xdp-for-windows).
4+
MsQuic does not support Linux XDP as a datapath.
55

66
## What is XDP
77

src/generated/linux/datapath_xplat.c.clog.h

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
#define _clog_MACRO_QuicTraceLogVerbose 1
2323
#define QuicTraceLogVerbose(a, ...) _clog_CAT(_clog_ARGN_SELECTOR(__VA_ARGS__), _clog_CAT(_,a(#a, __VA_ARGS__)))
2424
#endif
25+
#ifndef _clog_MACRO_QuicTraceLogError
26+
#define _clog_MACRO_QuicTraceLogError 1
27+
#define QuicTraceLogError(a, ...) _clog_CAT(_clog_ARGN_SELECTOR(__VA_ARGS__), _clog_CAT(_,a(#a, __VA_ARGS__)))
28+
#endif
2529
#ifdef __cplusplus
2630
extern "C" {
2731
#endif
@@ -41,22 +45,6 @@ tracepoint(CLOG_DATAPATH_XPLAT_C, WarnFallbackToOsSockets );\
4145

4246

4347

44-
/*----------------------------------------------------------
45-
// Decoder Ring for ErrNoXdpForRaw
46-
// [sock] Error: app requested QTIP but XDP not enabled/available/initialized.
47-
// QuicTraceLogWarning(
48-
ErrNoXdpForRaw,
49-
"[sock] Error: app requested QTIP but XDP not enabled/available/initialized.");
50-
----------------------------------------------------------*/
51-
#ifndef _clog_2_ARGS_TRACE_ErrNoXdpForRaw
52-
#define _clog_2_ARGS_TRACE_ErrNoXdpForRaw(uniqueId, encoded_arg_string)\
53-
tracepoint(CLOG_DATAPATH_XPLAT_C, ErrNoXdpForRaw );\
54-
55-
#endif
56-
57-
58-
59-
6048
/*----------------------------------------------------------
6149
// Decoder Ring for WarnNoXdpForCibirSockets
6250
// [sock] Warning: app requested CIBIR but XDP not enabled/available/initialized. \
@@ -126,6 +114,22 @@ tracepoint(CLOG_DATAPATH_XPLAT_C, RawSockCreateFail , arg2);\
126114

127115

128116

117+
/*----------------------------------------------------------
118+
// Decoder Ring for ErrNoXdpForQtip
119+
// [sock] Error: app requested QTIP but XDP not enabled/available/initialized.
120+
// QuicTraceLogError(
121+
ErrNoXdpForQtip,
122+
"[sock] Error: app requested QTIP but XDP not enabled/available/initialized.");
123+
----------------------------------------------------------*/
124+
#ifndef _clog_2_ARGS_TRACE_ErrNoXdpForQtip
125+
#define _clog_2_ARGS_TRACE_ErrNoXdpForQtip(uniqueId, encoded_arg_string)\
126+
tracepoint(CLOG_DATAPATH_XPLAT_C, ErrNoXdpForQtip );\
127+
128+
#endif
129+
130+
131+
132+
129133
#ifdef __cplusplus
130134
}
131135
#endif

src/generated/linux/datapath_xplat.c.clog.h.lttng.h

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,6 @@ TRACEPOINT_EVENT(CLOG_DATAPATH_XPLAT_C, WarnFallbackToOsSockets,
1717

1818

1919

20-
/*----------------------------------------------------------
21-
// Decoder Ring for ErrNoXdpForRaw
22-
// [sock] Error: app requested QTIP but XDP not enabled/available/initialized.
23-
// QuicTraceLogWarning(
24-
ErrNoXdpForRaw,
25-
"[sock] Error: app requested QTIP but XDP not enabled/available/initialized.");
26-
----------------------------------------------------------*/
27-
TRACEPOINT_EVENT(CLOG_DATAPATH_XPLAT_C, ErrNoXdpForRaw,
28-
TP_ARGS(
29-
),
30-
TP_FIELDS(
31-
)
32-
)
33-
34-
35-
3620
/*----------------------------------------------------------
3721
// Decoder Ring for WarnNoXdpForCibirSockets
3822
// [sock] Warning: app requested CIBIR but XDP not enabled/available/initialized. \
@@ -102,3 +86,19 @@ TRACEPOINT_EVENT(CLOG_DATAPATH_XPLAT_C, RawSockCreateFail,
10286
ctf_integer(int, arg2, arg2)
10387
)
10488
)
89+
90+
91+
92+
/*----------------------------------------------------------
93+
// Decoder Ring for ErrNoXdpForQtip
94+
// [sock] Error: app requested QTIP but XDP not enabled/available/initialized.
95+
// QuicTraceLogError(
96+
ErrNoXdpForQtip,
97+
"[sock] Error: app requested QTIP but XDP not enabled/available/initialized.");
98+
----------------------------------------------------------*/
99+
TRACEPOINT_EVENT(CLOG_DATAPATH_XPLAT_C, ErrNoXdpForQtip,
100+
TP_ARGS(
101+
),
102+
TP_FIELDS(
103+
)
104+
)

src/manifest/clog.sidecar

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4222,6 +4222,13 @@
42224222
],
42234223
"macroName": "QuicTraceLogVerbose"
42244224
},
4225+
"ErrNoXdpForQtip": {
4226+
"ModuleProperites": {},
4227+
"TraceString": "[sock] Error: app requested QTIP but XDP not enabled/available/initialized.",
4228+
"UniqueId": "ErrNoXdpForQtip",
4229+
"splitArgs": [],
4230+
"macroName": "QuicTraceLogError"
4231+
},
42254232
"ErrNoXdpForRaw": {
42264233
"ModuleProperites": {},
42274234
"TraceString": "[sock] Error: app requested QTIP but XDP not enabled/available/initialized.",
@@ -14742,6 +14749,11 @@
1474214749
"TraceID": "EpollSocketRelease",
1474314750
"EncodingString": "[data][%p] Socket Freed"
1474414751
},
14752+
{
14753+
"UniquenessHash": "b93c5e28-04ab-7e11-fe17-83c24bf864d3",
14754+
"TraceID": "ErrNoXdpForQtip",
14755+
"EncodingString": "[sock] Error: app requested QTIP but XDP not enabled/available/initialized."
14756+
},
1474514757
{
1474614758
"UniquenessHash": "c8f7a72c-21b5-b5b0-8978-883ca5df98ee",
1474714759
"TraceID": "ErrNoXdpForRaw",

src/platform/datapath_xplat.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ CxPlatSocketCreateUdp(
166166
if (IsWildcardAddr && RequiresQtip) {
167167
//
168168
// This retry loop is purely for QTIP listener sockets that try to reserve both a UDP/TCP port,
169-
// which may run into a WSA_ADDR_INUSE for TCP if the UDP ephemeral port collides with something
169+
// which may run into a port collision for TCP if the UDP ephemeral port collides with something
170170
// in the TCP pool. So just try it again.
171171
//
172172
CxPlatSocketDelete(*NewSocket);
@@ -191,8 +191,8 @@ CxPlatSocketCreateUdp(
191191
goto Error;
192192
}
193193
} else if (RequiresQtip) {
194-
QuicTraceLogWarning(
195-
ErrNoXdpForRaw,
194+
QuicTraceLogError(
195+
ErrNoXdpForQtip,
196196
"[sock] Error: app requested QTIP but XDP not enabled/available/initialized.");
197197
CxPlatSocketDelete(*NewSocket);
198198
Status = QUIC_STATUS_INVALID_STATE;

src/test/lib/HandshakeTest.cpp

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3920,25 +3920,12 @@ QuicTestCibirExtension(
39203920
// CIBIR + XDP requires an explicit local port. Reserve an ephemeral port
39213921
// up front so the listener always binds to a known port.
39223922
//
3923-
if (PortReservation.Port != 0) {
3924-
ServerLocalAddr.SetPort(PortReservation.Port);
3925-
}
3926-
} else {
3927-
//
3928-
// If no XDP, CIBIR just falls back to normal OS sockets.
3929-
// We can just rely upon the Listener created sockets. Passing in
3930-
// a local port of 0 is fine here.
3931-
//
3932-
PortReservation.Release();
3923+
ServerLocalAddr.SetPort(PortReservation.Port);
39333924
}
39343925
#endif
39353926
MsQuicAutoAcceptListener Listener(Registration, ServerConfiguration, MsQuicConnection::NoOpCallback);
39363927
if (Mode & 1) {
39373928
TEST_QUIC_SUCCEEDED(Listener.SetCibirId(CibirId, CibirIdLength));
3938-
} else {
3939-
#ifdef _WIN32
3940-
PortReservation.Release();
3941-
#endif
39423929
}
39433930

39443931
TEST_QUIC_SUCCEEDED(Listener.Start("MsQuicTest", &ServerLocalAddr.SockAddr));
@@ -4491,15 +4478,14 @@ QuicTestConnectionPoolCreate(
44914478
TEST_QUIC_SUCCEEDED(ClientConfiguration.GetInitStatus());
44924479

44934480
QuicAddr ServerAddr(QuicAddrFamily);
4494-
#ifdef _WIN32
4495-
QuicTestPortReservation PortReservation(QuicAddrFamily);
4496-
#endif
4481+
44974482
if (XdpSupported) {
44984483
QuicAddrSetToDuoNic(&ServerAddr.SockAddr);
44994484
}
45004485

45014486
#ifdef _WIN32
4502-
if (PortReservation.Port != 0 && UseDuoNic && TestCibirSupport) {
4487+
QuicTestPortReservation PortReservation(QuicAddrFamily);
4488+
if (UseDuoNic && TestCibirSupport) {
45034489
//
45044490
// If Cibir+XDP mode is active, we can't pass in a 0 local port
45054491
// hoping the stack will assign us an ephemeral port.

src/test/lib/TestHelpers.h

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,6 @@ QuitTestIsFeatureSupported(uint32_t Feature) {
102102
// is destroyed). This minimizes the TOCTOU window between discovering a free
103103
// port and having the listener bind to it.
104104
//
105-
// In kernel mode, raw socket APIs are unavailable, so this is a no-op
106-
// (Port stays 0 and the listener falls back to OS-assigned ephemeral ports).
107-
//
108-
#ifdef _WIN32
109105
struct QuicTestPortReservation {
110106
uint16_t Port{0};
111107

@@ -115,7 +111,6 @@ struct QuicTestPortReservation {
115111
_In_ QUIC_ADDRESS_FAMILY Family
116112
)
117113
{
118-
#ifndef _KERNEL_MODE
119114
int af = (Family == QUIC_ADDRESS_FAMILY_INET) ? AF_INET : AF_INET6;
120115

121116
Sock = socket(af, SOCK_DGRAM, IPPROTO_UDP);
@@ -140,9 +135,7 @@ struct QuicTestPortReservation {
140135
if (Port == 0) {
141136
Release();
142137
}
143-
#else
144138
UNREFERENCED_PARAMETER(Family);
145-
#endif
146139
}
147140

148141
~QuicTestPortReservation() { Release(); }
@@ -156,17 +149,14 @@ struct QuicTestPortReservation {
156149
//
157150
void Release()
158151
{
159-
#ifndef _KERNEL_MODE
160-
if (Sock != INVALID_SOCKET) { closesocket(Sock); Sock = INVALID_SOCKET; }
161-
#endif
152+
if (Sock != INVALID_SOCKET) {
153+
closesocket(Sock); Sock = INVALID_SOCKET;
154+
}
162155
}
163156

164157
private:
165-
#ifndef _KERNEL_MODE
166158
SOCKET Sock{INVALID_SOCKET};
167-
#endif
168159
};
169-
#endif
170160

171161
#define OLD_SUPPORTED_VERSION QUIC_VERSION_1_MS_H
172162
#define LATEST_SUPPORTED_VERSION QUIC_VERSION_LATEST_H

0 commit comments

Comments
 (0)