UDPLink.cc 14.6 KB
Newer Older
1 2 3 4 5 6 7 8
/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/
pixhawk's avatar
pixhawk committed
9 10 11 12 13 14 15 16 17


/**
 * @file
 *   @brief Definition of UDP connection (server) for unmanned vehicles
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

18
#include <QtGlobal>
pixhawk's avatar
pixhawk committed
19 20 21 22
#include <QTimer>
#include <QList>
#include <QDebug>
#include <QMutexLocker>
23
#include <QNetworkProxy>
24
#include <QNetworkInterface>
pixhawk's avatar
pixhawk committed
25
#include <iostream>
26

pixhawk's avatar
pixhawk committed
27
#include "UDPLink.h"
28
#include "QGC.h"
29
#include <QHostInfo>
pixhawk's avatar
pixhawk committed
30

31 32
#define REMOVE_GONE_HOSTS 0

33 34
static const char* kZeroconfRegistration = "_qgroundcontrol._udp";

35 36 37
static bool is_ip(const QString& address)
{
    int a,b,c,d;
38 39
    if (sscanf(address.toStdString().c_str(), "%d.%d.%d.%d", &a, &b, &c, &d) != 4
            && strcmp("::1", address.toStdString().c_str())) {
40
        return false;
41 42 43
    } else {
        return true;
    }
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
}

static QString get_ip_address(const QString& address)
{
    if(is_ip(address))
        return address;
    // Need to look it up
    QHostInfo info = QHostInfo::fromName(address);
    if (info.error() == QHostInfo::NoError)
    {
        QList<QHostAddress> hostAddresses = info.addresses();
        QHostAddress address;
        for (int i = 0; i < hostAddresses.size(); i++)
        {
            // Exclude all IPv6 addresses
            if (!hostAddresses.at(i).toString().contains(":"))
            {
                return hostAddresses.at(i).toString();
            }
        }
    }
    return QString("");
}

68 69 70
UDPLink::UDPLink(SharedLinkConfigurationPointer& config)
    : LinkInterface(config)
#if defined(QGC_ZEROCONF_ENABLED)
71
    , _dnssServiceRef(NULL)
72
#endif
73
    , _running(false)
74 75 76
    , _socket(NULL)
    , _udpConfig(qobject_cast<UDPConfiguration*>(config.data()))
    , _connectState(false)
pixhawk's avatar
pixhawk committed
77
{
78
    Q_ASSERT(_udpConfig);
79
    moveToThread(this);
pixhawk's avatar
pixhawk committed
80 81 82 83
}

UDPLink::~UDPLink()
{
84
    _disconnect();
Lorenz Meier's avatar
Lorenz Meier committed
85
    // Tell the thread to exit
86
    _running = false;
87
    quit();
Lorenz Meier's avatar
Lorenz Meier committed
88 89
    // Wait for it to exit
    wait();
90
    this->deleteLater();
pixhawk's avatar
pixhawk committed
91 92 93 94 95 96 97 98
}

/**
 * @brief Runs the thread
 *
 **/
void UDPLink::run()
{
99
    if(_hardwareConnect()) {
100
        exec();
101
    }
102
    if (_socket) {
103
        _deregisterZeroconf();
104 105
        _socket->close();
    }
pixhawk's avatar
pixhawk committed
106 107
}

108
void UDPLink::_restartConnection()
pixhawk's avatar
pixhawk committed
109
{
110 111 112 113 114
    if(this->isConnected())
    {
        _disconnect();
        _connect();
    }
pixhawk's avatar
pixhawk committed
115 116
}

117
QString UDPLink::getName() const
pixhawk's avatar
pixhawk committed
118
{
119
    return _udpConfig->name();
120 121 122 123
}

void UDPLink::addHost(const QString& host)
{
124
    _udpConfig->addHost(host);
125 126
}

