commit
a89400a4e5
98
client/fieldedit.cpp
Normal file
98
client/fieldedit.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
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 "fieldedit.h"
|
||||
|
||||
FieldEdit::FieldEdit(QWidget *parent)
|
||||
: QLineEdit(parent)
|
||||
{
|
||||
setType(kUInt64);
|
||||
}
|
||||
|
||||
void FieldEdit::setType(FieldType type)
|
||||
{
|
||||
// clear existing contents before changing the validator
|
||||
clear();
|
||||
setPlaceholderText("");
|
||||
|
||||
type_ = type;
|
||||
switch (type_) {
|
||||
case kUInt64:
|
||||
setValidator(&uint64Validator_);
|
||||
if (isMaskMode_)
|
||||
setText("0xFFFFFFFFFFFFFFFF");
|
||||
break;
|
||||
case kMacAddress:
|
||||
setValidator(&macValidator_);
|
||||
setPlaceholderText("00:00:00:00:00:00");
|
||||
if (isMaskMode_)
|
||||
setText("FF:FF:FF:FF:FF:FF");
|
||||
break;
|
||||
case kIp4Address:
|
||||
setValidator(&ip4Validator_);
|
||||
setPlaceholderText("0.0.0.0");
|
||||
if (isMaskMode_)
|
||||
setText("255.255.255.255");
|
||||
break;
|
||||
case kIp6Address:
|
||||
setValidator(&ip6Validator_);
|
||||
setPlaceholderText("::");
|
||||
if (isMaskMode_)
|
||||
setText("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF");
|
||||
break;
|
||||
default:
|
||||
setValidator(nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Applicable only if type is kUInt64
|
||||
void FieldEdit::setRange(quint64 min, quint64 max)
|
||||
{
|
||||
uint64Validator_.setRange(min, max);
|
||||
if (type_ == kUInt64) {
|
||||
setPlaceholderText(QString("%1 - %2").arg(min).arg(max));
|
||||
if (isMaskMode_)
|
||||
setText(QString::number(max, 16).toUpper().prepend("0x"));
|
||||
}
|
||||
}
|
||||
|
||||
void FieldEdit::setMaskMode(bool maskMode)
|
||||
{
|
||||
isMaskMode_ = maskMode;
|
||||
}
|
||||
|
||||
QString FieldEdit::text() const
|
||||
{
|
||||
QString str = QLineEdit::text();
|
||||
|
||||
switch (type_) {
|
||||
case kMacAddress:
|
||||
str.remove(QRegularExpression("[:-]"));
|
||||
str.prepend("0x");
|
||||
break;
|
||||
case kIp4Address:
|
||||
str = QString::number(QHostAddress(str).toIPv4Address());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
60
client/fieldedit.h
Normal file
60
client/fieldedit.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 _FIELD_EDIT_H
|
||||
#define _FIELD_EDIT_H
|
||||
|
||||
#include "ipv4addressvalidator.h"
|
||||
#include "ipv6addressvalidator.h"
|
||||
#include "macaddressvalidator.h"
|
||||
#include "ulonglongvalidator.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
class FieldEdit: public QLineEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum FieldType {
|
||||
kUInt64,
|
||||
kMacAddress,
|
||||
kIp4Address,
|
||||
kIp6Address
|
||||
};
|
||||
FieldEdit(QWidget *parent = 0);
|
||||
|
||||
void setType(FieldType type);
|
||||
void setRange(quint64 min, quint64 max);
|
||||
|
||||
void setMaskMode(bool maskMode);
|
||||
|
||||
QString text() const;
|
||||
|
||||
private:
|
||||
FieldType type_{kUInt64};
|
||||
bool isMaskMode_{false};
|
||||
|
||||
IPv4AddressValidator ip4Validator_;
|
||||
IPv6AddressValidator ip6Validator_;
|
||||
MacAddressValidator macValidator_;
|
||||
ULongLongValidator uint64Validator_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
230
client/findreplace.cpp
Normal file
230
client/findreplace.cpp
Normal file
@ -0,0 +1,230 @@
|
||||
/*
|
||||
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 "findreplace.h"
|
||||
|
||||
#include "abstractprotocol.h"
|
||||
#include "iputils.h"
|
||||
#include "mandatoryfieldsgroup.h"
|
||||
#include "protocolmanager.h"
|
||||
#include "stream.h"
|
||||
#include "uint128.h"
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
extern ProtocolManager *OstProtocolManager;
|
||||
|
||||
// TODO: It might be useful for this dialog to support a "preview"
|
||||
// of the replacements
|
||||
|
||||
FindReplaceDialog::FindReplaceDialog(Action *action, QWidget *parent)
|
||||
: QDialog(parent), action_(action)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
findMask->setMaskMode(true);
|
||||
replaceMask->setMaskMode(true);
|
||||
|
||||
// Keep things simple and don't use mask(s) (default)
|
||||
useFindMask->setChecked(false);
|
||||
useReplaceMask->setChecked(false);
|
||||
|
||||
// TODO: remove combo protocols - see note in StreamBase::findReplace
|
||||
QStringList protocolList = OstProtocolManager->protocolDatabase();
|
||||
protocolList.sort();
|
||||
protocol->addItems(protocolList);
|
||||
|
||||
// Enable this setting if we have streams selected on input
|
||||
selectedStreamsOnly->setEnabled(action->selectedStreamsOnly);
|
||||
|
||||
// Reset for user input
|
||||
action->selectedStreamsOnly = false;
|
||||
|
||||
QPushButton *ok = buttonBox->button(QDialogButtonBox::Ok);
|
||||
ok->setText(tr("Replace All"));
|
||||
ok->setDisabled(true);
|
||||
|
||||
mandatoryFields_ = new MandatoryFieldsGroup(this);
|
||||
mandatoryFields_->add(protocol);
|
||||
mandatoryFields_->add(field);
|
||||
mandatoryFields_->add(findValue);
|
||||
mandatoryFields_->add(findMask);
|
||||
mandatoryFields_->add(replaceValue);
|
||||
mandatoryFields_->add(replaceMask);
|
||||
mandatoryFields_->setSubmitButton(ok);
|
||||
}
|
||||
|
||||
FindReplaceDialog::~FindReplaceDialog()
|
||||
{
|
||||
delete mandatoryFields_;
|
||||
}
|
||||
|
||||
void FindReplaceDialog::on_protocol_currentIndexChanged(const QString &name)
|
||||
{
|
||||
field->clear();
|
||||
fieldAttrib_.clear();
|
||||
|
||||
Stream stream;
|
||||
AbstractProtocol *protocol = OstProtocolManager->createProtocol(
|
||||
name, &stream);
|
||||
int count = protocol->fieldCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
// XXX: It might be useful to support meta fields too, later!
|
||||
if (!protocol->fieldFlags(i).testFlag(AbstractProtocol::FrameField))
|
||||
continue;
|
||||
|
||||
int bitSize = protocol->fieldData(i, AbstractProtocol::FieldBitSize)
|
||||
.toInt();
|
||||
if (bitSize <= 0) // skip optional fields
|
||||
continue;
|
||||
|
||||
FieldAttrib fieldAttrib;
|
||||
fieldAttrib.index = i; // fieldIndex
|
||||
fieldAttrib.bitSize = bitSize;
|
||||
|
||||
// field and fieldAttrib_ have same count and order of fields
|
||||
fieldAttrib_.append(fieldAttrib);
|
||||
field->addItem(protocol->fieldData(i, AbstractProtocol::FieldName)
|
||||
.toString());
|
||||
}
|
||||
|
||||
protocolId_ = protocol->protocolNumber();
|
||||
delete protocol;
|
||||
}
|
||||
|
||||
void FindReplaceDialog::on_field_currentIndexChanged(int index)
|
||||
{
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
QString fieldName = field->currentText();
|
||||
FieldAttrib fieldAttrib = fieldAttrib_.at(index);
|
||||
|
||||
// Use heuristics to determine field type
|
||||
if (fieldAttrib.bitSize == 48) {
|
||||
findMask->setType(FieldEdit::kMacAddress);
|
||||
findValue->setType(FieldEdit::kMacAddress);
|
||||
replaceMask->setType(FieldEdit::kMacAddress);
|
||||
replaceValue->setType(FieldEdit::kMacAddress);
|
||||
} else if ((fieldAttrib.bitSize == 32)
|
||||
&& (fieldName.contains(QRegularExpression(
|
||||
"address|source|destination",
|
||||
QRegularExpression::CaseInsensitiveOption)))) {
|
||||
findMask->setType(FieldEdit::kIp4Address);
|
||||
findValue->setType(FieldEdit::kIp4Address);
|
||||
replaceMask->setType(FieldEdit::kIp4Address);
|
||||
replaceValue->setType(FieldEdit::kIp4Address);
|
||||
} else if ((fieldAttrib.bitSize == 128)
|
||||
&& (fieldName.contains(QRegularExpression(
|
||||
"address|source|destination",
|
||||
QRegularExpression::CaseInsensitiveOption)))) {
|
||||
findMask->setType(FieldEdit::kIp6Address);
|
||||
findValue->setType(FieldEdit::kIp6Address);
|
||||
replaceMask->setType(FieldEdit::kIp6Address);
|
||||
replaceValue->setType(FieldEdit::kIp6Address);
|
||||
} else {
|
||||
quint64 max = quint64(~0) >> (64-fieldAttrib.bitSize);
|
||||
qDebug("XXXXXX %s bitSize %d max %llx",
|
||||
qPrintable(field->currentText()),
|
||||
fieldAttrib.bitSize, max);
|
||||
|
||||
findMask->setType(FieldEdit::kUInt64);
|
||||
findMask->setRange(0, max);
|
||||
findValue->setType(FieldEdit::kUInt64);
|
||||
findValue->setRange(0, max);
|
||||
|
||||
replaceMask->setType(FieldEdit::kUInt64);
|
||||
replaceMask->setRange(0, max);
|
||||
replaceValue->setType(FieldEdit::kUInt64);
|
||||
replaceValue->setRange(0, max);
|
||||
}
|
||||
}
|
||||
|
||||
void FindReplaceDialog::on_matchAny_toggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
findValueLabel->setHidden(true);
|
||||
findValue->setHidden(true);
|
||||
useFindMask->setHidden(true);
|
||||
findMask->setHidden(true);
|
||||
findMaskHint->setHidden(true);
|
||||
} else {
|
||||
findValueLabel->setVisible(true);
|
||||
findValue->setVisible(true);
|
||||
useFindMask->setVisible(true);
|
||||
if (useFindMask->isChecked()) {
|
||||
findMask->setVisible(true);
|
||||
findMaskHint->setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FindReplaceDialog::on_buttonBox_accepted()
|
||||
{
|
||||
FieldAttrib fieldAttrib = fieldAttrib_.at(field->currentIndex());
|
||||
action_->protocolField = QString("%1 %2")
|
||||
.arg(protocol->currentText())
|
||||
.arg(field->currentText());
|
||||
action_->protocolNumber = protocolId_;
|
||||
action_->fieldIndex = fieldAttrib.index;
|
||||
action_->fieldBitSize = fieldAttrib.bitSize;
|
||||
|
||||
if (fieldAttrib.bitSize == 128) { // IPv6 address
|
||||
if (matchAny->isChecked()) {
|
||||
action_->findMask.setValue(UInt128(0));
|
||||
action_->findValue.setValue(UInt128(0));
|
||||
} else {
|
||||
action_->findMask.setValue(
|
||||
useFindMask->isChecked() ?
|
||||
ipUtils::ip6StringToUInt128(findMask->text()) :
|
||||
~UInt128(0));
|
||||
action_->findValue.setValue(
|
||||
ipUtils::ip6StringToUInt128(findValue->text()));
|
||||
}
|
||||
|
||||
action_->replaceMask.setValue(
|
||||
useReplaceMask->isChecked() ?
|
||||
ipUtils::ip6StringToUInt128(replaceMask->text()) :
|
||||
~UInt128(0));
|
||||
action_->replaceValue.setValue(
|
||||
ipUtils::ip6StringToUInt128(replaceValue->text()));
|
||||
} else { // everything else
|
||||
if (matchAny->isChecked()) {
|
||||
action_->findMask.setValue(0);
|
||||
action_->findValue.setValue(0);
|
||||
} else {
|
||||
action_->findMask.setValue(
|
||||
useFindMask->isChecked() ?
|
||||
findMask->text().toULongLong(nullptr, 0) :
|
||||
~quint64(0));
|
||||
action_->findValue.setValue(
|
||||
findValue->text().toULongLong(nullptr, 0));
|
||||
}
|
||||
|
||||
action_->replaceMask.setValue(
|
||||
useReplaceMask->isChecked() ?
|
||||
replaceMask->text().toULongLong(nullptr, 0) :
|
||||
~quint64(0));
|
||||
action_->replaceValue.setValue(QString::number(
|
||||
replaceValue->text().toULongLong(nullptr, 0)));
|
||||
}
|
||||
|
||||
action_->selectedStreamsOnly = selectedStreamsOnly->isChecked();
|
||||
}
|
||||
|
71
client/findreplace.h
Normal file
71
client/findreplace.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
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 _FIND_REPLACE_H
|
||||
#define _FIND_REPLACE_H
|
||||
|
||||
#include "ui_findreplace.h"
|
||||
|
||||
class MandatoryFieldsGroup;
|
||||
|
||||
class FindReplaceDialog: public QDialog, public Ui::FindReplace
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct Action;
|
||||
|
||||
FindReplaceDialog(Action *action, QWidget *parent = 0);
|
||||
~FindReplaceDialog();
|
||||
|
||||
private slots:
|
||||
void on_protocol_currentIndexChanged(const QString &name);
|
||||
void on_field_currentIndexChanged(int index);
|
||||
void on_matchAny_toggled(bool checked);
|
||||
void on_buttonBox_accepted();
|
||||
|
||||
private:
|
||||
struct FieldAttrib;
|
||||
|
||||
quint32 protocolId_{0};
|
||||
Action *action_{nullptr};
|
||||
QList<FieldAttrib> fieldAttrib_;
|
||||
MandatoryFieldsGroup *mandatoryFields_{nullptr};
|
||||
};
|
||||
|
||||
struct FindReplaceDialog::Action
|
||||
{
|
||||
QString protocolField;
|
||||
quint32 protocolNumber;
|
||||
quint32 fieldIndex;
|
||||
int fieldBitSize;
|
||||
QVariant findValue;
|
||||
QVariant findMask;
|
||||
QVariant replaceValue;
|
||||
QVariant replaceMask;
|
||||
|
||||
bool selectedStreamsOnly; // in-out param
|
||||
};
|
||||
|
||||
struct FindReplaceDialog::FieldAttrib
|
||||
{
|
||||
quint32 index;
|
||||
int bitSize;
|
||||
};
|
||||
#endif
|
||||
|
265
client/findreplace.ui
Normal file
265
client/findreplace.ui
Normal file
@ -0,0 +1,265 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FindReplace</class>
|
||||
<widget class="QDialog" name="FindReplace">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>361</width>
|
||||
<height>309</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Find & Replace</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/find.png</normaloff>:/icons/find.png</iconset>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="find">
|
||||
<property name="title">
|
||||
<string>Find</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1,0">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="protocolLabel">
|
||||
<property name="text">
|
||||
<string>Protocol</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>protocol</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QComboBox" name="protocol"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="fieldLabel">
|
||||
<property name="text">
|
||||
<string>Field</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>field</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QComboBox" name="field"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="findValueLabel">
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="2">
|
||||
<widget class="FieldEdit" name="findValue"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="useFindMask">
|
||||
<property name="text">
|
||||
<string>Mask</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="FieldEdit" name="findMask"/>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QLabel" name="findMaskHint">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p align="center">Matches a field only if <span style="white-space:nowrap">(FieldValue &amp; FindMask) = FindValue</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="ostinato.qrc">:/icons/info.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="matchAny">
|
||||
<property name="text">
|
||||
<string>Match any value</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="replace">
|
||||
<property name="title">
|
||||
<string>Replace with</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1,0">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="replaceValueLabel">
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="FieldEdit" name="replaceValue"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="useReplaceMask">
|
||||
<property name="text">
|
||||
<string>Mask</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="FieldEdit" name="replaceMask"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="replaceMaskHint">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p align="center">New field value = <span style="white-space:nowrap">(OldFieldValue &amp; ~ReplaceMask) | (ReplaceValue &amp; ReplaceMask)</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="ostinato.qrc">:/icons/info.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="selectedStreamsOnly">
|
||||
<property name="text">
|
||||
<string>Selected Streams Only</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>FieldEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>fieldedit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="ostinato.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>FindReplace</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>277</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>FindReplace</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>283</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>useFindMask</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>findMask</receiver>
|
||||
<slot>setVisible(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>60</x>
|
||||
<y>115</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>76</x>
|
||||
<y>119</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>useReplaceMask</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>replaceMask</receiver>
|
||||
<slot>setVisible(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>56</x>
|
||||
<y>228</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>73</x>
|
||||
<y>227</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>useReplaceMask</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>replaceMaskHint</receiver>
|
||||
<slot>setVisible(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>50</x>
|
||||
<y>230</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>333</x>
|
||||
<y>233</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>useFindMask</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>findMaskHint</receiver>
|
||||
<slot>setVisible(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>46</x>
|
||||
<y>116</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>335</x>
|
||||
<y>122</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
BIN
client/icons/find.png
Normal file
BIN
client/icons/find.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 659 B |
BIN
client/icons/info.png
Normal file
BIN
client/icons/info.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 778 B |
126
client/mandatoryfieldsgroup.cpp
Normal file
126
client/mandatoryfieldsgroup.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
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 "mandatoryfieldsgroup.h"
|
||||
|
||||
// No need for QDateEdit, QSpinBox, etc., since these always return values
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
void MandatoryFieldsGroup::add(QWidget *widget)
|
||||
{
|
||||
if (!widgets_.contains(widget)) {
|
||||
if (widget->inherits("QCheckBox"))
|
||||
connect(qobject_cast<QCheckBox*>(widget),
|
||||
SIGNAL(clicked()),
|
||||
this, SLOT(changed()));
|
||||
else if (widget->inherits("QComboBox"))
|
||||
connect(qobject_cast<QComboBox*>(widget),
|
||||
SIGNAL(highlighted(int)),
|
||||
this, SLOT(changed()));
|
||||
else if (widget->inherits("QLineEdit"))
|
||||
connect(qobject_cast<QLineEdit*>(widget),
|
||||
SIGNAL(textChanged(const QString&)),
|
||||
this, SLOT(changed()));
|
||||
else {
|
||||
qWarning("MandatoryFieldsGroup: unsupported class %s",
|
||||
widget->metaObject()->className());
|
||||
return;
|
||||
}
|
||||
|
||||
widgets_.append(widget);
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
void MandatoryFieldsGroup::remove(QWidget *widget)
|
||||
{
|
||||
widgets_.removeAll(widget);
|
||||
|
||||
changed();
|
||||
}
|
||||
|
||||
void MandatoryFieldsGroup::setSubmitButton(QPushButton *button)
|
||||
{
|
||||
if (submitButton_ && submitButton_ != button)
|
||||
submitButton_->setEnabled(true);
|
||||
submitButton_ = button;
|
||||
|
||||
changed();
|
||||
}
|
||||
|
||||
void MandatoryFieldsGroup::changed()
|
||||
{
|
||||
if (!submitButton_)
|
||||
return;
|
||||
|
||||
bool enable = true;
|
||||
for (auto widget : widgets_) {
|
||||
// Invisible mandatory widgets are treated as non-mandatory
|
||||
if (!widget->isVisible())
|
||||
continue;
|
||||
|
||||
if (widget->inherits("QCheckBox")) {
|
||||
// Makes sense only for tristate checkbox
|
||||
auto checkBox = qobject_cast<const QCheckBox*>(widget);
|
||||
if (checkBox->checkState() == Qt::PartiallyChecked) {
|
||||
enable = false;
|
||||
break;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
|
||||
if (widget->inherits("QComboBox")) {
|
||||
auto comboBox = qobject_cast<const QComboBox*>(widget);
|
||||
if (comboBox->currentText().isEmpty()) {
|
||||
enable = false;
|
||||
break;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
|
||||
if (widget->inherits("QLineEdit")) {
|
||||
auto lineEdit = qobject_cast<const QLineEdit*>(widget);
|
||||
if (lineEdit->text().isEmpty()
|
||||
|| !lineEdit->hasAcceptableInput()) {
|
||||
enable = false;
|
||||
break;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
submitButton_->setEnabled(enable);
|
||||
}
|
||||
|
||||
void MandatoryFieldsGroup::clear()
|
||||
{
|
||||
widgets_.clear();
|
||||
|
||||
if (submitButton_) {
|
||||
submitButton_->setEnabled(true);
|
||||
submitButton_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
59
client/mandatoryfieldsgroup.h
Normal file
59
client/mandatoryfieldsgroup.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
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 _MANDATORY_FIELDS_GROUP_H
|
||||
#define _MANDATORY_FIELDS_GROUP_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
|
||||
class QPushButton;
|
||||
class QWidget;
|
||||
|
||||
// Adapted from https://doc.qt.io/archives/qq/qq11-mandatoryfields.html
|
||||
// and improved
|
||||
|
||||
class MandatoryFieldsGroup : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MandatoryFieldsGroup(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void add(QWidget *widget);
|
||||
void remove(QWidget *widget);
|
||||
void setSubmitButton(QPushButton *button);
|
||||
|
||||
public slots:
|
||||
void clear();
|
||||
|
||||
private slots:
|
||||
void changed();
|
||||
|
||||
private:
|
||||
QList<const QWidget*> widgets_;
|
||||
QPushButton *submitButton_{nullptr};
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -41,10 +41,13 @@ HEADERS += \
|
||||
devicemodel.h \
|
||||
deviceswidget.h \
|
||||
dumpview.h \
|
||||
fieldedit.h \
|
||||
hexlineedit.h \
|
||||
logsmodel.h \
|
||||
logswindow.h \
|
||||
findreplace.h \
|
||||
mainwindow.h \
|
||||
mandatoryfieldsgroup.h \
|
||||
ndpstatusmodel.h \
|
||||
packetmodel.h \
|
||||
port.h \
|
||||
@ -74,6 +77,7 @@ FORMS += \
|
||||
about.ui \
|
||||
devicegroupdialog.ui \
|
||||
deviceswidget.ui \
|
||||
findreplace.ui \
|
||||
logswindow.ui \
|
||||
mainwindow.ui \
|
||||
portconfigdialog.ui \
|
||||
@ -99,8 +103,11 @@ SOURCES += \
|
||||
hexlineedit.cpp \
|
||||
logsmodel.cpp \
|
||||
logswindow.cpp \
|
||||
fieldedit.cpp \
|
||||
findreplace.cpp \
|
||||
main.cpp \
|
||||
mainwindow.cpp \
|
||||
mandatoryfieldsgroup.cpp \
|
||||
ndpstatusmodel.cpp \
|
||||
packetmodel.cpp \
|
||||
params.cpp \
|
||||
|
@ -1,5 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>icons/find.png</file>
|
||||
<file>icons/info.png</file>
|
||||
<file>icons/about.png</file>
|
||||
<file>icons/add.png</file>
|
||||
<file>icons/anime_error.gif</file>
|
||||
|
@ -609,7 +609,6 @@ bool Port::openStreams(QString fileName, bool append, QString &error)
|
||||
int ret;
|
||||
optDialog->setParent(mainWindow, Qt::Dialog);
|
||||
ret = optDialog->exec();
|
||||
optDialog->setParent(0, Qt::Dialog);
|
||||
if (ret == QDialog::Rejected)
|
||||
goto _user_opt_cancel;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "streamswidget.h"
|
||||
|
||||
#include "clipboardhelper.h"
|
||||
#include "findreplace.h"
|
||||
#include "portgrouplist.h"
|
||||
#include "streamconfigdialog.h"
|
||||
#include "streamfileformat.h"
|
||||
@ -52,6 +53,12 @@ StreamsWidget::StreamsWidget(QWidget *parent)
|
||||
sep2->setSeparator(true);
|
||||
tvStreamList->addAction(sep2);
|
||||
|
||||
tvStreamList->addAction(actionFind_Replace);
|
||||
|
||||
QAction *sep3 = new QAction(this);
|
||||
sep3->setSeparator(true);
|
||||
tvStreamList->addAction(sep3);
|
||||
|
||||
tvStreamList->addAction(actionOpen_Streams);
|
||||
tvStreamList->addAction(actionSave_Streams);
|
||||
|
||||
@ -61,9 +68,9 @@ StreamsWidget::StreamsWidget(QWidget *parent)
|
||||
// Add the clipboard actions to the context menu of streamList
|
||||
// but not to StreamsWidget's actions since they are already available
|
||||
// in the global Edit Menu
|
||||
QAction *sep3 = new QAction("Clipboard", this);
|
||||
sep3->setSeparator(true);
|
||||
tvStreamList->insertAction(sep2, sep3);
|
||||
QAction *sep4 = new QAction("Clipboard", this);
|
||||
sep4->setSeparator(true);
|
||||
tvStreamList->insertAction(sep2, sep4);
|
||||
tvStreamList->insertActions(sep2, clipboardHelper->actions());
|
||||
}
|
||||
|
||||
@ -186,6 +193,9 @@ void StreamsWidget::updateStreamViewActions()
|
||||
actionDuplicate_Stream->setDisabled(true);
|
||||
actionDelete_Stream->setDisabled(true);
|
||||
}
|
||||
|
||||
actionFind_Replace->setEnabled(tvStreamList->model()->rowCount() > 0);
|
||||
|
||||
actionOpen_Streams->setEnabled(plm->isPort(currentPortIndex_));
|
||||
actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0);
|
||||
}
|
||||
@ -290,6 +300,76 @@ void StreamsWidget::on_actionDelete_Stream_triggered()
|
||||
qDebug("No selection");
|
||||
}
|
||||
|
||||
void StreamsWidget::on_actionFind_Replace_triggered()
|
||||
{
|
||||
qDebug("Find & Replace Action");
|
||||
|
||||
QItemSelectionModel* selectionModel = tvStreamList->selectionModel();
|
||||
FindReplaceDialog::Action action;
|
||||
|
||||
action.selectedStreamsOnly = selectionModel->selection().size() > 0 ?
|
||||
true : false;
|
||||
|
||||
FindReplaceDialog findReplace(&action, this);
|
||||
if (findReplace.exec() == QDialog::Accepted) {
|
||||
QProgressDialog progress(this);
|
||||
progress.setLabelText(tr("Replacing %1 ...").arg(action.protocolField));
|
||||
progress.setWindowModality(Qt::WindowModal);
|
||||
int c, fc = 0, sc = 0; // replace counts
|
||||
Port &port = plm->port(currentPortIndex_);
|
||||
if (action.selectedStreamsOnly) {
|
||||
QModelIndexList selected = selectionModel->selectedRows();
|
||||
int count = selected.size();
|
||||
progress.setMaximum(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
QModelIndex index = selected.at(i);
|
||||
Stream *stream = port.mutableStreamByIndex(index.row(), false);
|
||||
c = stream->protocolFieldReplace(action.protocolNumber,
|
||||
action.fieldIndex,
|
||||
action.fieldBitSize,
|
||||
action.findValue,
|
||||
action.findMask,
|
||||
action.replaceValue,
|
||||
action.replaceMask);
|
||||
if (c) {
|
||||
fc += c;
|
||||
sc++;
|
||||
}
|
||||
progress.setValue(i+1);
|
||||
if (progress.wasCanceled())
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
int count = tvStreamList->model()->rowCount();
|
||||
progress.setMaximum(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
Stream *stream = port.mutableStreamByIndex(i, false);
|
||||
c = stream->protocolFieldReplace(action.protocolNumber,
|
||||
action.fieldIndex,
|
||||
action.fieldBitSize,
|
||||
action.findValue,
|
||||
action.findMask,
|
||||
action.replaceValue,
|
||||
action.replaceMask);
|
||||
if (c) {
|
||||
fc += c;
|
||||
sc++;
|
||||
}
|
||||
progress.setValue(i+1);
|
||||
if (progress.wasCanceled())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fc)
|
||||
port.setLocalConfigChanged(true);
|
||||
|
||||
QMessageBox::information(this, tr("Find & Replace"),
|
||||
tr("%1 fields replaced in %2 streams").arg(fc).arg(sc));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void StreamsWidget::on_actionOpen_Streams_triggered()
|
||||
{
|
||||
qDebug("Open Streams Action");
|
||||
|
@ -49,6 +49,8 @@ private slots:
|
||||
void on_actionDuplicate_Stream_triggered();
|
||||
void on_actionDelete_Stream_triggered();
|
||||
|
||||
void on_actionFind_Replace_triggered();
|
||||
|
||||
void on_actionOpen_Streams_triggered();
|
||||
void on_actionSave_Streams_triggered();
|
||||
|
||||
|
@ -105,6 +105,18 @@ Right-click to create a stream</string>
|
||||
<string>Duplicate Stream</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFind_Replace">
|
||||
<property name="icon">
|
||||
<iconset resource="ostinato.qrc">
|
||||
<normaloff>:/icons/find.png</normaloff>:/icons/find.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Find && Replace</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Find & Replace protocol field values</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
@ -671,6 +671,7 @@ bool GmpProtocol::setFieldData(int index, const QVariant &value,
|
||||
ba.append(QByteArray(4 - (ba.size() % 4), char(0)));
|
||||
rec->set_aux_data(std::string(ba.constData(), ba.size()));
|
||||
}
|
||||
isOk = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ void GmpConfigForm::loadWidget(AbstractProtocol *proto)
|
||||
groupAddress->setText(
|
||||
proto->fieldData(
|
||||
GmpProtocol::kGroupAddress,
|
||||
AbstractProtocol::FieldValue
|
||||
AbstractProtocol::FieldTextValue
|
||||
).toString());
|
||||
groupMode->setCurrentIndex(
|
||||
proto->fieldData(
|
||||
|
@ -56,7 +56,7 @@ void PdmlIcmpProtocol::preProtocolHandler(QString name,
|
||||
else if (name == "icmpv6")
|
||||
icmp->set_icmp_version(OstProto::Icmp::kIcmp6);
|
||||
|
||||
icmp->set_is_override_checksum(true);
|
||||
icmp->set_is_override_checksum(overrideCksum_);
|
||||
|
||||
icmp->set_type(kIcmpInvalidType);
|
||||
}
|
||||
|
@ -134,6 +134,7 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib,
|
||||
case FieldName:
|
||||
return QString("Group Address");
|
||||
case FieldValue:
|
||||
return grpIp;
|
||||
case FieldTextValue:
|
||||
return QHostAddress(grpIp).toString();
|
||||
case FieldFrameValue:
|
||||
@ -289,8 +290,14 @@ bool IgmpProtocol::setFieldData(int index, const QVariant &value,
|
||||
}
|
||||
case kGroupAddress:
|
||||
{
|
||||
quint32 ip = value.toUInt(&isOk);
|
||||
if (isOk) {
|
||||
data.mutable_group_address()->set_v4(ip);
|
||||
break;
|
||||
}
|
||||
|
||||
QHostAddress addr(value.toString());
|
||||
quint32 ip = addr.toIPv4Address();
|
||||
ip = addr.toIPv4Address();
|
||||
isOk = (addr.protocol() == QAbstractSocket::IPv4Protocol);
|
||||
if (isOk)
|
||||
data.mutable_group_address()->set_v4(ip);
|
||||
@ -306,6 +313,7 @@ bool IgmpProtocol::setFieldData(int index, const QVariant &value,
|
||||
quint32 ip = QHostAddress(str).toIPv4Address();
|
||||
data.add_sources()->set_v4(ip);
|
||||
}
|
||||
isOk = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -332,6 +340,7 @@ bool IgmpProtocol::setFieldData(int index, const QVariant &value,
|
||||
QHostAddress(src).toIPv4Address());
|
||||
}
|
||||
}
|
||||
isOk = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ void PdmlIgmpProtocol::preProtocolHandler(QString /*name*/,
|
||||
OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp);
|
||||
|
||||
igmp->set_is_override_rsvd_code(true);
|
||||
igmp->set_is_override_checksum(true);
|
||||
igmp->set_is_override_checksum(overrideCksum_);
|
||||
igmp->set_is_override_source_count(true);
|
||||
igmp->set_is_override_group_record_count(true);
|
||||
|
||||
|
@ -88,6 +88,6 @@ void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto,
|
||||
ip4->set_is_override_hdrlen(true);
|
||||
ip4->set_is_override_totlen(true);
|
||||
ip4->set_is_override_proto(true);
|
||||
ip4->set_is_override_cksum(true);
|
||||
ip4->set_is_override_cksum(overrideCksum_);
|
||||
}
|
||||
|
||||
|
@ -354,6 +354,11 @@ QVariant Ip6Protocol::fieldData(int index, FieldAttrib attrib,
|
||||
case FieldName:
|
||||
return QString("Source");
|
||||
case FieldValue:
|
||||
{
|
||||
QVariant v;
|
||||
v.setValue(src);
|
||||
return v;
|
||||
}
|
||||
case FieldFrameValue:
|
||||
case FieldTextValue:
|
||||
{
|
||||
@ -412,6 +417,11 @@ QVariant Ip6Protocol::fieldData(int index, FieldAttrib attrib,
|
||||
case FieldName:
|
||||
return QString("Destination");
|
||||
case FieldValue:
|
||||
{
|
||||
QVariant v;
|
||||
v.setValue(dst);
|
||||
return v;
|
||||
}
|
||||
case FieldFrameValue:
|
||||
case FieldTextValue:
|
||||
{
|
||||
@ -594,6 +604,14 @@ bool Ip6Protocol::setFieldData(int index, const QVariant &value,
|
||||
}
|
||||
case ip6_srcAddress:
|
||||
{
|
||||
if (value.typeName() == QString("UInt128")) {
|
||||
UInt128 addr = value.value<UInt128>();
|
||||
data.set_src_addr_hi(addr.hi64());
|
||||
data.set_src_addr_lo(addr.lo64());
|
||||
isOk = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address();
|
||||
quint64 x;
|
||||
|
||||
@ -616,10 +634,19 @@ bool Ip6Protocol::setFieldData(int index, const QVariant &value,
|
||||
| (quint64(addr[14]) << 8)
|
||||
| (quint64(addr[15]) << 0);
|
||||
data.set_src_addr_lo(x);
|
||||
isOk = true;
|
||||
break;
|
||||
}
|
||||
case ip6_dstAddress:
|
||||
{
|
||||
if (value.typeName() == QString("UInt128")) {
|
||||
UInt128 addr = value.value<UInt128>();
|
||||
data.set_dst_addr_hi(addr.hi64());
|
||||
data.set_dst_addr_lo(addr.lo64());
|
||||
isOk = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address();
|
||||
quint64 x;
|
||||
|
||||
@ -642,6 +669,7 @@ bool Ip6Protocol::setFieldData(int index, const QVariant &value,
|
||||
| (quint64(addr[14]) << 8)
|
||||
| (quint64(addr[15]) << 0);
|
||||
data.set_dst_addr_lo(x);
|
||||
isOk = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#ifndef _IP_UTILS_H
|
||||
#define _IP_UTILS_H
|
||||
|
||||
#include "uint128.h"
|
||||
|
||||
#include <QHostAddress>
|
||||
|
||||
namespace ipUtils {
|
||||
enum AddrMode {
|
||||
kFixed = 0,
|
||||
@ -118,5 +122,31 @@ void inline ipAddress(quint64 baseIpHi, quint64 baseIpLo, int prefix,
|
||||
}
|
||||
}
|
||||
|
||||
UInt128 inline ip6StringToUInt128(QString ip6Str)
|
||||
{
|
||||
Q_IPV6ADDR addr = QHostAddress(ip6Str).toIPv6Address();
|
||||
quint64 hi, lo;
|
||||
|
||||
hi = (quint64(addr[0]) << 56)
|
||||
| (quint64(addr[1]) << 48)
|
||||
| (quint64(addr[2]) << 40)
|
||||
| (quint64(addr[3]) << 32)
|
||||
| (quint64(addr[4]) << 24)
|
||||
| (quint64(addr[5]) << 16)
|
||||
| (quint64(addr[6]) << 8)
|
||||
| (quint64(addr[7]) << 0);
|
||||
|
||||
lo = (quint64(addr[ 8]) << 56)
|
||||
| (quint64(addr[ 9]) << 48)
|
||||
| (quint64(addr[10]) << 40)
|
||||
| (quint64(addr[11]) << 32)
|
||||
| (quint64(addr[12]) << 24)
|
||||
| (quint64(addr[13]) << 16)
|
||||
| (quint64(addr[14]) << 8)
|
||||
| (quint64(addr[15]) << 0);
|
||||
|
||||
return UInt128(hi, lo);
|
||||
}
|
||||
|
||||
} // namespace ipUtils
|
||||
#endif
|
||||
|
48
common/ipv4addressvalidator.h
Normal file
48
common/ipv4addressvalidator.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
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 _IPV4_ADDRESS_VALIDATOR_H
|
||||
#define _IPV4_ADDRESS_VALIDATOR_H
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QRegularExpressionValidator>
|
||||
|
||||
class IPv4AddressValidator : public QRegularExpressionValidator
|
||||
{
|
||||
public:
|
||||
IPv4AddressValidator(QObject *parent = 0)
|
||||
: QRegularExpressionValidator(
|
||||
QRegularExpression(
|
||||
"((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)(\\.(?!$)|$)){4}"),
|
||||
parent)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void fixup(QString &input) const
|
||||
{
|
||||
QStringList bytes = input.split('.', QString::SkipEmptyParts);
|
||||
|
||||
while (bytes.count() < 4)
|
||||
bytes.append("0");
|
||||
|
||||
input = bytes.join('.');
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -279,14 +279,16 @@ bool MacProtocol::setFieldData(int index, const QVariant &value,
|
||||
{
|
||||
case mac_dstAddr:
|
||||
{
|
||||
quint64 mac = value.toULongLong();
|
||||
data.set_dst_mac(mac);
|
||||
quint64 mac = value.toULongLong(&isOk);
|
||||
if (isOk)
|
||||
data.set_dst_mac(mac);
|
||||
break;
|
||||
}
|
||||
case mac_srcAddr:
|
||||
{
|
||||
quint64 mac = value.toULongLong();
|
||||
data.set_src_mac(mac);
|
||||
quint64 mac = value.toULongLong(&isOk);
|
||||
if (isOk)
|
||||
data.set_src_mac(mac);
|
||||
break;
|
||||
}
|
||||
|
||||
|
52
common/macaddressvalidator.h
Normal file
52
common/macaddressvalidator.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
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 _MAC_ADDRESS_VALIDATOR_H
|
||||
#define _MAC_ADDRESS_VALIDATOR_H
|
||||
|
||||
#include <QRegularExpressionValidator>
|
||||
|
||||
// Allow : or - as separator
|
||||
class MacAddressValidator : public QRegularExpressionValidator
|
||||
{
|
||||
public:
|
||||
MacAddressValidator(QObject *parent = 0)
|
||||
: QRegularExpressionValidator(
|
||||
QRegularExpression(
|
||||
"([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"),
|
||||
parent)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void fixup(QString &input) const
|
||||
{
|
||||
QStringList bytes = input.split(QRegularExpression("[:-]"),
|
||||
QString::SkipEmptyParts);
|
||||
|
||||
if (!bytes.isEmpty() && bytes.last().size() == 1)
|
||||
bytes.last().prepend("0");
|
||||
|
||||
while (bytes.count() < 6)
|
||||
bytes.append("00");
|
||||
|
||||
input = bytes.join(":");
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -195,6 +195,7 @@ QVariant MldProtocol::fieldData(int index, FieldAttrib attrib,
|
||||
case FieldName:
|
||||
return QString("Group Address");
|
||||
case FieldValue:
|
||||
return QVariant::fromValue(UInt128(grpHi, grpLo));
|
||||
case FieldTextValue:
|
||||
case FieldFrameValue:
|
||||
{
|
||||
@ -401,6 +402,14 @@ bool MldProtocol::setFieldData(int index, const QVariant &value,
|
||||
{
|
||||
case kGroupAddress:
|
||||
{
|
||||
if (value.typeName() == QString("UInt128")) {
|
||||
UInt128 addr = value.value<UInt128>();
|
||||
data.mutable_group_address()->set_v6_hi(addr.hi64());
|
||||
data.mutable_group_address()->set_v6_lo(addr.lo64());
|
||||
isOk = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address();
|
||||
quint64 x;
|
||||
|
||||
@ -423,6 +432,7 @@ bool MldProtocol::setFieldData(int index, const QVariant &value,
|
||||
| (quint64(addr[14]) << 8)
|
||||
| (quint64(addr[15]) << 0);
|
||||
data.mutable_group_address()->set_v6_lo(x);
|
||||
isOk = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -457,6 +467,7 @@ bool MldProtocol::setFieldData(int index, const QVariant &value,
|
||||
| (quint64(addr[15]) << 0);
|
||||
src->set_v6_lo(x);
|
||||
}
|
||||
isOk = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -524,7 +535,7 @@ bool MldProtocol::setFieldData(int index, const QVariant &value,
|
||||
src->set_v6_lo(x);
|
||||
}
|
||||
}
|
||||
|
||||
isOk = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ void PdmlMldProtocol::preProtocolHandler(QString /*name*/,
|
||||
OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld);
|
||||
|
||||
mld->set_is_override_rsvd_code(true);
|
||||
mld->set_is_override_checksum(true);
|
||||
mld->set_is_override_checksum(overrideCksum_);
|
||||
mld->set_is_override_source_count(true);
|
||||
mld->set_is_override_group_record_count(true);
|
||||
|
||||
|
@ -44,11 +44,19 @@ 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()));
|
||||
}
|
||||
|
||||
@ -59,6 +67,7 @@ PcapImportOptionsDialog::~PcapImportOptionsDialog()
|
||||
void PcapImportOptionsDialog::accept()
|
||||
{
|
||||
options_->insert("ViaPdml", viaPdml->isChecked());
|
||||
options_->insert("RecalculateCksums", recalculateCksums->isChecked());
|
||||
options_->insert("DoDiff", doDiff->isChecked());
|
||||
|
||||
QDialog::accept();
|
||||
@ -67,14 +76,12 @@ void PcapImportOptionsDialog::accept()
|
||||
PcapFileFormat::PcapFileFormat()
|
||||
{
|
||||
importOptions_.insert("ViaPdml", true);
|
||||
importOptions_.insert("RecalculateCksums", false);
|
||||
importOptions_.insert("DoDiff", true);
|
||||
|
||||
importDialog_ = NULL;
|
||||
}
|
||||
|
||||
PcapFileFormat::~PcapFileFormat()
|
||||
{
|
||||
delete importDialog_;
|
||||
}
|
||||
|
||||
bool PcapFileFormat::open(const QString fileName,
|
||||
@ -224,7 +231,7 @@ _retry:
|
||||
{
|
||||
QProcess tshark;
|
||||
QTemporaryFile pdmlFile;
|
||||
PdmlReader reader(&streams);
|
||||
PdmlReader reader(&streams, importOptions_);
|
||||
|
||||
if (!pdmlFile.open())
|
||||
{
|
||||
@ -705,10 +712,7 @@ _exit:
|
||||
|
||||
QDialog* PcapFileFormat::openOptionsDialog()
|
||||
{
|
||||
if (!importDialog_)
|
||||
importDialog_ = new PcapImportOptionsDialog(&importOptions_);
|
||||
|
||||
return importDialog_;
|
||||
return new PcapImportOptionsDialog(&importOptions_);
|
||||
}
|
||||
|
||||
bool PcapFileFormat::isMyFileFormat(const QString /*fileName*/)
|
||||
|
@ -81,7 +81,6 @@ private:
|
||||
|
||||
QDataStream fd_;
|
||||
QVariantMap importOptions_;
|
||||
PcapImportOptionsDialog *importDialog_;
|
||||
};
|
||||
|
||||
extern PcapFileFormat pcapFileFormat;
|
||||
|
@ -1,62 +1,69 @@
|
||||
<ui version="4.0" >
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PcapFileImport</class>
|
||||
<widget class="QDialog" name="PcapFileImport" >
|
||||
<property name="geometry" >
|
||||
<widget class="QDialog" name="PcapFileImport">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>326</width>
|
||||
<height>93</height>
|
||||
<height>132</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<property name="windowTitle">
|
||||
<string>PCAP import options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QCheckBox" name="viaPdml" >
|
||||
<property name="text" >
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="viaPdml">
|
||||
<property name="text">
|
||||
<string>Intelligent Import (via PDML)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
<widget class="QWidget" native="1" name="widget" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize" >
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="doDiff" >
|
||||
<property name="enabled" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Do a diff after import</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<item row="1" column="0" rowspan="2">
|
||||
<widget class="QWidget" name="indentSpacing" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||
<property name="orientation" >
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="recalculateCksums">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Recalculate Checksums</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="doDiff">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Do a diff after import</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<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>
|
||||
@ -70,11 +77,11 @@
|
||||
<receiver>PcapFileImport</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>249</x>
|
||||
<y>81</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>90</y>
|
||||
</hint>
|
||||
@ -86,11 +93,11 @@
|
||||
<receiver>PcapFileImport</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>249</x>
|
||||
<y>81</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>258</x>
|
||||
<y>90</y>
|
||||
</hint>
|
||||
@ -102,13 +109,13 @@
|
||||
<receiver>doDiff</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>15</x>
|
||||
<y>16</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>37</x>
|
||||
<y>42</y>
|
||||
<hint type="destinationlabel">
|
||||
<x>68</x>
|
||||
<y>71</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
@ -118,13 +125,45 @@
|
||||
<receiver>doDiff</receiver>
|
||||
<slot>setChecked(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>151</x>
|
||||
<y>14</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>150</x>
|
||||
<y>34</y>
|
||||
<hint type="destinationlabel">
|
||||
<x>181</x>
|
||||
<y>71</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>viaPdml</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>recalculateCksums</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>29</x>
|
||||
<y>17</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>38</x>
|
||||
<y>39</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>viaPdml</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>recalculateCksums</receiver>
|
||||
<slot>setChecked(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>67</x>
|
||||
<y>18</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>66</x>
|
||||
<y>33</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
|
@ -102,6 +102,11 @@ int PdmlProtocol::fieldId(QString name) const
|
||||
return fieldMap_.value(name);
|
||||
}
|
||||
|
||||
void PdmlProtocol::setRecalculateCksum(bool recalculate)
|
||||
{
|
||||
overrideCksum_ = !recalculate;
|
||||
}
|
||||
|
||||
/*!
|
||||
This method is called by PdmlReader before any fields within the protocol
|
||||
are processed. All attributes associated with the 'proto' tag in the PDML
|
||||
|
@ -40,6 +40,8 @@ public:
|
||||
bool hasField(QString name) const;
|
||||
int fieldId(QString name) const;
|
||||
|
||||
void setRecalculateCksum(bool recalculate);
|
||||
|
||||
virtual void preProtocolHandler(QString name,
|
||||
const QXmlStreamAttributes &attributes, int expectedPos,
|
||||
OstProto::Protocol *pbProto, OstProto::Stream *stream);
|
||||
@ -63,6 +65,8 @@ protected:
|
||||
int ostProtoId_;
|
||||
//!< Map of PDML field names to protobuf field numbers for 'known' fields
|
||||
QMap<QString, int> fieldMap_;
|
||||
|
||||
bool overrideCksum_{true};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -42,12 +42,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "udppdml.h"
|
||||
#include "vlanpdml.h"
|
||||
|
||||
PdmlReader::PdmlReader(OstProto::StreamConfigList *streams)
|
||||
PdmlReader::PdmlReader(OstProto::StreamConfigList *streams,
|
||||
const QVariantMap &options)
|
||||
{
|
||||
//gPdmlReader = this;
|
||||
pcap_ = NULL;
|
||||
streams_ = streams;
|
||||
|
||||
recalculateCksums_ = options.value("RecalculateCksums").toBool();
|
||||
|
||||
currentStream_ = NULL;
|
||||
prevStream_ = NULL;
|
||||
|
||||
@ -353,6 +356,8 @@ void PdmlReader::readProto()
|
||||
|
||||
pdmlProto = appendPdmlProto(protoName, &pbProto);
|
||||
|
||||
pdmlProto->setRecalculateCksum(recalculateCksums_);
|
||||
|
||||
qDebug("%s: preProtocolHandler(expPos = %d)",
|
||||
qPrintable(protoName), expPos_);
|
||||
pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, pbProto,
|
||||
|
@ -24,13 +24,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QVariantMap>
|
||||
|
||||
class PcapFileFormat;
|
||||
class PdmlReader : public QObject, public QXmlStreamReader
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PdmlReader(OstProto::StreamConfigList *streams);
|
||||
PdmlReader(OstProto::StreamConfigList *streams,
|
||||
const QVariantMap &options = QVariantMap());
|
||||
~PdmlReader();
|
||||
|
||||
bool read(QIODevice *device, PcapFileFormat *pcap = NULL,
|
||||
@ -64,6 +66,8 @@ private:
|
||||
PcapFileFormat *pcap_;
|
||||
QByteArray pktBuf_;
|
||||
|
||||
bool recalculateCksums_{false};
|
||||
|
||||
bool isMldSupport_;
|
||||
int packetCount_;
|
||||
int expPos_;
|
||||
|
@ -236,3 +236,33 @@ QStringList ProtocolManager::protocolDatabase()
|
||||
{
|
||||
return numberToNameMap.values();
|
||||
}
|
||||
|
||||
#if 0
|
||||
void ProtocolManager::showFieldAttribs()
|
||||
{
|
||||
QStringList protocolList = protocolDatabase();
|
||||
Stream stream;
|
||||
foreach(QString name, protocolList) {
|
||||
if (name.contains("/")) // assume combo
|
||||
continue;
|
||||
AbstractProtocol *protocol = OstProtocolManager->createProtocol(name, &stream);
|
||||
int count = protocol->fieldCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!protocol->fieldFlags(i).testFlag(AbstractProtocol::FrameField))
|
||||
continue;
|
||||
|
||||
uint bitSize = protocol->fieldData(i, AbstractProtocol::FieldBitSize)
|
||||
.toInt();
|
||||
qDebug("$$$$, %s, %d, %s, %u, %x, %llu",
|
||||
qPrintable(name),
|
||||
i,
|
||||
qPrintable(protocol->fieldData(i, AbstractProtocol::FieldName).toString()),
|
||||
bitSize,
|
||||
0, // min
|
||||
(1 << bitSize) - 1);
|
||||
}
|
||||
|
||||
delete protocol;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -87,7 +87,9 @@ void PdmlSampleProtocol::prematureEndHandler(int /*pos*/,
|
||||
fields such as length, checksum etc. may be correct or incorrect in the
|
||||
PCAP/PDML - to retain the same value as in the PCAP/PDML and not let
|
||||
Ostinato recalculate these, you can set the is_override_length,
|
||||
is_override_cksum meta-fields to true here
|
||||
is_override_cksum meta-fields to true here; for cksum, use the base
|
||||
class attribute overrideCksum_ to decide - this is set based on user
|
||||
input
|
||||
*/
|
||||
void PdmlSampleProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/,
|
||||
OstProto::Stream* /*stream*/)
|
||||
|
@ -18,11 +18,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "streambase.h"
|
||||
|
||||
#include "abstractprotocol.h"
|
||||
#include "framevalueattrib.h"
|
||||
#include "protocollist.h"
|
||||
#include "protocollistiterator.h"
|
||||
#include "protocolmanager.h"
|
||||
#include "uint128.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
extern ProtocolManager *OstProtocolManager;
|
||||
extern quint64 getDeviceMacAddress(int portId, int streamId, int frameIndex);
|
||||
@ -598,6 +602,64 @@ int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex,
|
||||
return len;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int StreamBase::findReplace(quint32 protocolNumber, int fieldIndex,
|
||||
QVariant findValue, QVariant findMask,
|
||||
QVariant replaceValue, QVariant replaceMask)
|
||||
{
|
||||
int replaceCount = 0;
|
||||
ProtocolListIterator *iter = createProtocolListIterator();
|
||||
|
||||
// FIXME: Because protocol list iterator is unaware of combo protocols
|
||||
// search for ip4.src will NOT succeed in a combo protocol containing ip4
|
||||
while (iter->hasNext()) {
|
||||
AbstractProtocol *proto = iter->next();
|
||||
if (proto->protocolNumber() != protocolNumber)
|
||||
continue;
|
||||
|
||||
T fieldValue = proto->fieldData(fieldIndex,
|
||||
AbstractProtocol::FieldValue).value<T>();
|
||||
qDebug() << "findReplace:"
|
||||
<< "stream" << mStreamId->id()
|
||||
<< "field" << fieldValue
|
||||
<< "findMask" << hex << findMask.value<T>() << dec
|
||||
<< "findValue" << findValue.value<T>();
|
||||
if ((fieldValue & findMask.value<T>()) == findValue.value<T>()) {
|
||||
T newValue = (fieldValue & ~replaceMask.value<T>())
|
||||
| (replaceValue.value<T>() & replaceMask.value<T>());
|
||||
qDebug() << "findReplace:"
|
||||
<< "replaceMask" << hex << replaceMask.value<T>() << dec
|
||||
<< "replaceValue" << replaceValue.value<T>()
|
||||
<< "newValue" << newValue;
|
||||
|
||||
QVariant nv;
|
||||
nv.setValue(newValue);
|
||||
if (proto->setFieldData(fieldIndex, nv))
|
||||
replaceCount++;
|
||||
}
|
||||
}
|
||||
delete iter;
|
||||
|
||||
return replaceCount;
|
||||
}
|
||||
|
||||
int StreamBase::protocolFieldReplace(quint32 protocolNumber,
|
||||
int fieldIndex, int fieldBitSize,
|
||||
QVariant findValue, QVariant findMask,
|
||||
QVariant replaceValue, QVariant replaceMask)
|
||||
{
|
||||
if (fieldBitSize <= 64)
|
||||
return findReplace<qulonglong>(protocolNumber, fieldIndex,
|
||||
findValue, findMask, replaceValue, replaceMask);
|
||||
|
||||
if (fieldBitSize == 128)
|
||||
return findReplace<UInt128>(protocolNumber, fieldIndex,
|
||||
findValue, findMask, replaceValue, replaceMask);
|
||||
|
||||
qWarning("Unknown find/replace type %d", findValue.type());
|
||||
return 0;
|
||||
}
|
||||
|
||||
quint64 StreamBase::deviceMacAddress(int frameIndex) const
|
||||
{
|
||||
return getDeviceMacAddress(portId_, int(mStreamId->id()), frameIndex);
|
||||
|
@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include <QString>
|
||||
#include <QLinkedList>
|
||||
#include <QVariant>
|
||||
|
||||
#include "protocol.pb.h"
|
||||
|
||||
@ -141,6 +142,11 @@ public:
|
||||
int frameValue(uchar *buf, int bufMaxSize, int frameIndex,
|
||||
FrameValueAttrib *attrib = nullptr) const;
|
||||
|
||||
int protocolFieldReplace(quint32 protocolNumber,
|
||||
int fieldIndex, int fieldBitSize,
|
||||
QVariant findValue, QVariant findMask,
|
||||
QVariant replaceValue, QVariant replaceMask);
|
||||
|
||||
quint64 deviceMacAddress(int frameIndex) const;
|
||||
quint64 neighborMacAddress(int frameIndex) const;
|
||||
|
||||
@ -149,6 +155,10 @@ public:
|
||||
static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
int findReplace(quint32 protocolNumber, int fieldIndex,
|
||||
QVariant findValue, QVariant findMask,
|
||||
QVariant replaceValue, QVariant replaceMask);
|
||||
int portId_;
|
||||
|
||||
OstProto::StreamId *mStreamId;
|
||||
|
@ -78,7 +78,7 @@ void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto,
|
||||
tcp->set_is_override_src_port(true);
|
||||
tcp->set_is_override_dst_port(true);
|
||||
tcp->set_is_override_hdrlen(true);
|
||||
tcp->set_is_override_cksum(true);
|
||||
tcp->set_is_override_cksum(overrideCksum_);
|
||||
|
||||
if (options_.size())
|
||||
{
|
||||
|
@ -48,6 +48,6 @@ void PdmlUdpProtocol::postProtocolHandler(OstProto::Protocol *pbProto,
|
||||
udp->set_is_override_src_port(true);
|
||||
udp->set_is_override_dst_port(true);
|
||||
udp->set_is_override_totlen(true);
|
||||
udp->set_is_override_cksum(true);
|
||||
udp->set_is_override_cksum(overrideCksum_);
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,7 @@ private:
|
||||
quint64 lo_;
|
||||
quint8 array_[16];
|
||||
};
|
||||
Q_DECLARE_METATYPE(UInt128);
|
||||
|
||||
inline UInt128::UInt128()
|
||||
{
|
||||
|
80
common/ulonglongvalidator.h
Normal file
80
common/ulonglongvalidator.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
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 _ULONGLONG_VALIDATOR_H
|
||||
#define _ULONGLONG_VALIDATOR_H
|
||||
|
||||
#include <QValidator>
|
||||
|
||||
class ULongLongValidator : public QValidator
|
||||
{
|
||||
public:
|
||||
ULongLongValidator(QObject *parent = 0)
|
||||
: QValidator(parent)
|
||||
{
|
||||
}
|
||||
~ULongLongValidator() {}
|
||||
|
||||
void setRange(qulonglong min, qulonglong max)
|
||||
{
|
||||
min_ = min;
|
||||
max_ = max;
|
||||
}
|
||||
|
||||
virtual QValidator::State validate(QString &input, int& /*pos*/) const
|
||||
{
|
||||
if (input.isEmpty())
|
||||
return Intermediate;
|
||||
|
||||
if (input.compare("0x", Qt::CaseInsensitive) == 0)
|
||||
return Intermediate;
|
||||
|
||||
bool isOk;
|
||||
qulonglong v = input.toULongLong(&isOk, 0);
|
||||
|
||||
//qDebug("input: %s, ok: %d, %llu", qPrintable(input), isOk, v);
|
||||
if (!isOk)
|
||||
return Invalid;
|
||||
|
||||
if (v > max_)
|
||||
return Invalid;
|
||||
|
||||
if (v < min_)
|
||||
return Intermediate;
|
||||
|
||||
return Acceptable;
|
||||
}
|
||||
|
||||
virtual void fixup(QString &input) const
|
||||
{
|
||||
int dummyPos = 0;
|
||||
State state = validate(input, dummyPos);
|
||||
|
||||
if (state == Acceptable)
|
||||
return;
|
||||
|
||||
input.setNum(min_);
|
||||
}
|
||||
|
||||
private:
|
||||
qulonglong min_{0};
|
||||
qulonglong max_{~0ULL};
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user