Compare commits
175 Commits
find-repla
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
318fe124bf | ||
|
b3ac5f8d5d | ||
|
f185ceb206 | ||
|
cf84060277 | ||
|
469e0b054e | ||
|
23ee0e6f00 | ||
|
f3dccb9484 | ||
|
8f89c577ef | ||
|
71435869cf | ||
|
f779b0138b | ||
|
8c58d8610f | ||
|
c285e91d4a | ||
|
f5350663cd | ||
|
483f7fb4c5 | ||
|
d067019959 | ||
|
eb9d5eaf1a | ||
|
e677dc7d16 | ||
|
ebe6aef62d | ||
|
1e8486991d | ||
|
fff61d7773 | ||
|
eb3331f74d | ||
|
f3f553d149 | ||
|
49cb7c40e0 | ||
|
5581118e2f | ||
|
3bcd31a1ea | ||
|
268fad0690 | ||
|
28b308ce6c | ||
|
bfda96a888 | ||
|
a2734647b6 | ||
|
5eb2ad1979 | ||
|
1fa84ec644 | ||
|
c044880f1a | ||
|
2941c7ec22 | ||
|
ee45aaf0eb | ||
|
5ec7010c51 | ||
|
bef0f1d162 | ||
|
598e6bf243 | ||
|
63ed64a9d2 | ||
|
8ef9da062d | ||
|
7fee90313e | ||
|
bafdd948f8 | ||
|
e138fa0d3d | ||
|
8583299a1c | ||
|
4ba98cc520 | ||
|
649fa03575 | ||
|
650c098370 | ||
|
d375736a39 | ||
|
976fc72de8 | ||
|
4426a51d0f | ||
|
3c6632b6a2 | ||
|
d3be505a0c | ||
|
70f1b6c6e6 | ||
|
d65fea00d5 | ||
|
0afc150264 | ||
|
6ba942d00f | ||
|
4ee91c1bc2 | ||
|
aebb609e37 | ||
|
7160f724cb | ||
|
a48a11fe02 | ||
|
91a6efdeb7 | ||
|
bfdcee2baa | ||
|
c2967b663d | ||
|
70b5e60440 | ||
|
5540253e61 | ||
|
8ecbe78ddd | ||
|
3e3b5144aa | ||
|
596df69519 | ||
|
680f6eb89f | ||
|
1b18149aaa | ||
|
bf749847e0 | ||
|
ab713ce043 | ||
|
fc2d8408fa | ||
|
39c8d6f5f3 | ||
|
219ad576ad | ||
|
3060701386 | ||
|
47325c38b0 | ||
|
21ce331c43 | ||
|
fd2fae711b | ||
|
2d998b3708 | ||
|
f65aed7bb0 | ||
|
fdf8c77350 | ||
|
429eff123d | ||
|
3c98900092 | ||
|
159cd7c0da | ||
|
46b148b62b | ||
|
f29d31d38a | ||
|
1056b8d170 | ||
|
4a4de23d8a | ||
|
425e4ef261 | ||
|
d950432bc9 | ||
|
9849973562 | ||
|
4f6749f16d | ||
|
8dbd01622c | ||
|
2f3add63d8 | ||
|
7b7ede351b | ||
|
c70811eaa4 | ||
|
e2369c02bc | ||
|
c07d9e8691 | ||
|
d44fdf4ae7 | ||
|
f07cba39d5 | ||
|
1e50f9b095 | ||
|
3cea0244d4 | ||
|
8d3f0c807f | ||
|
449a489986 | ||
|
b645e02963 | ||
|
32ddf223b6 | ||
|
42091e5221 | ||
|
82db82d85b | ||
|
e9fa8a0c5b | ||
|
027b0562de | ||
|
c82cccc5eb | ||
|
94a6423a96 | ||
|
cda08d9afb | ||
|
97068b97e3 | ||
|
06ad12777f | ||
|
2970a292c6 | ||
|
701a058c7d | ||
|
2103f2c5a6 | ||
|
4be8a2969d | ||
|
46a54fd56b | ||
|
d32f89d30b | ||
|
1b647ade1b | ||
|
789338c8e1 | ||
|
898b56fc76 | ||
|
9d42ed12cc | ||
|
c949dc6682 | ||
|
eb2ca12f32 | ||
|
e79b61b189 | ||
|
741202ca76 | ||
|
843733567a | ||
|
6f4ef70dbc | ||
|
b296c5fddd | ||
|
d9cd90a13d | ||
|
fb879d2c72 | ||
|
5e7bf77b0c | ||
|
a1d985e44b | ||
|
b8d5d9421f | ||
|
2868806f3f | ||
|
ccf5b5ca47 | ||
|
c03038167c | ||
|
de9018166d | ||
|
c0d860b92d | ||
|
81c975d6d1 | ||
|
1854f1ab9e | ||
|
5a91dc4561 | ||
|
3d6f39e84d | ||
|
60f61ed947 | ||
|
26d0c8ab9c | ||
|
25a91e52f6 | ||
|
c8cc7a021f | ||
|
564687ffe3 | ||
|
9d4aaa4f7b | ||
|
329469dd6e | ||
|
7374c74b95 | ||
|
6f433bb7a1 | ||
|
2ee19da15c | ||
|
22b8c405f7 | ||
|
2ce7f0c7a0 | ||
|
041f500e65 | ||
|
7dd070fd98 | ||
|
ac52844b3f | ||
|
2bf2973a23 | ||
|
341d0c3be8 | ||
|
6534312968 | ||
|
a089cc1751 | ||
|
b0a81fb231 | ||
|
735e960dcb | ||
|
226705f015 | ||
|
45d5e15f23 | ||
|
d09d83000e | ||
|
0f322fb2d8 | ||
|
0587e22de9 | ||
|
5045b7f273 | ||
|
a89400a4e5 | ||
|
73dd198069 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
github: pstavirs
|
@ -13,11 +13,11 @@ I have been developing and maintaining Ostinato [single-handedly](https://github
|
||||
|
||||
I sell binary licenses on [ostinato.org](https://ostinato.org/downloads) to try and cover the costs of development. Please consider buying those - they are priced low enough that you can afford it or you could just as easily expense them to your organisation.
|
||||
|
||||
If you build Ostinato from source and find it useful, please donate to keep the lights on and sustain the project.
|
||||
If you build Ostinato from source and find it useful, please sponsor to keep the lights on and sustain the project.
|
||||
|
||||
[](https://github.com/sponsors/pstavirs)
|
||||
|
||||
Read the Ostinato [origin story](https://ostinato.org/about).
|
||||
|
||||
[](https://gum.co/ostdonate)
|
||||
|
||||
Srivats P<br/>
|
||||
Author, Ostinato
|
||||
|
@ -94,7 +94,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="CopyrightLabel" >
|
||||
<property name="text" >
|
||||
<string>Copyright (c) 2007-2020 Srivats P.</string>
|
||||
<string>Copyright (c) 2007-2023 Srivats P.</string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignCenter</set>
|
||||
|
@ -209,7 +209,7 @@ void DeviceGroupDialog::updateTotalDeviceCount()
|
||||
|
||||
void DeviceGroupDialog::updateIp4Gateway()
|
||||
{
|
||||
quint32 net = ip4Address->value() & (~0 << (32 - ip4PrefixLength->value()));
|
||||
quint32 net = ip4Address->value() & (~0UL << (32 - ip4PrefixLength->value()));
|
||||
ip4Gateway->setValue(net | 0x01);
|
||||
}
|
||||
|
||||
|
@ -262,8 +262,10 @@ void DumpView::paintEvent(QPaintEvent* /*event*/)
|
||||
// FIXME(LOW): unable to set the self widget's font in constructor
|
||||
painter.setFont(QFont("Courier"));
|
||||
|
||||
// set a white background
|
||||
painter.fillRect(rect(), QBrush(QColor(Qt::white)));
|
||||
// Qt automatically clears the background before we are called
|
||||
// QWidget::paintEvent doc:
|
||||
// When the paint event occurs, the update region has normally
|
||||
// been erased, so you are painting on the widget's background.
|
||||
|
||||
if (model())
|
||||
{
|
||||
|
@ -31,9 +31,13 @@ class IconOnlyDelegate : public QStyledItemDelegate
|
||||
{
|
||||
QStyleOptionViewItem opt = option;
|
||||
opt.decorationPosition = QStyleOptionViewItem::Top;
|
||||
opt.features &= ~QStyleOptionViewItem::HasDisplay;
|
||||
QStyledItemDelegate::paint(painter, opt, index);
|
||||
}
|
||||
|
||||
QString displayText(const QVariant&, const QLocale&) const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -182,6 +182,8 @@ void LogsWindow::alert(State state)
|
||||
// start - center of main window
|
||||
QRect start;
|
||||
QWidget *view = mainWindow;
|
||||
if (!view)
|
||||
return;
|
||||
alert_->setParent(view);
|
||||
alert_->raise();
|
||||
start.setSize(QSize(256, 256).scaled(view->size()/2, Qt::KeepAspectRatio));
|
||||
|
@ -24,6 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "params.h"
|
||||
#include "preferences.h"
|
||||
#include "settings.h"
|
||||
#include "thememanager.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDateTime>
|
||||
@ -84,6 +85,8 @@ int main(int argc, char* argv[])
|
||||
appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(),
|
||||
appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString());
|
||||
|
||||
ThemeManager::instance()->setTheme(appSettings->value(kThemeKey).toString());
|
||||
|
||||
qsrand(QDateTime::currentDateTime().toTime_t());
|
||||
|
||||
mainWindow = new MainWindow;
|
||||
|
@ -24,6 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#endif
|
||||
|
||||
#include "clipboardhelper.h"
|
||||
#include "fileformatoptions.h"
|
||||
#include "jumpurl.h"
|
||||
#include "logsmodel.h"
|
||||
#include "logswindow.h"
|
||||
@ -85,8 +86,14 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
localServer_ = new QProcess(this);
|
||||
connect(localServer_, SIGNAL(finished(int, QProcess::ExitStatus)),
|
||||
SLOT(onLocalServerFinished(int, QProcess::ExitStatus)));
|
||||
|
||||
#if QT_VERSION >= 0x050600
|
||||
connect(localServer_, SIGNAL(errorOccurred(QProcess::ProcessError)),
|
||||
SLOT(onLocalServerError(QProcess::ProcessError)));
|
||||
#else
|
||||
connect(localServer_, SIGNAL(error(QProcess::ProcessError)),
|
||||
SLOT(onLocalServerError(QProcess::ProcessError)));
|
||||
#endif
|
||||
localServer_->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
localServer_->start(serverApp, QStringList());
|
||||
QTimer::singleShot(5000, this, SLOT(stopLocalServerMonitor()));
|
||||
@ -119,8 +126,11 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
|
||||
setupUi(this);
|
||||
|
||||
menuFile->insertActions(menuFile->actions().at(3), portsWindow->actions());
|
||||
menuFile->insertActions(menuFile->actions().at(3),
|
||||
portsWindow->portActions());
|
||||
menuEdit->addActions(clipboardHelper->actions());
|
||||
menuStreams->addActions(portsWindow->streamActions());
|
||||
menuDevices->addActions(portsWindow->deviceActions());
|
||||
|
||||
statsDock->setWidget(statsWindow);
|
||||
addDockWidget(Qt::BottomDockWidgetArea, statsDock);
|
||||
@ -165,6 +175,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
this, SLOT(onNewVersion(QString)));
|
||||
updater->checkForNewVersion();
|
||||
|
||||
// TODO: If session file specified (and valid?), don't add local drone PG
|
||||
// Add the "Local" Port Group
|
||||
if (appParams.optLocalDrone()) {
|
||||
PortGroup *pg = new PortGroup;
|
||||
@ -409,8 +420,13 @@ void MainWindow::on_actionHelpAbout_triggered()
|
||||
void MainWindow::stopLocalServerMonitor()
|
||||
{
|
||||
// We are only interested in startup errors
|
||||
#if QT_VERSION >= 0x050600
|
||||
disconnect(localServer_, SIGNAL(errorOccurred(QProcess::ProcessError)),
|
||||
this, SLOT(onLocalServerError(QProcess::ProcessError)));
|
||||
#else
|
||||
disconnect(localServer_, SIGNAL(error(QProcess::ProcessError)),
|
||||
this, SLOT(onLocalServerError(QProcess::ProcessError)));
|
||||
#endif
|
||||
disconnect(localServer_, SIGNAL(finished(int, QProcess::ExitStatus)),
|
||||
this, SLOT(onLocalServerFinished(int, QProcess::ExitStatus)));
|
||||
}
|
||||
@ -451,8 +467,8 @@ void MainWindow::reportLocalServerError()
|
||||
if (localServer_->exitCode() == STATUS_DLL_NOT_FOUND)
|
||||
errorStr.append(tr("<p>This is most likely because Packet.dll "
|
||||
"was not found - make sure you have "
|
||||
"<a href='%1'>WinPcap"
|
||||
"</a> installed.</p>")
|
||||
"<a href='%1'>npcap installed and accessible</a>."
|
||||
"</p>")
|
||||
.arg(jumpUrl("winpcap")));
|
||||
#endif
|
||||
msgBox.setText(errorStr);
|
||||
@ -532,7 +548,7 @@ bool MainWindow::openSession(QString fileName, QString &error)
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if ((optDialog = fmt->openOptionsDialog()))
|
||||
if ((optDialog = FileFormatOptions::openOptionsDialog(fmt)))
|
||||
{
|
||||
int ret;
|
||||
optDialog->setParent(this, Qt::Dialog);
|
||||
|
@ -25,21 +25,10 @@
|
||||
<addaction name="actionOpenSession" />
|
||||
<addaction name="actionSaveSession" />
|
||||
<addaction name="separator" />
|
||||
<addaction name="separator" />
|
||||
<addaction name="actionPreferences" />
|
||||
<addaction name="actionFileExit" />
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuHelp" >
|
||||
<property name="title" >
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="actionHelpOnline" />
|
||||
<addaction name="separator" />
|
||||
<addaction name="actionDonate" />
|
||||
<addaction name="actionCheckForUpdates" />
|
||||
<addaction name="separator" />
|
||||
<addaction name="actionHelpAbout" />
|
||||
<addaction name="actionAboutQt" />
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuEdit" >
|
||||
<property name="title" >
|
||||
<string>&Edit</string>
|
||||
@ -52,9 +41,34 @@
|
||||
<addaction name="actionViewShowMyReservedPortsOnly" />
|
||||
<addaction name="actionViewRestoreDefaults" />
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuStreams" >
|
||||
<property name="title" >
|
||||
<string>&Streams</string>
|
||||
</property>
|
||||
<addaction name="actionTest" />
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuDevices" >
|
||||
<property name="title" >
|
||||
<string>&Devices</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuHelp" >
|
||||
<property name="title" >
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="actionHelpOnline" />
|
||||
<addaction name="separator" />
|
||||
<addaction name="actionDonate" />
|
||||
<addaction name="actionCheckForUpdates" />
|
||||
<addaction name="separator" />
|
||||
<addaction name="actionHelpAbout" />
|
||||
<addaction name="actionAboutQt" />
|
||||
</widget>
|
||||
<addaction name="menuFile" />
|
||||
<addaction name="menuEdit" />
|
||||
<addaction name="menuView" />
|
||||
<addaction name="menuStreams" />
|
||||
<addaction name="menuDevices" />
|
||||
<addaction name="menuHelp" />
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar" />
|
||||
|
@ -5,31 +5,26 @@ win32:RC_FILE = ostinato.rc
|
||||
macx:ICON = icons/logo.icns
|
||||
QT += widgets network script xml svg
|
||||
INCLUDEPATH += "../rpc/" "../common/"
|
||||
|
||||
OBJDIR = .
|
||||
win32 {
|
||||
QMAKE_LFLAGS += -static
|
||||
CONFIG(debug, debug|release) {
|
||||
LIBS += -L"../common/debug" -lostprotogui -lostproto
|
||||
LIBS += -L"../rpc/debug" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/debug/libostprotogui.a" \
|
||||
"../common/debug/libostproto.a" \
|
||||
"../rpc/debug/libpbrpc.a"
|
||||
OBJDIR = debug
|
||||
} else {
|
||||
LIBS += -L"../common/release" -lostprotogui -lostproto
|
||||
LIBS += -L"../rpc/release" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/release/libostprotogui.a" \
|
||||
"../common/release/libostproto.a" \
|
||||
"../rpc/release/libpbrpc.a"
|
||||
OBJDIR = release
|
||||
}
|
||||
} else {
|
||||
LIBS += -L"../common" -lostprotogui -lostproto
|
||||
LIBS += -L"../rpc" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/libostprotogui.a" \
|
||||
"../common/libostproto.a" \
|
||||
"../rpc/libpbrpc.a"
|
||||
}
|
||||
LIBS += -L"../common/$$OBJDIR" -lostfile -lostfilegui
|
||||
LIBS += -L"../common/$$OBJDIR" -lostprotogui -lostproto
|
||||
LIBS += -L"../rpc/$$OBJDIR" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/$$OBJDIR/libostfilegui.a" \
|
||||
"../common/$$OBJDIR/libostfile.a" \
|
||||
"../common/$$OBJDIR/libostprotogui.a" \
|
||||
"../common/$$OBJDIR/libostproto.a" \
|
||||
"../rpc/$$OBJDIR/libpbrpc.a"
|
||||
|
||||
LIBS += -lprotobuf
|
||||
LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2
|
||||
RESOURCES += ostinato.qrc
|
||||
@ -128,12 +123,23 @@ SOURCES += \
|
||||
streamstatsmodel.cpp \
|
||||
streamstatswindow.cpp \
|
||||
streamswidget.cpp \
|
||||
thememanager.cpp \
|
||||
variablefieldswidget.cpp
|
||||
|
||||
THEMES += \
|
||||
themes/material-dark.qss \
|
||||
themes/material-dark.rcc \
|
||||
themes/material-light.qss \
|
||||
themes/material-light.rcc \
|
||||
themes/qds-dark.qss \
|
||||
themes/qds-dark.rcc \
|
||||
themes/qds-light.qss \
|
||||
themes/qds-light.rcc \
|
||||
|
||||
QMAKE_DISTCLEAN += object_script.*
|
||||
|
||||
include(../install.pri)
|
||||
include(../shared.pri)
|
||||
include(../version.pri)
|
||||
include(../options.pri)
|
||||
|
||||
|
@ -45,11 +45,11 @@ int Params::parseCommandLine(int argc, char* argv[])
|
||||
logsDisabled_ = false;
|
||||
break;
|
||||
case 'v':
|
||||
qDebug("Ostinato %s rev %s\n", version, revision);
|
||||
printf("Ostinato %s rev %s\n", version, revision);
|
||||
exit(0);
|
||||
case 'h':
|
||||
default:
|
||||
qDebug("usage: %s [-cdhv]\n", argv[0]);
|
||||
printf("usage: %s [-cdhv]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
n++;
|
||||
|
@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "port.h"
|
||||
|
||||
#include "emulation.h"
|
||||
#include "fileformatoptions.h"
|
||||
#include "streamfileformat.h"
|
||||
|
||||
#include <QApplication>
|
||||
@ -604,7 +605,7 @@ bool Port::openStreams(QString fileName, bool append, QString &error)
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if ((optDialog = fmt->openOptionsDialog()))
|
||||
if ((optDialog = FileFormatOptions::openOptionsDialog(fmt)))
|
||||
{
|
||||
int ret;
|
||||
optDialog->setParent(mainWindow, Qt::Dialog);
|
||||
|
@ -88,8 +88,15 @@ public:
|
||||
{ return d.port_id().id(); }
|
||||
const QString name() const
|
||||
{ return QString().fromStdString(d.name()); }
|
||||
const QString description() const
|
||||
const QString systemDescription() const
|
||||
{ return QString().fromStdString(d.description()); }
|
||||
const QString userDescription() const
|
||||
{ return QString().fromStdString(d.user_description()); }
|
||||
const QString description() const
|
||||
{
|
||||
return userDescription().isEmpty() ?
|
||||
systemDescription() : userDescription();
|
||||
}
|
||||
const QString notes() const
|
||||
{ return QString().fromStdString(d.notes()); }
|
||||
const QString userName() const
|
||||
|
@ -32,6 +32,8 @@ PortConfigDialog::PortConfigDialog(
|
||||
|
||||
setupUi(this);
|
||||
|
||||
description->setPlaceholderText(portConfig_.description().c_str());
|
||||
description->setText(portConfig_.user_description().c_str());
|
||||
switch(portConfig_.transmit_mode())
|
||||
{
|
||||
case OstProto::kSequentialTransmit:
|
||||
@ -80,6 +82,8 @@ void PortConfigDialog::accept()
|
||||
{
|
||||
OstProto::Port pc;
|
||||
|
||||
pc.set_user_description(description->text().toStdString());
|
||||
|
||||
if (sequentialStreamsButton->isChecked())
|
||||
pc.set_transmit_mode(OstProto::kSequentialTransmit);
|
||||
else if (interleavedStreamsButton->isChecked())
|
||||
@ -109,6 +113,11 @@ void PortConfigDialog::accept()
|
||||
pc.set_is_tracking_stream_stats(streamStatsButton->isChecked());
|
||||
|
||||
// Update fields that have changed, clear the rest
|
||||
if (pc.user_description() != portConfig_.user_description())
|
||||
portConfig_.set_user_description(pc.user_description());
|
||||
else
|
||||
portConfig_.clear_user_description();
|
||||
|
||||
if (pc.transmit_mode() != portConfig_.transmit_mode())
|
||||
portConfig_.set_transmit_mode(pc.transmit_mode());
|
||||
else
|
||||
|
@ -1,37 +1,51 @@
|
||||
<ui version="4.0" >
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PortConfigDialog</class>
|
||||
<widget class="QDialog" name="PortConfigDialog" >
|
||||
<property name="geometry" >
|
||||
<widget class="QDialog" name="PortConfigDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>244</width>
|
||||
<height>257</height>
|
||||
<width>248</width>
|
||||
<height>292</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<property name="windowTitle">
|
||||
<string>Port Config</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="transmitModeBox" >
|
||||
<property name="title" >
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Description</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>description</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="description"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="transmitModeBox">
|
||||
<property name="title">
|
||||
<string>Transmit Mode</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="sequentialStreamsButton" >
|
||||
<property name="text" >
|
||||
<widget class="QRadioButton" name="sequentialStreamsButton">
|
||||
<property name="text">
|
||||
<string>Sequential Streams</string>
|
||||
</property>
|
||||
<property name="checked" >
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="interleavedStreamsButton" >
|
||||
<property name="text" >
|
||||
<widget class="QRadioButton" name="interleavedStreamsButton">
|
||||
<property name="text">
|
||||
<string>Interleaved Streams</string>
|
||||
</property>
|
||||
</widget>
|
||||
@ -40,21 +54,21 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="title" >
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Reservation</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="reservedBy" >
|
||||
<property name="text" >
|
||||
<widget class="QLabel" name="reservedBy">
|
||||
<property name="text">
|
||||
<string>Reserved by: </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="reserveButton" >
|
||||
<property name="text" >
|
||||
<widget class="QCheckBox" name="reserveButton">
|
||||
<property name="text">
|
||||
<string>Reserve</string>
|
||||
</property>
|
||||
</widget>
|
||||
@ -63,25 +77,25 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="exclusiveControlButton" >
|
||||
<property name="text" >
|
||||
<widget class="QCheckBox" name="exclusiveControlButton">
|
||||
<property name="text">
|
||||
<string>Exclusive Control</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="streamStatsButton" >
|
||||
<property name="text" >
|
||||
<widget class="QCheckBox" name="streamStatsButton">
|
||||
<property name="text">
|
||||
<string>Stream Statistics</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>226</width>
|
||||
<height>71</height>
|
||||
@ -90,17 +104,25 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||
<property name="orientation" >
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons" >
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>description</tabstop>
|
||||
<tabstop>sequentialStreamsButton</tabstop>
|
||||
<tabstop>interleavedStreamsButton</tabstop>
|
||||
<tabstop>reserveButton</tabstop>
|
||||
<tabstop>exclusiveControlButton</tabstop>
|
||||
<tabstop>streamStatsButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
@ -109,11 +131,11 @@
|
||||
<receiver>PortConfigDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>234</x>
|
||||
<y>205</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>214</y>
|
||||
</hint>
|
||||
@ -125,11 +147,11 @@
|
||||
<receiver>PortConfigDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>234</x>
|
||||
<y>205</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>243</x>
|
||||
<y>214</y>
|
||||
</hint>
|
||||
|
@ -1111,8 +1111,11 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
|
||||
// * modify (new) deviceGroups
|
||||
// * add (new) stream ids
|
||||
// * modify (new) streams
|
||||
// * resolve neighbors
|
||||
// * build packets
|
||||
// XXX: This assumes getDeviceGroupIdList() was invoked before
|
||||
// getStreamIdList() - if the order changes this code will break!
|
||||
// XXX: See resolve/build notes below
|
||||
|
||||
// XXX: same name as input param, but shouldn't cause any problem
|
||||
PbRpcController *controller;
|
||||
@ -1153,6 +1156,7 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
|
||||
}
|
||||
|
||||
// add/modify deviceGroups
|
||||
bool resolve = false;
|
||||
if (newPortContent->device_groups_size())
|
||||
{
|
||||
OstProto::DeviceGroupIdList *deviceGroupIdList
|
||||
@ -1184,6 +1188,7 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
|
||||
deviceGroupConfigList, ack,
|
||||
NewCallback(this, &PortGroup::processModifyDeviceGroupAck,
|
||||
portIndex, controller));
|
||||
resolve = true;
|
||||
}
|
||||
|
||||
// add/modify streams
|
||||
@ -1214,6 +1219,26 @@ void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller)
|
||||
serviceStub->modifyStream(controller, streamConfigList, ack,
|
||||
NewCallback(this, &PortGroup::processModifyStreamAck,
|
||||
portIndex, controller));
|
||||
resolve = true;
|
||||
}
|
||||
|
||||
// XXX: Ideally resolve and build should be called after **all**
|
||||
// ports and portgroups are configured. As of now, any resolve
|
||||
// replied to by ports/portgroups configured later in the open
|
||||
// session sequence will fail.
|
||||
// However, to do that, we may need to rethink the open session
|
||||
// implementation - so going with this for now
|
||||
if (resolve)
|
||||
{
|
||||
OstProto::PortIdList *portIdList = new OstProto::PortIdList;
|
||||
portIdList->add_port_id()->set_id(portId);
|
||||
OstProto::Ack *ack = new OstProto::Ack;
|
||||
controller = new PbRpcController(portIdList, ack);
|
||||
serviceStub->resolveDeviceNeighbors(controller, portIdList, ack,
|
||||
NewCallback(this,
|
||||
&PortGroup::processResolveDeviceNeighborsAck,
|
||||
controller));
|
||||
resolve = false;
|
||||
}
|
||||
|
||||
// build packets using the new config
|
||||
|
@ -68,6 +68,7 @@ bool PortGroupList::isPort(const QModelIndex& index)
|
||||
|
||||
PortGroup& PortGroupList::portGroup(const QModelIndex& index)
|
||||
{
|
||||
Q_ASSERT(index.isValid());
|
||||
Q_ASSERT(mPortGroupListModel.isPortGroup(index));
|
||||
|
||||
return *(mPortGroups[index.row()]);
|
||||
@ -75,6 +76,8 @@ PortGroup& PortGroupList::portGroup(const QModelIndex& index)
|
||||
|
||||
Port& PortGroupList::port(const QModelIndex& index)
|
||||
{
|
||||
Q_ASSERT(index.isValid());
|
||||
Q_ASSERT(index.parent().isValid());
|
||||
Q_ASSERT(mPortGroupListModel.isPort(index));
|
||||
return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]);
|
||||
}
|
||||
@ -97,6 +100,9 @@ void PortGroupList::addPortGroup(PortGroup &portGroup)
|
||||
connect(&portGroup, SIGNAL(portListChanged(quint32)),
|
||||
&mPortStatsModel, SLOT(when_portListChanged()));
|
||||
|
||||
connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)),
|
||||
&mPortStatsModel, SLOT(when_portGroupDataChanged(int, int)));
|
||||
|
||||
connect(&portGroup, SIGNAL(statsChanged(quint32)),
|
||||
&mPortStatsModel, SLOT(when_portGroup_stats_update(quint32)));
|
||||
|
||||
@ -110,8 +116,10 @@ void PortGroupList::addPortGroup(PortGroup &portGroup)
|
||||
|
||||
void PortGroupList::removePortGroup(PortGroup &portGroup)
|
||||
{
|
||||
mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup);
|
||||
// Disconnect before removing from list
|
||||
portGroup.disconnectFromHost();
|
||||
|
||||
mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup);
|
||||
PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup));
|
||||
qDebug("after takeAt()");
|
||||
mPortGroupListModel.portGroupRemoved();
|
||||
@ -128,11 +136,12 @@ void PortGroupList::removeAllPortGroups()
|
||||
|
||||
do {
|
||||
PortGroup *pg = mPortGroups.at(0);
|
||||
pg->disconnectFromHost();
|
||||
mPortGroupListModel.portGroupAboutToBeRemoved(pg);
|
||||
mPortGroups.removeFirst();
|
||||
delete pg;
|
||||
} while (!mPortGroups.isEmpty());
|
||||
mPortGroupListModel.portGroupRemoved();
|
||||
} while (!mPortGroups.isEmpty());
|
||||
|
||||
mPortGroupListModel.when_portListChanged();
|
||||
mPortStatsModel.when_portListChanged();
|
||||
|
@ -49,7 +49,8 @@ QList<uint> PortStatsFilterDialog::getItemList(bool* ok,
|
||||
{
|
||||
QStandardItem *item;
|
||||
|
||||
item = new QStandardItem(model->headerData(i, orientation).toString());
|
||||
item = new QStandardItem(model->headerData(i, orientation)
|
||||
.toString().replace('\n', ' '));
|
||||
item->setData(i, kLogicalIndex);
|
||||
item->setData(initial.indexOf(i), kVisualIndex);
|
||||
item->setFlags(Qt::ItemIsSelectable
|
||||
|
@ -20,7 +20,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "portstatsmodel.h"
|
||||
#include "portgrouplist.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPainter>
|
||||
#include <QPalette>
|
||||
#include <QPixmapCache>
|
||||
#include <QTimer>
|
||||
|
||||
@ -135,7 +137,10 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
// States
|
||||
case e_COMBO_STATE:
|
||||
return QVariant();
|
||||
return QString("Link %1%2%3")
|
||||
.arg(LinkStateName.at(stats.state().link_state()))
|
||||
.arg(stats.state().is_transmit_on() ? ";Tx On" : "")
|
||||
.arg(stats.state().is_capture_on() ? ";Cap On" : "");
|
||||
|
||||
// Statistics
|
||||
case e_STAT_FRAMES_RCVD:
|
||||
@ -156,11 +161,13 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const
|
||||
case e_STAT_BYTES_SENT:
|
||||
return QString("%L1").arg(quint64(stats.tx_bytes()));
|
||||
|
||||
#if 0
|
||||
case e_STAT_BYTE_SEND_RATE:
|
||||
return QString("%L1").arg(quint64(stats.tx_bps()));
|
||||
|
||||
case e_STAT_BYTE_RECV_RATE:
|
||||
return QString("%L1").arg(quint64(stats.rx_bps()));
|
||||
#endif
|
||||
|
||||
case e_STAT_BIT_SEND_RATE:
|
||||
return QString("%L1").arg(quint64(
|
||||
@ -270,6 +277,14 @@ QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, in
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if ((role == Qt::BackgroundRole) && (orientation == Qt::Vertical)
|
||||
&& qApp->styleSheet().isEmpty())
|
||||
{
|
||||
QPalette palette = QApplication::palette();
|
||||
return section & 0x1 ?
|
||||
palette.alternateBase() : palette.base();
|
||||
}
|
||||
|
||||
if (role != Qt::DisplayRole)
|
||||
return QVariant();
|
||||
|
||||
@ -280,16 +295,23 @@ QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, in
|
||||
|
||||
if (numPorts.isEmpty() || section >= numPorts.last())
|
||||
return QVariant();
|
||||
|
||||
getDomainIndexes(index(0, section), portGroupIdx, portIdx);
|
||||
|
||||
PortGroup *portGroup = pgl->mPortGroups.at(portGroupIdx);
|
||||
Port *port = portGroup->mPorts.at(portIdx);
|
||||
|
||||
portName = QString("Port %1-%2")
|
||||
.arg(pgl->mPortGroups.at(portGroupIdx)->id())
|
||||
.arg(pgl->mPortGroups.at(portGroupIdx)->mPorts.at(portIdx)->id());
|
||||
.arg(portGroup->id())
|
||||
.arg(port->id());
|
||||
if (portGroupIdx < (uint) pgl->mPortGroups.size()
|
||||
&& portIdx < (uint) pgl->mPortGroups.at(portGroupIdx)->mPorts.size())
|
||||
&& portIdx < (uint) portGroup->mPorts.size())
|
||||
{
|
||||
if (!pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes()
|
||||
.isEmpty())
|
||||
if (!port->notes().isEmpty())
|
||||
portName += " *";
|
||||
portName += "\n";
|
||||
portName += port->userDescription().isEmpty() ?
|
||||
port->userAlias() : port->userDescription();
|
||||
}
|
||||
return portName;
|
||||
}
|
||||
@ -363,6 +385,16 @@ void PortStatsModel::when_portListChanged()
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void PortStatsModel::when_portGroupDataChanged(int /*portGroupId*/, int /*portId*/)
|
||||
{
|
||||
if (!columnCount())
|
||||
return;
|
||||
|
||||
// Port (user) description may have changed - update column headers
|
||||
// TODO: update only the changed ports, not all
|
||||
emit headerDataChanged(Qt::Horizontal, 0, columnCount()-1);
|
||||
}
|
||||
|
||||
// FIXME: unused? if used, the index calculation row/column needs to be swapped
|
||||
#if 0
|
||||
void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/)
|
||||
|
@ -26,15 +26,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
class QTimer;
|
||||
|
||||
typedef enum {
|
||||
// Info
|
||||
e_INFO_START = 0,
|
||||
|
||||
e_INFO_USER = e_INFO_START,
|
||||
|
||||
e_INFO_END = e_INFO_USER,
|
||||
|
||||
// State
|
||||
e_STATE_START,
|
||||
e_STATE_START = 0,
|
||||
|
||||
e_COMBO_STATE = e_STATE_START,
|
||||
|
||||
@ -43,14 +36,16 @@ typedef enum {
|
||||
// Statistics
|
||||
e_STATISTICS_START,
|
||||
|
||||
e_STAT_FRAMES_RCVD = e_STATISTICS_START,
|
||||
e_STAT_FRAMES_SENT,
|
||||
e_STAT_FRAMES_SENT = e_STATISTICS_START,
|
||||
e_STAT_FRAMES_RCVD,
|
||||
e_STAT_BYTES_SENT,
|
||||
e_STAT_BYTES_RCVD,
|
||||
e_STAT_FRAME_SEND_RATE,
|
||||
e_STAT_FRAME_RECV_RATE,
|
||||
e_STAT_BYTES_RCVD,
|
||||
e_STAT_BYTES_SENT,
|
||||
#if 0
|
||||
e_STAT_BYTE_SEND_RATE,
|
||||
e_STAT_BYTE_RECV_RATE,
|
||||
#endif
|
||||
e_STAT_BIT_SEND_RATE,
|
||||
e_STAT_BIT_RECV_RATE,
|
||||
#if 0
|
||||
@ -69,24 +64,34 @@ typedef enum {
|
||||
|
||||
e_STATISTICS_END = e_STAT_RX_FRAME_ERRORS,
|
||||
|
||||
// Info
|
||||
e_INFO_START,
|
||||
|
||||
// XXX: keep hidden rows at end to avoid having to recalculate rows
|
||||
e_INFO_USER = e_INFO_START,
|
||||
|
||||
e_INFO_END = e_INFO_USER,
|
||||
|
||||
|
||||
e_STAT_MAX
|
||||
} PortStat;
|
||||
|
||||
static QStringList PortStatName = (QStringList()
|
||||
<< "User"
|
||||
|
||||
static const QStringList PortStatName = (QStringList()
|
||||
<< "Status"
|
||||
|
||||
<< "Frames Received"
|
||||
<< "Frames Sent"
|
||||
<< "Frame Send Rate (fps)"
|
||||
<< "Frame Receive Rate (fps)"
|
||||
<< "Bytes Received"
|
||||
<< "Bytes Sent"
|
||||
<< "Byte Send Rate (Bps)"
|
||||
<< "Byte Receive Rate (Bps)"
|
||||
<< "Bit Send Rate (bps)"
|
||||
<< "Bit Receive Rate (bps)"
|
||||
<< "Sent Frames"
|
||||
<< "Received Frames"
|
||||
<< "Sent Bytes"
|
||||
<< "Received Bytes"
|
||||
|
||||
<< "Send Frame Rate (fps)"
|
||||
<< "Receive Frame Rate (fps)"
|
||||
#if 0
|
||||
<< "Send Byte Rate (Bps)"
|
||||
<< "Receive Byte Rate (Bps)"
|
||||
#endif
|
||||
<< "Send Bit Rate (bps)"
|
||||
<< "Receive Bit Rate (bps)"
|
||||
#if 0
|
||||
<< "Frames Received (NIC)"
|
||||
<< "Frames Sent (NIC)"
|
||||
@ -98,6 +103,8 @@ static QStringList PortStatName = (QStringList()
|
||||
<< "Receive Errors"
|
||||
<< "Receive Fifo Errors"
|
||||
<< "Receive Frame Errors"
|
||||
|
||||
<< "User"
|
||||
);
|
||||
|
||||
static QStringList LinkStateName = (QStringList()
|
||||
@ -136,6 +143,7 @@ class PortStatsModel : public QAbstractTableModel
|
||||
public slots:
|
||||
void when_portListChanged();
|
||||
//void on_portStatsUpdate(int port, void*stats);
|
||||
void when_portGroupDataChanged(int portGroupId, int portId);
|
||||
void when_portGroup_stats_update(quint32 portGroupId);
|
||||
|
||||
private slots:
|
||||
|
@ -26,8 +26,8 @@ class PortStatsProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PortStatsProxyModel(QObject *parent = 0)
|
||||
: QSortFilterProxyModel(parent)
|
||||
PortStatsProxyModel(int userRow, QObject *parent = 0)
|
||||
: QSortFilterProxyModel(parent), userRow_(userRow)
|
||||
{
|
||||
setFilterRegExp(QRegExp(".*"));
|
||||
}
|
||||
@ -36,7 +36,7 @@ protected:
|
||||
bool filterAcceptsColumn(int sourceColumn,
|
||||
const QModelIndex &sourceParent) const
|
||||
{
|
||||
QModelIndex index = sourceModel()->index(0, sourceColumn, sourceParent);
|
||||
QModelIndex index = sourceModel()->index(userRow_, sourceColumn,sourceParent);
|
||||
QString user = sourceModel()->data(index).toString();
|
||||
|
||||
return filterRegExp().exactMatch(user) ? true : false;
|
||||
@ -44,9 +44,10 @@ protected:
|
||||
bool filterAcceptsRow(int sourceRow,
|
||||
const QModelIndex &/*sourceParent*/) const
|
||||
{
|
||||
// Hide row 0 - username (needed only by this filter class)
|
||||
return (sourceRow > 0) ? true : false;
|
||||
return sourceRow == userRow_ ? false : true;
|
||||
}
|
||||
private:
|
||||
int userRow_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -24,6 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "portstatsfilterdialog.h"
|
||||
#include "portstatsmodel.h"
|
||||
#include "portstatsproxymodel.h"
|
||||
#include "rowborderdelegate.h"
|
||||
#include "streamstatsmodel.h"
|
||||
#include "streamstatswindow.h"
|
||||
#include "settings.h"
|
||||
@ -42,7 +43,8 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
|
||||
this->pgl = pgl;
|
||||
model = pgl->getPortStatsModel();
|
||||
|
||||
proxyStatsModel = new PortStatsProxyModel(this);
|
||||
// Hide 'user' row
|
||||
proxyStatsModel = new PortStatsProxyModel(e_INFO_USER, this);
|
||||
if (proxyStatsModel) {
|
||||
proxyStatsModel->setSourceModel(model);
|
||||
tvPortStats->setModel(proxyStatsModel);
|
||||
@ -55,6 +57,17 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
|
||||
tvPortStats->verticalHeader()->setDefaultSectionSize(
|
||||
tvPortStats->verticalHeader()->minimumSectionSize());
|
||||
|
||||
// XXX: Set Delegates for port stats view
|
||||
// RowBorderDelegate: Group related stats using a horizontal line
|
||||
// IconOnlyDelegate : For status, show only icons not icons+text
|
||||
tvPortStats->setItemDelegate(
|
||||
new RowBorderDelegate(
|
||||
QSet<int>({
|
||||
e_STAT_FRAMES_SENT,
|
||||
e_STAT_FRAME_SEND_RATE,
|
||||
e_STAT_RX_DROPS}),
|
||||
this));
|
||||
|
||||
statusDelegate = new IconOnlyDelegate(this);
|
||||
#if 0
|
||||
// XXX: Ideally we should use this, but it doesn't work because in
|
||||
@ -68,9 +81,7 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
|
||||
statusDelegate);
|
||||
#else
|
||||
// ... so we use this hard-coded hack
|
||||
tvPortStats->setItemDelegateForRow(
|
||||
proxyStatsModel ? e_COMBO_STATE-1 : e_COMBO_STATE,
|
||||
statusDelegate);
|
||||
tvPortStats->setItemDelegateForRow(e_COMBO_STATE, statusDelegate);
|
||||
#endif
|
||||
|
||||
connect(tvPortStats->selectionModel(),
|
||||
|
@ -165,7 +165,7 @@
|
||||
<string>Stop Capture</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>End capture on selecteed port(s)</string>
|
||||
<string>End capture on selected port(s)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stop</string>
|
||||
|
@ -97,14 +97,19 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent)
|
||||
connect(plm->getPortModel(), SIGNAL(modelReset()),
|
||||
SLOT(when_portModel_reset()));
|
||||
|
||||
connect( tvPortList->selectionModel(),
|
||||
connect(actionPort_Configuration, SIGNAL(triggered()),
|
||||
SLOT(when_actionPort_Configuration_triggered()));
|
||||
connect(tvPortList, SIGNAL(activated(const QModelIndex&)),
|
||||
SLOT(when_actionPort_Configuration_triggered(const QModelIndex&)));
|
||||
connect(tvPortList->selectionModel(),
|
||||
SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT(when_portView_currentChanged(const QModelIndex&,
|
||||
const QModelIndex&)));
|
||||
|
||||
connect(this,
|
||||
SIGNAL(currentPortChanged(const QModelIndex&, const QModelIndex&)),
|
||||
portWidget, SLOT(setCurrentPortIndex(const QModelIndex&)));
|
||||
portWidget,
|
||||
SLOT(setCurrentPortIndex(const QModelIndex&, const QModelIndex&)));
|
||||
connect(this,
|
||||
SIGNAL(currentPortChanged(const QModelIndex&, const QModelIndex&)),
|
||||
streamsWidget, SLOT(setCurrentPortIndex(const QModelIndex&)));
|
||||
@ -233,6 +238,21 @@ bool PortsWindow::saveSession(
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<QAction*> PortsWindow::portActions()
|
||||
{
|
||||
return tvPortList->actions();
|
||||
}
|
||||
|
||||
QList<QAction*> PortsWindow::streamActions()
|
||||
{
|
||||
return streamsWidget->actions();
|
||||
}
|
||||
|
||||
QList<QAction*> PortsWindow::deviceActions()
|
||||
{
|
||||
return devicesWidget->actions();
|
||||
}
|
||||
|
||||
void PortsWindow::clearCurrentSelection()
|
||||
{
|
||||
tvPortList->selectionModel()->clearCurrentIndex();
|
||||
@ -582,9 +602,11 @@ void PortsWindow::on_actionExclusive_Control_triggered(bool checked)
|
||||
}
|
||||
}
|
||||
|
||||
void PortsWindow::on_actionPort_Configuration_triggered()
|
||||
void PortsWindow::when_actionPort_Configuration_triggered(
|
||||
const QModelIndex &portIndex)
|
||||
{
|
||||
QModelIndex current = tvPortList->selectionModel()->currentIndex();
|
||||
QModelIndex current = portIndex.isValid() ?
|
||||
portIndex : tvPortList->selectionModel()->currentIndex();
|
||||
|
||||
if (proxyPortModel)
|
||||
current = proxyPortModel->mapToSource(current);
|
||||
@ -599,6 +621,8 @@ void PortsWindow::on_actionPort_Configuration_triggered()
|
||||
// TODO: extend Port::protoDataCopyInto() to accept an optional param
|
||||
// which says copy only modifiable fields
|
||||
//plm->port(current).protoDataCopyInto(&config);
|
||||
config.set_description(port.systemDescription().toStdString());
|
||||
config.set_user_description(port.userDescription().toStdString());
|
||||
config.set_transmit_mode(port.transmitMode());
|
||||
config.set_is_tracking_stream_stats(port.trackStreamStats());
|
||||
config.set_is_exclusive_control(port.hasExclusiveControl());
|
||||
|
@ -50,6 +50,10 @@ public:
|
||||
QString &error,
|
||||
QProgressDialog *progress = NULL);
|
||||
|
||||
QList<QAction*> portActions();
|
||||
QList<QAction*> streamActions();
|
||||
QList<QAction*> deviceActions();
|
||||
|
||||
signals:
|
||||
void currentPortChanged(const QModelIndex ¤t,
|
||||
const QModelIndex &previous);
|
||||
@ -76,7 +80,8 @@ private slots:
|
||||
void on_actionDisconnect_Port_Group_triggered();
|
||||
|
||||
void on_actionExclusive_Control_triggered(bool checked);
|
||||
void on_actionPort_Configuration_triggered();
|
||||
void when_actionPort_Configuration_triggered(
|
||||
const QModelIndex &portIndex = QModelIndex());
|
||||
|
||||
private:
|
||||
PortGroupList *plm;
|
||||
|
@ -39,7 +39,7 @@
|
||||
<widget class="QStackedWidget" name="swDetail">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>2</horstretch>
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
|
@ -48,27 +48,31 @@ PortWidget::~PortWidget()
|
||||
{
|
||||
}
|
||||
|
||||
void PortWidget::setCurrentPortIndex(const QModelIndex &portIndex)
|
||||
void PortWidget::setCurrentPortIndex(const QModelIndex ¤tIndex,
|
||||
const QModelIndex &previousIndex)
|
||||
{
|
||||
if (!plm)
|
||||
return;
|
||||
|
||||
// XXX: We assume portIndex corresponds to sourceModel, not proxyModel
|
||||
if (!plm->isPort(portIndex))
|
||||
return;
|
||||
qDebug("In %s", __PRETTY_FUNCTION__);
|
||||
|
||||
qDebug("In %s", __FUNCTION__);
|
||||
// XXX: We assume indices corresponds to sourceModel, not proxyModel
|
||||
// - caller/sender should ensure this
|
||||
|
||||
// Disconnect previous port
|
||||
if (plm->isPort(currentPortIndex_))
|
||||
disconnect(&(plm->port(currentPortIndex_)),
|
||||
if (plm->isPort(previousIndex))
|
||||
disconnect(&(plm->port(previousIndex)),
|
||||
SIGNAL(portRateChanged(int, int)),
|
||||
this, SLOT(updatePortRates()));
|
||||
|
||||
currentPortIndex_ = portIndex;
|
||||
if (!plm->isPort(currentIndex)) {
|
||||
currentPortIndex_ = QModelIndex(); // set to invalid
|
||||
return;
|
||||
}
|
||||
|
||||
currentPortIndex_ = currentIndex;
|
||||
|
||||
// Connect current port
|
||||
if (plm->isPort(currentPortIndex_))
|
||||
connect(&(plm->port(currentPortIndex_)),
|
||||
SIGNAL(portRateChanged(int, int)),
|
||||
this, SLOT(updatePortRates()));
|
||||
|
@ -37,7 +37,8 @@ public:
|
||||
void setPortGroupList(PortGroupList *portGroups);
|
||||
|
||||
public slots:
|
||||
void setCurrentPortIndex(const QModelIndex &portIndex);
|
||||
void setCurrentPortIndex(const QModelIndex ¤tIndex,
|
||||
const QModelIndex &previousIndex);
|
||||
|
||||
private slots:
|
||||
|
||||
|
@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "../common/ostprotolib.h"
|
||||
#include "settings.h"
|
||||
#include "thememanager.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QtGlobal>
|
||||
@ -40,6 +41,7 @@ Preferences::Preferences()
|
||||
|
||||
setupUi(this);
|
||||
|
||||
// Program paths
|
||||
wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey,
|
||||
kWiresharkPathDefaultValue).toString());
|
||||
tsharkPathEdit->setText(appSettings->value(kTsharkPathKey,
|
||||
@ -51,6 +53,10 @@ Preferences::Preferences()
|
||||
awkPathEdit->setText(appSettings->value(kAwkPathKey,
|
||||
kAwkPathDefaultValue).toString());
|
||||
|
||||
// Theme
|
||||
theme->addItems(ThemeManager::instance()->themeList());
|
||||
theme->setCurrentText(appSettings->value(kThemeKey).toString());
|
||||
|
||||
// TODO(only if required): kUserKey
|
||||
}
|
||||
|
||||
@ -78,6 +84,7 @@ void Preferences::initDefaults()
|
||||
|
||||
void Preferences::accept()
|
||||
{
|
||||
// Program paths
|
||||
appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text());
|
||||
appSettings->setValue(kTsharkPathKey, tsharkPathEdit->text());
|
||||
appSettings->setValue(kGzipPathKey, gzipPathEdit->text());
|
||||
@ -90,6 +97,9 @@ void Preferences::accept()
|
||||
appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(),
|
||||
appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString());
|
||||
|
||||
// Theme
|
||||
ThemeManager::instance()->setTheme(theme->currentText());
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
<ui version="4.0" >
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Preferences</class>
|
||||
<widget class="QDialog" name="Preferences" >
|
||||
<property name="geometry" >
|
||||
<widget class="QDialog" name="Preferences">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
@ -9,148 +10,149 @@
|
||||
<height>220</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<property name="windowTitle">
|
||||
<string>Preferences</string>
|
||||
</property>
|
||||
<property name="windowIcon" >
|
||||
<iconset resource="ostinato.qrc" >:/icons/preferences.png</iconset>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/preferences.png</normaloff>:/icons/preferences.png</iconset>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<widget class="QFrame" name="frame" >
|
||||
<property name="frameShape" >
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Box</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>'wireshark' Path</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<property name="buddy">
|
||||
<cstring>wiresharkPathEdit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" >
|
||||
<widget class="QLineEdit" name="wiresharkPathEdit" >
|
||||
<property name="enabled" >
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="wiresharkPathEdit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2" >
|
||||
<widget class="QToolButton" name="wiresharkPathButton" >
|
||||
<property name="text" >
|
||||
<item row="0" column="2">
|
||||
<widget class="QToolButton" name="wiresharkPathButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QLabel" name="label_2" >
|
||||
<property name="text" >
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>'tshark' Path</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<property name="buddy">
|
||||
<cstring>tsharkPathEdit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<widget class="QLineEdit" name="tsharkPathEdit" >
|
||||
<property name="enabled" >
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="tsharkPathEdit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2" >
|
||||
<widget class="QToolButton" name="tsharkPathButton" >
|
||||
<property name="text" >
|
||||
<item row="1" column="2">
|
||||
<widget class="QToolButton" name="tsharkPathButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QLabel" name="label_5" >
|
||||
<property name="text" >
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>'gzip' Path</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<property name="buddy">
|
||||
<cstring>diffPathEdit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" >
|
||||
<widget class="QLineEdit" name="gzipPathEdit" >
|
||||
<property name="enabled" >
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="gzipPathEdit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2" >
|
||||
<widget class="QToolButton" name="gzipPathButton" >
|
||||
<property name="text" >
|
||||
<item row="2" column="2">
|
||||
<widget class="QToolButton" name="gzipPathButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" >
|
||||
<widget class="QLabel" name="label_3" >
|
||||
<property name="text" >
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>'diff' Path</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<property name="buddy">
|
||||
<cstring>diffPathEdit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" >
|
||||
<widget class="QLineEdit" name="diffPathEdit" >
|
||||
<property name="enabled" >
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="diffPathEdit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2" >
|
||||
<widget class="QToolButton" name="diffPathButton" >
|
||||
<property name="text" >
|
||||
<item row="3" column="2">
|
||||
<widget class="QToolButton" name="diffPathButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" >
|
||||
<widget class="QLabel" name="label_4" >
|
||||
<property name="text" >
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>'awk' Path</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<property name="buddy">
|
||||
<cstring>awkPathEdit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1" >
|
||||
<widget class="QLineEdit" name="awkPathEdit" >
|
||||
<property name="enabled" >
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="awkPathEdit">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2" >
|
||||
<widget class="QToolButton" name="awkPathButton" >
|
||||
<property name="text" >
|
||||
<item row="4" column="2">
|
||||
<widget class="QToolButton" name="awkPathButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" >
|
||||
<item row="5" column="1">
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>21</width>
|
||||
<height>61</height>
|
||||
@ -162,12 +164,26 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||
<property name="orientation" >
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Theme (Experimental)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="theme"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons" >
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -187,7 +203,7 @@
|
||||
<tabstop>buttonBox</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="ostinato.qrc" />
|
||||
<include location="ostinato.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
@ -196,11 +212,11 @@
|
||||
<receiver>Preferences</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
@ -212,11 +228,11 @@
|
||||
<receiver>Preferences</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
|
50
client/rowborderdelegate.h
Normal file
50
client/rowborderdelegate.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright (C) 2023 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef _ROW_BORDER_DELEGATE
|
||||
#define _ROW_BORDER_DELEGATE
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
#include <QSet>
|
||||
|
||||
class RowBorderDelegate : public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
RowBorderDelegate(QSet<int> rows, QObject *parent = nullptr)
|
||||
: QStyledItemDelegate(parent), rows_(rows)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
QStyledItemDelegate::paint(painter, option, index);
|
||||
if (rows_.contains(index.row())) {
|
||||
const QRect rect(option.rect);
|
||||
painter->drawLine(rect.topLeft(), rect.topRight());
|
||||
}
|
||||
}
|
||||
|
||||
QSet<int> rows_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -74,6 +74,8 @@ const QString kAwkPathDefaultValue("/usr/bin/awk");
|
||||
const QString kAwkPathDefaultValue("/usr/bin/awk");
|
||||
#endif
|
||||
|
||||
const QString kThemeKey("Theme");
|
||||
|
||||
const QString kUserKey("User");
|
||||
extern QString kUserDefaultValue;
|
||||
|
||||
|
@ -675,6 +675,19 @@ QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-colo
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2">
|
||||
|
@ -361,15 +361,13 @@ void StreamModel::setCurrentPortIndex(const QModelIndex ¤t)
|
||||
else
|
||||
{
|
||||
qDebug("change to valid port");
|
||||
// Disconnect any existing connection to avoid duplication
|
||||
// Qt 4.6 has Qt::UniqueConnection, but we want to remain compatible
|
||||
// with earlier Qt versions
|
||||
if (mCurrentPort)
|
||||
{
|
||||
disconnect(mCurrentPort, SIGNAL(streamListChanged(int, int)),
|
||||
this, SLOT(when_mCurrentPort_streamListChanged(int, int)));
|
||||
}
|
||||
quint16 pg = current.internalId() >> 16;
|
||||
// TODO: make mCurrentPort a smart weak pointer
|
||||
mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()];
|
||||
connect(mCurrentPort, SIGNAL(streamListChanged(int, int)),
|
||||
this, SLOT(when_mCurrentPort_streamListChanged(int, int)));
|
||||
|
@ -20,8 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "streamstatsmodel.h"
|
||||
|
||||
#include "protocol.pb.h"
|
||||
#include "xqlocale.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBrush>
|
||||
#include <QFont>
|
||||
#include <QPalette>
|
||||
|
||||
// XXX: Keep the enum in sync with it's string
|
||||
enum {
|
||||
@ -42,12 +46,26 @@ enum {
|
||||
kAggrTxPkts,
|
||||
kAggrRxPkts,
|
||||
kAggrPktLoss,
|
||||
kTxDuration,
|
||||
kAvgTxFrameRate,
|
||||
kAvgRxFrameRate,
|
||||
kAvgTxBitRate,
|
||||
kAvgRxBitRate,
|
||||
kAvgLatency,
|
||||
kAvgJitter,
|
||||
kMaxAggrStreamStats
|
||||
};
|
||||
static QStringList aggrStatTitles = QStringList()
|
||||
<< "Total\nTx Pkts"
|
||||
<< "Total\nRx Pkts"
|
||||
<< "Total\nPkt Loss";
|
||||
<< "Total\nPkt Loss"
|
||||
<< "Duration\n(secs)"
|
||||
<< "Avg\nTx PktRate"
|
||||
<< "Avg\nRx PktRate"
|
||||
<< "Avg\nTx BitRate"
|
||||
<< "Avg\nRx BitRate"
|
||||
<< "Avg\nLatency"
|
||||
<< "Avg\nJitter";
|
||||
|
||||
static const uint kAggrGuid = 0xffffffff;
|
||||
|
||||
@ -103,20 +121,35 @@ QVariant StreamStatsModel::data(const QModelIndex &index, int role) const
|
||||
return Qt::AlignRight;
|
||||
|
||||
int portColumn = index.column() - kMaxAggrStreamStats;
|
||||
if (role == Qt::BackgroundRole) {
|
||||
if (portColumn < 0)
|
||||
return QBrush(QColor("lavender")); // Aggregate Column
|
||||
if (index.row() == (guidList_.size() - 1))
|
||||
return QBrush(QColor("burlywood")); // Aggregate Row
|
||||
else if ((portColumn/kMaxStreamStats) & 1)
|
||||
return QBrush(QColor("beige")); // Color alternate Ports
|
||||
|
||||
// Stylesheets typically don't use or set palette colors, so if
|
||||
// using one, don't use palette colors
|
||||
if ((role == Qt::BackgroundRole) && qApp->styleSheet().isEmpty()) {
|
||||
QPalette palette = QApplication::palette();
|
||||
if (index.row() == (guidList_.size() - 1)) // Aggregate Row
|
||||
return palette.dark();
|
||||
if (portColumn < 0) // Aggregate Column
|
||||
return palette.alternateBase();
|
||||
if ((portColumn/kMaxStreamStats) & 1) // Color alternate Ports
|
||||
return palette.alternateBase();
|
||||
}
|
||||
|
||||
Guid guid = guidList_.at(index.row());
|
||||
if (role == Qt::ForegroundRole) {
|
||||
if ((role == Qt::ForegroundRole && qApp->styleSheet().isEmpty())) {
|
||||
QPalette palette = QApplication::palette();
|
||||
if ((index.column() == kAggrPktLoss)
|
||||
&& aggrGuidStats_.value(guid).pktLoss)
|
||||
return QBrush(QColor("firebrick"));
|
||||
return palette.link();
|
||||
if (index.row() == (guidList_.size() - 1)) // Aggregate Row
|
||||
return palette.brightText();
|
||||
}
|
||||
|
||||
if (role == Qt::FontRole ) {
|
||||
if (index.row() == (guidList_.size() - 1)) { // Aggregate Row
|
||||
QFont font;
|
||||
font.setBold(true);
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
||||
if (role != Qt::DisplayRole)
|
||||
@ -131,6 +164,42 @@ QVariant StreamStatsModel::data(const QModelIndex &index, int role) const
|
||||
return QString("%L1").arg(aggrGuidStats_.value(guid).txPkts);
|
||||
case kAggrPktLoss:
|
||||
return QString("%L1").arg(aggrGuidStats_.value(guid).pktLoss);
|
||||
case kTxDuration:
|
||||
return QString("%L1").arg(aggrGuidStats_.value(guid).txDuration);
|
||||
case kAvgTxFrameRate:
|
||||
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
|
||||
XLocale().toPktRateString(
|
||||
aggrGuidStats_.value(guid).txPkts
|
||||
/ aggrGuidStats_.value(guid).txDuration);
|
||||
case kAvgRxFrameRate:
|
||||
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
|
||||
XLocale().toPktRateString(
|
||||
aggrGuidStats_.value(guid).rxPkts
|
||||
/ aggrGuidStats_.value(guid).txDuration);
|
||||
case kAvgTxBitRate:
|
||||
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
|
||||
XLocale().toBitRateString(
|
||||
(aggrGuidStats_.value(guid).txBytes
|
||||
+ 24 * aggrGuidStats_.value(guid).txPkts) * 8
|
||||
/ aggrGuidStats_.value(guid).txDuration);
|
||||
case kAvgRxBitRate:
|
||||
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
|
||||
XLocale().toBitRateString(
|
||||
(aggrGuidStats_.value(guid).rxBytes
|
||||
+ 24 * aggrGuidStats_.value(guid).rxPkts) * 8
|
||||
/ aggrGuidStats_.value(guid).txDuration);
|
||||
case kAvgLatency:
|
||||
return aggrGuidStats_.value(guid).latencyCount <= 0
|
||||
|| aggrGuidStats_.value(guid).latencySum <= 0 ? QString("-") :
|
||||
XLocale().toTimeIntervalString(
|
||||
aggrGuidStats_.value(guid).latencySum
|
||||
/ aggrGuidStats_.value(guid).latencyCount);
|
||||
case kAvgJitter:
|
||||
return aggrGuidStats_.value(guid).latencyCount <= 0
|
||||
|| aggrGuidStats_.value(guid).latencySum <= 0 ? QString("-") :
|
||||
XLocale().toTimeIntervalString(
|
||||
aggrGuidStats_.value(guid).jitterSum
|
||||
/ aggrGuidStats_.value(guid).latencyCount);
|
||||
default:
|
||||
break;
|
||||
};
|
||||
@ -205,6 +274,8 @@ void StreamStatsModel::appendStreamStatsList(
|
||||
ss.txPkts = s.tx_pkts();
|
||||
ss.rxBytes = s.rx_bytes();
|
||||
ss.txBytes = s.tx_bytes();
|
||||
ss.rxLatency = s.latency();
|
||||
ss.rxJitter = s.jitter();
|
||||
|
||||
aggrPort.rxPkts += ss.rxPkts;
|
||||
aggrPort.txPkts += ss.txPkts;
|
||||
@ -214,10 +285,28 @@ void StreamStatsModel::appendStreamStatsList(
|
||||
aggrGuid.rxPkts += ss.rxPkts;
|
||||
aggrGuid.txPkts += ss.txPkts;
|
||||
aggrGuid.pktLoss += ss.txPkts - ss.rxPkts;
|
||||
aggrGuid.rxBytes += ss.rxBytes;
|
||||
aggrGuid.txBytes += ss.txBytes;
|
||||
if (s.tx_duration() > aggrGuid.txDuration)
|
||||
aggrGuid.txDuration = s.tx_duration(); // XXX: use largest or avg?
|
||||
if (ss.rxLatency) {
|
||||
aggrGuid.latencySum += ss.rxLatency;
|
||||
aggrGuid.jitterSum += ss.rxJitter;
|
||||
aggrGuid.latencyCount++;
|
||||
}
|
||||
|
||||
aggrAggr.rxPkts += ss.rxPkts;
|
||||
aggrAggr.txPkts += ss.txPkts;
|
||||
aggrAggr.pktLoss += ss.txPkts - ss.rxPkts;
|
||||
aggrAggr.rxBytes += ss.rxBytes;
|
||||
aggrAggr.txBytes += ss.txBytes;
|
||||
if (aggrGuid.txDuration > aggrAggr.txDuration)
|
||||
aggrAggr.txDuration = aggrGuid.txDuration;
|
||||
if (ss.rxLatency) {
|
||||
aggrAggr.latencySum += ss.rxLatency;
|
||||
aggrAggr.jitterSum += ss.rxJitter;
|
||||
aggrAggr.latencyCount++;
|
||||
}
|
||||
|
||||
if (!portList_.contains(pgp))
|
||||
portList_.append(pgp);
|
||||
@ -228,6 +317,8 @@ void StreamStatsModel::appendStreamStatsList(
|
||||
if (guidList_.size() && !guidList_.contains(kAggrGuid))
|
||||
guidList_.append(kAggrGuid);
|
||||
|
||||
std::sort(guidList_.begin(), guidList_.end());
|
||||
|
||||
#if QT_VERSION >= 0x040600
|
||||
endResetModel();
|
||||
#else
|
||||
|
@ -57,11 +57,19 @@ private:
|
||||
quint64 txPkts;
|
||||
quint64 rxBytes;
|
||||
quint64 txBytes;
|
||||
quint64 rxLatency;
|
||||
quint64 rxJitter;
|
||||
};
|
||||
struct AggrGuidStats {
|
||||
quint64 rxPkts;
|
||||
quint64 txPkts;
|
||||
quint64 rxBytes;
|
||||
quint64 txBytes;
|
||||
qint64 pktLoss;
|
||||
double txDuration;
|
||||
quint64 latencySum;
|
||||
quint64 jitterSum;
|
||||
uint latencyCount;
|
||||
};
|
||||
QList<Guid> guidList_;
|
||||
QList<PortGroupPort> portList_;
|
||||
|
@ -31,7 +31,7 @@ StreamStatsWindow::StreamStatsWindow(QAbstractItemModel *model, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
streamStats->addAction(actionShowByteCounters);
|
||||
streamStats->addAction(actionShowDetails);
|
||||
|
||||
if (id)
|
||||
setWindowTitle(windowTitle() + QString("(%1)").arg(id));
|
||||
@ -39,13 +39,17 @@ StreamStatsWindow::StreamStatsWindow(QAbstractItemModel *model, QWidget *parent)
|
||||
count++;
|
||||
|
||||
filterModel_ = new StreamStatsFilterModel(this);
|
||||
filterModel_->setFilterRegExp(QRegExp(".*Pkt.*"));
|
||||
filterModel_->setFilterRegExp(QRegExp(kDefaultFilter_));
|
||||
filterModel_->setSourceModel(model);
|
||||
streamStats->setModel(filterModel_);
|
||||
|
||||
streamStats->verticalHeader()->setHighlightSections(false);
|
||||
streamStats->verticalHeader()->setDefaultSectionSize(
|
||||
streamStats->verticalHeader()->minimumSectionSize());
|
||||
|
||||
// Fit all columns in window whenever data changes
|
||||
connect(model, &QAbstractItemModel::modelReset,
|
||||
[=]() { streamStats->resizeColumnsToContents(); });
|
||||
}
|
||||
|
||||
StreamStatsWindow::~StreamStatsWindow()
|
||||
@ -56,10 +60,12 @@ StreamStatsWindow::~StreamStatsWindow()
|
||||
id = 0;
|
||||
}
|
||||
|
||||
void StreamStatsWindow::on_actionShowByteCounters_triggered(bool checked)
|
||||
void StreamStatsWindow::on_actionShowDetails_triggered(bool checked)
|
||||
{
|
||||
if (checked)
|
||||
filterModel_->setFilterRegExp(QRegExp(".*"));
|
||||
else
|
||||
filterModel_->setFilterRegExp(QRegExp(".*Pkt.*"));
|
||||
filterModel_->setFilterRegExp(QRegExp(kDefaultFilter_));
|
||||
|
||||
streamStats->resizeColumnsToContents();
|
||||
}
|
||||
|
@ -33,9 +33,10 @@ public:
|
||||
~StreamStatsWindow();
|
||||
|
||||
private slots:
|
||||
void on_actionShowByteCounters_triggered(bool checked);
|
||||
void on_actionShowDetails_triggered(bool checked);
|
||||
|
||||
private:
|
||||
QString kDefaultFilter_{"^(?!Port).*"};
|
||||
QSortFilterProxyModel *filterModel_;
|
||||
};
|
||||
|
||||
|
@ -19,6 +19,9 @@
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ActionsContextMenu</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Oops! We don't seem to have any stream statistics for the requested port(s)
|
||||
|
||||
@ -27,12 +30,12 @@ Wait a little bit to see if they appear, otherwise verify your stream stats conf
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<action name="actionShowByteCounters">
|
||||
<action name="actionShowDetails">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show Byte Counters</string>
|
||||
<string>Show Details</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
|
@ -126,6 +126,8 @@ void StreamsWidget::on_tvStreamList_activated(const QModelIndex & index)
|
||||
|
||||
qDebug("stream list activated\n");
|
||||
|
||||
Q_ASSERT(plm->isPort(currentPortIndex_));
|
||||
|
||||
Port &curPort = plm->port(currentPortIndex_);
|
||||
|
||||
QList<Stream*> streams;
|
||||
@ -143,11 +145,10 @@ void StreamsWidget::setCurrentPortIndex(const QModelIndex &portIndex)
|
||||
if (!plm)
|
||||
return;
|
||||
|
||||
// XXX: We assume portIndex corresponds to sourceModel, not proxyModel
|
||||
if (!plm->isPort(portIndex))
|
||||
return;
|
||||
// XXX: We assume portIndex corresponds to sourceModel, not proxyModel;
|
||||
// caller should ensure this
|
||||
|
||||
qDebug("In %s", __FUNCTION__);
|
||||
qDebug("In %s", __PRETTY_FUNCTION__);
|
||||
|
||||
currentPortIndex_ = portIndex;
|
||||
plm->getStreamModel()->setCurrentPortIndex(portIndex);
|
||||
@ -223,6 +224,8 @@ void StreamsWidget::on_actionNew_Stream_triggered()
|
||||
count = selectionModel->selection().at(0).height();
|
||||
}
|
||||
|
||||
Q_ASSERT(plm->isPort(currentPortIndex_));
|
||||
|
||||
Port &curPort = plm->port(currentPortIndex_);
|
||||
|
||||
QList<Stream*> streams;
|
||||
@ -243,6 +246,8 @@ void StreamsWidget::on_actionEdit_Stream_triggered()
|
||||
if (!streamModel->hasSelection())
|
||||
return;
|
||||
|
||||
Q_ASSERT(plm->isPort(currentPortIndex_));
|
||||
|
||||
Port &curPort = plm->port(currentPortIndex_);
|
||||
|
||||
QList<Stream*> streams;
|
||||
@ -262,6 +267,8 @@ void StreamsWidget::on_actionDuplicate_Stream_triggered()
|
||||
|
||||
qDebug("Duplicate Stream Action");
|
||||
|
||||
Q_ASSERT(plm->isPort(currentPortIndex_));
|
||||
|
||||
if (model->hasSelection())
|
||||
{
|
||||
bool isOk;
|
||||
@ -304,6 +311,8 @@ void StreamsWidget::on_actionFind_Replace_triggered()
|
||||
{
|
||||
qDebug("Find & Replace Action");
|
||||
|
||||
Q_ASSERT(plm->isPort(currentPortIndex_));
|
||||
|
||||
QItemSelectionModel* selectionModel = tvStreamList->selectionModel();
|
||||
FindReplaceDialog::Action action;
|
||||
|
||||
|
130
client/thememanager.cpp
Normal file
130
client/thememanager.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
Copyright (C) 2021 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "thememanager.h"
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QRegularExpression>
|
||||
#include <QResource>
|
||||
|
||||
ThemeManager *ThemeManager::instance_{nullptr};
|
||||
|
||||
ThemeManager::ThemeManager()
|
||||
{
|
||||
themeDir_ = QCoreApplication::applicationDirPath() + "/themes/";
|
||||
#if defined(Q_OS_MAC)
|
||||
/*
|
||||
* Executable and Theme directory location inside app bundle -
|
||||
* Ostinato.app/Contents/MacOS/
|
||||
* Ostinato.app/Contents/SharedSupport/themes/
|
||||
*/
|
||||
themeDir_.replace("/MacOS/", "/SharedSupport/");
|
||||
#elif defined(Q_OS_UNIX)
|
||||
/*
|
||||
* Possible (but not comprehensive) locations for Ostinato executable
|
||||
* and theme directory locations
|
||||
*
|
||||
* non-install-dir/
|
||||
* non-install-dir/themes/
|
||||
*
|
||||
* /usr/[local]/bin/
|
||||
* /usr/[local]/share/ostinato-controller/themes/
|
||||
*
|
||||
* /opt/ostinato/bin/
|
||||
* /opt/ostinato/share/themes/
|
||||
*/
|
||||
if (themeDir_.contains(QRegularExpression("^/usr/.*/bin/")))
|
||||
themeDir_.replace("/bin/", "/share/ostinato-controller/");
|
||||
else if (themeDir_.contains(QRegularExpression("^/opt/.*/bin/")))
|
||||
themeDir_.replace("/bin/", "/share/");
|
||||
#endif
|
||||
qDebug("Themes directory: %s", qPrintable(themeDir_));
|
||||
}
|
||||
|
||||
QStringList ThemeManager::themeList()
|
||||
{
|
||||
QDir themeDir(themeDir_);
|
||||
|
||||
themeDir.setFilter(QDir::Files);
|
||||
themeDir.setNameFilters(QStringList() << "*.qss");
|
||||
themeDir.setSorting(QDir::Name);
|
||||
|
||||
QStringList themes = themeDir.entryList();
|
||||
for (QString& theme : themes)
|
||||
theme.remove(".qss");
|
||||
themes.prepend("default");
|
||||
|
||||
return themes;
|
||||
}
|
||||
|
||||
void ThemeManager::setTheme(QString theme)
|
||||
{
|
||||
// Remove current theme, if we have one
|
||||
QString oldTheme = appSettings->value(kThemeKey).toString();
|
||||
if (!oldTheme.isEmpty()) {
|
||||
// Remove stylesheet first so that there are
|
||||
// no references to resources when unregistering 'em
|
||||
qApp->setStyleSheet("");
|
||||
QString rccFile = themeDir_ + oldTheme + ".rcc";
|
||||
if (QResource::unregisterResource(rccFile)) {
|
||||
qDebug("Unable to unregister theme rccFile %s",
|
||||
qPrintable(rccFile));
|
||||
}
|
||||
appSettings->setValue(kThemeKey, QVariant());
|
||||
}
|
||||
|
||||
if (theme.isEmpty() || (theme == "default"))
|
||||
return;
|
||||
|
||||
// Apply new theme
|
||||
QFile qssFile(themeDir_ + theme + ".qss");
|
||||
if (!qssFile.open(QFile::ReadOnly)) {
|
||||
qDebug("Unable to open theme qssFile %s",
|
||||
qPrintable(qssFile.fileName()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Register theme resource before applying theme style sheet
|
||||
QString rccFile = themeDir_ + theme + ".rcc";
|
||||
if (!QResource::registerResource(rccFile))
|
||||
qDebug("Unable to register theme rccFile %s", qPrintable(rccFile));
|
||||
|
||||
#if 0 // FIXME: debug only
|
||||
QDirIterator it(":", QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
qDebug() << it.next();
|
||||
}
|
||||
#endif
|
||||
|
||||
QString styleSheet { qssFile.readAll() };
|
||||
qApp->setStyleSheet(styleSheet);
|
||||
appSettings->setValue(kThemeKey, theme);
|
||||
}
|
||||
|
||||
ThemeManager* ThemeManager::instance()
|
||||
{
|
||||
if (!instance_)
|
||||
instance_ = new ThemeManager();
|
||||
return instance_;
|
||||
}
|
42
client/thememanager.h
Normal file
42
client/thememanager.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright (C) 2021 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef _THEME_MANAGER_H
|
||||
#define _THEME_MANAGER_H
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
class ThemeManager
|
||||
{
|
||||
public:
|
||||
ThemeManager();
|
||||
|
||||
QStringList themeList();
|
||||
void setTheme(QString theme);
|
||||
|
||||
static ThemeManager* instance();
|
||||
|
||||
private:
|
||||
QString themeDir_;
|
||||
|
||||
static ThemeManager *instance_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
1307
client/themes/material-dark.qss
Normal file
1307
client/themes/material-dark.qss
Normal file
File diff suppressed because it is too large
Load Diff
BIN
client/themes/material-dark.rcc
Normal file
BIN
client/themes/material-dark.rcc
Normal file
Binary file not shown.
1307
client/themes/material-light.qss
Normal file
1307
client/themes/material-light.qss
Normal file
File diff suppressed because it is too large
Load Diff
BIN
client/themes/material-light.rcc
Normal file
BIN
client/themes/material-light.rcc
Normal file
Binary file not shown.
2213
client/themes/qds-dark.qss
Normal file
2213
client/themes/qds-dark.qss
Normal file
File diff suppressed because it is too large
Load Diff
BIN
client/themes/qds-dark.rcc
Normal file
BIN
client/themes/qds-dark.rcc
Normal file
Binary file not shown.
2213
client/themes/qds-light.qss
Normal file
2213
client/themes/qds-light.qss
Normal file
File diff suppressed because it is too large
Load Diff
BIN
client/themes/qds-light.rcc
Normal file
BIN
client/themes/qds-light.rcc
Normal file
Binary file not shown.
@ -376,6 +376,9 @@ void VariableFieldsWidget::loadProtocolFields(
|
||||
int byteOfs = bitOfs >> 3;
|
||||
uint bitSize = protocol->fieldData(i, AbstractProtocol::FieldBitSize)
|
||||
.toInt();
|
||||
if (bitSize == 0)
|
||||
continue;
|
||||
|
||||
vm["offset"] = byteOfs;
|
||||
if (bitSize <= 8) {
|
||||
vm["type"] = int(OstProto::VariableField::kCounter8);
|
||||
|
@ -85,20 +85,49 @@ public:
|
||||
return toDouble(text, ok) * multiplier;
|
||||
}
|
||||
|
||||
QString toPktRateString(double pps) const
|
||||
{
|
||||
QString text;
|
||||
|
||||
if (pps >= 1e6)
|
||||
return QObject::tr("%L1 Mpps").arg(pps/1e6, 0, 'f', 3);
|
||||
|
||||
if (pps >= 1e3)
|
||||
return QObject::tr("%L1 Kpps").arg(pps/1e3, 0, 'f', 3);
|
||||
|
||||
return QObject::tr("%L1").arg(pps, 0, 'f', 3);
|
||||
}
|
||||
|
||||
QString toBitRateString(double bps) const
|
||||
{
|
||||
QString text;
|
||||
|
||||
if (bps >= 1e9)
|
||||
return QObject::tr("%L1 Gbps").arg(bps/1e9, 0, 'f', 4);
|
||||
return QObject::tr("%L1 Gbps").arg(bps/1e9, 0, 'f', 3);
|
||||
|
||||
if (bps >= 1e6)
|
||||
return QObject::tr("%L1 Mbps").arg(bps/1e6, 0, 'f', 4);
|
||||
return QObject::tr("%L1 Mbps").arg(bps/1e6, 0, 'f', 3);
|
||||
|
||||
if (bps >= 1e3)
|
||||
return QObject::tr("%L1 Kbps").arg(bps/1e3, 0, 'f', 4);
|
||||
return QObject::tr("%L1 Kbps").arg(bps/1e3, 0, 'f', 3);
|
||||
|
||||
return QObject::tr("%L1 bps").arg(bps, 0, 'f', 4);
|
||||
return QObject::tr("%L1 bps").arg(bps, 0, 'f', 3);
|
||||
}
|
||||
|
||||
QString toTimeIntervalString(qint64 nanosecs) const
|
||||
{
|
||||
QString text;
|
||||
|
||||
if (nanosecs >= 1e9)
|
||||
return QObject::tr("%L1 s").arg(nanosecs/1e9, 0, 'f', 2);
|
||||
|
||||
if (nanosecs >= 1e6)
|
||||
return QObject::tr("%L1 ms").arg(nanosecs/1e6, 0, 'f', 2);
|
||||
|
||||
if (nanosecs >= 1e3)
|
||||
return QObject::tr("%L1 us").arg(nanosecs/1e3, 0, 'f', 2);
|
||||
|
||||
return QObject::tr("%L1 ns").arg(nanosecs);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -169,7 +169,8 @@ private:
|
||||
for (int i = start; i < end; i++)
|
||||
if (indexes.contains(model()->index(indexes.first().row(), i)))
|
||||
text.append(model()->headerData(i, Qt::Horizontal)
|
||||
.toString()+"\t");;
|
||||
.toString().replace('\n', ' ')
|
||||
+"\t");;
|
||||
text.append("\n");
|
||||
}
|
||||
|
||||
|
@ -62,12 +62,14 @@ void ArpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol)
|
||||
|
||||
QString ArpProtocol::name() const
|
||||
{
|
||||
return QString("Address Resolution Protocol");
|
||||
return isRarp() ?
|
||||
QString("Reverse Address Resolution Protocol") :
|
||||
QString("Address Resolution Protocol");
|
||||
}
|
||||
|
||||
QString ArpProtocol::shortName() const
|
||||
{
|
||||
return QString("ARP");
|
||||
return isRarp() ? QString("RARP") : QString("ARP");
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -96,7 +98,7 @@ quint32 ArpProtocol::protocolId(ProtocolIdType type) const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ProtocolIdEth: return 0x0806;
|
||||
case ProtocolIdEth: return isRarp() ? 0x8035 : 0x0806;
|
||||
default:break;
|
||||
}
|
||||
|
||||
@ -808,3 +810,11 @@ int ArpProtocol::protocolFrameVariableCount() const
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool ArpProtocol::isRarp() const
|
||||
{
|
||||
if ((data.op_code() == 3)
|
||||
|| (data.op_code() ==4))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -96,6 +96,8 @@ public:
|
||||
virtual int protocolFrameVariableCount() const;
|
||||
|
||||
private:
|
||||
bool isRarp() const;
|
||||
|
||||
OstProto::Arp data;
|
||||
};
|
||||
|
||||
|
@ -30,6 +30,8 @@ ArpConfigForm::ArpConfigForm(QWidget *parent)
|
||||
opCodeCombo->setValidator(new QIntValidator(0, 0xFFFF, this));
|
||||
opCodeCombo->addItem(1, "ARP Request");
|
||||
opCodeCombo->addItem(2, "ARP Reply");
|
||||
opCodeCombo->addItem(3, "Reverse ARP Request");
|
||||
opCodeCombo->addItem(4, "Reverse ARP Reply");
|
||||
|
||||
connect(senderHwAddrMode, SIGNAL(currentIndexChanged(int)),
|
||||
SLOT(on_senderHwAddrMode_currentIndexChanged(int)));
|
||||
|
@ -25,10 +25,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
template <int protoNumber, class ProtoA, class ProtoB>
|
||||
class ComboProtocol : public AbstractProtocol
|
||||
{
|
||||
protected:
|
||||
ProtoA *protoA;
|
||||
ProtoB *protoB;
|
||||
|
||||
public:
|
||||
ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0)
|
||||
: AbstractProtocol(stream, parent)
|
||||
@ -40,6 +36,12 @@ public:
|
||||
|
||||
qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__,
|
||||
protoNumber, protoA, protoB);
|
||||
|
||||
if (protoA->protocolNumber() == protoB->protocolNumber())
|
||||
fieldPrefix = OuterInnerPrefix;
|
||||
else if (similarProto())
|
||||
fieldPrefix = ProtoNamePrefix;
|
||||
|
||||
}
|
||||
|
||||
virtual ~ComboProtocol()
|
||||
@ -125,11 +127,29 @@ public:
|
||||
int streamIndex = 0) const
|
||||
{
|
||||
int cnt = protoA->fieldCount();
|
||||
QVariant value = index < cnt ?
|
||||
protoA->fieldData(index, attrib, streamIndex) :
|
||||
protoB->fieldData(index - cnt, attrib, streamIndex);
|
||||
|
||||
if (index < cnt)
|
||||
return protoA->fieldData(index, attrib, streamIndex);
|
||||
else
|
||||
return protoB->fieldData(index - cnt, attrib, streamIndex);
|
||||
if (attrib == FieldName) {
|
||||
switch (fieldPrefix) {
|
||||
case OuterInnerPrefix:
|
||||
value = QString("%1 %2")
|
||||
.arg(index < cnt ? QString("Outer") : QString("Inner"))
|
||||
.arg(value.toString());
|
||||
break;
|
||||
case ProtoNamePrefix:
|
||||
value = QString("%1 %2")
|
||||
.arg(index < cnt ? protoA->shortName() : protoB->shortName())
|
||||
.arg(value.toString());
|
||||
break;
|
||||
case NoPrefix:
|
||||
// Fall-through
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
virtual bool setFieldData(int index, const QVariant &value,
|
||||
FieldAttrib attrib = FieldValue)
|
||||
@ -186,6 +206,32 @@ public:
|
||||
CksumType cksumType = CksumIp) const;
|
||||
#endif
|
||||
template <int protocolNumber, class FormA, class FormB, class ProtocolA, class ProtocolB> friend class ComboProtocolConfigForm;
|
||||
|
||||
protected:
|
||||
ProtoA *protoA;
|
||||
ProtoB *protoB;
|
||||
|
||||
private:
|
||||
bool similarProto()
|
||||
{
|
||||
// TODO: Use Levenshtein distance or something similar with > 70% match
|
||||
// For now we use an ugly hack!
|
||||
if (protoA->shortName().contains("IPv")
|
||||
&& protoB->shortName().contains("IPv"))
|
||||
return true;
|
||||
if (protoA->shortName().contains("Vlan")
|
||||
&& protoB->shortName().contains("Vlan"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
enum FieldNamePrefix {
|
||||
NoPrefix,
|
||||
ProtoNamePrefix,
|
||||
OuterInnerPrefix
|
||||
};
|
||||
|
||||
FieldNamePrefix fieldPrefix{NoPrefix};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
29
common/debugdefs.h
Normal file
29
common/debugdefs.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright (C) 2023 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef _DEBUG_DEFS_H
|
||||
#define _DEBUG_DEFS_H
|
||||
|
||||
#if 0
|
||||
#define timingDebug(fmt, ...) qDebug("TIMING:" fmt, __VA_ARGS__)
|
||||
#else
|
||||
#define timingDebug(...)
|
||||
#endif
|
||||
|
||||
#endif
|
47
common/fileformatoptions.cpp
Normal file
47
common/fileformatoptions.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright (C) 2022 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "fileformatoptions.h"
|
||||
|
||||
#include "pcapfileformat.h"
|
||||
#include "pcapoptionsdialog.h"
|
||||
#include "streamfileformat.h"
|
||||
|
||||
QDialog* FileFormatOptions::openOptionsDialog(StreamFileFormat *fileFormat)
|
||||
{
|
||||
if (dynamic_cast<PcapFileFormat*>(fileFormat))
|
||||
return new PcapImportOptionsDialog(fileFormat->options());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QDialog* FileFormatOptions::saveOptionsDialog(StreamFileFormat* /*fileFormat*/)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QDialog* FileFormatOptions::openOptionsDialog(SessionFileFormat* /*fileFormat*/)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QDialog* FileFormatOptions::saveOptionsDialog(SessionFileFormat* /*fileFormat*/)
|
||||
{
|
||||
return NULL;
|
||||
}
|
41
common/fileformatoptions.h
Normal file
41
common/fileformatoptions.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright (C) 2022 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef _FILE_FORMAT_OPTIONS_H
|
||||
#define _FILE_FORMAT_OPTIONS_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class SessionFileFormat;
|
||||
class StreamFileFormat;
|
||||
class QDialog;
|
||||
|
||||
class FileFormatOptions : QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static QDialog* openOptionsDialog(StreamFileFormat *fileFormat);
|
||||
static QDialog* saveOptionsDialog(StreamFileFormat *fileFormat);
|
||||
|
||||
static QDialog* openOptionsDialog(SessionFileFormat *fileFormat);
|
||||
static QDialog* saveOptionsDialog(SessionFileFormat *fileFormat);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -27,6 +27,7 @@ struct FrameValueAttrib
|
||||
enum ErrorFlag {
|
||||
UnresolvedSrcMacError = 0x1,
|
||||
UnresolvedDstMacError = 0x2,
|
||||
OutOfMemoryError = 0x4,
|
||||
};
|
||||
Q_DECLARE_FLAGS(ErrorFlags, ErrorFlag);
|
||||
ErrorFlags errorFlags{0};
|
||||
|
471
common/gre.cpp
Normal file
471
common/gre.cpp
Normal file
@ -0,0 +1,471 @@
|
||||
/*
|
||||
Copyright (C) 2021 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "gre.h"
|
||||
|
||||
GreProtocol::GreProtocol(StreamBase *stream, AbstractProtocol *parent)
|
||||
: AbstractProtocol(stream, parent)
|
||||
{
|
||||
}
|
||||
|
||||
GreProtocol::~GreProtocol()
|
||||
{
|
||||
}
|
||||
|
||||
AbstractProtocol* GreProtocol::createInstance(StreamBase *stream,
|
||||
AbstractProtocol *parent)
|
||||
{
|
||||
return new GreProtocol(stream, parent);
|
||||
}
|
||||
|
||||
quint32 GreProtocol::protocolNumber() const
|
||||
{
|
||||
return OstProto::Protocol::kGreFieldNumber;
|
||||
}
|
||||
|
||||
void GreProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const
|
||||
{
|
||||
protocol.MutableExtension(OstProto::gre)->CopyFrom(data);
|
||||
protocol.mutable_protocol_id()->set_id(protocolNumber());
|
||||
}
|
||||
|
||||
void GreProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol)
|
||||
{
|
||||
if (protocol.protocol_id().id() == protocolNumber() &&
|
||||
protocol.HasExtension(OstProto::gre))
|
||||
data.MergeFrom(protocol.GetExtension(OstProto::gre));
|
||||
}
|
||||
|
||||
QString GreProtocol::name() const
|
||||
{
|
||||
return QString("General Routing Encapsulation Protocol");
|
||||
}
|
||||
|
||||
QString GreProtocol::shortName() const
|
||||
{
|
||||
return QString("GRE");
|
||||
}
|
||||
|
||||
AbstractProtocol::ProtocolIdType GreProtocol::protocolIdType() const
|
||||
{
|
||||
return ProtocolIdEth;
|
||||
}
|
||||
|
||||
quint32 GreProtocol::protocolId(ProtocolIdType type) const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ProtocolIdIp: return 47;
|
||||
default:break;
|
||||
}
|
||||
|
||||
return AbstractProtocol::protocolId(type);
|
||||
}
|
||||
|
||||
int GreProtocol::fieldCount() const
|
||||
{
|
||||
return gre_fieldCount;
|
||||
}
|
||||
|
||||
AbstractProtocol::FieldFlags GreProtocol::fieldFlags(int index) const
|
||||
{
|
||||
AbstractProtocol::FieldFlags flags;
|
||||
|
||||
flags = AbstractProtocol::fieldFlags(index);
|
||||
|
||||
switch (index) {
|
||||
case gre_checksum:
|
||||
flags |= CksumField;
|
||||
break;
|
||||
|
||||
case gre_isOverrideChecksum:
|
||||
flags &= ~FrameField;
|
||||
flags |= MetaField;
|
||||
break;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
QVariant GreProtocol::fieldData(int index, FieldAttrib attrib,
|
||||
int streamIndex) const
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case gre_flags:
|
||||
{
|
||||
switch(attrib)
|
||||
{
|
||||
case FieldName:
|
||||
return QString("Flags");
|
||||
case FieldValue:
|
||||
return data.flags();
|
||||
case FieldTextValue:
|
||||
{
|
||||
QString fstr;
|
||||
fstr.append("Cksum:");
|
||||
fstr.append(data.flags() & GRE_FLAG_CKSUM ? "Y" : "N");
|
||||
fstr.append(" Key:");
|
||||
fstr.append(data.flags() & GRE_FLAG_KEY ? "Y" : "N");
|
||||
fstr.append(" Seq:");
|
||||
fstr.append(data.flags() & GRE_FLAG_SEQ ? "Y" : "N");
|
||||
return fstr;
|
||||
}
|
||||
case FieldFrameValue:
|
||||
return QByteArray(1, char(data.flags()));
|
||||
case FieldBitSize:
|
||||
return 4;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case gre_rsvd0:
|
||||
{
|
||||
switch(attrib)
|
||||
{
|
||||
case FieldName:
|
||||
return QString("Reserved0");
|
||||
case FieldValue:
|
||||
return data.rsvd0();
|
||||
case FieldTextValue:
|
||||
return QString("%1").arg(data.rsvd0());
|
||||
case FieldFrameValue:
|
||||
{
|
||||
QByteArray fv;
|
||||
fv.resize(2);
|
||||
qToBigEndian(quint16(data.rsvd0()), (uchar*)fv.data());
|
||||
return fv;
|
||||
}
|
||||
case FieldBitSize:
|
||||
return 9;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case gre_version:
|
||||
{
|
||||
switch(attrib)
|
||||
{
|
||||
case FieldName:
|
||||
return QString("Version");
|
||||
case FieldValue:
|
||||
return data.version();
|
||||
case FieldFrameValue:
|
||||
return QByteArray(1, char(data.version()));
|
||||
case FieldTextValue:
|
||||
return QString("%1").arg(data.version());
|
||||
case FieldBitSize:
|
||||
return 3;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case gre_protocol:
|
||||
{
|
||||
quint16 protocol = payloadProtocolId(ProtocolIdEth);
|
||||
|
||||
switch(attrib)
|
||||
{
|
||||
case FieldName:
|
||||
return QString("Protocol");
|
||||
case FieldValue:
|
||||
return protocol;
|
||||
case FieldFrameValue:
|
||||
{
|
||||
QByteArray fv;
|
||||
fv.resize(2);
|
||||
qToBigEndian(protocol, (uchar*) fv.data());
|
||||
return fv;
|
||||
}
|
||||
case FieldTextValue:
|
||||
return QString("0x%1").arg(
|
||||
protocol, 4, BASE_HEX, QChar('0'));;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case gre_checksum:
|
||||
{
|
||||
if (attrib == FieldName)
|
||||
return QString("Checksum");
|
||||
|
||||
if ((data.flags() & GRE_FLAG_CKSUM) == 0)
|
||||
{
|
||||
if (attrib == FieldTextValue)
|
||||
return QObject::tr("<not-included>");
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (attrib == FieldBitSize)
|
||||
return 16;
|
||||
|
||||
quint16 cksum;
|
||||
if (data.is_override_checksum()) {
|
||||
cksum = data.checksum();
|
||||
} else {
|
||||
quint32 sum = 0;
|
||||
cksum = protocolFrameCksum(streamIndex, CksumIp);
|
||||
sum += (quint16) ~cksum;
|
||||
cksum = protocolFramePayloadCksum(streamIndex, CksumIp);
|
||||
sum += (quint16) ~cksum;
|
||||
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
|
||||
cksum = (~sum) & 0xFFFF;
|
||||
}
|
||||
|
||||
switch(attrib)
|
||||
{
|
||||
case FieldValue:
|
||||
return cksum;
|
||||
case FieldFrameValue:
|
||||
{
|
||||
QByteArray fv;
|
||||
fv.resize(2);
|
||||
qToBigEndian(cksum, (uchar*) fv.data());
|
||||
return fv;
|
||||
}
|
||||
case FieldTextValue:
|
||||
return QString("0x%1").arg(
|
||||
cksum, 4, BASE_HEX, QChar('0'));;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case gre_rsvd1:
|
||||
{
|
||||
if (attrib == FieldName)
|
||||
return QString("Reserved1");
|
||||
|
||||
if ((data.flags() & GRE_FLAG_CKSUM) == 0)
|
||||
{
|
||||
if (attrib == FieldTextValue)
|
||||
return QObject::tr("<not-included>");
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
switch(attrib)
|
||||
{
|
||||
case FieldValue:
|
||||
return data.rsvd1();
|
||||
case FieldTextValue:
|
||||
return QString("%1").arg(data.rsvd1());
|
||||
case FieldFrameValue:
|
||||
{
|
||||
QByteArray fv;
|
||||
fv.resize(2);
|
||||
qToBigEndian((quint16) data.rsvd1(), (uchar*) fv.data());
|
||||
return fv;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case gre_key:
|
||||
{
|
||||
if (attrib == FieldName)
|
||||
return QString("Key");
|
||||
|
||||
if ((data.flags() & GRE_FLAG_KEY) == 0)
|
||||
{
|
||||
if (attrib == FieldTextValue)
|
||||
return QObject::tr("<not-included>");
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
switch(attrib)
|
||||
{
|
||||
case FieldValue:
|
||||
return data.key();
|
||||
case FieldTextValue:
|
||||
return QString("0x%1").arg(data.key(), 8, BASE_HEX, QChar('0'));
|
||||
case FieldFrameValue:
|
||||
{
|
||||
QByteArray fv;
|
||||
fv.resize(4);
|
||||
qToBigEndian((quint32) data.key(), (uchar*) fv.data());
|
||||
return fv;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case gre_sequence:
|
||||
{
|
||||
if (attrib == FieldName)
|
||||
return QString("Sequence Number");
|
||||
|
||||
if ((data.flags() & GRE_FLAG_SEQ) == 0)
|
||||
{
|
||||
if (attrib == FieldTextValue)
|
||||
return QObject::tr("<not-included>");
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
switch(attrib)
|
||||
{
|
||||
case FieldValue:
|
||||
return data.sequence_num();
|
||||
case FieldTextValue:
|
||||
return QString("%1").arg(data.sequence_num());
|
||||
case FieldFrameValue:
|
||||
{
|
||||
QByteArray fv;
|
||||
fv.resize(4);
|
||||
qToBigEndian((quint32) data.sequence_num(), (uchar*) fv.data());
|
||||
return fv;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Meta fields
|
||||
case gre_isOverrideChecksum:
|
||||
{
|
||||
switch(attrib)
|
||||
{
|
||||
case FieldValue:
|
||||
return data.is_override_checksum();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__,
|
||||
index);
|
||||
break;
|
||||
}
|
||||
|
||||
return AbstractProtocol::fieldData(index, attrib, streamIndex);
|
||||
}
|
||||
|
||||
bool GreProtocol::setFieldData(int index, const QVariant &value,
|
||||
FieldAttrib attrib)
|
||||
{
|
||||
bool isOk = false;
|
||||
|
||||
if (attrib != FieldValue)
|
||||
goto _exit;
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case gre_flags:
|
||||
{
|
||||
uint flags = value.toUInt(&isOk);
|
||||
if (isOk)
|
||||
data.set_flags(flags);
|
||||
break;
|
||||
}
|
||||
case gre_rsvd0:
|
||||
{
|
||||
uint rsvd0 = value.toUInt(&isOk);
|
||||
if (isOk)
|
||||
data.set_rsvd0(rsvd0);
|
||||
break;
|
||||
}
|
||||
case gre_version:
|
||||
{
|
||||
uint ver = value.toUInt(&isOk);
|
||||
if (isOk)
|
||||
data.set_version(ver);
|
||||
break;
|
||||
}
|
||||
case gre_protocol:
|
||||
{
|
||||
uint proto = value.toUInt(&isOk);
|
||||
if (isOk)
|
||||
data.set_protocol_type(proto);
|
||||
break;
|
||||
}
|
||||
case gre_checksum:
|
||||
{
|
||||
uint csum = value.toUInt(&isOk);
|
||||
if (isOk)
|
||||
data.set_checksum(csum);
|
||||
break;
|
||||
}
|
||||
case gre_isOverrideChecksum:
|
||||
{
|
||||
data.set_is_override_checksum(value.toBool());
|
||||
break;
|
||||
}
|
||||
case gre_rsvd1:
|
||||
{
|
||||
uint rsvd1 = value.toUInt(&isOk);
|
||||
if (isOk)
|
||||
data.set_rsvd1(rsvd1);
|
||||
break;
|
||||
}
|
||||
case gre_key:
|
||||
{
|
||||
uint key = value.toUInt(&isOk);
|
||||
if (isOk)
|
||||
data.set_key(key);
|
||||
break;
|
||||
}
|
||||
case gre_sequence:
|
||||
{
|
||||
uint seq = value.toUInt(&isOk);
|
||||
if (isOk)
|
||||
data.set_sequence_num(seq);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__,
|
||||
index);
|
||||
break;
|
||||
}
|
||||
|
||||
_exit:
|
||||
return isOk;
|
||||
}
|
||||
|
||||
int GreProtocol::protocolFrameSize(int /*streamIndex*/) const
|
||||
{
|
||||
int size = 4; // mandatory fields - flags, rsvd0, version, protocol
|
||||
|
||||
if (data.flags() & GRE_FLAG_CKSUM)
|
||||
size += 4;
|
||||
if (data.flags() & GRE_FLAG_KEY)
|
||||
size += 4;
|
||||
if (data.flags() & GRE_FLAG_SEQ)
|
||||
size += 4;
|
||||
|
||||
return size;
|
||||
}
|
97
common/gre.h
Normal file
97
common/gre.h
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright (C) 2021 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef _GRE_H
|
||||
#define _GRE_H
|
||||
|
||||
#include "abstractprotocol.h"
|
||||
#include "gre.pb.h"
|
||||
|
||||
/*
|
||||
GRE Protocol Frame Format (RFC2890)-
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|C| |K|S| Reserved0 | Ver | Protocol Type |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Checksum (optional) | Reserved1 (Optional) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Key (optional) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Sequence Number (Optional) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
Figures in brackets represent field width in bits
|
||||
*/
|
||||
|
||||
#define GRE_FLAG_CKSUM 0x8
|
||||
#define GRE_FLAG_KEY 0x2
|
||||
#define GRE_FLAG_SEQ 0x1
|
||||
|
||||
class GreProtocol : public AbstractProtocol
|
||||
{
|
||||
public:
|
||||
enum grefield
|
||||
{
|
||||
// Frame Fields
|
||||
gre_flags = 0,
|
||||
gre_rsvd0,
|
||||
gre_version,
|
||||
gre_protocol,
|
||||
gre_checksum,
|
||||
gre_rsvd1,
|
||||
gre_key,
|
||||
gre_sequence,
|
||||
|
||||
// Meta Fields
|
||||
gre_isOverrideChecksum,
|
||||
|
||||
gre_fieldCount
|
||||
};
|
||||
|
||||
GreProtocol(StreamBase *stream, AbstractProtocol *parent = 0);
|
||||
virtual ~GreProtocol();
|
||||
|
||||
static AbstractProtocol* createInstance(StreamBase *stream,
|
||||
AbstractProtocol *parent = 0);
|
||||
virtual quint32 protocolNumber() const;
|
||||
|
||||
virtual void protoDataCopyInto(OstProto::Protocol &protocol) const;
|
||||
virtual void protoDataCopyFrom(const OstProto::Protocol &protocol);
|
||||
|
||||
virtual ProtocolIdType protocolIdType() const;
|
||||
virtual quint32 protocolId(ProtocolIdType type) const;
|
||||
|
||||
virtual QString name() const;
|
||||
virtual QString shortName() const;
|
||||
|
||||
virtual int fieldCount() const;
|
||||
|
||||
virtual AbstractProtocol::FieldFlags fieldFlags(int index) const;
|
||||
virtual QVariant fieldData(int index, FieldAttrib attrib,
|
||||
int streamIndex = 0) const;
|
||||
virtual bool setFieldData(int index, const QVariant &value,
|
||||
FieldAttrib attrib = FieldValue);
|
||||
|
||||
virtual int protocolFrameSize(int streamIndex = 0) const;
|
||||
|
||||
private:
|
||||
OstProto::Gre data;
|
||||
};
|
||||
|
||||
#endif
|
39
common/gre.proto
Normal file
39
common/gre.proto
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (C) 2021 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import "protocol.proto";
|
||||
|
||||
package OstProto;
|
||||
|
||||
// GRE Protocol
|
||||
message Gre {
|
||||
optional uint32 flags = 1 [default = 0xa];
|
||||
optional uint32 rsvd0 = 2;
|
||||
optional uint32 version = 3;
|
||||
optional uint32 protocol_type = 4;
|
||||
optional uint32 checksum = 5;
|
||||
optional bool is_override_checksum = 6;
|
||||
optional uint32 rsvd1 = 7;
|
||||
optional uint32 key = 8 [default = 0x2020bad7];
|
||||
optional uint32 sequence_num = 9;
|
||||
}
|
||||
|
||||
extend Protocol {
|
||||
optional Gre gre = 405;
|
||||
}
|
193
common/gre.ui
Normal file
193
common/gre.ui
Normal file
@ -0,0 +1,193 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Gre</class>
|
||||
<widget class="QWidget" name="Gre">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>264</width>
|
||||
<height>140</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Gre</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="version">
|
||||
<property name="specialValueText">
|
||||
<string>0 (RFC2784)</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>7</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="hasChecksum">
|
||||
<property name="text">
|
||||
<string>Checksum</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="IntEdit" name="checksum">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="specialValueText">
|
||||
<string><auto></string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>0x</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="displayIntegerBase">
|
||||
<number>16</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="hasKey">
|
||||
<property name="text">
|
||||
<string>Key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="UIntEdit" name="key">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="hasSequence">
|
||||
<property name="text">
|
||||
<string>Sequence No</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="UIntEdit" name="sequence">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>IntEdit</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>intedit.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>UIntEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>uintedit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>hasChecksum</tabstop>
|
||||
<tabstop>checksum</tabstop>
|
||||
<tabstop>hasKey</tabstop>
|
||||
<tabstop>key</tabstop>
|
||||
<tabstop>hasSequence</tabstop>
|
||||
<tabstop>sequence</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>hasKey</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>key</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>32</x>
|
||||
<y>69</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>107</x>
|
||||
<y>71</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>hasSequence</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>sequence</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>75</x>
|
||||
<y>99</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>125</x>
|
||||
<y>97</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>hasChecksum</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>checksum</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>87</x>
|
||||
<y>43</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>109</x>
|
||||
<y>45</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
118
common/greconfig.cpp
Normal file
118
common/greconfig.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
Copyright (C) 2021 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "greconfig.h"
|
||||
#include "gre.h"
|
||||
|
||||
GreConfigForm::GreConfigForm(QWidget *parent)
|
||||
: AbstractProtocolConfigForm(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
connect(hasChecksum, SIGNAL(clicked(bool)),
|
||||
this, SLOT(setAutoChecksum(bool)));
|
||||
}
|
||||
|
||||
GreConfigForm::~GreConfigForm()
|
||||
{
|
||||
}
|
||||
|
||||
GreConfigForm* GreConfigForm::createInstance()
|
||||
{
|
||||
return new GreConfigForm;
|
||||
}
|
||||
|
||||
// Load widget contents from proto
|
||||
void GreConfigForm::loadWidget(AbstractProtocol *proto)
|
||||
{
|
||||
uint flags = proto->fieldData(GreProtocol::gre_flags,
|
||||
AbstractProtocol::FieldValue)
|
||||
.toUInt();
|
||||
|
||||
version->setValue(
|
||||
proto->fieldData(
|
||||
GreProtocol::gre_version,
|
||||
AbstractProtocol::FieldValue
|
||||
).toUInt());
|
||||
|
||||
hasChecksum->setChecked(flags & GRE_FLAG_CKSUM);
|
||||
checksum->setValue(
|
||||
proto->fieldData(
|
||||
GreProtocol::gre_isOverrideChecksum,
|
||||
AbstractProtocol::FieldValue).toBool() ?
|
||||
proto->fieldData(
|
||||
GreProtocol::gre_checksum,
|
||||
AbstractProtocol::FieldValue).toUInt() : -1);
|
||||
|
||||
hasKey->setChecked(flags & GRE_FLAG_KEY);
|
||||
key->setValue(
|
||||
proto->fieldData(
|
||||
GreProtocol::gre_key,
|
||||
AbstractProtocol::FieldValue
|
||||
).toUInt());
|
||||
|
||||
hasSequence->setChecked(flags & GRE_FLAG_SEQ);
|
||||
sequence->setValue(
|
||||
proto->fieldData(
|
||||
GreProtocol::gre_sequence,
|
||||
AbstractProtocol::FieldValue
|
||||
).toUInt());
|
||||
}
|
||||
|
||||
// Store widget contents into proto
|
||||
void GreConfigForm::storeWidget(AbstractProtocol *proto)
|
||||
{
|
||||
uint flags = 0;
|
||||
|
||||
if (hasChecksum->isChecked())
|
||||
flags |= GRE_FLAG_CKSUM;
|
||||
if (hasKey->isChecked())
|
||||
flags |= GRE_FLAG_KEY;
|
||||
if (hasSequence->isChecked())
|
||||
flags |= GRE_FLAG_SEQ;
|
||||
|
||||
proto->setFieldData(
|
||||
GreProtocol::gre_flags,
|
||||
flags);
|
||||
|
||||
proto->setFieldData(
|
||||
GreProtocol::gre_version,
|
||||
version->value());
|
||||
|
||||
proto->setFieldData(
|
||||
GreProtocol::gre_checksum,
|
||||
checksum->value());
|
||||
proto->setFieldData(
|
||||
GreProtocol::gre_isOverrideChecksum,
|
||||
checksum->value() > -1 ? true: false);
|
||||
|
||||
proto->setFieldData(
|
||||
GreProtocol::gre_key,
|
||||
key->value());
|
||||
|
||||
proto->setFieldData(
|
||||
GreProtocol::gre_sequence,
|
||||
sequence->value());
|
||||
}
|
||||
|
||||
void GreConfigForm::setAutoChecksum(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
checksum->setValue(-1); // auto
|
||||
}
|
44
common/greconfig.h
Normal file
44
common/greconfig.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright (C) 2010, 2014 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef _GRE_CONFIG_H
|
||||
#define _GRE_CONFIG_H
|
||||
|
||||
#include "abstractprotocolconfig.h"
|
||||
#include "ui_gre.h"
|
||||
|
||||
class GreConfigForm :
|
||||
public AbstractProtocolConfigForm,
|
||||
private Ui::Gre
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
GreConfigForm(QWidget *parent = 0);
|
||||
virtual ~GreConfigForm();
|
||||
|
||||
static GreConfigForm* createInstance();
|
||||
|
||||
virtual void loadWidget(AbstractProtocol *proto);
|
||||
virtual void storeWidget(AbstractProtocol *proto);
|
||||
|
||||
private slots:
|
||||
void setAutoChecksum(bool enabled);
|
||||
};
|
||||
|
||||
#endif
|
69
common/grepdml.cpp
Normal file
69
common/grepdml.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
Copyright (C) 2021 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "grepdml.h"
|
||||
|
||||
#include "gre.pb.h"
|
||||
|
||||
PdmlGreProtocol::PdmlGreProtocol()
|
||||
{
|
||||
ostProtoId_ = OstProto::Protocol::kGreFieldNumber;
|
||||
|
||||
fieldMap_.insert("gre.proto", OstProto::Gre::kProtocolTypeFieldNumber);
|
||||
fieldMap_.insert("gre.checksum", OstProto::Gre::kChecksumFieldNumber);
|
||||
fieldMap_.insert("gre.offset", OstProto::Gre::kRsvd1FieldNumber);
|
||||
fieldMap_.insert("gre.key", OstProto::Gre::kKeyFieldNumber);
|
||||
fieldMap_.insert("gre.sequence_number", OstProto::Gre::kSequenceNumFieldNumber);
|
||||
}
|
||||
|
||||
PdmlGreProtocol::~PdmlGreProtocol()
|
||||
{
|
||||
}
|
||||
|
||||
PdmlProtocol* PdmlGreProtocol::createInstance()
|
||||
{
|
||||
return new PdmlGreProtocol();
|
||||
}
|
||||
|
||||
void PdmlGreProtocol::postProtocolHandler(OstProto::Protocol* pbProto,
|
||||
OstProto::Stream* /*stream*/)
|
||||
{
|
||||
OstProto::Gre *gre = pbProto->MutableExtension(OstProto::gre);
|
||||
|
||||
qDebug("GRE: post");
|
||||
|
||||
gre->set_is_override_checksum(overrideCksum_);
|
||||
return;
|
||||
}
|
||||
|
||||
void PdmlGreProtocol::unknownFieldHandler(QString name,
|
||||
int /*pos*/, int /*size*/, const QXmlStreamAttributes& attributes,
|
||||
OstProto::Protocol* proto, OstProto::Stream* /*stream*/)
|
||||
{
|
||||
if (name == "gre.flags_and_version") {
|
||||
bool isOk;
|
||||
OstProto::Gre *gre = proto->MutableExtension(OstProto::gre);
|
||||
quint16 flagsAndVersion = attributes.value("value")
|
||||
.toUInt(&isOk, kBaseHex);
|
||||
|
||||
gre->set_flags(flagsAndVersion >> 12);
|
||||
gre->set_rsvd0((flagsAndVersion & 0x0FFF) >> 3);
|
||||
gre->set_version(flagsAndVersion & 0x0007);
|
||||
}
|
||||
}
|
45
common/grepdml.h
Normal file
45
common/grepdml.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright (C) 2021 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef _GRE_PDML_H
|
||||
#define _GRE_PDML_H
|
||||
|
||||
#include "pdmlprotocol.h"
|
||||
|
||||
class PdmlGreProtocol : public PdmlProtocol
|
||||
{
|
||||
public:
|
||||
virtual ~PdmlGreProtocol();
|
||||
|
||||
static PdmlProtocol* createInstance();
|
||||
|
||||
virtual void postProtocolHandler(OstProto::Protocol *pbProto,
|
||||
OstProto::Stream *stream);
|
||||
|
||||
void fieldHandler(QString name, const QXmlStreamAttributes &attributes,
|
||||
OstProto::Protocol *pbProto, OstProto::Stream *stream);
|
||||
virtual void unknownFieldHandler(QString name, int pos, int size,
|
||||
const QXmlStreamAttributes &attributes,
|
||||
OstProto::Protocol *pbProto, OstProto::Stream *stream);
|
||||
|
||||
protected:
|
||||
PdmlGreProtocol();
|
||||
};
|
||||
|
||||
#endif
|
@ -214,12 +214,6 @@ Length (x4)</string>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<widget class="QLineEdit" name="leIpSrcAddr" >
|
||||
<property name="inputMask" >
|
||||
<string>009.009.009.009; </string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
@ -264,12 +258,6 @@ Length (x4)</string>
|
||||
<property name="enabled" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="inputMask" >
|
||||
<string>009.009.009.009; </string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
@ -281,12 +269,6 @@ Length (x4)</string>
|
||||
</item>
|
||||
<item row="2" column="1" >
|
||||
<widget class="QLineEdit" name="leIpDstAddr" >
|
||||
<property name="inputMask" >
|
||||
<string>000.000.000.000; </string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
@ -331,12 +313,6 @@ Length (x4)</string>
|
||||
<property name="enabled" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="inputMask" >
|
||||
<string>009.009.009.009; </string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "ip4config.h"
|
||||
#include "ip4.h"
|
||||
#include "ipv4addressvalidator.h"
|
||||
|
||||
#include <QHostAddress>
|
||||
|
||||
@ -30,6 +31,10 @@ Ip4ConfigForm::Ip4ConfigForm(QWidget *parent)
|
||||
leIpVersion->setValidator(new QIntValidator(0, 15, this));
|
||||
leIpOptions->setValidator(new QRegExpValidator(QRegExp("[0-9a-fA-F]*"),
|
||||
this));
|
||||
leIpSrcAddr->setValidator(new IPv4AddressValidator(this));
|
||||
leIpSrcAddrMask->setValidator(new IPv4AddressValidator(this));
|
||||
leIpDstAddr->setValidator(new IPv4AddressValidator(this));
|
||||
leIpDstAddrMask->setValidator(new IPv4AddressValidator(this));
|
||||
|
||||
connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int)));
|
||||
|
@ -21,7 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "crc32c.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCoreApplication>
|
||||
#include <QFile>
|
||||
#include <QVariant>
|
||||
|
||||
@ -437,17 +437,19 @@ _exit:
|
||||
|
||||
void NativeFileFormat::initFileMetaData(OstProto::FileMetaData &metaData)
|
||||
{
|
||||
QCoreApplication *app = QCoreApplication::instance();
|
||||
|
||||
// Fill in the "native" file format version
|
||||
metaData.set_format_version_major(kFileFormatVersionMajor);
|
||||
metaData.set_format_version_minor(kFileFormatVersionMinor);
|
||||
metaData.set_format_version_revision(kFileFormatVersionRevision);
|
||||
|
||||
metaData.set_generator_name(
|
||||
qApp->applicationName().toUtf8().constData());
|
||||
app->applicationName().toUtf8().constData());
|
||||
metaData.set_generator_version(
|
||||
qApp->property("version").toString().toUtf8().constData());
|
||||
app->property("version").toString().toUtf8().constData());
|
||||
metaData.set_generator_revision(
|
||||
qApp->property("revision").toString().toUtf8().constData());
|
||||
app->property("revision").toString().toUtf8().constData());
|
||||
}
|
||||
|
||||
int NativeFileFormat::fileMetaSize(const quint8* file, int size)
|
||||
|
60
common/ostfile.pro
Normal file
60
common/ostfile.pro
Normal file
@ -0,0 +1,60 @@
|
||||
TEMPLATE = lib
|
||||
CONFIG += qt staticlib
|
||||
QT += network xml script
|
||||
LIBS += \
|
||||
-lprotobuf
|
||||
|
||||
PROTOS = \
|
||||
fileformat.proto
|
||||
|
||||
HEADERS = \
|
||||
ostprotolib.h \
|
||||
nativefileformat.h \
|
||||
ossnfileformat.h \
|
||||
ostmfileformat.h \
|
||||
pcapfileformat.h \
|
||||
pdmlfileformat.h \
|
||||
pythonfileformat.h \
|
||||
pdmlprotocol.h \
|
||||
pdmlprotocols.h \
|
||||
pdmlreader.h \
|
||||
sessionfileformat.h \
|
||||
streamfileformat.h
|
||||
|
||||
SOURCES += \
|
||||
ostprotolib.cpp \
|
||||
nativefileformat.cpp \
|
||||
ossnfileformat.cpp \
|
||||
ostmfileformat.cpp \
|
||||
pcapfileformat.cpp \
|
||||
pdmlfileformat.cpp \
|
||||
pythonfileformat.cpp \
|
||||
pdmlprotocol.cpp \
|
||||
pdmlprotocols.cpp \
|
||||
pdmlreader.cpp \
|
||||
sessionfileformat.cpp \
|
||||
streamfileformat.cpp \
|
||||
|
||||
SOURCES += \
|
||||
vlanpdml.cpp \
|
||||
svlanpdml.cpp \
|
||||
stppdml.cpp \
|
||||
eth2pdml.cpp \
|
||||
llcpdml.cpp \
|
||||
arppdml.cpp \
|
||||
ip4pdml.cpp \
|
||||
ip6pdml.cpp \
|
||||
grepdml.cpp \
|
||||
icmppdml.cpp \
|
||||
icmp6pdml.cpp \
|
||||
igmppdml.cpp \
|
||||
mldpdml.cpp \
|
||||
tcppdml.cpp \
|
||||
udppdml.cpp \
|
||||
textprotopdml.cpp \
|
||||
samplepdml.cpp
|
||||
|
||||
QMAKE_DISTCLEAN += object_script.*
|
||||
|
||||
include(../protobuf.pri)
|
||||
include(../options.pri)
|
15
common/ostfilegui.pro
Normal file
15
common/ostfilegui.pro
Normal file
@ -0,0 +1,15 @@
|
||||
TEMPLATE = lib
|
||||
CONFIG += qt staticlib
|
||||
QT += widgets
|
||||
|
||||
FORMS = \
|
||||
pcapfileimport.ui
|
||||
|
||||
HEADERS = \
|
||||
fileformatoptions.h \
|
||||
pcapoptionsdialog.h
|
||||
|
||||
SOURCES = \
|
||||
fileformatoptions.cpp \
|
||||
pcapoptionsdialog.cpp
|
||||
|
@ -29,6 +29,7 @@ PROTOS += \
|
||||
ip4over6.proto \
|
||||
ip4over4.proto \
|
||||
ip6over6.proto \
|
||||
gre.proto \
|
||||
icmp.proto \
|
||||
gmp.proto \
|
||||
igmp.proto \
|
||||
@ -69,6 +70,7 @@ HEADERS += \
|
||||
ip6over4.h \
|
||||
ip6over6.h \
|
||||
gmp.h \
|
||||
gre.h \
|
||||
icmp.h \
|
||||
igmp.h \
|
||||
mld.h \
|
||||
@ -103,6 +105,7 @@ SOURCES += \
|
||||
ip4.cpp \
|
||||
ip6.cpp \
|
||||
gmp.cpp \
|
||||
gre.cpp \
|
||||
icmp.cpp \
|
||||
igmp.cpp \
|
||||
mld.cpp \
|
||||
@ -110,6 +113,7 @@ SOURCES += \
|
||||
udp.cpp \
|
||||
textproto.cpp \
|
||||
hexdump.cpp \
|
||||
packet.cpp \
|
||||
payload.cpp \
|
||||
sample.cpp \
|
||||
sign.cpp \
|
||||
|
@ -2,11 +2,6 @@ TEMPLATE = lib
|
||||
CONFIG += qt staticlib
|
||||
QT += widgets network xml script
|
||||
INCLUDEPATH += "../extra/qhexedit2/src"
|
||||
LIBS += \
|
||||
-lprotobuf
|
||||
|
||||
FORMS = \
|
||||
pcapfileimport.ui \
|
||||
|
||||
FORMS += \
|
||||
mac.ui \
|
||||
@ -20,6 +15,7 @@ FORMS += \
|
||||
ip4.ui \
|
||||
ip6.ui \
|
||||
gmp.ui \
|
||||
gre.ui \
|
||||
icmp.ui \
|
||||
tcp.ui \
|
||||
udp.ui \
|
||||
@ -31,28 +27,10 @@ FORMS += \
|
||||
sign.ui \
|
||||
userscript.ui
|
||||
|
||||
PROTOS = \
|
||||
fileformat.proto
|
||||
|
||||
# TODO: Move fileformat related stuff into a different library - why?
|
||||
HEADERS = \
|
||||
ostprotolib.h \
|
||||
ipv4addressdelegate.h \
|
||||
ipv6addressdelegate.h \
|
||||
nativefileformat.h \
|
||||
ossnfileformat.h \
|
||||
ostmfileformat.h \
|
||||
pcapfileformat.h \
|
||||
pdmlfileformat.h \
|
||||
pythonfileformat.h \
|
||||
pdmlprotocol.h \
|
||||
pdmlprotocols.h \
|
||||
pdmlreader.h \
|
||||
sessionfileformat.h \
|
||||
streamfileformat.h \
|
||||
spinboxdelegate.h
|
||||
|
||||
HEADERS += \
|
||||
spinboxdelegate.h \
|
||||
tosdscp.h
|
||||
|
||||
HEADERS += \
|
||||
@ -75,6 +53,7 @@ HEADERS += \
|
||||
ip6config.h \
|
||||
ip4over4config.h \
|
||||
gmpconfig.h \
|
||||
greconfig.h \
|
||||
icmpconfig.h \
|
||||
igmpconfig.h \
|
||||
mldconfig.h \
|
||||
@ -88,21 +67,7 @@ HEADERS += \
|
||||
userscriptconfig.h
|
||||
|
||||
SOURCES += \
|
||||
ostprotolib.cpp \
|
||||
nativefileformat.cpp \
|
||||
ossnfileformat.cpp \
|
||||
ostmfileformat.cpp \
|
||||
pcapfileformat.cpp \
|
||||
pdmlfileformat.cpp \
|
||||
pythonfileformat.cpp \
|
||||
pdmlprotocol.cpp \
|
||||
pdmlprotocols.cpp \
|
||||
pdmlreader.cpp \
|
||||
sessionfileformat.cpp \
|
||||
streamfileformat.cpp \
|
||||
spinboxdelegate.cpp
|
||||
|
||||
SOURCES += \
|
||||
spinboxdelegate.cpp \
|
||||
tosdscp.cpp
|
||||
|
||||
SOURCES += \
|
||||
@ -118,6 +83,7 @@ SOURCES += \
|
||||
ip4config.cpp \
|
||||
ip6config.cpp \
|
||||
gmpconfig.cpp \
|
||||
greconfig.cpp \
|
||||
icmpconfig.cpp \
|
||||
igmpconfig.cpp \
|
||||
mldconfig.cpp \
|
||||
@ -130,25 +96,6 @@ SOURCES += \
|
||||
signconfig.cpp \
|
||||
userscriptconfig.cpp
|
||||
|
||||
SOURCES += \
|
||||
vlanpdml.cpp \
|
||||
svlanpdml.cpp \
|
||||
stppdml.cpp \
|
||||
eth2pdml.cpp \
|
||||
llcpdml.cpp \
|
||||
arppdml.cpp \
|
||||
ip4pdml.cpp \
|
||||
ip6pdml.cpp \
|
||||
icmppdml.cpp \
|
||||
icmp6pdml.cpp \
|
||||
igmppdml.cpp \
|
||||
mldpdml.cpp \
|
||||
tcppdml.cpp \
|
||||
udppdml.cpp \
|
||||
textprotopdml.cpp \
|
||||
samplepdml.cpp
|
||||
|
||||
QMAKE_DISTCLEAN += object_script.*
|
||||
|
||||
include(../protobuf.pri)
|
||||
include(../options.pri)
|
||||
|
117
common/packet.cpp
Normal file
117
common/packet.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
Copyright (C) 2023 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "packet.h"
|
||||
|
||||
using namespace Packet;
|
||||
|
||||
quint16 Packet::l4ChecksumOffset(const uchar *pktData, int pktLen)
|
||||
{
|
||||
Parser parser(pktData, pktLen);
|
||||
quint16 offset = kEthTypeOffset;
|
||||
|
||||
// Skip VLANs, if any
|
||||
quint16 ethType = parser.field16(offset);
|
||||
if (!parser.ok()) return 0;
|
||||
|
||||
// TODO: support 802.3 frames
|
||||
if (ethType <= 1500)
|
||||
return 0;
|
||||
|
||||
while (kVlanEthTypes.contains(ethType)) {
|
||||
offset += kVlanTagSize;
|
||||
ethType = parser.field16(offset);
|
||||
if (!parser.ok()) return 0;
|
||||
}
|
||||
offset += kEthTypeSize;
|
||||
|
||||
// XXX: offset now points to Eth payload
|
||||
|
||||
// Skip MPLS tags, if any
|
||||
if (ethType == kMplsEthType) {
|
||||
while (1) {
|
||||
quint32 mplsTag = parser.field32(offset);
|
||||
if (!parser.ok()) return 0;
|
||||
offset += kMplsTagSize;
|
||||
if (mplsTag & 0x100) { // BOS bit
|
||||
quint32 nextWord = parser.field32(offset);
|
||||
if (!parser.ok()) return 0;
|
||||
if (nextWord == 0) { // PW Control Word
|
||||
offset += kMplsTagSize;
|
||||
ethType = 0;
|
||||
break;
|
||||
}
|
||||
quint8 firstPayloadNibble = nextWord >> 28;
|
||||
if (firstPayloadNibble == 0x4)
|
||||
ethType = kIp4EthType;
|
||||
else if (firstPayloadNibble == 0x6)
|
||||
ethType = kIp6EthType;
|
||||
else
|
||||
ethType = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quint8 ipProto = 0;
|
||||
if (ethType == kIp4EthType) {
|
||||
ipProto = parser.field8(offset + kIp4ProtocolOffset);
|
||||
if (!parser.ok()) return 0;
|
||||
|
||||
quint8 ipHdrLen = parser.field8(offset) & 0x0F;
|
||||
if (!parser.ok()) return 0;
|
||||
offset += 4*ipHdrLen;
|
||||
} else if (ethType == kIp6EthType) {
|
||||
ipProto = parser.field8(offset + kIp6NextHeaderOffset);
|
||||
if (!parser.ok()) return 0;
|
||||
offset += kIp6HeaderSize;
|
||||
|
||||
// XXX: offset now points to IPv6 payload
|
||||
|
||||
// Skip IPv6 extension headers, if any
|
||||
while (kIp6ExtensionHeaders.contains(ipProto)) {
|
||||
ipProto = parser.field8(offset + kIp6ExtNextHeaderOffset);
|
||||
if (!parser.ok()) return 0;
|
||||
|
||||
quint16 extHdrLen = parser.field8(offset + kIp6ExtLengthOffset);
|
||||
offset += 8 + 8*extHdrLen;
|
||||
}
|
||||
} else {
|
||||
// Non-IP
|
||||
// TODO: support MPLS PW with Eth payload
|
||||
return 0;
|
||||
}
|
||||
|
||||
// XXX: offset now points to IP payload
|
||||
|
||||
if (ipProto == kIpProtoTcp) {
|
||||
parser.field16(offset + kTcpChecksumOffset);
|
||||
if (!parser.ok()) return 0;
|
||||
|
||||
return offset + kTcpChecksumOffset;
|
||||
} else if (ipProto == kIpProtoUdp) {
|
||||
parser.field16(offset + kUdpChecksumOffset);
|
||||
if (!parser.ok()) return 0;
|
||||
|
||||
return offset + kUdpChecksumOffset;
|
||||
}
|
||||
|
||||
// No L4
|
||||
return 0;
|
||||
}
|
113
common/packet.h
Normal file
113
common/packet.h
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright (C) 2023 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef _PACKET_H
|
||||
#define _PACKET_H
|
||||
|
||||
#include <QSet>
|
||||
#include <QtGlobal>
|
||||
|
||||
namespace Packet {
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
Parser(const uchar *data, int length)
|
||||
: pktData_(data), pktLen_(length) {}
|
||||
quint8 field8(int offset) {
|
||||
if (offset >= pktLen_) {
|
||||
ok_ = false;
|
||||
return 0;
|
||||
}
|
||||
ok_ = true;
|
||||
return pktData_[offset];
|
||||
}
|
||||
quint16 field16(int offset) {
|
||||
if (offset + 1 >= pktLen_) {
|
||||
ok_ = false;
|
||||
return 0;
|
||||
}
|
||||
ok_ = true;
|
||||
return pktData_[offset] << 8
|
||||
| pktData_[offset+1];
|
||||
}
|
||||
quint32 field32(int offset) {
|
||||
if (offset + 3 >= pktLen_) {
|
||||
ok_ = false;
|
||||
return 0;
|
||||
}
|
||||
ok_ = true;
|
||||
return pktData_[offset] << 24
|
||||
| pktData_[offset+1] << 16
|
||||
| pktData_[offset+2] << 8
|
||||
| pktData_[offset+3];
|
||||
}
|
||||
bool ok() {
|
||||
return ok_;
|
||||
}
|
||||
private:
|
||||
const uchar *pktData_;
|
||||
int pktLen_;
|
||||
bool ok_{false};
|
||||
};
|
||||
|
||||
quint16 l4ChecksumOffset(const uchar *pktData, int pktLen);
|
||||
|
||||
//
|
||||
// Constants
|
||||
//
|
||||
// Ethernet
|
||||
const quint16 kEthTypeOffset = 12;
|
||||
const quint16 kEthTypeSize = 2;
|
||||
const quint16 kIp4EthType = 0x0800;
|
||||
const quint16 kIp6EthType = 0x86dd;
|
||||
const quint16 kMplsEthType = 0x8847;
|
||||
const QSet<quint16> kVlanEthTypes = {0x8100, 0x9100, 0x88a8};
|
||||
const uint kEthOverhead = 20;
|
||||
|
||||
// VLAN
|
||||
const quint16 kVlanTagSize = 4;
|
||||
|
||||
// MPLS
|
||||
const quint16 kMplsTagSize = 4;
|
||||
|
||||
// IPv4
|
||||
const quint16 kIp4ProtocolOffset = 9;
|
||||
|
||||
// IPv6
|
||||
const quint16 kIp6HeaderSize = 40;
|
||||
const quint16 kIp6NextHeaderOffset = 6;
|
||||
|
||||
// IPv6 Extension Header
|
||||
const quint16 kIp6ExtNextHeaderOffset = 0;
|
||||
const quint16 kIp6ExtLengthOffset = 1;
|
||||
|
||||
// IPv4/IPv6 Proto/NextHeader values
|
||||
const quint8 kIpProtoTcp = 6;
|
||||
const quint8 kIpProtoUdp = 17;
|
||||
|
||||
const QSet<quint8> kIp6ExtensionHeaders = {0, 60, 43, 44, 51, 50, 60, 135}; // FIXME: use names
|
||||
|
||||
// TCP
|
||||
const quint16 kTcpChecksumOffset = 16;
|
||||
|
||||
// UDP
|
||||
const quint16 kUdpChecksumOffset = 6;
|
||||
};
|
||||
|
||||
#endif
|
@ -33,6 +33,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
const quint32 kPcapFileMagic = 0xa1b2c3d4;
|
||||
const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1;
|
||||
const quint32 kNanoSecondPcapFileMagic = 0xa1b23c4d;
|
||||
const quint32 kNanoSecondPcapFileMagicSwapped = 0x4d3cb2a1;
|
||||
const quint16 kPcapFileVersionMajor = 2;
|
||||
const quint16 kPcapFileVersionMinor = 4;
|
||||
const quint32 kMaxSnapLen = 65535;
|
||||
@ -40,43 +42,9 @@ const quint32 kDltEthernet = 1;
|
||||
|
||||
PcapFileFormat pcapFileFormat;
|
||||
|
||||
PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options)
|
||||
: QDialog(NULL)
|
||||
{
|
||||
setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
options_ = options;
|
||||
|
||||
viaPdml->setChecked(options_->value("ViaPdml").toBool());
|
||||
recalculateCksums->setChecked(
|
||||
options_->value("RecalculateCksums").toBool());
|
||||
doDiff->setChecked(options_->value("DoDiff").toBool());
|
||||
|
||||
// XXX: By default this is false - for pcap import tests to show
|
||||
// minimal diffs. However, for the user, this should be enabled
|
||||
// by default
|
||||
recalculateCksums->setChecked(true);
|
||||
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
}
|
||||
|
||||
PcapImportOptionsDialog::~PcapImportOptionsDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void PcapImportOptionsDialog::accept()
|
||||
{
|
||||
options_->insert("ViaPdml", viaPdml->isChecked());
|
||||
options_->insert("RecalculateCksums", recalculateCksums->isChecked());
|
||||
options_->insert("DoDiff", doDiff->isChecked());
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
PcapFileFormat::PcapFileFormat()
|
||||
{
|
||||
importOptions_.insert("ViaPdml", true);
|
||||
importOptions_.insert("RecalculateCksums", false);
|
||||
importOptions_.insert("DoDiff", true);
|
||||
}
|
||||
|
||||
@ -92,11 +60,12 @@ bool PcapFileFormat::open(const QString fileName,
|
||||
QTemporaryFile file2;
|
||||
quint32 magic;
|
||||
uchar gzipMagic[2];
|
||||
bool nsecResolution = false;
|
||||
int len;
|
||||
PcapFileHeader fileHdr;
|
||||
PcapPacketHeader pktHdr;
|
||||
OstProto::Stream *prevStream = NULL;
|
||||
uint lastUsec = 0;
|
||||
quint64 lastXsec = 0;
|
||||
int pktCount;
|
||||
qint64 byteCount = 0;
|
||||
qint64 byteTotal;
|
||||
@ -166,15 +135,22 @@ _retry:
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
else if (magic == kPcapFileMagicSwapped)
|
||||
else if (magic == kNanoSecondPcapFileMagic)
|
||||
{
|
||||
nsecResolution = true;
|
||||
}
|
||||
else if ((magic == kPcapFileMagicSwapped)
|
||||
|| (magic == kNanoSecondPcapFileMagicSwapped))
|
||||
{
|
||||
// Toggle Byte order
|
||||
if (fd_.byteOrder() == QDataStream::BigEndian)
|
||||
fd_.setByteOrder(QDataStream::LittleEndian);
|
||||
else
|
||||
fd_.setByteOrder(QDataStream::BigEndian);
|
||||
|
||||
nsecResolution = (magic == kNanoSecondPcapFileMagicSwapped);
|
||||
}
|
||||
else // Not a pcap file
|
||||
else // Not a pcap file (could be pcapng or something else)
|
||||
{
|
||||
if (tryConvert)
|
||||
{
|
||||
@ -226,6 +202,9 @@ _retry:
|
||||
|
||||
pktBuf.resize(fileHdr.snapLen);
|
||||
|
||||
// XXX: PDML also needs the PCAP file to cross check packet bytes
|
||||
// with the PDML data, so we can't do PDML conversion any earlier
|
||||
// than this
|
||||
qDebug("pdml check");
|
||||
if (importOptions_.value("ViaPdml").toBool())
|
||||
{
|
||||
@ -265,6 +244,8 @@ _retry:
|
||||
|
||||
emit status("Reading PDML packets...");
|
||||
emit target(100); // in percentage
|
||||
|
||||
// pdml reader needs pcap, so pass self
|
||||
isOk = reader.read(&pdmlFile, this, &stop_);
|
||||
|
||||
if (stop_)
|
||||
@ -450,7 +431,7 @@ _retry:
|
||||
diffFile.close();
|
||||
if (diffFile.size())
|
||||
{
|
||||
error.append(tr("<p>There is a diff between the original and imported streams. See details to review the diff.</p><p>Why a diff? See <a href='%1'>possible reasons</a>.</p>\n\n\n\n").arg("https://jump.ostinato.org/pcapdiff"));
|
||||
error.append(tr("<p>There is a diff between the original and imported streams. See details to review the diff.</p><p>💡 If you don't need to edit packets, you can retry the import and uncheck the Intelligent Import option.</p><p>Why a diff? See <a href='%1'>possible reasons</a>.</p>\n\n\n\n").arg("https://jump.ostinato.org/pcapdiff"));
|
||||
diffFile.open();
|
||||
diffFile.seek(0);
|
||||
error.append(QString(diffFile.readAll()));
|
||||
@ -460,6 +441,7 @@ _retry:
|
||||
}
|
||||
|
||||
_non_pdml:
|
||||
qDebug("pcap resolution: %s", nsecResolution ? "nsec" : "usec");
|
||||
emit status("Reading Packets...");
|
||||
emit target(100); // in percentage
|
||||
pktCount = 1;
|
||||
@ -487,20 +469,22 @@ _non_pdml:
|
||||
stream->mutable_control()->set_num_packets(1);
|
||||
|
||||
// setup packet rate to the timing in pcap (as close as possible)
|
||||
const double kUsecsInSec = 1e6;
|
||||
uint usec = (pktHdr.tsSec*kUsecsInSec + pktHdr.tsUsec);
|
||||
uint delta = usec - lastUsec;
|
||||
// use quint64 rather than double to store micro/nano second as
|
||||
// it has a larger range (~580 years) and therefore better accuracy
|
||||
const quint64 kXsecsInSec = nsecResolution ? 1e9 : 1e6;
|
||||
quint64 xsec = (pktHdr.tsSec*kXsecsInSec + pktHdr.tsUsec);
|
||||
quint64 delta = xsec - lastXsec;
|
||||
qDebug("pktCount = %d, delta = %llu", pktCount, delta);
|
||||
|
||||
if ((pktCount != 1) && delta)
|
||||
stream->mutable_control()->set_packets_per_sec(kUsecsInSec/delta);
|
||||
stream->mutable_control()->set_packets_per_sec(double(kXsecsInSec)/delta);
|
||||
|
||||
if (prevStream)
|
||||
prevStream->mutable_control()->CopyFrom(stream->control());
|
||||
|
||||
lastUsec = usec;
|
||||
lastXsec = xsec;
|
||||
prevStream = stream;
|
||||
pktCount++;
|
||||
qDebug("pktCount = %d", pktCount);
|
||||
byteCount += pktHdr.inclLen + sizeof(pktHdr);
|
||||
emit progress(int(byteCount*100/byteTotal)); // in percentage
|
||||
if (stop_)
|
||||
@ -576,7 +560,7 @@ bool PcapFileFormat::convertToStandardPcap(
|
||||
tshark.start(OstProtoLib::tsharkPath(),
|
||||
QStringList()
|
||||
<< QString("-r%1").arg(fileName)
|
||||
<< "-Fpcap"
|
||||
<< "-Fnsecpcap"
|
||||
<< QString("-w%1").arg(outputFileName));
|
||||
if (!tshark.waitForStarted(-1))
|
||||
{
|
||||
@ -639,7 +623,7 @@ bool PcapFileFormat::save(const OstProto::StreamConfigList streams,
|
||||
|
||||
fd_.setDevice(&file);
|
||||
|
||||
fileHdr.magicNumber = kPcapFileMagic;
|
||||
fileHdr.magicNumber = kNanoSecondPcapFileMagic;
|
||||
fileHdr.versionMajor = kPcapFileVersionMajor;
|
||||
fileHdr.versionMinor = kPcapFileVersionMinor;
|
||||
fileHdr.thisZone = 0;
|
||||
@ -687,11 +671,16 @@ bool PcapFileFormat::save(const OstProto::StreamConfigList streams,
|
||||
fd_.writeRawData(pktBuf.data(), pktHdr.inclLen);
|
||||
|
||||
if (s.packetRate())
|
||||
pktHdr.tsUsec += quint32(1e6/s.packetRate());
|
||||
if (pktHdr.tsUsec >= 1000000)
|
||||
{
|
||||
quint64 delta = quint64(1e9/s.packetRate());
|
||||
pktHdr.tsSec += delta/quint32(1e9);
|
||||
pktHdr.tsUsec += delta % quint32(1e9);
|
||||
}
|
||||
|
||||
if (pktHdr.tsUsec >= quint32(1e9))
|
||||
{
|
||||
pktHdr.tsSec++;
|
||||
pktHdr.tsUsec -= 1000000;
|
||||
pktHdr.tsUsec -= quint32(1e9);
|
||||
}
|
||||
|
||||
emit progress(i);
|
||||
@ -710,9 +699,9 @@ _exit:
|
||||
return isOk;
|
||||
}
|
||||
|
||||
QDialog* PcapFileFormat::openOptionsDialog()
|
||||
QVariantMap* PcapFileFormat::options()
|
||||
{
|
||||
return new PcapImportOptionsDialog(&importOptions_);
|
||||
return &importOptions_;
|
||||
}
|
||||
|
||||
bool PcapFileFormat::isMyFileFormat(const QString /*fileName*/)
|
||||
|
@ -20,24 +20,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#define _PCAP_FILE_FORMAT_H
|
||||
|
||||
#include "streamfileformat.h"
|
||||
#include "ui_pcapfileimport.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QVariantMap>
|
||||
|
||||
class PcapImportOptionsDialog: public QDialog, public Ui::PcapFileImport
|
||||
{
|
||||
public:
|
||||
PcapImportOptionsDialog(QVariantMap *options);
|
||||
~PcapImportOptionsDialog();
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
|
||||
private:
|
||||
QVariantMap *options_;
|
||||
};
|
||||
|
||||
class PdmlReader;
|
||||
class PcapFileFormat : public StreamFileFormat
|
||||
{
|
||||
@ -52,7 +38,7 @@ public:
|
||||
bool save(const OstProto::StreamConfigList streams,
|
||||
const QString fileName, QString &error);
|
||||
|
||||
virtual QDialog* openOptionsDialog();
|
||||
virtual QVariantMap* options();
|
||||
|
||||
bool isMyFileFormat(const QString fileName);
|
||||
bool isMyFileType(const QString fileType);
|
||||
|
55
common/pcapoptionsdialog.cpp
Normal file
55
common/pcapoptionsdialog.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright (C) 2011 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "pcapoptionsdialog.h"
|
||||
|
||||
PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options)
|
||||
: QDialog(NULL)
|
||||
{
|
||||
Q_ASSERT(options != NULL);
|
||||
|
||||
setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
options_ = options;
|
||||
|
||||
viaPdml->setChecked(options_->value("ViaPdml").toBool());
|
||||
// XXX: By default this key is absent - so that pcap import tests
|
||||
// evaluate to false and hence show minimal diffs.
|
||||
// However, for the GUI user, this should be enabled by default.
|
||||
recalculateCksums->setChecked(
|
||||
options_->value("RecalculateCksums", QVariant(true))
|
||||
.toBool());
|
||||
doDiff->setChecked(options_->value("DoDiff").toBool());
|
||||
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
}
|
||||
|
||||
PcapImportOptionsDialog::~PcapImportOptionsDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void PcapImportOptionsDialog::accept()
|
||||
{
|
||||
options_->insert("ViaPdml", viaPdml->isChecked());
|
||||
options_->insert("RecalculateCksums", recalculateCksums->isChecked());
|
||||
options_->insert("DoDiff", doDiff->isChecked());
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
39
common/pcapoptionsdialog.h
Normal file
39
common/pcapoptionsdialog.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (C) 2011 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef _PCAP_OPTIONS_DIALOG_H
|
||||
#define _PCAP_OPTIONS_DIALOG_H
|
||||
|
||||
#include "ui_pcapfileimport.h"
|
||||
|
||||
#include <QVariantMap>
|
||||
|
||||
class PcapImportOptionsDialog: public QDialog, public Ui::PcapFileImport
|
||||
{
|
||||
public:
|
||||
PcapImportOptionsDialog(QVariantMap *options);
|
||||
~PcapImportOptionsDialog();
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
|
||||
private:
|
||||
QVariantMap *options_;
|
||||
};
|
||||
|
||||
#endif
|
@ -28,6 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "arppdml.h"
|
||||
#include "eth2pdml.h"
|
||||
#include "grepdml.h"
|
||||
#include "llcpdml.h"
|
||||
#include "icmppdml.h"
|
||||
#include "icmp6pdml.h"
|
||||
@ -62,6 +63,7 @@ PdmlReader::PdmlReader(OstProto::StreamConfigList *streams,
|
||||
|
||||
factory_.insert("arp", PdmlArpProtocol::createInstance);
|
||||
factory_.insert("eth", PdmlEthProtocol::createInstance);
|
||||
factory_.insert("gre", PdmlGreProtocol::createInstance);
|
||||
factory_.insert("http", PdmlTextProtocol::createInstance);
|
||||
factory_.insert("icmp", PdmlIcmpProtocol::createInstance);
|
||||
factory_.insert("icmpv6", PdmlIcmp6Protocol::createInstance);
|
||||
|
@ -157,6 +157,7 @@ message Protocol {
|
||||
kIcmpFieldNumber = 402;
|
||||
kIgmpFieldNumber = 403;
|
||||
kMldFieldNumber = 404;
|
||||
kGreFieldNumber = 405;
|
||||
|
||||
kTextProtocolFieldNumber = 500;
|
||||
}
|
||||
@ -215,6 +216,7 @@ message Port {
|
||||
|
||||
optional double speed = 10; // in Mbps
|
||||
optional uint32 mtu = 11;
|
||||
optional string user_description = 12;
|
||||
}
|
||||
|
||||
message PortConfigList {
|
||||
@ -289,6 +291,10 @@ message StreamStats {
|
||||
required PortId port_id = 1;
|
||||
required StreamGuid stream_guid = 2;
|
||||
|
||||
optional double tx_duration = 3; // in seconds
|
||||
optional uint64 latency = 4; // in nanoseconds
|
||||
optional uint64 jitter = 5; // in nanoseconds
|
||||
|
||||
optional uint64 rx_pkts = 11;
|
||||
optional uint64 rx_bytes = 12;
|
||||
optional uint64 tx_pkts = 13;
|
||||
|
@ -46,6 +46,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "ip6over6.h"
|
||||
|
||||
// L4 Protos
|
||||
#include "gre.h"
|
||||
#include "icmp.h"
|
||||
#include "igmp.h"
|
||||
#include "mld.h"
|
||||
@ -112,6 +113,8 @@ ProtocolManager::ProtocolManager()
|
||||
(void*) Ip6over6Protocol::createInstance);
|
||||
|
||||
// Layer 4 Protocols
|
||||
registerProtocol(OstProto::Protocol::kGreFieldNumber,
|
||||
(void*) GreProtocol::createInstance);
|
||||
registerProtocol(OstProto::Protocol::kIcmpFieldNumber,
|
||||
(void*) IcmpProtocol::createInstance);
|
||||
registerProtocol(OstProto::Protocol::kIgmpFieldNumber,
|
||||
|
@ -40,6 +40,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "ip6over4config.h"
|
||||
#include "ip6over6config.h"
|
||||
// L4 Protocol Widgets
|
||||
#include "greconfig.h"
|
||||
#include "icmpconfig.h"
|
||||
#include "igmpconfig.h"
|
||||
#include "mldconfig.h"
|
||||
@ -124,6 +125,9 @@ ProtocolWidgetFactory::ProtocolWidgetFactory()
|
||||
(void*) Ip6over6ConfigForm::createInstance);
|
||||
|
||||
// Layer 4 Protocols
|
||||
OstProtocolWidgetFactory->registerProtocolConfigWidget(
|
||||
OstProto::Protocol::kGreFieldNumber,
|
||||
(void*) GreConfigForm::createInstance);
|
||||
OstProtocolWidgetFactory->registerProtocolConfigWidget(
|
||||
OstProto::Protocol::kIcmpFieldNumber,
|
||||
(void*) IcmpConfigForm::createInstance);
|
||||
|
@ -63,6 +63,8 @@ bool PythonFileFormat::save(const OstProto::StreamConfigList streams,
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
||||
goto _open_fail;
|
||||
|
||||
out.setCodec("UTF-8");
|
||||
|
||||
// import standard modules
|
||||
emit status("Writing imports ...");
|
||||
emit target(0);
|
||||
@ -450,9 +452,16 @@ void PythonFileFormat::writeFieldAssignment(
|
||||
std::string val = fieldDesc->is_repeated() ?
|
||||
refl->GetRepeatedStringReference(msg, fieldDesc, index, &val) :
|
||||
refl->GetStringReference(msg, fieldDesc, &val);
|
||||
QString escVal = escapeString(QString::fromStdString(val));
|
||||
if (val != fieldDesc->default_value_string())
|
||||
out << fieldName << " = '" << escVal << "'\n";
|
||||
if (val == fieldDesc->default_value_string())
|
||||
break;
|
||||
if (fieldDesc->type() == FieldDescriptor::TYPE_BYTES) {
|
||||
QString strVal = byteString(QByteArray(val.c_str(),
|
||||
val.size()));
|
||||
out << fieldName << " = b'" << strVal << "'\n";
|
||||
} else {
|
||||
QString strVal = QString::fromStdString(val);
|
||||
out << fieldName << " = u'" << strVal << "'\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FieldDescriptor::CPPTYPE_ENUM:
|
||||
@ -533,16 +542,16 @@ QString PythonFileFormat::singularize(QString plural)
|
||||
return singular;
|
||||
}
|
||||
|
||||
QString PythonFileFormat::escapeString(QString str)
|
||||
QString PythonFileFormat::escapeString(QByteArray str)
|
||||
{
|
||||
QString escStr = "";
|
||||
for (int i=0; i < str.length(); i++) {
|
||||
uchar c = str[i].cell();
|
||||
uchar c = uchar(str.at(i));
|
||||
if ((c < 128) && isprint(c)) {
|
||||
if (c == '\'')
|
||||
escStr.append("\\'");
|
||||
else
|
||||
escStr.append(str[i]);
|
||||
escStr.append(QChar(c));
|
||||
}
|
||||
else
|
||||
escStr.append(QString("\\x%1").arg(int(c), 2, 16, QChar('0')));
|
||||
@ -550,6 +559,16 @@ QString PythonFileFormat::escapeString(QString str)
|
||||
return escStr;
|
||||
}
|
||||
|
||||
QString PythonFileFormat::byteString(QByteArray str)
|
||||
{
|
||||
QString byteStr = "";
|
||||
for (int i=0; i < str.length(); i++) {
|
||||
uchar c = uchar(str.at(i));
|
||||
byteStr.append(QString("\\x%1").arg(int(c), 2, 16, QChar('0')));
|
||||
}
|
||||
return byteStr;
|
||||
}
|
||||
|
||||
bool PythonFileFormat::useDecimalBase(QString fieldName)
|
||||
{
|
||||
// Heuristic - use Hex base for all except for the following
|
||||
|
@ -49,7 +49,8 @@ private:
|
||||
const google::protobuf::FieldDescriptor *fieldDesc,
|
||||
int index = -1);
|
||||
QString singularize(QString plural);
|
||||
QString escapeString(QString str);
|
||||
QString escapeString(QByteArray str);
|
||||
QString byteString(QByteArray str);
|
||||
bool useDecimalBase(QString fieldName);
|
||||
};
|
||||
|
||||
|
@ -32,12 +32,7 @@ SessionFileFormat::~SessionFileFormat()
|
||||
{
|
||||
}
|
||||
|
||||
QDialog* SessionFileFormat::openOptionsDialog()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QDialog* SessionFileFormat::saveOptionsDialog()
|
||||
QVariantMap* SessionFileFormat::options()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
@ -23,10 +23,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "fileformat.pb.h"
|
||||
#include "protocol.pb.h"
|
||||
|
||||
#include <QThread>
|
||||
#include <QString>
|
||||
|
||||
class QDialog;
|
||||
#include <QThread>
|
||||
#include <QVariantMap>
|
||||
|
||||
class SessionFileFormat : public QThread
|
||||
{
|
||||
@ -42,8 +41,7 @@ public:
|
||||
virtual bool save(const OstProto::SessionContent &session,
|
||||
const QString fileName, QString &error) = 0;
|
||||
|
||||
virtual QDialog* openOptionsDialog();
|
||||
virtual QDialog* saveOptionsDialog();
|
||||
virtual QVariantMap* options();
|
||||
|
||||
void openAsync(const QString fileName,
|
||||
OstProto::SessionContent &session, QString &error);
|
||||
|
@ -19,6 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "sign.h"
|
||||
|
||||
#include "../common/streambase.h"
|
||||
|
||||
SignProtocol::SignProtocol(StreamBase *stream, AbstractProtocol *parent)
|
||||
: AbstractProtocol(stream, parent)
|
||||
{
|
||||
@ -76,7 +78,9 @@ AbstractProtocol::FieldFlags SignProtocol::fieldFlags(int index) const
|
||||
switch (index)
|
||||
{
|
||||
case sign_magic:
|
||||
case sign_tlv_tx_port:
|
||||
case sign_tlv_guid:
|
||||
case sign_tlv_ttag:
|
||||
case sign_tlv_end:
|
||||
break;
|
||||
|
||||
@ -116,6 +120,52 @@ QVariant SignProtocol::fieldData(int index, FieldAttrib attrib,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case sign_tlv_ttag:
|
||||
{
|
||||
switch(attrib)
|
||||
{
|
||||
case FieldName:
|
||||
return QString("T-Tag");
|
||||
case FieldValue:
|
||||
return 0;
|
||||
case FieldTextValue:
|
||||
return QString("%1").arg(0);
|
||||
case FieldFrameValue:
|
||||
{
|
||||
QByteArray fv;
|
||||
fv.resize(2);
|
||||
fv[0] = 0;
|
||||
fv[1] = kTypeLenTtagPlaceholder;
|
||||
return fv;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case sign_tlv_tx_port:
|
||||
{
|
||||
switch(attrib)
|
||||
{
|
||||
case FieldName:
|
||||
return QString("TxPort");
|
||||
case FieldValue:
|
||||
return mpStream->portId();
|
||||
case FieldTextValue:
|
||||
return QString("%1").arg(mpStream->portId());
|
||||
case FieldFrameValue:
|
||||
{
|
||||
QByteArray fv;
|
||||
fv.resize(2);
|
||||
fv[0] = mpStream->portId() & 0xFF;
|
||||
fv[1] = kTypeLenTxPort;
|
||||
return fv;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case sign_tlv_guid:
|
||||
{
|
||||
quint32 guid = data.stream_guid() & 0xFFFFFF;
|
||||
@ -217,3 +267,29 @@ bool SignProtocol::packetGuid(const uchar *pkt, int pktLen, uint *guid)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SignProtocol::packetTtagId(const uchar *pkt, int pktLen, uint *ttagId, uint *guid)
|
||||
{
|
||||
bool ret = false;
|
||||
const uchar *p = pkt + pktLen - sizeof(kSignMagic);
|
||||
quint32 magic = qFromBigEndian<quint32>(p);
|
||||
if (magic != kSignMagic)
|
||||
return ret;
|
||||
|
||||
*guid = kInvalidGuid;
|
||||
p--;
|
||||
while (*p != kTypeLenEnd) {
|
||||
if (*p == kTypeLenTtag) {
|
||||
*ttagId = *(p - 1);
|
||||
ret = true;
|
||||
} else if (*p == kTypeLenGuid) {
|
||||
*guid = qFromBigEndian<quint32>(p - 3) >> 8;
|
||||
} else if (*p == kTypeLenTxPort) {
|
||||
#ifdef Q_OS_WIN32
|
||||
*ttagId |= uint(*(p - 1)) << 8;
|
||||
#endif
|
||||
}
|
||||
p -= 1 + (*p >> 5); // move to next TLV
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "abstractprotocol.h"
|
||||
#include "sign.pb.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
/*
|
||||
Sign Protocol is expected at the end of the frame (just before the Eth FCS)
|
||||
---+--------+-------+
|
||||
@ -39,9 +41,19 @@ TLVs are encoded as
|
||||
Len does NOT include the one byte of TypeLen
|
||||
Size of the value field varies between 0 to 7 bytes
|
||||
|
||||
Defined TLVs
|
||||
Defined TLVs
|
||||
Type = 0, Len = 0 (0x00): End of TLVs
|
||||
Type = 1, Len = 3 (0x61): Stream GUID
|
||||
Type = 2, Len = 1 (0x22): T-Tag Placeholder (0 value)
|
||||
Type = 3, Len = 1 (0x23): T-Tag with actual value
|
||||
Type = 4, Len = 1 (0x24): Tx Port Id
|
||||
|
||||
Order of TLVs from end of packet towards beginning [Offset, Size]
|
||||
[ -4, 4 bytes] Magic
|
||||
[ -6, 2 bytes] TTag (Placeholder or actual)
|
||||
[-10, 4 bytes] Stream Guid
|
||||
[-12, 2 bytes] Tx Port Id
|
||||
[-13, 1 byte ] End
|
||||
*/
|
||||
|
||||
class SignProtocol : public AbstractProtocol
|
||||
@ -51,7 +63,9 @@ public:
|
||||
{
|
||||
// Frame Fields
|
||||
sign_tlv_end = 0,
|
||||
sign_tlv_tx_port,
|
||||
sign_tlv_guid,
|
||||
sign_tlv_ttag,
|
||||
sign_magic,
|
||||
|
||||
// Meta Fields
|
||||
@ -83,10 +97,19 @@ public:
|
||||
|
||||
static quint32 magic();
|
||||
static bool packetGuid(const uchar *pkt, int pktLen, uint *guid);
|
||||
static bool packetTtagId(const uchar *pkt, int pktLen, uint *ttagId, uint *guid);
|
||||
|
||||
// XXX: Any change in kTypeLenXXX or magic value should also be done in
|
||||
// TxThread/Ttag code as well where hardcoded values are used
|
||||
static const quint32 kMaxGuid = 0x00ffffff;
|
||||
static const quint32 kInvalidGuid = UINT_MAX;
|
||||
static const quint8 kTypeLenTtagPlaceholder = 0x22;
|
||||
static const quint8 kTypeLenTtag = 0x23;
|
||||
private:
|
||||
static const quint32 kSignMagic = 0x1d10c0da; // coda! (unicode - 0x1d10c)
|
||||
static const quint8 kTypeLenEnd = 0x00;
|
||||
static const quint8 kTypeLenGuid = 0x61;
|
||||
static const quint8 kTypeLenTxPort = 0x24;
|
||||
OstProto::Sign data;
|
||||
};
|
||||
|
||||
|
@ -145,7 +145,7 @@ void StreamBase::setFrameProtocol(ProtocolList protocolList)
|
||||
}
|
||||
#endif
|
||||
|
||||
bool StreamBase::hasProtocol(quint32 protocolNumber)
|
||||
bool StreamBase::hasProtocol(quint32 protocolNumber) const
|
||||
{
|
||||
foreach(const AbstractProtocol *proto, *currentFrameProtocols)
|
||||
if (proto->protocolNumber() == protocolNumber)
|
||||
@ -681,14 +681,38 @@ quint64 StreamBase::neighborMacAddress(int frameIndex) const
|
||||
bool StreamBase::preflightCheck(QStringList &result) const
|
||||
{
|
||||
bool pass = true;
|
||||
bool chkShort = true;
|
||||
bool chkTrunc = true;
|
||||
bool chkJumbo = true;
|
||||
bool chkSignIcmp = true;
|
||||
int count = isFrameSizeVariable() ? frameSizeVariableCount() : 1;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int pktLen = frameLen(i);
|
||||
|
||||
if (chkShort && hasProtocol(OstProto::Protocol::kSignFieldNumber)
|
||||
&& (pktLen > (frameProtocolLength(i) + kFcsSize)))
|
||||
{
|
||||
result << QObject::tr("Stream statistics may not work since "
|
||||
"frame content < 64 bytes and hence will get padded - "
|
||||
"make sure special signature is at the end of the "
|
||||
"frame and frame content ≥ 64 bytes");
|
||||
chkShort = false;
|
||||
pass = false;
|
||||
}
|
||||
|
||||
if (chkSignIcmp && hasProtocol(OstProto::Protocol::kSignFieldNumber)
|
||||
&& hasProtocol(OstProto::Protocol::kIcmpFieldNumber))
|
||||
{
|
||||
result << QObject::tr("Stream statistics are not supported "
|
||||
"for ICMP packets - please use a non-ICMP protocol or "
|
||||
"remove special signature from ICMP streams");
|
||||
chkSignIcmp = false;
|
||||
pass = false;
|
||||
}
|
||||
|
||||
|
||||
if (chkTrunc && (pktLen < (frameProtocolLength(i) + kFcsSize)))
|
||||
{
|
||||
result << QObject::tr("One or more frames may be truncated - "
|
||||
|
@ -42,7 +42,7 @@ public:
|
||||
void protoDataCopyFrom(const OstProto::Stream &stream);
|
||||
void protoDataCopyInto(OstProto::Stream &stream) const;
|
||||
|
||||
bool hasProtocol(quint32 protocolNumber);
|
||||
bool hasProtocol(quint32 protocolNumber) const;
|
||||
ProtocolListIterator* createProtocolListIterator() const;
|
||||
|
||||
//! \todo (LOW) should we have a copy constructor??
|
||||
@ -75,9 +75,8 @@ public:
|
||||
quint32 id() const;
|
||||
bool setId(quint32 id);
|
||||
|
||||
quint32 portId() { return portId_;}
|
||||
#if 0 // FIXME(HI): needed?
|
||||
quint32 portId()
|
||||
{ return mCore->port_id();}
|
||||
bool setPortId(quint32 id)
|
||||
{ mCore->set_port_id(id); return true;}
|
||||
#endif
|
||||
|
@ -35,12 +35,7 @@ StreamFileFormat::~StreamFileFormat()
|
||||
{
|
||||
}
|
||||
|
||||
QDialog* StreamFileFormat::openOptionsDialog()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QDialog* StreamFileFormat::saveOptionsDialog()
|
||||
QVariantMap* StreamFileFormat::options()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
@ -41,8 +41,7 @@ public:
|
||||
virtual bool save(const OstProto::StreamConfigList streams,
|
||||
const QString fileName, QString &error) = 0;
|
||||
|
||||
virtual QDialog* openOptionsDialog();
|
||||
virtual QDialog* saveOptionsDialog();
|
||||
virtual QVariantMap* options();
|
||||
|
||||
void openAsync(const QString fileName,
|
||||
OstProto::StreamConfigList &streams, QString &error);
|
||||
|
57
common/uintedit.h
Normal file
57
common/uintedit.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright (C) 2022 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef _UINT_EDIT_H
|
||||
#define _UINT_EDIT_H
|
||||
|
||||
#include "ulonglongvalidator.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
class UIntEdit: public QLineEdit
|
||||
{
|
||||
public:
|
||||
UIntEdit(QWidget *parent = 0);
|
||||
|
||||
quint32 value();
|
||||
void setValue(quint32 val);
|
||||
};
|
||||
|
||||
// -------------------- //
|
||||
|
||||
inline UIntEdit::UIntEdit(QWidget *parent)
|
||||
: QLineEdit(parent)
|
||||
{
|
||||
setValidator(new ULongLongValidator(0, UINT_MAX));
|
||||
}
|
||||
|
||||
inline quint32 UIntEdit::value()
|
||||
{
|
||||
return text().toUInt(Q_NULLPTR, 0);
|
||||
}
|
||||
|
||||
inline void UIntEdit::setValue(quint32 val)
|
||||
{
|
||||
setText(QString::number(val));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -29,6 +29,11 @@ public:
|
||||
: QValidator(parent)
|
||||
{
|
||||
}
|
||||
ULongLongValidator(qulonglong min, qulonglong max, QObject *parent = 0)
|
||||
: QValidator(parent)
|
||||
{
|
||||
setRange(min, max);
|
||||
}
|
||||
~ULongLongValidator() {}
|
||||
|
||||
void setRange(qulonglong min, qulonglong max)
|
||||
|
@ -32,6 +32,9 @@ Updater::Updater()
|
||||
|
||||
#if 1
|
||||
// Tests!
|
||||
Q_ASSERT(isVersionNewer("1.3.0", "1.2.0") == true);
|
||||
Q_ASSERT(isVersionNewer("1.3.0", "1.1") == true);
|
||||
Q_ASSERT(isVersionNewer("1.2.0", "1.1") == true);
|
||||
Q_ASSERT(isVersionNewer("1.1", "1") == true);
|
||||
Q_ASSERT(isVersionNewer("10.1", "2") == true);
|
||||
Q_ASSERT(isVersionNewer("0.10", "0.2") == true);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user