QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
VehicleSigningController.cc
Go to the documentation of this file.
2
3#include <QtCore/QMetaEnum>
4
5#include "MAVLinkProtocol.h"
6#include "MAVLinkSigning.h"
8#include "QGCApplication.h"
10#include "SecureMemory.h"
11#include "SigningController.h"
12#include "Vehicle.h"
13#include "VehicleLinkManager.h"
14
15QGC_LOGGING_CATEGORY(VehicleSigningControllerLog, "Vehicle.SigningController")
16
17VehicleSigningController::VehicleSigningController(Vehicle* vehicle) : QObject(vehicle), _vehicle(vehicle)
18{
19 qCDebug(VehicleSigningControllerLog) << "VehicleSigningController ctor — vehicle" << _vehicle->id();
20
21 _retryTimer.setInterval(kRetransmitIntervalMs);
22 connect(&_retryTimer, &QTimer::timeout, this, &VehicleSigningController::_onRetryTimer);
23
24 connect(vehicle->vehicleLinkManager(), &VehicleLinkManager::primaryLinkChanged, this,
25 &VehicleSigningController::_onPrimaryLinkChanged);
26
27 if (const SharedLinkInterfacePtr sharedLink = vehicle->vehicleLinkManager()->primaryLink().lock()) {
28 if (SigningController* ctrl = sharedLink->signing()) {
29 _connect(ctrl);
30 }
31 }
32}
33
35{
36 qCDebug(VehicleSigningControllerLog) << "VehicleSigningController dtor — vehicle" << _vehicle->id();
37}
38
39void VehicleSigningController::_onPrimaryLinkChanged()
40{
41 _disconnect(_active);
42
43 if (const SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock()) {
44 if (SigningController* ctrl = sharedLink->signing()) {
45 _connect(ctrl);
46 }
47 }
48}
49
50void VehicleSigningController::_connect(SigningController* ctrl)
51{
52 if (!ctrl || ctrl == _active) {
53 return;
54 }
55 _active = ctrl;
56 // Permanent listeners; signingConfirmed/signingFailed wired per-operation via Qt::SingleShotConnection in
57 // enable/disable.
58 connect(ctrl, &SigningController::stateChanged, this, &VehicleSigningController::_onSigningStateChanged);
59 connect(ctrl, &SigningController::keyAutoDetected, this, &VehicleSigningController::_onSigningStateChanged);
60 connect(ctrl, &SigningController::alertRaised, this, &VehicleSigningController::_onSigningAlertRaised);
61 _refreshStatus();
62}
63
64void VehicleSigningController::_disconnect(SigningController* ctrl)
65{
66 if (!ctrl) {
67 return;
68 }
69 if (ctrl->state() == SigningController::State::Enabling || ctrl->state() == SigningController::State::Disabling) {
70 ctrl->cancelPending();
71 }
72 disconnect(ctrl, nullptr, this, nullptr);
73 if (_active == ctrl) {
74 _active = nullptr;
75 }
76 _refreshStatus();
77}
78
79void VehicleSigningController::_onSigningStateChanged()
80{
81 _refreshStatus();
82}
83
84void VehicleSigningController::_refreshStatus()
85{
86 _signingStatus = _active ? _active->status() : SigningStatus{};
87}
88
89void VehicleSigningController::_onSigningAlertRaised(const QString& detail)
90{
91 qCWarning(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "] signing alert:" << detail;
92 qgcApp()->showAppMessage(tr("Vehicle %1: %2").arg(_vehicle->id()).arg(detail));
93}
94
95void VehicleSigningController::_onSigningConfirmed(const QString& confirmedKey)
96{
97 _stopRetransmit();
98 if (auto link = _vehicle->vehicleLinkManager()->primaryLink().lock()) {
100 }
101 qCDebug(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "] signing confirmed — key"
102 << (confirmedKey.isEmpty() ? "<disabled>" : confirmedKey);
103}
104
105void VehicleSigningController::_onSigningFailed(const SigningFailure& failure)
106{
107 _stopRetransmit();
108 qCWarning(VehicleSigningControllerLog)
109 << "[veh" << _vehicle->id() << "] signing failed:"
110 << QMetaEnum::fromType<SigningFailure::Reason>().valueToKey(static_cast<int>(failure.reason)) << failure.detail;
111 emit signingFailed(failure);
112 qgcApp()->showAppMessage(failure.detail);
113}
114
115void VehicleSigningController::_startRetransmit()
116{
117 _retryTimer.start();
118}
119
120void VehicleSigningController::_stopRetransmit()
121{
122 _retryTimer.stop();
123 QGC::secureZero(_pendingKey);
124 _pendingHasKey = false;
125}
126
127void VehicleSigningController::_onRetryTimer()
128{
129 if (!_active || _active->state() == SigningController::State::On ||
130 _active->state() == SigningController::State::Off) {
131 _stopRetransmit();
132 return;
133 }
134 const SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock();
135 if (!sharedLink) {
136 return;
137 }
138 const QByteArrayView keyView = _pendingHasKey ? QByteArrayView(reinterpret_cast<const char*>(_pendingKey.data()),
139 static_cast<qsizetype>(_pendingKey.size()))
140 : QByteArrayView{};
141 (void)_sendSetupSigning(sharedLink, keyView);
142 qCDebug(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "] retransmitted SETUP_SIGNING ("
143 << (_pendingHasKey ? "enable" : "disable") << ")";
144}
145
146bool VehicleSigningController::_sendSetupSigning(const SharedLinkInterfacePtr& sharedLink, QByteArrayView keyView)
147{
148 const auto channel = static_cast<mavlink_channel_t>(sharedLink->mavlinkChannel());
149 const mavlink_system_t targetSystem{static_cast<uint8_t>(_vehicle->id()),
150 static_cast<uint8_t>(_vehicle->defaultComponentId())};
151
154 MAVLinkProtocol::getComponentId(), targetSystem, keyView, msg)) {
155 qCWarning(VehicleSigningControllerLog)
156 << "[veh" << _vehicle->id() << "] failed to encode SETUP_SIGNING (channel" << channel << ")";
157 return false;
158 }
159
160 // Send twice for lossy-link redundancy; enable goes unsigned (signing not yet installed), disable goes signed.
161 for (uint8_t i = 0; i < 2; ++i) {
162 _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
163 }
164 return true;
165}
166
167void VehicleSigningController::enable(const QString& keyName)
168{
169 if (!_active) {
170 qCWarning(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "] enable: no signing controller";
171 return;
172 }
173
174 const SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock();
175 if (!sharedLink) {
176 qCWarning(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "] enable: no primary link";
177 return;
178 }
179
180 const auto keyBytes = MAVLinkSigningKeys::instance()->keyBytesByName(keyName);
181 if (!keyBytes) {
182 qCCritical(VehicleSigningControllerLog)
183 << "[veh" << _vehicle->id() << "] enable: unknown signing key:" << keyName;
184 return;
185 }
186
187 // Atomic FSM commit BEFORE _sendSetupSigning — re-entry rejected without putting bytes on the wire.
188 if (auto fail = _active->tryBeginEnable(static_cast<uint8_t>(_vehicle->id()), keyName, *keyBytes)) {
189 qgcApp()->showAppMessage(fail->detail);
190 emit signingFailed(*fail);
191 return;
192 }
193
194 connect(_active, &SigningController::signingConfirmed, this, &VehicleSigningController::_onSigningConfirmed,
195 Qt::SingleShotConnection);
196 connect(_active, &SigningController::signingFailed, this, &VehicleSigningController::_onSigningFailed,
197 Qt::SingleShotConnection);
198
199 const QByteArrayView keyView(reinterpret_cast<const char*>(keyBytes->data()), keyBytes->size());
200 if (!_sendSetupSigning(sharedLink, keyView)) {
201 _active->cancelPending(tr("Failed to transmit SETUP_SIGNING to vehicle"));
202 return;
203 }
204
205 _pendingKey = *keyBytes;
206 _pendingHasKey = true;
207 _startRetransmit();
208
209 qCDebug(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "] SETUP_SIGNING sent — key" << keyName
210 << "awaiting signed HEARTBEAT";
211}
212
214{
215 if (!_active) {
216 qCWarning(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "] disable: no signing controller";
217 return;
218 }
219
220 const SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock();
221 if (!sharedLink) {
222 qCWarning(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "] disable: no primary link";
223 return;
224 }
225
226 if (auto fail = _active->tryBeginDisable(static_cast<uint8_t>(_vehicle->id()))) {
227 qgcApp()->showAppMessage(fail->detail);
228 emit signingFailed(*fail);
229 return;
230 }
231
232 connect(_active, &SigningController::signingConfirmed, this, &VehicleSigningController::_onSigningConfirmed,
233 Qt::SingleShotConnection);
234 connect(_active, &SigningController::signingFailed, this, &VehicleSigningController::_onSigningFailed,
235 Qt::SingleShotConnection);
236
237 // Wipe any leftover key bytes from a prior enable; disable retransmits send no key.
238 QGC::secureZero(_pendingKey);
239 _pendingHasKey = false;
240
241 if (!_sendSetupSigning(sharedLink, QByteArrayView{})) {
242 _active->cancelPending(tr("Failed to transmit SETUP_SIGNING to vehicle"));
243 return;
244 }
245
246 _startRetransmit();
247
248 qCDebug(VehicleSigningControllerLog) << "[veh" << _vehicle->id()
249 << "] disable SETUP_SIGNING sent — awaiting unsigned HEARTBEAT";
250}
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
mavlink_channel_t
#define qgcApp()
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
static int getComponentId()
void resetSequenceTracking(LinkInterface *link)
Reset sequence tracking so signing transitions don't inflate loss counters.
static MAVLinkProtocol * instance()
int getSystemId() const
static MAVLinkSigningKeys * instance()
std::optional< MAVLinkSigning::SigningKey > keyBytesByName(const QString &name) const
Key bytes for the key with the given name, or nullopt if not found.
Owns MAVLink signing state and the deferred-confirmation state machine for one LinkInterface.
void signingConfirmed(const QString &keyName)
Emitted exactly once per begin*() on success. keyName is the enabled key, or empty for disable.
void cancelPending(const QString &detail={})
void alertRaised(const QString &detail)
void keyAutoDetected(const QString &keyName)
void signingFailed(SigningFailure failure)
Emitted exactly once per begin*() on failure (timeout, init error, cancel, re-entry).
Reason a signing operation failed. Used by SigningController error path and Vehicle::signingFailed.
WeakLinkInterfacePtr primaryLink() const
Per-vehicle signing facade. Owns the wiring between Vehicle and the active SigningController (which l...
Q_INVOKABLE void enable(const QString &keyName)
void signingFailed(SigningFailure info)
VehicleLinkManager * vehicleLinkManager()
Definition Vehicle.h:575
int id() const
Definition Vehicle.h:425
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)
Definition Vehicle.cc:1389
int defaultComponentId() const
Definition Vehicle.h:670
bool encodeSetupSigning(mavlink_channel_t channel, uint8_t srcSysId, uint8_t srcCompId, mavlink_system_t target_system, QByteArrayView keyBytes, mavlink_message_t &message)
void secureZero(void *data, size_t size)