19#include <freerdp/config.h>
21#include <winpr/assert.h>
22#include <winpr/sysinfo.h>
24#include <freerdp/freerdp.h>
25#include <freerdp/autodetect.h>
26#include <freerdp/log.h>
29#include "shadow_autodetect.h"
31#define TAG SERVER_TAG("shadow.autodetect")
34#define SHADOW_AUTODETECT_RTT_INTERVAL_MS 2000
36#define SHADOW_AUTODETECT_RTT_GIVEUP_MS 10000
39#define SHADOW_AUTODETECT_BW_PAYLOADS 3
40#define SHADOW_AUTODETECT_BW_PAYLOAD_SIZE 8192
44 SHADOW_AUTODETECT_PHASE_NONE,
45 SHADOW_AUTODETECT_PHASE_CONNECT_RTT,
46 SHADOW_AUTODETECT_PHASE_CONNECT_BW,
47 SHADOW_AUTODETECT_PHASE_CONTINUOUS
48} SHADOW_AUTODETECT_PHASE;
52 rdpShadowClient* client;
53 SHADOW_AUTODETECT_PHASE phase;
54 UINT16 sequenceNumber;
57 UINT16 rttSequenceNumber;
70static ShadowAutoDetect* shadow_autodetect_get(rdpAutoDetect* autodetect)
72 WINPR_ASSERT(autodetect);
73 return (ShadowAutoDetect*)autodetect->custom;
76static BOOL shadow_autodetect_enabled(
const ShadowAutoDetect* ad)
79 WINPR_ASSERT(ad->client);
85static UINT32 shadow_autodetect_us_to_ms(UINT64 us)
87 const UINT64 ms = (us + 999) / 1000;
88 return (UINT32)MIN(MAX(ms, 1), UINT32_MAX);
91static BOOL shadow_autodetect_send_rtt(ShadowAutoDetect* ad, rdpAutoDetect* autodetect)
94 WINPR_ASSERT(autodetect);
96 if (!autodetect->RTTMeasureRequest)
99 ad->rttSequenceNumber = ad->sequenceNumber++;
100 ad->rttPending = TRUE;
101 ad->rttSentMs = GetTickCount64();
102 ad->rttSentNs = winpr_GetTickCount64NS();
103 return autodetect->RTTMeasureRequest(autodetect, RDP_TRANSPORT_TCP, ad->rttSequenceNumber);
106static BOOL shadow_autodetect_send_bandwidth_burst(ShadowAutoDetect* ad, rdpAutoDetect* autodetect)
109 WINPR_ASSERT(autodetect);
111 if (!autodetect->BandwidthMeasureStart || !autodetect->BandwidthMeasurePayload ||
112 !autodetect->BandwidthMeasureStop)
115 const UINT16 sequenceNumber = ad->sequenceNumber++;
116 if (!autodetect->BandwidthMeasureStart(autodetect, RDP_TRANSPORT_TCP, sequenceNumber))
118 for (
size_t x = 0; x < SHADOW_AUTODETECT_BW_PAYLOADS; x++)
120 if (!autodetect->BandwidthMeasurePayload(autodetect, RDP_TRANSPORT_TCP, sequenceNumber,
121 SHADOW_AUTODETECT_BW_PAYLOAD_SIZE))
124 return autodetect->BandwidthMeasureStop(autodetect, RDP_TRANSPORT_TCP, sequenceNumber,
125 SHADOW_AUTODETECT_BW_PAYLOAD_SIZE);
128static BOOL shadow_autodetect_send_result(ShadowAutoDetect* ad, rdpAutoDetect* autodetect)
131 WINPR_ASSERT(autodetect);
133 if (!autodetect->NetworkCharacteristicsResult || (ad->rttSamples == 0))
136 rdpNetworkCharacteristicsResult result = { .type = RDP_NETCHAR_RESULT_TYPE_BASE_RTT_AVG_RTT,
137 .baseRTT = shadow_autodetect_us_to_ms(ad->baseRttUs),
139 shadow_autodetect_us_to_ms(ad->averageRttUs),
141 if (ad->bandwidthKbps > 0)
143 result.type = RDP_NETCHAR_RESULT_TYPE_BASE_RTT_BW_AVG_RTT;
144 result.bandwidth = ad->bandwidthKbps;
148 "Network Characteristics Result: base RTT %" PRIu32
" ms, average RTT %" PRIu32
149 " ms, bandwidth %" PRIu32
" kbit/s",
150 result.baseRTT, result.averageRTT, result.bandwidth);
151 return autodetect->NetworkCharacteristicsResult(autodetect, RDP_TRANSPORT_TCP,
152 ad->sequenceNumber++, &result);
155static BOOL shadow_autodetect_rtt_response(rdpAutoDetect* autodetect,
156 WINPR_ATTR_UNUSED RDP_TRANSPORT_TYPE transport,
157 UINT16 sequenceNumber)
159 ShadowAutoDetect* ad = shadow_autodetect_get(autodetect);
160 if (!ad || !ad->rttPending || (sequenceNumber != ad->rttSequenceNumber))
163 ad->rttPending = FALSE;
164 const UINT64 now = winpr_GetTickCount64NS();
165 const UINT64 sample = (now > ad->rttSentNs) ? (now - ad->rttSentNs) / 1000 : 0;
168 if ((ad->rttSamples == 0) || (sample < ad->baseRttUs))
169 ad->baseRttUs = sample;
170 if (ad->rttSamples == 0)
171 ad->averageRttUs = sample;
173 ad->averageRttUs = (ad->averageRttUs * 7 + sample) / 8;
176 WLog_DBG(TAG,
"RTT %" PRIu64
" us (base %" PRIu64
" us, average %" PRIu64
" us)", sample,
177 ad->baseRttUs, ad->averageRttUs);
180 if (ad->phase == SHADOW_AUTODETECT_PHASE_CONTINUOUS)
181 return shadow_autodetect_send_result(ad, autodetect);
185static BOOL shadow_autodetect_bandwidth_results(rdpAutoDetect* autodetect,
186 WINPR_ATTR_UNUSED RDP_TRANSPORT_TYPE transport,
187 WINPR_ATTR_UNUSED UINT16 sequenceNumber,
188 UINT16 responseType, UINT32 timeDelta,
191 ShadowAutoDetect* ad = shadow_autodetect_get(autodetect);
192 if (!ad || (responseType != RDP_BW_RESULTS_RESPONSE_TYPE_CONNECTTIME))
197 const UINT64 kbps = (8ull * byteCount) / MAX(timeDelta, 1);
198 ad->bandwidthKbps = (UINT32)MIN(MAX(kbps, 1), UINT32_MAX);
199 ad->bandwidthDone = TRUE;
200 WLog_INFO(TAG,
"bandwidth %" PRIu32
" kbit/s (%" PRIu32
" bytes in %" PRIu32
" ms)",
201 ad->bandwidthKbps, byteCount, timeDelta);
206static BOOL shadow_autodetect_netchar_sync(rdpAutoDetect* autodetect,
207 WINPR_ATTR_UNUSED RDP_TRANSPORT_TYPE transport,
208 WINPR_ATTR_UNUSED UINT16 sequenceNumber,
209 UINT32 bandwidth, UINT32 rtt)
211 ShadowAutoDetect* ad = shadow_autodetect_get(autodetect);
215 ad->bandwidthKbps = bandwidth;
216 ad->bandwidthDone = TRUE;
217 if ((ad->rttSamples == 0) && (rtt > 0))
219 ad->baseRttUs = ad->averageRttUs = 1000ull * rtt;
222 WLog_INFO(TAG,
"bandwidth %" PRIu32
" kbit/s, RTT %" PRIu32
" ms (from the client)", bandwidth,
227static FREERDP_AUTODETECT_STATE shadow_autodetect_connect_time_begin(rdpAutoDetect* autodetect)
229 ShadowAutoDetect* ad = shadow_autodetect_get(autodetect);
232 ad->phase = SHADOW_AUTODETECT_PHASE_CONNECT_RTT;
233 if (!shadow_autodetect_send_rtt(ad, autodetect))
234 return FREERDP_AUTODETECT_STATE_FAIL;
235 return FREERDP_AUTODETECT_STATE_REQUEST;
238static FREERDP_AUTODETECT_STATE shadow_autodetect_connect_time_progress(rdpAutoDetect* autodetect)
240 ShadowAutoDetect* ad = shadow_autodetect_get(autodetect);
243 if (autodetect->state == FREERDP_AUTODETECT_STATE_FAIL)
244 return FREERDP_AUTODETECT_STATE_FAIL;
248 case SHADOW_AUTODETECT_PHASE_CONNECT_RTT:
250 return FREERDP_AUTODETECT_STATE_REQUEST;
251 ad->phase = SHADOW_AUTODETECT_PHASE_CONNECT_BW;
252 if (!shadow_autodetect_send_bandwidth_burst(ad, autodetect))
253 return FREERDP_AUTODETECT_STATE_FAIL;
254 return FREERDP_AUTODETECT_STATE_REQUEST;
256 case SHADOW_AUTODETECT_PHASE_CONNECT_BW:
257 if (!ad->bandwidthDone)
258 return FREERDP_AUTODETECT_STATE_REQUEST;
259 if (!shadow_autodetect_send_result(ad, autodetect))
260 return FREERDP_AUTODETECT_STATE_FAIL;
261 ad->phase = SHADOW_AUTODETECT_PHASE_CONTINUOUS;
262 ad->nextRttMs = GetTickCount64() + SHADOW_AUTODETECT_RTT_INTERVAL_MS;
263 return FREERDP_AUTODETECT_STATE_COMPLETE;
266 return FREERDP_AUTODETECT_STATE_COMPLETE;
270BOOL shadow_client_autodetect_init(rdpShadowClient* client)
272 WINPR_ASSERT(client);
274 rdpAutoDetect* autodetect = client->context.autodetect;
275 WINPR_ASSERT(autodetect);
276 WINPR_ASSERT(!autodetect->custom);
278 ShadowAutoDetect* ad = calloc(1,
sizeof(ShadowAutoDetect));
282 autodetect->custom = ad;
284 autodetect->RTTMeasureResponse = shadow_autodetect_rtt_response;
285 autodetect->BandwidthMeasureResults = shadow_autodetect_bandwidth_results;
286 autodetect->NetworkCharacteristicsSync = shadow_autodetect_netchar_sync;
287 autodetect->OnConnectTimeAutoDetectBegin = shadow_autodetect_connect_time_begin;
288 autodetect->OnConnectTimeAutoDetectProgress = shadow_autodetect_connect_time_progress;
292void shadow_client_autodetect_uninit(rdpShadowClient* client)
297 rdpAutoDetect* autodetect = client->context.autodetect;
301 ShadowAutoDetect* ad = shadow_autodetect_get(autodetect);
304 if (ad->rttSamples > 0)
305 WLog_DBG(TAG,
"%" PRIu64
" RTT measurements", ad->rttSamples);
306 autodetect->custom =
nullptr;
310DWORD shadow_client_autodetect_timeout(rdpShadowClient* client)
312 WINPR_ASSERT(client);
314 rdpAutoDetect* autodetect = client->context.autodetect;
318 ShadowAutoDetect* ad = shadow_autodetect_get(autodetect);
319 if (!ad || (ad->phase != SHADOW_AUTODETECT_PHASE_CONTINUOUS) || !shadow_autodetect_enabled(ad))
322 const UINT64 now = GetTickCount64();
323 if (now >= ad->nextRttMs)
325 return (DWORD)MIN(ad->nextRttMs - now, SHADOW_AUTODETECT_RTT_GIVEUP_MS);
328BOOL shadow_client_autodetect_poll(rdpShadowClient* client)
330 WINPR_ASSERT(client);
332 rdpAutoDetect* autodetect = client->context.autodetect;
336 ShadowAutoDetect* ad = shadow_autodetect_get(autodetect);
337 if (!ad || (ad->phase != SHADOW_AUTODETECT_PHASE_CONTINUOUS) || !shadow_autodetect_enabled(ad))
340 const UINT64 now = GetTickCount64();
341 if (now < ad->nextRttMs)
346 if (freerdp_get_state(&client->context) != CONNECTION_STATE_ACTIVE)
348 ad->nextRttMs = now + SHADOW_AUTODETECT_RTT_INTERVAL_MS;
352 if (ad->rttPending && (now - ad->rttSentMs < SHADOW_AUTODETECT_RTT_GIVEUP_MS))
354 ad->nextRttMs = ad->rttSentMs + SHADOW_AUTODETECT_RTT_GIVEUP_MS;
358 ad->nextRttMs = now + SHADOW_AUTODETECT_RTT_INTERVAL_MS;
359 return shadow_autodetect_send_rtt(ad, autodetect);
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.