a498dbf21a
For UDP encaps like VxLAN or Geneve, which can contain IP as payload, the UDP checksum was incorrect because when summing UDP payload (i.e. IP), we skipped the IPv4 checksum field, which should not be skipped in this case. Similar issue, for ICMP with IP as payload, ICMP checksum was incorrect. Essentially, any protocol which checksums over its payload and the payload contains protocols with checksum fields.
546 lines
14 KiB
C++
546 lines
14 KiB
C++
/*
|
|
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/>
|
|
*/
|
|
|
|
#include "userscript.h"
|
|
|
|
//
|
|
// -------------------- UserScriptProtocol --------------------
|
|
//
|
|
|
|
UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent)
|
|
: AbstractProtocol(stream, parent),
|
|
userProtocol_(this)
|
|
{
|
|
isScriptValid_ = false;
|
|
errorLineNumber_ = 0;
|
|
|
|
userProtocolScriptValue_ = engine_.newQObject(&userProtocol_);
|
|
engine_.globalObject().setProperty("protocol", userProtocolScriptValue_);
|
|
|
|
QScriptValue meta = engine_.newQMetaObject(userProtocol_.metaObject());
|
|
engine_.globalObject().setProperty("Protocol", meta);
|
|
}
|
|
|
|
UserScriptProtocol::~UserScriptProtocol()
|
|
{
|
|
}
|
|
|
|
AbstractProtocol* UserScriptProtocol::createInstance(StreamBase *stream,
|
|
AbstractProtocol *parent)
|
|
{
|
|
return new UserScriptProtocol(stream, parent);
|
|
}
|
|
|
|
quint32 UserScriptProtocol::protocolNumber() const
|
|
{
|
|
return OstProto::Protocol::kUserScriptFieldNumber;
|
|
}
|
|
|
|
void UserScriptProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const
|
|
{
|
|
protocol.MutableExtension(OstProto::userScript)->CopyFrom(data);
|
|
protocol.mutable_protocol_id()->set_id(protocolNumber());
|
|
}
|
|
|
|
void UserScriptProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol)
|
|
{
|
|
if (protocol.protocol_id().id() == protocolNumber() &&
|
|
protocol.HasExtension(OstProto::userScript))
|
|
data.MergeFrom(protocol.GetExtension(OstProto::userScript));
|
|
|
|
evaluateUserScript();
|
|
}
|
|
|
|
QString UserScriptProtocol::name() const
|
|
{
|
|
return QString("%1:{UserScript} [EXPERIMENTAL]").arg(userProtocol_.name());
|
|
}
|
|
|
|
QString UserScriptProtocol::shortName() const
|
|
{
|
|
return QString("%1:{Script} [EXPERIMENTAL]").arg(userProtocol_.name());
|
|
}
|
|
|
|
quint32 UserScriptProtocol::protocolId(ProtocolIdType type) const
|
|
{
|
|
QScriptValue userFunction;
|
|
QScriptValue userValue;
|
|
|
|
if (!isScriptValid_)
|
|
goto _do_default;
|
|
|
|
userFunction = userProtocolScriptValue_.property("protocolId");
|
|
|
|
if (!userFunction.isValid())
|
|
goto _do_default;
|
|
|
|
Q_ASSERT(userFunction.isFunction());
|
|
|
|
userValue = userFunction.call(QScriptValue(),
|
|
QScriptValueList() << QScriptValue(&engine_, type));
|
|
|
|
Q_ASSERT(userValue.isValid());
|
|
Q_ASSERT(userValue.isNumber());
|
|
|
|
return userValue.toUInt32();
|
|
|
|
_do_default:
|
|
return AbstractProtocol::protocolId(type);
|
|
}
|
|
|
|
int UserScriptProtocol::fieldCount() const
|
|
{
|
|
return userScript_fieldCount;
|
|
}
|
|
|
|
QVariant UserScriptProtocol::fieldData(int index, FieldAttrib attrib,
|
|
int streamIndex) const
|
|
{
|
|
switch (index)
|
|
{
|
|
case userScript_program:
|
|
|
|
switch(attrib)
|
|
{
|
|
case FieldName:
|
|
return QString("UserProtocol");
|
|
|
|
case FieldValue:
|
|
case FieldTextValue:
|
|
return QString().fromStdString(data.program());
|
|
|
|
case FieldFrameValue:
|
|
{
|
|
if (!isScriptValid_)
|
|
return QByteArray();
|
|
|
|
QScriptValue userFunction = userProtocolScriptValue_.property(
|
|
"protocolFrameValue");
|
|
|
|
Q_ASSERT(userFunction.isValid());
|
|
Q_ASSERT(userFunction.isFunction());
|
|
|
|
QScriptValue userValue = userFunction.call(QScriptValue(),
|
|
QScriptValueList() << QScriptValue(&engine_, streamIndex));
|
|
|
|
Q_ASSERT(userValue.isValid());
|
|
Q_ASSERT(userValue.isArray());
|
|
|
|
QByteArray fv;
|
|
QList<int> pktBuf;
|
|
|
|
qScriptValueToSequence(userValue, pktBuf);
|
|
|
|
fv.resize(pktBuf.size());
|
|
for (int i = 0; i < pktBuf.size(); i++)
|
|
fv[i] = pktBuf.at(i) & 0xFF;
|
|
|
|
return fv;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__,
|
|
index);
|
|
break;
|
|
}
|
|
|
|
return AbstractProtocol::fieldData(index, attrib, streamIndex);
|
|
}
|
|
|
|
bool UserScriptProtocol::setFieldData(int index, const QVariant &value,
|
|
FieldAttrib attrib)
|
|
{
|
|
bool isOk = false;
|
|
|
|
if (attrib != FieldValue)
|
|
goto _exit;
|
|
|
|
switch (index)
|
|
{
|
|
case userScript_program:
|
|
{
|
|
data.set_program(value.toString().toStdString());
|
|
evaluateUserScript();
|
|
break;
|
|
}
|
|
default:
|
|
qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__,
|
|
index);
|
|
break;
|
|
}
|
|
|
|
_exit:
|
|
return isOk;
|
|
}
|
|
|
|
int UserScriptProtocol::protocolFrameSize(int streamIndex) const
|
|
{
|
|
if (!isScriptValid_)
|
|
return 0;
|
|
|
|
QScriptValue userFunction = userProtocolScriptValue_.property(
|
|
"protocolFrameSize");
|
|
|
|
Q_ASSERT(userFunction.isValid());
|
|
Q_ASSERT(userFunction.isFunction());
|
|
|
|
QScriptValue userValue = userFunction.call(QScriptValue(),
|
|
QScriptValueList() << QScriptValue(&engine_, streamIndex));
|
|
|
|
Q_ASSERT(userValue.isNumber());
|
|
|
|
return userValue.toInt32();
|
|
}
|
|
|
|
bool UserScriptProtocol::isProtocolFrameSizeVariable() const
|
|
{
|
|
return userProtocol_.isProtocolFrameSizeVariable();
|
|
}
|
|
|
|
int UserScriptProtocol::protocolFrameVariableCount() const
|
|
{
|
|
return AbstractProtocol::lcm(
|
|
AbstractProtocol::protocolFrameVariableCount(),
|
|
userProtocol_.protocolFrameVariableCount());
|
|
}
|
|
|
|
quint32 UserScriptProtocol::protocolFrameCksum(int streamIndex,
|
|
CksumType cksumType, CksumFlags cksumFlags) const
|
|
{
|
|
QScriptValue userFunction;
|
|
QScriptValue userValue;
|
|
|
|
if (!isScriptValid_)
|
|
goto _do_default;
|
|
|
|
userFunction = userProtocolScriptValue_.property("protocolFrameCksum");
|
|
|
|
qDebug("userscript protoFrameCksum(): isValid:%d/isFunc:%d",
|
|
userFunction.isValid(), userFunction.isFunction());
|
|
|
|
if (!userFunction.isValid())
|
|
goto _do_default;
|
|
|
|
Q_ASSERT(userFunction.isFunction());
|
|
|
|
userValue = userFunction.call(
|
|
QScriptValue(),
|
|
QScriptValueList()
|
|
<< QScriptValue(&engine_, streamIndex)
|
|
<< QScriptValue(&engine_, cksumType)
|
|
<< QScriptValue(&engine_, cksumFlags));
|
|
|
|
Q_ASSERT(userValue.isValid());
|
|
Q_ASSERT(userValue.isNumber());
|
|
|
|
return userValue.toUInt32();
|
|
|
|
_do_default:
|
|
return AbstractProtocol::protocolFrameCksum(
|
|
streamIndex, cksumType, cksumFlags);
|
|
}
|
|
|
|
void UserScriptProtocol::evaluateUserScript() const
|
|
{
|
|
QScriptValue userFunction;
|
|
QScriptValue userValue;
|
|
QString property;
|
|
|
|
isScriptValid_ = false;
|
|
errorLineNumber_ = userScriptLineCount();
|
|
|
|
// Reset all properties including the dynamic ones
|
|
userProtocol_.reset();
|
|
userProtocolScriptValue_.setProperty("protocolFrameValue", QScriptValue());
|
|
userProtocolScriptValue_.setProperty("protocolFrameSize", QScriptValue());
|
|
userProtocolScriptValue_.setProperty("protocolFrameCksum", QScriptValue());
|
|
userProtocolScriptValue_.setProperty("protocolId", QScriptValue());
|
|
|
|
engine_.evaluate(fieldData(userScript_program, FieldValue).toString());
|
|
if (engine_.hasUncaughtException())
|
|
goto _error_exception;
|
|
|
|
// Validate protocolFrameValue()
|
|
property = QString("protocolFrameValue");
|
|
userFunction = userProtocolScriptValue_.property(property);
|
|
|
|
qDebug("userscript property %s: isValid:%d/isFunc:%d",
|
|
qPrintable(property),
|
|
userFunction.isValid(), userFunction.isFunction());
|
|
|
|
if (!userFunction.isValid())
|
|
{
|
|
errorText_ = property + QString(" not set");
|
|
goto _error_exit;
|
|
}
|
|
|
|
if (!userFunction.isFunction())
|
|
{
|
|
errorText_ = property + QString(" is not a function");
|
|
goto _error_exit;
|
|
}
|
|
|
|
userValue = userFunction.call();
|
|
if (engine_.hasUncaughtException())
|
|
goto _error_exception;
|
|
|
|
qDebug("userscript property %s return value: isValid:%d/isArray:%d",
|
|
qPrintable(property),
|
|
userValue.isValid(), userValue.isArray());
|
|
|
|
if (!userValue.isArray())
|
|
{
|
|
errorText_ = property + QString(" does not return an array");
|
|
goto _error_exit;
|
|
}
|
|
|
|
// Validate protocolFrameSize()
|
|
property = QString("protocolFrameSize");
|
|
userFunction = userProtocolScriptValue_.property(property);
|
|
|
|
qDebug("userscript property %s: isValid:%d/isFunc:%d",
|
|
qPrintable(property),
|
|
userFunction.isValid(), userFunction.isFunction());
|
|
|
|
if (!userFunction.isValid())
|
|
{
|
|
errorText_ = property + QString(" not set");
|
|
goto _error_exit;
|
|
}
|
|
|
|
if (!userFunction.isFunction())
|
|
{
|
|
errorText_ = property + QString(" is not a function");
|
|
goto _error_exit;
|
|
}
|
|
|
|
userValue = userFunction.call();
|
|
if (engine_.hasUncaughtException())
|
|
goto _error_exception;
|
|
|
|
qDebug("userscript property %s return value: isValid:%d/isNumber:%d",
|
|
qPrintable(property),
|
|
userValue.isValid(), userValue.isNumber());
|
|
|
|
if (!userValue.isNumber())
|
|
{
|
|
errorText_ = property + QString(" does not return a number");
|
|
goto _error_exit;
|
|
}
|
|
|
|
// Validate protocolFrameCksum() [optional]
|
|
property = QString("protocolFrameCksum");
|
|
userFunction = userProtocolScriptValue_.property(property);
|
|
|
|
qDebug("userscript property %s: isValid:%d/isFunc:%d",
|
|
qPrintable(property),
|
|
userFunction.isValid(), userFunction.isFunction());
|
|
|
|
if (!userFunction.isValid())
|
|
goto _skip_cksum;
|
|
|
|
if (!userFunction.isFunction())
|
|
{
|
|
errorText_ = property + QString(" is not a function");
|
|
goto _error_exit;
|
|
}
|
|
|
|
userValue = userFunction.call();
|
|
if (engine_.hasUncaughtException())
|
|
goto _error_exception;
|
|
|
|
qDebug("userscript property %s return value: isValid:%d/isNumber:%d",
|
|
qPrintable(property),
|
|
userValue.isValid(), userValue.isNumber());
|
|
|
|
if (!userValue.isNumber())
|
|
{
|
|
errorText_ = property + QString(" does not return a number");
|
|
goto _error_exit;
|
|
}
|
|
|
|
|
|
_skip_cksum:
|
|
// Validate protocolId() [optional]
|
|
property = QString("protocolId");
|
|
userFunction = userProtocolScriptValue_.property(property);
|
|
|
|
qDebug("userscript property %s: isValid:%d/isFunc:%d",
|
|
qPrintable(property),
|
|
userFunction.isValid(), userFunction.isFunction());
|
|
|
|
if (!userFunction.isValid())
|
|
goto _skip_protocol_id;
|
|
|
|
if (!userFunction.isFunction())
|
|
{
|
|
errorText_ = property + QString(" is not a function");
|
|
goto _error_exit;
|
|
}
|
|
|
|
userValue = userFunction.call();
|
|
if (engine_.hasUncaughtException())
|
|
goto _error_exception;
|
|
|
|
qDebug("userscript property %s return value: isValid:%d/isNumber:%d",
|
|
qPrintable(property),
|
|
userValue.isValid(), userValue.isNumber());
|
|
|
|
if (!userValue.isNumber())
|
|
{
|
|
errorText_ = property + QString(" does not return a number");
|
|
goto _error_exit;
|
|
}
|
|
|
|
|
|
_skip_protocol_id:
|
|
errorText_ = QString("");
|
|
isScriptValid_ = true;
|
|
return;
|
|
|
|
_error_exception:
|
|
errorLineNumber_ = engine_.uncaughtExceptionLineNumber();
|
|
errorText_ = engine_.uncaughtException().toString();
|
|
|
|
_error_exit:
|
|
userProtocol_.reset();
|
|
return;
|
|
}
|
|
|
|
bool UserScriptProtocol::isScriptValid() const
|
|
{
|
|
return isScriptValid_;
|
|
}
|
|
|
|
int UserScriptProtocol::userScriptErrorLineNumber() const
|
|
{
|
|
return errorLineNumber_;
|
|
}
|
|
|
|
QString UserScriptProtocol::userScriptErrorText() const
|
|
{
|
|
return errorText_;
|
|
}
|
|
|
|
int UserScriptProtocol::userScriptLineCount() const
|
|
{
|
|
return fieldData(userScript_program, FieldValue).toString().count(
|
|
QChar('\n')) + 1;
|
|
}
|
|
|
|
//
|
|
// -------------------- UserProtocol --------------------
|
|
//
|
|
|
|
UserProtocol::UserProtocol(AbstractProtocol *parent)
|
|
: parent_ (parent)
|
|
{
|
|
reset();
|
|
}
|
|
|
|
void UserProtocol::reset()
|
|
{
|
|
name_ = QString();
|
|
protocolFrameSizeVariable_ = false;
|
|
protocolFrameVariableCount_ = 1;
|
|
}
|
|
|
|
QString UserProtocol::name() const
|
|
{
|
|
return name_;
|
|
}
|
|
|
|
void UserProtocol::setName(QString &name)
|
|
{
|
|
name_ = name;
|
|
}
|
|
|
|
bool UserProtocol::isProtocolFrameSizeVariable() const
|
|
{
|
|
return protocolFrameSizeVariable_;
|
|
}
|
|
|
|
void UserProtocol::setProtocolFrameSizeVariable(bool variable)
|
|
{
|
|
protocolFrameSizeVariable_ = variable;
|
|
}
|
|
|
|
int UserProtocol::protocolFrameVariableCount() const
|
|
{
|
|
return protocolFrameVariableCount_;
|
|
}
|
|
|
|
void UserProtocol::setProtocolFrameVariableCount(int count)
|
|
{
|
|
protocolFrameVariableCount_ = count;
|
|
}
|
|
|
|
quint32 UserProtocol::payloadProtocolId(UserProtocol::ProtocolIdType type) const
|
|
{
|
|
return parent_->payloadProtocolId(
|
|
static_cast<AbstractProtocol::ProtocolIdType>(type));
|
|
}
|
|
|
|
int UserProtocol::protocolFrameOffset(int streamIndex) const
|
|
{
|
|
return parent_->protocolFrameOffset(streamIndex);
|
|
}
|
|
|
|
int UserProtocol::protocolFramePayloadSize(int streamIndex) const
|
|
{
|
|
return parent_->protocolFramePayloadSize(streamIndex);
|
|
}
|
|
|
|
bool UserProtocol::isProtocolFramePayloadValueVariable() const
|
|
{
|
|
return parent_->isProtocolFramePayloadValueVariable();
|
|
}
|
|
|
|
bool UserProtocol::isProtocolFramePayloadSizeVariable() const
|
|
{
|
|
return parent_->isProtocolFramePayloadSizeVariable();
|
|
}
|
|
|
|
int UserProtocol::protocolFramePayloadVariableCount() const
|
|
{
|
|
return parent_->protocolFramePayloadVariableCount();
|
|
}
|
|
|
|
quint32 UserProtocol::protocolFrameHeaderCksum(int streamIndex,
|
|
AbstractProtocol::CksumType cksumType) const
|
|
{
|
|
return parent_->protocolFrameHeaderCksum(streamIndex, cksumType);
|
|
}
|
|
|
|
quint32 UserProtocol::protocolFramePayloadCksum(int streamIndex,
|
|
AbstractProtocol::CksumType cksumType) const
|
|
{
|
|
quint32 cksum;
|
|
|
|
cksum = parent_->protocolFramePayloadCksum(streamIndex, cksumType);
|
|
qDebug("UserProto:%s = %d", __FUNCTION__, cksum);
|
|
return cksum;
|
|
}
|
|
|
|
/* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */
|