127
void UDPLink::removeHost(const QString& host)
128
{
129
    _udpConfig->removeHost(host);
pixhawk's avatar
pixhawk committed
130 131
}

132
void UDPLink::_writeBytes(const QByteArray data)
133
{
134 135 136
    if (!_socket)
        return;

137 138 139 140
    QStringList goneHosts;
    // Send to all connected systems
    QString host;
    int port;
141
    if(_udpConfig->firstHost(host, port)) {
142 143
        do {
            QHostAddress currentHost(host);
144
            if(_socket->writeDatagram(data, currentHost, (quint16)port) < 0) {
145
                // This host is gone. Add to list to be removed
146 147 148 149 150 151 152 153 154 155 156 157
                // We should keep track of hosts that were manually added (static) and
                // hosts that were added because we heard from them (dynamic). Only
                // dynamic hosts should be removed and even then, after a few tries, not
                // the first failure. In the mean time, we don't remove anything.
                if(REMOVE_GONE_HOSTS) {
                    goneHosts.append(host);
                }
            } else {
                // Only log rate if data actually got sent. Not sure about this as
                // "host not there" takes time too regardless of size of data. In fact,
                // 1 byte or "UDP frame size" bytes are the same as that's the data
                // unit sent by UDP.
158
                _logOutputDataRate(data.size(), QDateTime::currentMSecsSinceEpoch());
159
            }
160
        } while (_udpConfig->nextHost(host, port));
161
        //-- Remove hosts that are no longer there
162
        foreach (const QString& ghost, goneHosts) {
163
            _udpConfig->removeHost(ghost);
164 165 166 167
        }
    }
}

pixhawk's avatar
pixhawk committed
168 169 170
/**
 * @brief Read a number of bytes from the interface.
 **/
171
void UDPLink::readBytes()
pixhawk's avatar
pixhawk committed
172
{
dogmaphobic's avatar
dogmaphobic committed
173
    QByteArray databuffer;
174
    while (_socket->hasPendingDatagrams())
175 176
    {
        QByteArray datagram;
177
        datagram.resize(_socket->pendingDatagramSize());
178 179
        QHostAddress sender;
        quint16 senderPort;
180
        _socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
dogmaphobic's avatar
dogmaphobic committed
181 182 183 184 185 186
        databuffer.append(datagram);
        //-- Wait a bit before sending it over
        if(databuffer.size() > 10 * 1024) {
            emit bytesReceived(this, databuffer);
            databuffer.clear();
        }
187
        _logInputDataRate(datagram.length(), QDateTime::currentMSecsSinceEpoch());
188 189 190 191
        // TODO This doesn't validade the sender. Anything sending UDP packets to this port gets
        // added to the list and will start receiving datagrams from here. Even a port scanner
        // would trigger this.
        // Add host to broadcast list if not yet present, or update its port
192
        _udpConfig->addHost(sender.toString(), (int)senderPort);
pixhawk's avatar
pixhawk committed
193
    }
dogmaphobic's avatar
dogmaphobic committed
194 195 196 197
    //-- Send whatever is left
    if(databuffer.size()) {
        emit bytesReceived(this, databuffer);
    }
pixhawk's avatar
pixhawk committed
198 199 200 201 202 203 204
}

/**
 * @brief Disconnect the connection.
 *
 * @return True if connection has been disconnected, false if connection couldn't be disconnected.
 **/
Don Gagne's avatar
Don Gagne committed
205
void UDPLink::_disconnect(void)
pixhawk's avatar
pixhawk committed
206
{
207
    _running = false;
208
    quit();
209
    wait();
210 211 212 213
    if (_socket) {
        // Make sure delete happen on correct thread
        _socket->deleteLater();
        _socket = NULL;
214
        emit disconnected();
215 216
    }
    _connectState = false;
pixhawk's avatar
pixhawk committed
217 218 219 220 221 222 223
}

/**
 * @brief Connect the connection.
 *
 * @return True if connection has been established, false if connection couldn't be established.
 **/
