Skip to content

Commit

Permalink
Version Negotiation Draft5+ changes (microsoft#2458)
Browse files Browse the repository at this point in the history
  • Loading branch information
anrossi authored Mar 17, 2022
1 parent 2cf4d73 commit a7c6354
Show file tree
Hide file tree
Showing 34 changed files with 2,208 additions and 1,730 deletions.
1 change: 0 additions & 1 deletion docs/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ The following settings are available via registry as well as via [QUIC_SETTINGS]
| Client Migration Support | uint8_t | MigrationEnabled | 1 (TRUE) | Enable clients to migrate IP addresses and tuples. Requires a cooperative load-balancer, or no load-balancer. |
| Datagram Receive Support | uint8_t | DatagramReceiveEnabled | 0 (FALSE) | Advertise support for QUIC datagram extension. |
| Server Resumption Level | uint8_t | ServerResumptionLevel | 0 (No resumption) | Server only. Controls resumption tickets and/or 0-RTT server support. |
| Version Negotiation Extension | uint8_t | VersionNegotiationExtEnabled| 0 (FALSE) | Controls QUIC Version Negotiation Extension support. |
| Minimum MTU | uint16_t | MinimumMtu | 1248 | The minimum MTU supported by a connection. This will be used as the starting MTU. |
| Maximum MTU | uint16_t | MaximumMtu | 1500 | The maximum MTU supported by a connection. This will be the maximum probed value. |
| MTU Discovery Search Timeout | uint64_t | MtuDiscoverySearchCompleteTimeoutUs | 600000000 | The time in microseconds to wait before reattempting MTU probing if max was not reached. |
Expand Down
99 changes: 50 additions & 49 deletions docs/Versions.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,101 +4,102 @@ The QUIC protocol features a Version field to enable the protocol to evolve and
MsQuic is no exception and currently supports Draft-29 and Version 1 of the QUIC protocol.
By default, MsQuic clients start all connections with Version 1. MsQuic servers support Version 1 and Draft-29.

The [Version Negotiation Extension](https://tools.ietf.org/html/draft-ietf-quic-version-negotiation) is supported in MsQuic and is keeping pace with changes in the standard. It is disabled by default on both MsQuic client and server.
The [Version Negotiation Extension](https://tools.ietf.org/html/draft-ietf-quic-version-negotiation) is supported in MsQuic and is keeping pace with changes in the standard. It is enabled by default on both MsQuic client and server.

## Configuring QUIC Versions on MsQuic Clients

An application may decide that it needs a specific feature only availble in one version of QUIC. The application may also wish to change the order of preference of supported version in MsQuic. Both scenarios are supported via the `QUIC_SETTINGS` struct.
An application may decide that it needs a specific feature only availble in one version of QUIC. The application may also wish to change the order of preference of supported version in MsQuic. Both scenarios are supported via the `QUIC_VERSION_SETTINGS` struct. Since there are three different version lists, the client **MUST** set all three to be the same.

The first version in the list of `DesiredVersions` will always be the initial version MsQuic starts the connection with.
The first version in the list of `FullyDeployedVersions` will always be the initial version MsQuic starts the connection with.

**NOTE: A client may only set a version that MsQuic supports. Any other value will cause [`SetParam`](api/SetParam.md) to fail.**

Use the following code snippet to change the default initial version, and only support a single QUIC version. It must be used before [`ConnectionStart`](api/ConnectionStart.md) is called:
```c
QUIC_SETTINGS Settings = { 0 };
const uint32_t DesiredVersion = 0xff00001dU; // This is the Draft-29 version in HOST byte order. If the server does not support this, the connection will fail.
Settings.DesiredVersionsList = &DesiredVersion;
Settings.DesiredVersionsListLength = 1;
Settings.IsSet.DesiredVersionsList = TRUE;
QUIC_VERSION_SETTINGS Settings = { 0 };
const uint32_t SupportedVersion = 0xff00001dU; // This is the Draft-29 version in HOST byte order. If the server does not support this, the connection will fail.
Settings.AcceptableVersionsList = &SupportedVersion;
Settings.AcceptableVersionsListLength = 1;
Settings.OfferedVersionsList = &SupportedVersion;
Settings.OfferedVersionsListLength = 1;
Settings.FullyDeployedVersionsList = &SupportedVersion;
Settings.FullyDeployedVersionsListLength = 1;

MsQuic->SetParam(
Connection,
QUIC_PARAM_CONN_SETTINGS,
QUIC_PARAM_CONN_VERSION_SETTINGS,
sizeof(Settings),
&Settings);
```
Changing the order of supported versions is the same as above, with the following change:
```c
QUIC_SETTINGS Settings = { 0 };
const uint32_t DesiredVersions[2] = {
QUIC_VERSION_SETTINGS Settings = { 0 };
const uint32_t SupportedVersions[2] = {
0xff00001dU, // This is the Draft-29 version in HOST byte order. It will be used first.
0x00000001U // QUIC version 1 in HOST byte order. It will be used if a VN packet is received.
};
Settings.DesiredVersionsList = DesiredVersions;
Settings.DesiredVersionsListLength = 2;
Settings.IsSet.DesiredVersionsList = TRUE;
Settings.AcceptableVersionsList = SupportedVersions;
Settings.AcceptableVersionsListLength = 2;
Settings.OfferedVersionsList = SupportedVersions;
Settings.OfferedVersionsListLength = 2;
Settings.FullyDeployedVersionsList = SupportedVersions;
Settings.FullyDeployedVersionsListLength = 2;
```

The `QUIC_SETTINGS` which sets the desired QUIC version can be used in the [`ConfigurationOpen`](api/ConfigurationOpen.md) call and doesn't need to used exclusively with [`SetParam`](api/SetParam.md).
The `QUIC_VERSION_SETTINGS` can be set on a single `QUIC_CONNECTION`, as well as a `QUIC_CONFIGURATION` with [`SetParam`](api/SetParam.md).

## Configuring QUIC Versions on MsQuic Servers

A server application may also want to restrict the QUIC versions it supports to ensure a specific feature is available, or to prevent older versions of QUIC from being used.
Configuring the QUIC versions on a MsQuic server is similar to configuring them on a client, however, the setting for server **MUST** be set globally, and not on the `QUIC_CONFIGURATION`.
Configuring the QUIC versions on a MsQuic server is similar to configuring them on a client, however, the setting for server **MUST** be set globally, and not on the `QUIC_CONFIGURATION` used for the `QUIC_LISTENER` or `QUIC_CONNECTION`.

If a server is not in a fleet, or the operator/application does not ever need to change QUIC versions, then all three lists in `QUIC_VERSION_SETTINGS` **MUST** be the same.

If a server is deployed in a fleet, and the server operator wishes to change the supported QUIC versions, the Version Negotiation specification details how that should be done, quoted here:
> When adding support for a new version:
> * The first step is to progressively add support for the new version to all server instances. This step updates the Acceptable Versions but not the Offered Versions nor the Fully-Deployed Versions. Once all server instances have been updated, operators wait for at least one MSL to allow any in-flight Version Negotiation packets to arrive.
> * Then, the second step is to progressively add the new version to Offered Versions on all server instances. Once complete, operators wait for at least another MSL.
> * Finally, the third step is to progressively add the new version to Fully-Deployed Versions on all server instances.
>
> When removing support for a version:
> * The first step is to progressively remove the version from Fully-Deployed Versions on all server instances. Once it has been removed on all server instances, operators wait for at least one MSL to allow any in-flight Version Negotiation packets to arrive.
> * Then, the second step is to progressively remove the version from Offered Versions on all server instances. Once complete, operators wait for at least another MSL.
> * Finally, the third step is to progressively remove support for the version from all server instances. That step updates the Acceptable Versions.
**Note that this opens connections to version downgrades (but only for partially-deployed versions) during the update window, since those could be due to clients communicating with both updated and non-updated server instances.**


This snippet should execute before the server's `QUIC_CONFIGURATION` is created:
```c
QUIC_SETTINGS Settings = { 0 };
const uint32_t DesiredVersions[2] = {
QUIC_VERSION_SETTINGS Settings = { 0 };
const uint32_t SupportedVersions[2] = {
0xff00001dU, // This is the Draft-29 version in HOST byte order. It will be preferred over Version 1.
0x00000001U // QUIC version 1 in HOST byte order. It will be used if a client starts with Version 1, instead of Draft-29.
};
Settings.DesiredVersionsList = DesiredVersions;
Settings.DesiredVersionsListLength = 2;
Settings.IsSet.DesiredVersionsList = TRUE;
Settings.AcceptableVersionsList = SupportedVersion;
Settings.AcceptableVersionsListLength = 2;
Settings.OfferedVersionsList = SupportedVersion;
Settings.OfferedVersionsListLength = 2;
Settings.FullyDeployedVersionsList = SupportedVersion;
Settings.FullyDeployedVersionsListLength = 2;

MsQuic->SetParam(
NULL,
QUIC_PARAM_CONN_SETTINGS,
QUIC_PARAM_GLOBAL_VERSION_SETTINGS,
sizeof(Settings),
&Settings);
```
# QUIC Version Negotiation Extension
The Version Negotiation Extension is off by default. Since the standard is not yet complete, incompatible changes may be made preventing different drafts from working with each other. An application using MsQuic should be cautious about enabling the Version Negotiation Extension in production scenarios until the standard is complete.
The Version Negotiation Extension is on by default in our officially-released binaries. Since the standard is not yet complete, incompatible changes may be made preventing different drafts from working with each other. An application using MsQuic should be cautious about enabling the Version Negotiation Extension in production scenarios until the standard is complete.
## Enabling Version Negotiation Extension on MsQuic Client
The Version Negotiation Extension is enabled on client the same as the QUIC version. It can also be set via [`ConfigurationOpen`](api/ConfigurationOpen.md), as well as via [`SetParam`](api/SetParam.md).
The Version Negotiation Extension is enabled on client when `QUIC_VERSION_SETTINGS` are set on the `QUIC_CONFIGURATION` or `QUIC_CONNECTION` via [`SetParam`](api/SetParam.md).
This setting **MUST** be set before [`ConnectionStart`](api/ConnectionStart.md) to take effect.
```c
QUIC_SETTINGS Settings = { 0 };
Settings.VersionNegotiationExtEnabled = TRUE;
Settings.IsSet.VersionNegotiationExtEnabled = TRUE;
MsQuic->SetParam(
Connection,
QUIC_PARAM_CONN_SETTINGS,
sizeof(Settings),
&Settings);
```

## Enabling Version Negotiation Extension on MsQuic Server
Enabling the Version Negotiation Extension on server follows the same restrictions as setting the QUIC version on server, i.e. it **MUST** be set globally, using [`SetParam`](api/SetParam.md) before the `QUIC_CONFIGURATION` is opened for the server.

```c
QUIC_SETTINGS Settings = { 0 };
Settings.VersionNegotiationExtEnabled = TRUE;
Settings.IsSet.VersionNegotiationExtEnabled = TRUE;

MsQuic->SetParam(
NULL,
QUIC_PARAM_CONN_SETTINGS,
sizeof(Settings),
&Settings);
```
Enabling the Version Negotiation Extension on server follows the same restrictions as setting the QUIC version on server, i.e. it **MUST** be set globally, using [`SetParam`](api/SetParam.md) before the `QUIC_CONFIGURATION` is opened for the server. It is set automatically when `QUIC_VERSION_SETTINGS` are set.
10 changes: 5 additions & 5 deletions docs/api/GetParam.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,26 @@ The function returns a [QUIC_STATUS](QUIC_STATUS.md). The app may use `QUIC_FAIL
# Remarks
While many parameters are staticly-sized, some are dynamically-sized and will require the application to do a double call to `GetParam`: the first to find out the amount of memory needed to allocate, placed in `BufferLength`, and the second call to actually retrieve the parameter's value. For example, after setting a `DesiredVersionsList` on a `QUIC_SETTINGS`, retrieving the settings from the same API object will require a double call to allocate storage for the `DesiredVersionsList`.
While many parameters are staticly-sized, some are dynamically-sized and will require the application to do a double call to `GetParam`: the first to find out the amount of memory needed to allocate, placed in `BufferLength`, and the second call to actually retrieve the parameter's value. For example, after setting a `QUIC_VERSION_SETTINGS` on a `QUIC_CONFIGURATION`, retrieving the settings from the same API object will require a double call to allocate enough storage for the `QUIC_VERSION_SETTINGS` lists.
Sample of double-call:
```C
uint32_t SettingsSize = 0;
QUIC_SETTINGS* Settings = NULL;
QUIC_VERSION_SETTINGS* Settings = NULL;
if (QUIC_STATUS_BUFFER_TOO_SMALL ==
MsQuic->GetParam(
Configuration,
QUIC_PARAM_CONFIGURATION_SETTINGS,
QUIC_PARAM_CONFIGURATION_VERSION_SETTINGS,
&SettingsSize,
Settings)) {
Settings = (QUIC_SETTINGS*)malloc(SettingsSize);
Settings = (QUIC_VERSION_SETTINGS*)malloc(SettingsSize);
if (QUIC_FAILED(
MsQuic->GetParam(
Configuration,
QUIC_PARAM_CONFIGURATION_SETTINGS,
QUIC_PARAM_CONFIGURATION_VERSION_SETTINGS,
&SettingsSize,
Settings))) {
// Error.
Expand Down
57 changes: 16 additions & 41 deletions docs/api/QUIC_SETTINGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ typedef struct QUIC_SETTINGS {
uint64_t MaxBytesPerKey : 1;
uint64_t HandshakeIdleTimeoutMs : 1;
uint64_t IdleTimeoutMs : 1;
uint64_t MtuDiscoverySearchCompleteTimeoutUs : 1;
uint64_t TlsClientMaxSendBuffer : 1;
uint64_t TlsServerMaxSendBuffer : 1;
uint64_t StreamRecvWindowDefault : 1;
Expand All @@ -27,31 +28,28 @@ typedef struct QUIC_SETTINGS {
uint64_t MaxAckDelayMs : 1;
uint64_t DisconnectTimeoutMs : 1;
uint64_t KeepAliveIntervalMs : 1;
uint64_t CongestionControlAlgorithm : 1;
uint64_t PeerBidiStreamCount : 1;
uint64_t PeerUnidiStreamCount : 1;
uint64_t RetryMemoryLimit : 1;
uint64_t LoadBalancingMode : 1;
uint64_t MaxOperationsPerDrain : 1;
uint64_t MaxBindingStatelessOperations : 1;
uint64_t StatelessOperationExpirationMs : 1;
uint64_t MinimumMtu : 1;
uint64_t MaximumMtu : 1;
uint64_t SendBufferingEnabled : 1;
uint64_t PacingEnabled : 1;
uint64_t MigrationEnabled : 1;
uint64_t DatagramReceiveEnabled : 1;
uint64_t ServerResumptionLevel : 1;
uint64_t DesiredVersionsList : 1;
uint64_t VersionNegotiationExtEnabled : 1;
uint64_t MinimumMtu : 1;
uint64_t MaximumMtu : 1;
uint64_t MtuDiscoverySearchCompleteTimeoutUs : 1;
uint64_t MaxOperationsPerDrain : 1;
uint64_t MtuDiscoveryMissingProbeCount : 1;
uint64_t MaxBindingStatelessOperations : 1;
uint64_t StatelessOperationExpirationMs : 1;
uint64_t RESERVED : 30;
uint64_t RESERVED : 33;
} IsSet;
};

uint64_t MaxBytesPerKey;
uint64_t HandshakeIdleTimeoutMs;
uint64_t IdleTimeoutMs;
uint64_t MtuDiscoverySearchCompleteTimeoutUs;
uint32_t TlsClientMaxSendBuffer;
uint32_t TlsServerMaxSendBuffer;
uint32_t StreamRecvWindowDefault;
Expand All @@ -65,26 +63,21 @@ typedef struct QUIC_SETTINGS {
uint32_t MaxAckDelayMs;
uint32_t DisconnectTimeoutMs;
uint32_t KeepAliveIntervalMs;
uint16_t CongestionControlAlgorithm; // QUIC_CONGESTION_CONTROL_ALGORITHM
uint16_t PeerBidiStreamCount;
uint16_t PeerUnidiStreamCount;
uint16_t RetryMemoryLimit; // Global only
uint16_t LoadBalancingMode; // Global only
uint8_t MaxOperationsPerDrain;
uint16_t MaxBindingStatelessOperations;
uint16_t StatelessOperationExpirationMs;
uint16_t MinimumMtu;
uint16_t MaximumMtu;
uint8_t SendBufferingEnabled : 1;
uint8_t PacingEnabled : 1;
uint8_t MigrationEnabled : 1;
uint8_t DatagramReceiveEnabled : 1;
uint8_t ServerResumptionLevel : 2; // QUIC_SERVER_RESUMPTION_LEVEL
uint8_t VersionNegotiationExtEnabled : 1;
uint8_t RESERVED : 1;
const uint32_t* DesiredVersionsList;
uint32_t DesiredVersionsListLength;
uint16_t MinimumMtu;
uint16_t MaximumMtu;
uint64_t MtuDiscoverySearchCompleteTimeoutUs;
uint8_t RESERVED : 2;
uint8_t MaxOperationsPerDrain;
uint8_t MtuDiscoveryMissingProbeCount;
uint16_t MaxBindingStatelessOperations;
uint16_t StatelessOperationExpirationMs;

} QUIC_SETTINGS;
```
Expand Down Expand Up @@ -251,24 +244,6 @@ Server only. Controls resumption tickets and/or 0-RTT server support. `QUIC_SERV

**Default value:** `QUIC_SERVER_NO_RESUME` (disabled)

`VersionNegotiationExtEnabled`

Controls QUIC Version Negotiation Extension support.

**Default value:** 0 (`FALSE`)

`DesiredVersionsList`

Only takes effect if Version Negotiation Extension is enabled. Must be set to `NULL` unless `VersionNegotiationExtEnabled` is `TRUE`.

**Default value:** `NULL`

`DesiredVersionsListLength`

Number of QUIC protocol versions in the DesiredVersionsList. Must be set to 0 unless `VersionNegotiationExtEnabled` is `TRUE`.

**Default value:** 0

`MinimumMtu`

The minimum MTU supported by a connection. This will be used as the starting MTU.
Expand Down
6 changes: 3 additions & 3 deletions src/core/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -833,9 +833,9 @@ QuicBindingProcessStatelessOperation(

const uint32_t* SupportedVersions;
uint32_t SupportedVersionsLength;
if (MsQuicLib.Settings.IsSet.DesiredVersionsList) {
SupportedVersions = MsQuicLib.Settings.DesiredVersionsList;
SupportedVersionsLength = MsQuicLib.Settings.DesiredVersionsListLength;
if (MsQuicLib.Settings.IsSet.VersionSettings) {
SupportedVersions = MsQuicLib.Settings.VersionSettings->OfferedVersions;
SupportedVersionsLength = MsQuicLib.Settings.VersionSettings->OfferedVersionsLength;
} else {
SupportedVersions = DefaultSupportedVersionsList;
SupportedVersionsLength = ARRAYSIZE(DefaultSupportedVersionsList);
Expand Down
Loading

0 comments on commit a7c6354

Please sign in to comment.