From 8c9de2d78cc5d618271f434341e254c548bfe82a Mon Sep 17 00:00:00 2001 From: Floris Bos Date: Mon, 15 Jan 2024 00:16:43 +0100 Subject: [PATCH] Embedded: fix network detection --- src/CMakeLists.txt | 2 +- src/imagewriter.cpp | 22 +++++-- src/imagewriter.h | 2 + src/linux/stpanalyzer.cpp | 118 ++++++++++++++++++++++++++++++++++++++ src/linux/stpanalyzer.h | 32 +++++++++++ src/main.cpp | 4 +- src/main.qml | 14 +++++ 7 files changed, 188 insertions(+), 6 deletions(-) create mode 100644 src/linux/stpanalyzer.cpp create mode 100644 src/linux/stpanalyzer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f2447fb..a99568a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,7 +29,7 @@ if (APPLE) dependencies/drivelist/src/darwin/list.mm dependencies/drivelist/src/darwin/REDiskList.m icons/rpi-imager.icns) enable_language(OBJC C) elseif (UNIX) - set(DEPENDENCIES dependencies/mountutils/src/linux/functions.cpp linux/linuxdrivelist.cpp linux/networkmanagerapi.h linux/networkmanagerapi.cpp) + set(DEPENDENCIES dependencies/mountutils/src/linux/functions.cpp linux/linuxdrivelist.cpp linux/networkmanagerapi.h linux/networkmanagerapi.cpp linux/stpanalyzer.h linux/stpanalyzer.cpp) find_package(ZLIB) if(ZLIB_FOUND) set(EXTRALIBS ${EXTRALIBS} ZLIB::ZLIB) diff --git a/src/imagewriter.cpp b/src/imagewriter.cpp index 4d6119a..7485e5f 100644 --- a/src/imagewriter.cpp +++ b/src/imagewriter.cpp @@ -37,6 +37,8 @@ #ifndef QT_NO_WIDGETS #include #include +#else +#include #endif #ifdef Q_OS_DARWIN #include @@ -49,8 +51,8 @@ #include #endif -#ifdef QT_NO_WIDGETS -#include +#ifdef Q_OS_LINUX +#include "linux/stpanalyzer.h" #endif namespace { @@ -75,6 +77,7 @@ ImageWriter::ImageWriter(QObject *parent) platform = "cli"; } +#ifdef Q_OS_LINUX if (platform == "eglfs" || platform == "linuxfb") { _embeddedMode = true; @@ -98,7 +101,12 @@ ImageWriter::ImageWriter(QObject *parent) } } } + + StpAnalyzer *stpAnalyzer = new StpAnalyzer(5, this); + connect(stpAnalyzer, SIGNAL(detected()), SLOT(onSTPdetected())); + stpAnalyzer->startListening("eth0"); } +#endif #ifdef Q_OS_WIN _taskbarButton = nullptr; @@ -923,7 +931,7 @@ void ImageWriter::pollNetwork() if (!a.isLoopback() && a.scopeId().isEmpty()) { /* Not a loopback or IPv6 link-local address, so online */ - qDebug() << "IP:" << a; + emit networkInfo(QString("IP: %1").arg(a.toString())); _online = true; break; } @@ -961,11 +969,12 @@ void ImageWriter::onTimeSyncReply(QNetworkReply *reply) }; ::settimeofday(&tv, NULL); + beginOSListFetch(); emit networkOnline(); } else { - qDebug() << "Error synchronizing time. Trying again in 3 seconds"; + emit networkInfo(tr("Error synchronizing time. Trying again in 3 seconds")); QTimer::singleShot(3000, this, SLOT(syncTime())); } @@ -975,6 +984,11 @@ void ImageWriter::onTimeSyncReply(QNetworkReply *reply) #endif } +void ImageWriter::onSTPdetected() +{ + emit networkInfo(tr("STP is enabled on your Ethernet switch. Getting IP will take long time.")); +} + bool ImageWriter::isEmbeddedMode() { return _embeddedMode; diff --git a/src/imagewriter.h b/src/imagewriter.h index 670d996..2676c03 100644 --- a/src/imagewriter.h +++ b/src/imagewriter.h @@ -159,6 +159,7 @@ signals: void networkOnline(); void preparationStatusUpdate(QVariant msg); void osListPrepared(); + void networkInfo(QVariant msg); protected slots: @@ -176,6 +177,7 @@ protected slots: void onTimeSyncReply(QNetworkReply *reply); void onPreparationStatusUpdate(QString msg); void handleNetworkRequestFinished(QNetworkReply *data); + void onSTPdetected(); private: // Recursively walk all the entries with subitems and, for any which diff --git a/src/linux/stpanalyzer.cpp b/src/linux/stpanalyzer.cpp new file mode 100644 index 0000000..f15142c --- /dev/null +++ b/src/linux/stpanalyzer.cpp @@ -0,0 +1,118 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2024 Raspberry Pi Ltd + * + * Class to detect if the Spanning-Tree-Protocol + * is enabled on the Ethernet switch (which can + * cause a long delay in getting an IP-address) + */ + +#include "stpanalyzer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct stp_packet { + /* Ethernet header */ + struct ethhdr eth; + /* 802.2 LLC header */ + uint8_t dsap, ssap, control; + /* STP fields */ + uint16_t protocol; + uint8_t version; + uint8_t msgtype; + uint8_t flags; + uint16_t rootpriority; + unsigned char rootmac[6]; + uint32_t rootpathcost; + uint16_t bridgepriority; + unsigned char bridgemac[6]; + uint16_t portid; + uint16_t msgage; + uint16_t maxage; + uint16_t hellotime; + uint16_t forwarddelay; +} __attribute__((__packed__)); + +#define LSAP_BDPU 0x42 +#define MULTICAST_MAC_BDPU {0x1, 0x80, 0xC2, 0, 0, 0} +#define MULTICAST_MAC_BDPUPV {0x1, 0, 0x0C, 0xCC, 0xCC, 0xCD} + +StpAnalyzer::StpAnalyzer(int onlyReportIfForwardDelayIsAbove, QObject *parent) + : QObject(parent), _s(-1), _minForwardDelay(onlyReportIfForwardDelayIsAbove), _qsn(nullptr) +{ +} + +StpAnalyzer::~StpAnalyzer() +{ + stopListening(); +} + +void StpAnalyzer::startListening(const QByteArray &ifname) +{ + int iface; + + if (_s != -1) + return; /* Already listening */ + + _s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_802_2)); + if (_s < 0) + { + return; + } + + iface = if_nametoindex(ifname.constData()); + if (!iface) { + return; + } + + struct packet_mreq mreq = { iface, PACKET_MR_MULTICAST, ETH_ALEN, MULTICAST_MAC_BDPU }; + setsockopt(_s, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + struct packet_mreq mreq2 = { iface, PACKET_MR_MULTICAST, ETH_ALEN, MULTICAST_MAC_BDPUPV }; + setsockopt(_s, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq2, sizeof(mreq2)); + + _qsn = new QSocketNotifier(_s, QSocketNotifier::Read, this); + connect(_qsn, SIGNAL(activated(QSocketDescriptor,QSocketNotifier::Type)), SLOT(onPacket(QSocketDescriptor,QSocketNotifier::Type))); +} + +void StpAnalyzer::stopListening() +{ + if (_s != -1) + { + _qsn->setEnabled(false); + _qsn->deleteLater(); + _qsn = nullptr; + close(_s); + _s = -1; + } +} + +void StpAnalyzer::onPacket(QSocketDescriptor socket, QSocketNotifier::Type type) +{ + struct stp_packet packet = { 0 }; + + int len = recvfrom(_s, &packet, sizeof(packet), 0, NULL, 0); + + if (len == sizeof(packet) + && packet.dsap == LSAP_BDPU + && packet.ssap == LSAP_BDPU + && packet.protocol == 0) + { + /* It is a STP packet */ + int forwardDelay = qFromLittleEndian(packet.forwarddelay); + if (forwardDelay > _minForwardDelay) + { + emit detected(); + } + + /* Only analyze first packet */ + stopListening(); + } +} diff --git a/src/linux/stpanalyzer.h b/src/linux/stpanalyzer.h new file mode 100644 index 0000000..08aabf7 --- /dev/null +++ b/src/linux/stpanalyzer.h @@ -0,0 +1,32 @@ +#ifndef STPANALYZER_H +#define STPANALYZER_H + +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2024 Raspberry Pi Ltd + */ + +#include +#include + +class StpAnalyzer : public QObject +{ + Q_OBJECT +public: + StpAnalyzer(int onlyReportIfForwardDelayIsAbove = 5, QObject *parent = nullptr); + virtual ~StpAnalyzer(); + void startListening(const QByteArray &ifname); + void stopListening(); + +signals: + void detected(); + +protected: + int _s, _minForwardDelay; + QSocketNotifier *_qsn; + +protected slots: + void onPacket(QSocketDescriptor socket, QSocketNotifier::Type type); +}; + +#endif // STPANALYZER_H diff --git a/src/main.cpp b/src/main.cpp index 9ebe85c..c58a847 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -347,6 +347,7 @@ int main(int argc, char *argv[]) qmlwindow->connect(&imageWriter, SIGNAL(finalizing()), qmlwindow, SLOT(onFinalizing())); qmlwindow->connect(&imageWriter, SIGNAL(networkOnline()), qmlwindow, SLOT(fetchOSlist())); qmlwindow->connect(&imageWriter, SIGNAL(osListPrepared()), qmlwindow, SLOT(onOsListPrepared())); + qmlwindow->connect(&imageWriter, SIGNAL(networkInfo(QVariant)), qmlwindow, SLOT(onNetworkInfo(QVariant))); #ifndef QT_NO_WIDGETS /* Set window position */ @@ -375,7 +376,8 @@ int main(int argc, char *argv[]) qmlwindow->setProperty("y", y); #endif - imageWriter.beginOSListFetch(); + if (imageWriter.isOnline()) + imageWriter.beginOSListFetch(); int rc = app.exec(); diff --git a/src/main.qml b/src/main.qml index 9f7b109..1f6a2ed 100644 --- a/src/main.qml +++ b/src/main.qml @@ -320,6 +320,16 @@ ApplicationWindow { text: qsTr("Using custom repository: %1").arg(imageWriter.constantOsListUrl()) } + Text { + id: networkInfo + Layout.columnSpan: 3 + color: "#ffffff" + font.pixelSize: 18 + font.family: roboto.name + visible: imageWriter.isEmbeddedMode() + text: qsTr("Network not ready yet") + } + Text { Layout.columnSpan: 3 color: "#ffffff" @@ -1316,6 +1326,10 @@ ApplicationWindow { progressText.text = qsTr("Finalizing...") } + function onNetworkInfo(msg) { + networkInfo.text = msg + } + function shuffle(arr) { for (var i = 0; i < arr.length - 1; i++) { var j = i + Math.floor(Math.random() * (arr.length - i));