224
bool UDPLink::_connect(void)
pixhawk's avatar
pixhawk committed
225
{
226
    if(this->isRunning() || _running)
227
    {
228
        _running = false;
229
        quit();
230
        wait();
231
    }
232
    _running = true;
233
    start(NormalPriority);
234
    return true;
oberion's avatar
oberion committed
235 236
}

237
bool UDPLink::_hardwareConnect()
oberion's avatar
oberion committed
238
{
239 240 241 242
    if (_socket) {
        delete _socket;
        _socket = NULL;
    }
243
    QHostAddress host = QHostAddress::AnyIPv4;
244
    _socket = new QUdpSocket();
245
    _socket->setProxy(QNetworkProxy::NoProxy);
246
    _connectState = _socket->bind(host, _udpConfig->localPort(), QAbstractSocket::ReuseAddressHint | QUdpSocket::ShareAddress);
247
    if (_connectState) {
248
        _socket->joinMulticastGroup(QHostAddress("224.0.0.1"));
dogmaphobic's avatar
dogmaphobic committed
249
        //-- Make sure we have a large enough IO buffers
Don Gagne's avatar
Don Gagne committed
250
#ifdef __mobile__
dogmaphobic's avatar
dogmaphobic committed
251 252 253 254 255 256
        _socket->setSocketOption(QAbstractSocket::SendBufferSizeSocketOption,     64 * 1024);
        _socket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, 128 * 1024);
#else
        _socket->setSocketOption(QAbstractSocket::SendBufferSizeSocketOption,    256 * 1024);
        _socket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, 512 * 1024);
#endif
257
        _registerZeroconf(_udpConfig->localPort(), kZeroconfRegistration);
258
        QObject::connect(_socket, &QUdpSocket::readyRead, this, &UDPLink::readBytes);
259
        emit connected();
260
    } else {
Don Gagne's avatar
Don Gagne committed
261
        emit communicationError(tr("UDP Link Error"), tr("Error binding UDP port: %1").arg(_socket->errorString()));
262
    }
263
    return _connectState;
pixhawk's avatar
pixhawk committed
264 265 266 267 268 269 270
}

/**
 * @brief Check if connection is active.
 *
 * @return True if link is connected, false otherwise.
 **/
271
bool UDPLink::isConnected() const
272
{
273
    return _connectState;
pixhawk's avatar
pixhawk committed
274 275
}

276
qint64 UDPLink::getConnectionSpeed() const
pixhawk's avatar
pixhawk committed
277
{
278 279 280 281 282 283
    return 54000000; // 54 Mbit
}

qint64 UDPLink::getCurrentInDataRate() const
{
    return 0;
pixhawk's avatar
pixhawk committed
284 285
}

286
qint64 UDPLink::getCurrentOutDataRate() const
pixhawk's avatar
pixhawk committed
287
{
288
    return 0;
pixhawk's avatar
pixhawk committed
289 290
}

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
void UDPLink::_registerZeroconf(uint16_t port, const std::string &regType)
{
#if defined(QGC_ZEROCONF_ENABLED)
    DNSServiceErrorType result = DNSServiceRegister(&_dnssServiceRef, 0, 0, 0,
        regType.c_str(),
        NULL,
        NULL,
        htons(port),
        0,
        NULL,
        NULL,
        NULL);
    if (result != kDNSServiceErr_NoError)
    {
        emit communicationError("UDP Link Error", "Error registering Zeroconf");
        _dnssServiceRef = NULL;
    }
#else
    Q_UNUSED(port);
    Q_UNUSED(regType);
#endif
}

void UDPLink::_deregisterZeroconf()
{
#if defined(QGC_ZEROCONF_ENABLED)
    if (_dnssServiceRef)
     {
         DNSServiceRefDeallocate(_dnssServiceRef);
         _dnssServiceRef = NULL;
     }
#endif
}

