Skip to content

Commit

Permalink
Embedded: fix network detection
Browse files Browse the repository at this point in the history
  • Loading branch information
maxnet committed Jan 14, 2024
1 parent ebe72a4 commit 8c9de2d
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
22 changes: 18 additions & 4 deletions src/imagewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#ifndef QT_NO_WIDGETS
#include <QFileDialog>
#include <QApplication>
#else
#include <QtPlatformHeaders/QEglFSFunctions>
#endif
#ifdef Q_OS_DARWIN
#include <QMessageBox>
Expand All @@ -49,8 +51,8 @@
#include <QProcessEnvironment>
#endif

#ifdef QT_NO_WIDGETS
#include <QtPlatformHeaders/QEglFSFunctions>
#ifdef Q_OS_LINUX
#include "linux/stpanalyzer.h"
#endif

namespace {
Expand All @@ -75,6 +77,7 @@ ImageWriter::ImageWriter(QObject *parent)
platform = "cli";
}

#ifdef Q_OS_LINUX
if (platform == "eglfs" || platform == "linuxfb")
{
_embeddedMode = true;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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()));
}

Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/imagewriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ class ImageWriter : public QObject
void networkOnline();
void preparationStatusUpdate(QVariant msg);
void osListPrepared();
void networkInfo(QVariant msg);

protected slots:

Expand All @@ -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
Expand Down
118 changes: 118 additions & 0 deletions src/linux/stpanalyzer.cpp
Original file line number Diff line number Diff line change
@@ -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 <cstdint>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <net/if.h>
#include <QSocketNotifier>
#include <QtEndian>

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();
}
}
32 changes: 32 additions & 0 deletions src/linux/stpanalyzer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef STPANALYZER_H
#define STPANALYZER_H

/*
* SPDX-License-Identifier: Apache-2.0
* Copyright (C) 2024 Raspberry Pi Ltd
*/

#include <QObject>
#include <QSocketNotifier>

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
4 changes: 3 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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();

Expand Down
14 changes: 14 additions & 0 deletions src/main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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));
Expand Down

0 comments on commit 8c9de2d

Please sign in to comment.