Implement find-replace logic for fields <= 64 bits

This commit is contained in:
Srivats P 2021-12-03 19:01:32 +05:30
parent e19083ed3f
commit b60aad45d1
7 changed files with 199 additions and 30 deletions

View File

@ -19,7 +19,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "findreplace.h"
#include "../common/abstractprotocol.h"
#include "../common/protocolmanager.h"
#include "stream.h"
#include <QPushButton>
extern ProtocolManager *OstProtocolManager;
@ -32,23 +36,86 @@ FindReplaceDialog::FindReplaceDialog(Action *action, QWidget *parent)
useFindMask->setChecked(false);
useReplaceMask->setChecked(false);
protocol->addItems(OstProtocolManager->protocolDatabase());
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
protocol->setPlaceholderText(tr("Select"));
#endif
protocol->setCurrentIndex(-1);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
field->setPlaceholderText(tr("Select"));
#endif
// TODO: remove combo protocols, sample, userscript
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;
buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Replace All"));
}
void FindReplaceDialog::on_protocol_currentIndexChanged(const QString &/*name*/)
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;
fieldAttrib.max = (1 << bitSize) - 1; // min is always 0
// 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_buttonBox_accepted()
{
FieldAttrib fieldAttrib = fieldAttrib_.at(field->currentIndex());
action_->protocolNumber = protocolId_;
action_->fieldIndex = fieldAttrib.index;
action_->fieldBitSize = fieldAttrib.bitSize;
// XXX: All find/replace value/mask QVariants are set to
// 64-bit decimal number encoded as string
// - The action user is expected to convert to appropriate type
// (fieldBitSize is included as a hint)
// - QVariant can only do decimal conversions (not hex)
if (matchAny->isChecked()) {
action_->findMask.setValue(QString("0"));
action_->findValue.setValue(QString("0"));
} else {
action_->findMask.setValue(QString::number(
useFindMask->isChecked() ?
findMask->text().toULongLong(nullptr, BASE_HEX) :
quint64(~0)));
action_->findValue.setValue(QString::number(
findValue->text().toULongLong(nullptr, 0)));
}
action_->replaceMask.setValue(QString::number(
useReplaceMask->isChecked() ?
replaceMask->text().toULongLong(nullptr, BASE_HEX) :
quint64(~0)));
action_->replaceValue.setValue(QString::number(
replaceValue->text().toULongLong(nullptr, 0)));
action_->selectedStreamsOnly = selectedStreamsOnly->isChecked();
}

View File

@ -32,15 +32,21 @@ public:
private slots:
void on_protocol_currentIndexChanged(const QString &name);
void on_buttonBox_accepted();
private:
struct FieldAttrib;
quint32 protocolId_{0};
Action *action_{nullptr};
QList<FieldAttrib> fieldAttrib_;
};
struct FindReplaceDialog::Action
{
quint32 protocolNumber;
quint32 fieldIndex;
int fieldBitSize;
QVariant findValue;
QVariant findMask;
QVariant replaceValue;
@ -49,5 +55,11 @@ struct FindReplaceDialog::Action
bool selectedStreamsOnly; // in-out param
};
struct FindReplaceDialog::FieldAttrib
{
quint32 index;
int bitSize;
quint64 max;
};
#endif

View File

@ -314,33 +314,41 @@ void StreamsWidget::on_actionFind_Replace_triggered()
if (findReplace.exec() == QDialog::Accepted) {
int changed = 0;
Port &port = plm->port(currentPortIndex_);
// TODO: progress bar
if (action.selectedStreamsOnly) {
foreach(QModelIndex index, selectionModel->selectedRows()) {
Stream *stream = port.mutableStreamByIndex(index.row(), false);
if (stream->findReplace(action.protocolNumber,
action.fieldIndex,
action.findValue,
action.findMask,
action.replaceValue,
action.replaceMask))
if (stream->protocolFieldReplace(action.protocolNumber,
action.fieldIndex,
action.fieldBitSize,
action.findValue,
action.findMask,
action.replaceValue,
action.replaceMask))
changed++;
}
} else {
int count = tvStreamList->model()->rowCount();
for (int i = 0; i < count; i++) {
Stream *stream = port.mutableStreamByIndex(i, false);
if (stream->findReplace(action.protocolNumber,
action.fieldIndex,
action.findValue,
action.findMask,
action.replaceValue,
action.replaceMask))
if (stream->protocolFieldReplace(action.protocolNumber,
action.fieldIndex,
action.fieldBitSize,
action.findValue,
action.findMask,
action.replaceValue,
action.replaceMask))
changed++;
}
}
if (changed)
port.setLocalConfigChanged(true);
// TODO: count # of fields and # of streams changed
QMessageBox::information(this, tr("Find & Replace"),
tr("%1 streams changed").arg(changed));
}
}

View File

@ -270,6 +270,7 @@ QVariant MacProtocol::fieldData(int index, FieldAttrib attrib,
bool MacProtocol::setFieldData(int index, const QVariant &value,
FieldAttrib attrib)
{
// FIXME: make sure isOk is set correctly for all fields and protocols
bool isOk = false;
if (attrib != FieldValue)

View File

@ -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

View File

@ -24,6 +24,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "protocollistiterator.h"
#include "protocolmanager.h"
#include <QDebug>
extern ProtocolManager *OstProtocolManager;
extern quint64 getDeviceMacAddress(int portId, int streamId, int frameIndex);
extern quint64 getNeighborMacAddress(int portId, int streamId, int frameIndex);
@ -598,12 +600,56 @@ int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex,
return len;
}
bool StreamBase::findReplace(quint32 /*protocolNumber*/, int /*fieldIndex*/,
QVariant /*findValue*/, QVariant /*findMask*/,
QVariant /*replaceValue*/, QVariant /*replaceMask*/)
template <typename T>
int StreamBase::findReplace(quint32 protocolNumber, int fieldIndex,
QVariant findValue, QVariant findMask,
QVariant replaceValue, QVariant replaceMask)
{
// TODO
return false;
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:"
<< "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;
if (proto->setFieldData(fieldIndex, newValue))
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);
// TODO: > 64 (e.g. IPv6 128 bit)
qWarning("Unknown find/replace type %d", findValue.type());
return 0;
}
quint64 StreamBase::deviceMacAddress(int frameIndex) const

View File

@ -142,9 +142,10 @@ public:
int frameValue(uchar *buf, int bufMaxSize, int frameIndex,
FrameValueAttrib *attrib = nullptr) const;
bool findReplace(quint32 protocolNumber, int fieldIndex,
QVariant findValue, QVariant findMask,
QVariant replaceValue, QVariant replaceMask);
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;
@ -154,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;