325 326
//--------------------------------------------------------------------------
//-- UDPConfiguration
327

328
UDPConfiguration::UDPConfiguration(const QString& name) : LinkConfiguration(name)
329
{
330
    _localPort = QGC_UDP_LOCAL_PORT;
331 332
}

333
UDPConfiguration::UDPConfiguration(UDPConfiguration* source) : LinkConfiguration(source)
334
{
335 336 337
    _localPort = source->localPort();
    QString host;
    int port;
338
    _hostList.clear();
339 340 341 342 343
    if(source->firstHost(host, port)) {
        do {
            addHost(host, port);
        } while(source->nextHost(host, port));
    }
344 345
}

346
void UDPConfiguration::copyFrom(LinkConfiguration *source)
347
{
348
    LinkConfiguration::copyFrom(source);
349 350 351
    UDPConfiguration* usource = dynamic_cast<UDPConfiguration*>(source);
    Q_ASSERT(usource != NULL);
    _localPort = usource->localPort();
352
    _hosts.clear();
353 354 355 356 357 358 359 360 361 362 363 364
    QString host;
    int port;
    if(usource->firstHost(host, port)) {
        do {
            addHost(host, port);
        } while(usource->nextHost(host, port));
    }
}

/**
 * @param host Hostname in standard formatt, e.g. localhost:14551 or 192.168.1.1:14551
 */
365
void UDPConfiguration::addHost(const QString host)
366
{
367
    // Handle x.x.x.x:p
368 369
    if (host.contains(":"))
    {
370
        addHost(host.split(":").first(), host.split(":").last().toInt());
371
    }
372
    // If no port, use default
373 374
    else
    {
dogmaphobic's avatar
dogmaphobic committed
375
        addHost(host, (int)_localPort);
376 377 378 379 380
    }
}

void UDPConfiguration::addHost(const QString& host, int port)
{
dogmaphobic's avatar
dogmaphobic committed
381
    bool changed = false;
dogmaphobic's avatar
dogmaphobic committed
382 383 384 385
    QMutexLocker locker(&_confMutex);
    if(_hosts.contains(host)) {
        if(_hosts[host] != port) {
            _hosts[host] = port;
dogmaphobic's avatar
dogmaphobic committed
386
            changed = true;
dogmaphobic's avatar
dogmaphobic committed
387 388
        }
    } else {
389 390
        QString ipAdd = get_ip_address(host);
        if(ipAdd.isEmpty()) {
391
            qWarning() << "UDP:" << "Could not resolve host:" << host << "port:" << port;
392
        } else {
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
            // In simulation and testing setups the vehicle and the GCS can be
            // running on the same host. This leads to packets arriving through
            // the local network or the loopback adapter, which makes it look
            // like the vehicle is connected through two different links,
            // complicating routing.
            //
            // We detect this case and force all traffic to a simulated instance
            // onto the local loopback interface.
            bool not_local = true;
            // Run through all IPv4 interfaces and check if their canonical
            // IP address in string representation matches the source IP address
            foreach (const QHostAddress &address, QNetworkInterface::allAddresses()) {
                if (address.protocol() == QAbstractSocket::IPv4Protocol) {
                    if (ipAdd.endsWith(address.toString())) {
                        // This is a local address of the same host
                        not_local = false;
                    }
                }
            }
            if (not_local) {
                // This is a normal remote host, add it using its IPv4 address
                _hosts[ipAdd] = port;
                //qDebug() << "UDP:" << "Adding Host:" << ipAdd << ":" << port;
            } else {
                // It is localhost, so talk to it through the IPv4 loopback interface
                _hosts["127.0.0.1"] = port;
            }
dogmaphobic's avatar
dogmaphobic committed
420
            changed = true;
dogmaphobic's avatar
dogmaphobic committed
421 422
        }
    }
dogmaphobic's avatar
dogmaphobic committed
423 424 425
    if(changed) {
        _updateHostList();
    }
426 427
}

