/* Copyright (C) 2010 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 */ #ifndef _COMBO_PROTOCOL_H #define _COMBO_PROTOCOL_H #include "abstractprotocol.h" template class ComboProtocol : public AbstractProtocol { public: ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0) : AbstractProtocol(stream, parent) { protoA = new ProtoA(stream, this); protoB = new ProtoB(stream, this); protoA->next = protoB; protoB->prev = protoA; qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, protoNumber, protoA, protoB); if (protoA->protocolNumber() == protoB->protocolNumber()) fieldPrefix = OuterInnerPrefix; else if (similarProto()) fieldPrefix = ProtoNamePrefix; } virtual ~ComboProtocol() { delete protoA; delete protoB; } static ComboProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent) { return new ComboProtocol(stream, parent); } virtual quint32 protocolNumber() const { return protoNumber; } virtual void protoDataCopyInto(OstProto::Protocol &protocol) const { protoA->protoDataCopyInto(protocol); protoB->protoDataCopyInto(protocol); protocol.mutable_protocol_id()->set_id(protocolNumber()); } virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber()) { OstProto::Protocol proto; // NOTE: To use protoX->protoDataCopyFrom() we need to arrange // so that it sees its own protocolNumber() - but since the // input param 'protocol' is 'const', we work on a copy proto.CopyFrom(protocol); proto.mutable_protocol_id()->set_id(protoA->protocolNumber()); protoA->protoDataCopyFrom(proto); proto.mutable_protocol_id()->set_id(protoB->protocolNumber()); protoB->protoDataCopyFrom(proto); } } virtual QString name() const { return protoA->name() + "/" + protoB->name(); } virtual QString shortName() const { return protoA->shortName() + "/" + protoB->shortName(); } virtual ProtocolIdType protocolIdType() const { return protoB->protocolIdType(); } virtual quint32 protocolId(ProtocolIdType type) const { return protoA->protocolId(type); } //quint32 payloadProtocolId(ProtocolIdType type) const; virtual int fieldCount() const { return protoA->fieldCount() + protoB->fieldCount(); } //virtual int metaFieldCount() const; //int frameFieldCount() const; virtual FieldFlags fieldFlags(int index) const { int cnt = protoA->fieldCount(); if (index < cnt) return protoA->fieldFlags(index); else return protoB->fieldFlags(index - cnt); } virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const { int cnt = protoA->fieldCount(); QVariant value = index < cnt ? protoA->fieldData(index, attrib, streamIndex) : protoB->fieldData(index - cnt, attrib, streamIndex); if (attrib == FieldName) { switch (fieldPrefix) { case OuterInnerPrefix: value = QString("%1 %2") .arg(index < cnt ? QString("Outer") : QString("Inner")) .arg(value.toString()); break; case ProtoNamePrefix: value = QString("%1 %2") .arg(index < cnt ? protoA->shortName() : protoB->shortName()) .arg(value.toString()); break; case NoPrefix: // Fall-through break; } } return value; } virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue) { int cnt = protoA->fieldCount(); if (index < cnt) return protoA->setFieldData(index, value, attrib); else return protoB->setFieldData(index - cnt, value, attrib); } #if 0 QByteArray protocolFrameValue(int streamIndex = 0, bool forCksum = false) const; virtual int protocolFrameSize() const; int protocolFrameOffset() const; int protocolFramePayloadSize() const; #endif virtual bool isProtocolFrameSizeVariable() const { return (protoA->isProtocolFrameSizeVariable() || protoB->isProtocolFrameSizeVariable()); } virtual int protocolFrameVariableCount() const { int count = AbstractProtocol::protocolFrameVariableCount(); count = AbstractProtocol::lcm( count, protoA->protocolFrameVariableCount()); count = AbstractProtocol::lcm( count, protoB->protocolFrameVariableCount()); return count; } virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp, CksumFlags cksumFlags = 0) const { // For a Pseudo IP cksum, we assume it is the succeeding protocol // that is requesting it and hence return protoB's cksum; if (cksumType == CksumIpPseudo) return protoB->protocolFrameCksum( streamIndex,cksumType, cksumFlags); return AbstractProtocol::protocolFrameCksum( streamIndex, cksumType, cksumFlags); } #if 0 quint32 protocolFrameHeaderCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; quint32 protocolFramePayloadCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; #endif template friend class ComboProtocolConfigForm; protected: ProtoA *protoA; ProtoB *protoB; private: bool similarProto() { // TODO: Use Levenshtein distance or something similar with > 70% match // For now we use an ugly hack! if (protoA->shortName().contains("IPv") && protoB->shortName().contains("IPv")) return true; if (protoA->shortName().contains("Vlan") && protoB->shortName().contains("Vlan")) return true; return false; } enum FieldNamePrefix { NoPrefix, ProtoNamePrefix, OuterInnerPrefix }; FieldNamePrefix fieldPrefix{NoPrefix}; }; #endif