Skip to content

Commit

Permalink
Add support for setting TypeOfService/TrafficClass on connections
Browse files Browse the repository at this point in the history
  • Loading branch information
anrossi committed Jan 21, 2025
1 parent d88de36 commit 02c82b2
Show file tree
Hide file tree
Showing 15 changed files with 511 additions and 9 deletions.
1 change: 1 addition & 0 deletions docs/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ These parameters are accessed by calling [GetParam](./api/GetParam.md) or [SetPa
| `QUIC_PARAM_CONN_STATISTICS_V2`<br> 22 | QUIC_STATISTICS_V2 | Get-only | Connection-level statistics, version 2. |
| `QUIC_PARAM_CONN_STATISTICS_V2_PLAT`<br> 23 | QUIC_STATISTICS_V2 | Get-only | Connection-level statistics with platform-specific time format, version 2. |
| `QUIC_PARAM_CONN_ORIG_DEST_CID` <br> 24 | uint8_t[] | Get-only | The original destination connection ID used by the client to connect to the server. |
| `QUIC_PARAM_CONN_TYPE_OF_SERVICE` <br> 25 | uint8_t | Both | The value put in the Type of Service/Traffic Class field on packets sent from this connection. |

### QUIC_PARAM_CONN_STATISTICS_V2

Expand Down
58 changes: 58 additions & 0 deletions src/core/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -1837,6 +1837,7 @@ QuicConnStart(
UdpConfig.Flags = Connection->State.ShareBinding ? CXPLAT_SOCKET_FLAG_SHARE : 0;
UdpConfig.InterfaceIndex = Connection->State.LocalInterfaceSet ? (uint32_t)Path->Route.LocalAddress.Ipv6.sin6_scope_id : 0, // NOLINT(google-readability-casting)
UdpConfig.PartitionIndex = QuicPartitionIdGetIndex(Connection->PartitionID);
UdpConfig.TypeOfService = Connection->TypeOfService;
#ifdef QUIC_COMPARTMENT_ID
UdpConfig.CompartmentId = Configuration->CompartmentId;
#endif
Expand Down Expand Up @@ -6218,6 +6219,7 @@ QuicConnParamSet(
UdpConfig.RemoteAddress = &Connection->Paths[0].Route.RemoteAddress;
UdpConfig.Flags = Connection->State.ShareBinding ? CXPLAT_SOCKET_FLAG_SHARE : 0;
UdpConfig.InterfaceIndex = 0;
UdpConfig.TypeOfService = Connection->TypeOfService;
#ifdef QUIC_COMPARTMENT_ID
UdpConfig.CompartmentId = Connection->Configuration->CompartmentId;
#endif
Expand Down Expand Up @@ -6623,6 +6625,34 @@ QuicConnParamSet(
return QUIC_STATUS_SUCCESS;
}

case QUIC_PARAM_CONN_TYPE_OF_SERVICE:
{
if (BufferLength != sizeof(uint8_t) || Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

uint8_t TypeOfService = 0;
CxPlatCopyMemory(&TypeOfService, Buffer, BufferLength);

if (TypeOfService > CXPLAT_MAX_TYPE_OF_SERVICE) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

Connection->TypeOfService = TypeOfService;

if (Connection->State.Started) {
Status =
CxPlatSocketSetTypeOfService(
Connection->Paths[0].Binding->Socket,
TypeOfService);
} else {
Status = QUIC_STATUS_SUCCESS;
}
break;
}

//
// Private
//
Expand Down Expand Up @@ -7207,27 +7237,55 @@ QuicConnParamGet(
}

case QUIC_PARAM_CONN_ORIG_DEST_CID:

if (Connection->OrigDestCID == NULL) {
Status = QUIC_STATUS_INVALID_STATE;
break;
}

if (*BufferLength < Connection->OrigDestCID->Length) {
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
*BufferLength = Connection->OrigDestCID->Length;
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

CxPlatCopyMemory(
Buffer,
Connection->OrigDestCID->Data,
Connection->OrigDestCID->Length);

//
// Tell app how much buffer we copied.
//
*BufferLength = Connection->OrigDestCID->Length;

Status = QUIC_STATUS_SUCCESS;
break;

case QUIC_PARAM_CONN_TYPE_OF_SERVICE:

if (*BufferLength < sizeof(Connection->TypeOfService)) {
Status = QUIC_STATUS_BUFFER_TOO_SMALL;
*BufferLength = sizeof(Connection->TypeOfService);
break;
}

if (Buffer == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
break;
}

CxPlatCopyMemory(
Buffer,
&Connection->TypeOfService,
sizeof(Connection->TypeOfService));

*BufferLength = sizeof(Connection->TypeOfService);
Status = QUIC_STATUS_SUCCESS;
break;

Expand Down
6 changes: 6 additions & 0 deletions src/core/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,12 @@ typedef struct QUIC_CONNECTION {
//
uint8_t PeerReorderingThreshold;

//
// Type of Service value to set on the socket when the connection is started.
// Default value of 0.
//
uint8_t TypeOfService;

//
// The ACK frequency sequence number we are currently using to send.
//
Expand Down
1 change: 1 addition & 0 deletions src/inc/msquic.h
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,7 @@ typedef struct QUIC_SCHANNEL_CREDENTIAL_ATTRIBUTE_W {
#define QUIC_PARAM_CONN_STATISTICS_V2 0x05000016 // QUIC_STATISTICS_V2
#define QUIC_PARAM_CONN_STATISTICS_V2_PLAT 0x05000017 // QUIC_STATISTICS_V2
#define QUIC_PARAM_CONN_ORIG_DEST_CID 0x05000018 // uint8_t[]
#define QUIC_PARAM_CONN_TYPE_OF_SERVICE 0x50000019 // uint8_t

//
// Parameters for TLS.
Expand Down
19 changes: 19 additions & 0 deletions src/inc/quic_datapath.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ typedef enum CXPLAT_ECN_TYPE {
//
#define CXPLAT_ECN_FROM_TOS(ToS) (CXPLAT_ECN_TYPE)((ToS) & 0x3)

//
// Define the maximum type of service value allowed.
// Note: this is without the ECN bits included
//
#define CXPLAT_MAX_TYPE_OF_SERVICE 63

//
// The maximum IP MTU this implementation supports for QUIC.
//
Expand Down Expand Up @@ -444,6 +450,7 @@ CxPlatDataPathUpdateConfig(
#define CXPLAT_DATAPATH_FEATURE_TCP 0x0020
#define CXPLAT_DATAPATH_FEATURE_RAW 0x0040
#define CXPLAT_DATAPATH_FEATURE_TTL 0x0080
#define CXPLAT_DATAPATH_FEATURE_TYPE_OF_SERVICE 0x0100

//
// Queries the currently supported features of the datapath.
Expand Down Expand Up @@ -544,6 +551,7 @@ typedef struct CXPLAT_UDP_CONFIG {
#ifdef QUIC_OWNING_PROCESS
QUIC_PROCESS OwningProcess; // Kernel client-only
#endif
uint8_t TypeOfService; // Default 0. Optional.

// used for RAW datapath
uint8_t CibirIdLength; // CIBIR ID length. Value of 0 indicates CIBIR isn't used
Expand Down Expand Up @@ -646,6 +654,17 @@ CxPlatSocketGetRemoteAddress(
_Out_ QUIC_ADDR* Address
);

//
// Sets TypeOfService on the socket. May fail if insufficient privileges exist
// to set the desired value on the socket.
//
_IRQL_requires_max_(PASSIVE_LEVEL)
QUIC_STATUS
CxPlatSocketSetTypeOfService(
_In_ CXPLAT_SOCKET* Socket,
_In_ uint8_t TypeOfService
);

//
// Queries a raw socket availability.
//
Expand Down
94 changes: 94 additions & 0 deletions src/platform/datapath_epoll.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ CxPlatDataPathCalculateFeatureSupport(

Datapath->Features |= CXPLAT_DATAPATH_FEATURE_TCP;
Datapath->Features |= CXPLAT_DATAPATH_FEATURE_TTL;
Datapath->Features |= CXPLAT_DATAPATH_FEATURE_TYPE_OF_SERVICE;
}

void
Expand Down Expand Up @@ -887,6 +888,45 @@ CxPlatSocketContextInitialize(
goto Exit;
}

if (Config->TypeOfService != 0) {
Option = Config->TypeOfService;
Result =
setsockopt(
SocketContext->SocketFd,
IPPROTO_IP,
IP_TOS,
(const void*)&Option,
sizeof(Option));
if (Result == SOCKET_ERROR) {
Status = errno;
QuicTraceEvent(
DatapathErrorStatus,
"[data][%p] ERROR, %u, %s.",
Binding,
Status,
"setsockopt(IP_TOS) failed");
goto Exit;
}

Option = Config->TypeOfService;
Result =
setsockopt(
SocketContext->SocketFd,
IPPROTO_IPV6,
IPV6_TCLASS,
(const void*)&Option,
sizeof(Option));
if (Result == SOCKET_ERROR) {
Status = errno;
QuicTraceEvent(
DatapathErrorStatus,
"[data][%p] ERROR, %u, %s.",
Binding,
Status,
"setsockopt(IPV6_TCLASS) failed");
goto Exit;
}
}

#ifdef UDP_GRO
if (SocketContext->DatapathPartition->Datapath->Features & CXPLAT_DATAPATH_FEATURE_RECV_COALESCING) {
Expand Down Expand Up @@ -2455,6 +2495,60 @@ SocketSend(
}
}

_IRQL_requires_max_(PASSIVE_LEVEL)
QUIC_STATUS
SocketSetTypeOfService(
_In_ CXPLAT_SOCKET* Socket,
_In_ uint8_t TypeOfService
)
{
const uint16_t SocketCount = Socket->NumPerProcessorSockets ? (uint16_t)CxPlatProcCount() : 1;
QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
for (uint16_t i = 0; i < SocketCount; i++) {
CXPLAT_SOCKET_CONTEXT* SocketContext = &Socket->SocketContexts[i];
int Option = TypeOfService;
int if (Config->TypeOfService != 0) {
Option = Config->TypeOfService;
Result =
setsockopt(
SocketContext->SocketFd,
IPPROTO_IP,
IP_TOS,
(const void*)&Option,
sizeof(Option));
if (Result == SOCKET_ERROR) {
Status = errno;
QuicTraceEvent(
DatapathErrorStatus,
"[data][%p] ERROR, %u, %s.",
Binding,
Status,
"setsockopt(IP_TOS) failed");
break;
}

Option = TypeOfService;
Result =
setsockopt(
SocketContext->SocketFd,
IPPROTO_IPV6,
IPV6_TCLASS,
(const void*)&Option,
sizeof(Option));
if (Result == SOCKET_ERROR) {
Status = errno;
QuicTraceEvent(
DatapathErrorStatus,
"[data][%p] ERROR, %u, %s.",
Binding,
Status,
"setsockopt(IPV6_TCLASS) failed");
break;
}
}
}
return Status;
}
//
// This is defined and used instead of CMSG_NXTHDR because (1) we've already
// done the work to ensure the necessary space is available and (2) CMSG_NXTHDR
Expand Down
14 changes: 13 additions & 1 deletion src/platform/datapath_raw.c
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ RawSocketSend(
const CXPLAT_INTERFACE* Interface = CxPlatDpRawGetInterfaceFromQueue(Route->Queue);

CxPlatFramingWriteHeaders(
Socket, Route, &SendData->Buffer, SendData->ECN,
Socket, Route, &SendData->Buffer, SendData->ECN, Socket->TypeOfService,
Interface->OffloadStatus.Transmit.NetworkLayerXsum,
Interface->OffloadStatus.Transmit.TransportLayerXsum,
Route->TcpState.SequenceNumber,
Expand All @@ -380,3 +380,15 @@ RawSocketSend(
CxPlatDpRawTxEnqueue(SendData);
return QUIC_STATUS_SUCCESS;
}


_IRQL_requires_max_(DISPATCH_LEVEL)
QUIC_STATUS
RawSocketSetTypeOfService(
_In_ CXPLAT_SOCKET_RAW* Socket,
_In_ uint8_t TypeOfService
)
{
Socket->TypeOfService = TypeOfService;
return QUIC_STATUS_SUCCESS;
}
2 changes: 2 additions & 0 deletions src/platform/datapath_raw.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ typedef struct CXPLAT_SOCKET_RAW {
SOCKET AuxSocket;
BOOLEAN Wildcard; // Using a wildcard local address. Optimization
// to avoid always reading LocalAddress.
uint8_t TypeOfService; // IP TypeOfService/IPv6 Traffic Class value. Default 0.
uint8_t CibirIdLength; // CIBIR ID length. Value of 0 indicates CIBIR isn't used
uint8_t CibirIdOffsetSrc; // CIBIR ID offset in source CID
uint8_t CibirIdOffsetDst; // CIBIR ID offset in destination CID
Expand Down Expand Up @@ -377,6 +378,7 @@ CxPlatFramingWriteHeaders(
_In_ const CXPLAT_ROUTE* Route,
_Inout_ QUIC_BUFFER* Buffer,
_In_ CXPLAT_ECN_TYPE ECN,
_In_ uint8_t TypeOfService,
_In_ BOOLEAN SkipNetworkLayerXsum,
_In_ BOOLEAN SkipTransportLayerXsum,
_In_ uint32_t TcpSeqNum,
Expand Down
1 change: 1 addition & 0 deletions src/platform/datapath_raw_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ RawSocketCreateUdp(
NewSocket->CibirIdLength = Config->CibirIdLength;
NewSocket->CibirIdOffsetSrc = Config->CibirIdOffsetSrc;
NewSocket->CibirIdOffsetDst = Config->CibirIdOffsetDst;
NewSocket->TypeOfService = Config->TypeOfService;
NewSocket->AuxSocket = INVALID_SOCKET;
NewSocket->UseTcp = Raw->UseTcp;
if (Config->CibirIdLength) {
Expand Down
Loading

0 comments on commit 02c82b2

Please sign in to comment.