428
void UDPConfiguration::removeHost(const QString host)
429
{
dogmaphobic's avatar
dogmaphobic committed
430
    QMutexLocker locker(&_confMutex);
431 432 433 434 435 436 437
    QString tHost = host;
    if (tHost.contains(":")) {
        tHost = tHost.split(":").first();
    }
    tHost = tHost.trimmed();
    QMap<QString, int>::iterator i = _hosts.find(tHost);
    if(i != _hosts.end()) {
438
        //qDebug() << "UDP:" << "Removed host:" << host;
439
        _hosts.erase(i);
440 441
    } else {
        qWarning() << "UDP:" << "Could not remove unknown host:" << host;
442
    }
443
    _updateHostList();
444 445 446 447
}

bool UDPConfiguration::firstHost(QString& host, int& port)
{
dogmaphobic's avatar
dogmaphobic committed
448
    _confMutex.lock();
449 450
    _it = _hosts.begin();
    if(_it == _hosts.end()) {
dogmaphobic's avatar
dogmaphobic committed
451
        _confMutex.unlock();
452 453
        return false;
    }
dogmaphobic's avatar
dogmaphobic committed
454
    _confMutex.unlock();
455 456 457 458 459
    return nextHost(host, port);
}

bool UDPConfiguration::nextHost(QString& host, int& port)
{
dogmaphobic's avatar
dogmaphobic committed
460
    QMutexLocker locker(&_confMutex);
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
    if(_it != _hosts.end()) {
        host = _it.key();
        port = _it.value();
        _it++;
        return true;
    }
    return false;
}

void UDPConfiguration::setLocalPort(quint16 port)
{
    _localPort = port;
}

void UDPConfiguration::saveSettings(QSettings& settings, const QString& root)
{
    _confMutex.lock();
    settings.beginGroup(root);
    settings.setValue("port", (int)_localPort);
    settings.setValue("hostCount", _hosts.count());
    int index = 0;
    QMap<QString, int>::const_iterator it = _hosts.begin();
    while(it != _hosts.end()) {
        QString hkey = QString("host%1").arg(index);
        settings.setValue(hkey, it.key());
        QString pkey = QString("port%1").arg(index);
        settings.setValue(pkey, it.value());
        it++;
        index++;
    }
    settings.endGroup();
    _confMutex.unlock();
}

void UDPConfiguration::loadSettings(QSettings& settings, const QString& root)
{
    _confMutex.lock();
    _hosts.clear();
dogmaphobic's avatar
dogmaphobic committed
499 500
    _confMutex.unlock();
    settings.beginGroup(root);
501
    _localPort = (quint16)settings.value("port", QGC_UDP_LOCAL_PORT).toUInt();
502 503 504 505 506 507 508 509 510
    int hostCount = settings.value("hostCount", 0).toInt();
    for(int i = 0; i < hostCount; i++) {
        QString hkey = QString("host%1").arg(i);
        QString pkey = QString("port%1").arg(i);
        if(settings.contains(hkey) && settings.contains(pkey)) {
            addHost(settings.value(hkey).toString(), settings.value(pkey).toInt());
        }
    }
    settings.endGroup();
511
    _updateHostList();
512 513 514 515 516 517 518 519 520 521
}

void UDPConfiguration::updateSettings()
{
    if(_link) {
        UDPLink* ulink = dynamic_cast<UDPLink*>(_link);
        if(ulink) {
            ulink->_restartConnection();
        }
    }
522
}
523 524 525 526 527 528 529 530 531 532 533 534

void UDPConfiguration::_updateHostList()
{
    _hostList.clear();
    QMap<QString, int>::const_iterator it = _hosts.begin();
    while(it != _hosts.end()) {
        QString host = QString("%1").arg(it.key()) + ":" + QString("%1").arg(it.value());
        _hostList += host;
        it++;
    }
    emit hostListChanged();
}