ostinato/common/gmp.cpp

787 lines
21 KiB
C++
Executable File

/*
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 <http://www.gnu.org/licenses/>
*/
#include "gmp.h"
#include <qendian.h>
GmpConfigForm::GmpConfigForm(QWidget *parent)
: QWidget(parent)
{
setupUi(this);
msgTypeCombo->setValueMask(0xFF);
msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query");
msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report");
msgTypeCombo->addItem(kIgmpV2Query, "IGMPv2 Query");
msgTypeCombo->addItem(kIgmpV2Report, "IGMPv2 Report");
msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave");
msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query");
msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report");
}
void GmpConfigForm::on_addSource_clicked()
{
QListWidgetItem *item=new QListWidgetItem("0.0.0.0");
item->setFlags(item->flags() | Qt::ItemIsEditable);
sourceList->insertItem(sourceList->currentRow(), item);
if (!overrideSourceCount->isChecked())
sourceCount->setText(QString().setNum(sourceList->count()));
}
void GmpConfigForm::on_deleteSource_clicked()
{
delete sourceList->takeItem(sourceList->currentRow());
if (!overrideSourceCount->isChecked())
sourceCount->setText(QString().setNum(sourceList->count()));
}
void GmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/)
{
switch(msgTypeCombo->currentValue())
{
case kIgmpV1Query:
case kIgmpV1Report:
case kIgmpV2Query:
case kIgmpV2Report:
case kIgmpV2Leave:
case kMldV1Query:
case kMldV1Report:
case kMldV1Done:
asmGroup->show();
ssmWidget->hide();
break;
case kIgmpV3Query:
case kMldV2Query:
asmGroup->hide();
ssmWidget->setCurrentIndex(0);
ssmWidget->show();
break;
case kIgmpV3Report:
case kMldV2Report:
asmGroup->hide();
ssmWidget->setCurrentIndex(1);
ssmWidget->show();
break;
default:
asmGroup->hide();
ssmWidget->hide();
break;
}
}
GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent)
: AbstractProtocol(stream, parent)
{
/* The configWidget is created lazily */
configForm = NULL;
}
GmpProtocol::~GmpProtocol()
{
delete configForm;
}
AbstractProtocol::ProtocolIdType GmpProtocol::protocolIdType() const
{
return ProtocolIdIp;
}
int GmpProtocol::fieldCount() const
{
return FIELD_COUNT;
}
int GmpProtocol::frameFieldCount() const
{
switch(msgType())
{
// IGMP
case kIgmpV1Query:
case kIgmpV1Report:
case kIgmpV2Query:
case kIgmpV2Report:
case kIgmpV2Leave:
return FIELD_COUNT_ASM_ALL;
case kIgmpV3Query:
return FIELD_COUNT_SSM_QUERY;
case kIgmpV3Report:
return FIELD_COUNT_SSM_REPORT;
// MLD
case kMldV1Query:
case kMldV1Report:
case kMldV1Done:
return FIELD_COUNT_ASM_ALL;
case kMldV2Query:
return FIELD_COUNT_SSM_QUERY;
case kMldV2Report:
return FIELD_COUNT_SSM_REPORT;
default:
return FIELD_COUNT_ASM_ALL;
}
}
AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const
{
AbstractProtocol::FieldFlags flags;
flags = AbstractProtocol::fieldFlags(index);
flags &= ~FrameField;
switch(index)
{
// Frame Fields - check against msgType()
case kType:
case kRsvdMrtCode:
flags |= FrameField;
break;
case kChecksum:
flags |= FrameField;
flags |= CksumField;
break;
case kMldMrt:
case kMldRsvd:
break;
case kGroupAddress:
if (!isSsmReport())
flags |= FrameField;
break;
case kRsvd1:
case kSFlag:
case kQrv:
case kQqic:
case kSourceCount:
case kSources:
if (isSsmQuery())
flags |= FrameField;
break;
case kRsvd2:
case kGroupRecordCount:
case kGroupRecords:
if (isSsmReport())
flags |= FrameField;
break;
// Meta Fields
case kIsOverrideChecksum:
case kGroupMode:
case kGroupCount:
case kGroupPrefix:
case kIsOverrideSourceCount:
case kIsOverrideGroupRecordCount:
flags |= MetaField;
break;
default:
qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__,
index);
break;
}
return flags;
}
QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib,
int streamIndex) const
{
switch (index)
{
case kType:
{
uint type = data.type();
switch(attrib)
{
case FieldName:
return QString("Type");
case FieldValue:
return type;
case FieldTextValue:
return QString("%1").arg(type);
case FieldFrameValue:
return QByteArray(1, quint8(type));
default:
break;
}
break;
}
case kRsvdMrtCode:
{
quint8 rsvd = 0;
switch(attrib)
{
case FieldName:
return QString("Reserved");
case FieldValue:
return rsvd;
case FieldTextValue:
return QString("%1").arg(rsvd);
case FieldFrameValue:
return QByteArray(1, rsvd);
default:
break;
}
break;
}
case kChecksum:
{
quint16 cksum = data.is_override_checksum() ?
data.checksum() : checksum(streamIndex);
switch(attrib)
{
case FieldName:
return QString("Checksum");
case FieldValue:
return cksum;
case FieldFrameValue:
{
QByteArray fv;
fv.resize(2);
qToBigEndian(cksum, (uchar*) fv.data());
return fv;
}
case FieldTextValue:
return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0'));
case FieldBitSize:
return 16;
default:
break;
}
break;
}
case kMldMrt:
// XXX: Present only in MLD - hence handled by the mld subclass
break;
case kGroupAddress:
// XXX: Handled by each subclass
break;
case kRsvd1:
{
int rsvd = 0;
switch(attrib)
{
case FieldName:
return QString("Reserved");
case FieldValue:
return rsvd;
case FieldTextValue:
return QString("%1").arg(rsvd);
case FieldFrameValue:
return QByteArray(1, char(rsvd));
case FieldBitSize:
return 4;
default:
break;
}
break;
}
case kSFlag:
{
switch(attrib)
{
case FieldName:
return QString("S Flag");
case FieldValue:
return data.s_flag();
case FieldTextValue:
return data.s_flag() ? QString("True") : QString("False");
case FieldFrameValue:
return QByteArray(1, char(data.s_flag()));
case FieldBitSize:
return 1;
default:
break;
}
break;
}
case kQrv:
{
int qrv = data.qrv() & 0x7;
switch(attrib)
{
case FieldName:
return QString("Querier's Robustness Variable (QRV)");
case FieldValue:
return qrv;
case FieldTextValue:
return QString("%1").arg(qrv);
case FieldFrameValue:
return QByteArray(1, char(qrv));
case FieldBitSize:
return 3;
default:
break;
}
break;
}
case kQqic:
{
int qqi = data.qqi();
switch(attrib)
{
case FieldName:
return QString("Querier's Robustness Variable (QRV)");
case FieldValue:
return qqi;
case FieldTextValue:
return QString("%1").arg(qqi);
case FieldFrameValue:
{
quint8 qqic = qqi; // TODO: derive code from qqi
return QByteArray(1, char(qqic));
}
default:
break;
}
break;
}
case kSourceCount:
{
quint16 count = data.sources_size();
if (data.is_override_source_count())
count = data.source_count();
switch(attrib)
{
case FieldName:
return QString("Number of Sources");
case FieldValue:
return count;
case FieldTextValue:
return QString("%1").arg(count);
case FieldFrameValue:
{
QByteArray fv;
fv.resize(2);
qToBigEndian(count, (uchar*) fv.data());
return fv;
}
default:
break;
}
break;
}
case kSources:
// XXX: Handled by each subclass
break;
case kRsvd2:
{
quint16 rsvd = 0;
switch(attrib)
{
case FieldName:
return QString("Reserved");
case FieldValue:
return rsvd;
case FieldTextValue:
return QString("%1").arg(rsvd);
case FieldFrameValue:
{
QByteArray fv;
fv.resize(2);
qToBigEndian(rsvd, (uchar*) fv.data());
return fv;
}
default:
break;
}
break;
}
case kGroupRecordCount:
{
quint16 count = data.group_records_size();
if (data.is_override_group_record_count())
count = data.group_record_count();
switch(attrib)
{
case FieldName:
return QString("Number of Group Records");
case FieldValue:
return count;
case FieldTextValue:
return QString("%1").arg(count);
case FieldFrameValue:
{
QByteArray fv;
fv.resize(2);
qToBigEndian(count, (uchar*) fv.data());
return fv;
}
default:
break;
}
break;
}
case kGroupRecords:
// XXX:Handled by each subclass
break;
// Meta Fields
case kIsOverrideChecksum:
{
switch(attrib)
{
case FieldValue: return data.is_override_checksum();
default: break;
}
break;
}
case kGroupMode:
{
switch(attrib)
{
case FieldValue: return data.group_mode();
default: break;
}
break;
}
case kGroupCount:
{
switch(attrib)
{
case FieldValue: return data.group_count();
default: break;
}
break;
}
case kGroupPrefix:
{
switch(attrib)
{
case FieldValue: return data.group_prefix();
default: break;
}
break;
}
case kIsOverrideSourceCount:
{
switch(attrib)
{
case FieldValue: return data.is_override_source_count();
default: break;
}
break;
}
case kIsOverrideGroupRecordCount:
{
switch(attrib)
{
case FieldValue: return data.is_override_group_record_count();
default: break;
}
break;
}
default:
qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__,
index);
break;
}
return AbstractProtocol::fieldData(index, attrib, streamIndex);
}
bool GmpProtocol::setFieldData(int index, const QVariant &value,
FieldAttrib attrib)
{
bool isOk = false;
if (attrib != FieldValue)
goto _exit;
switch (index)
{
case kType:
{
uint type = value.toUInt(&isOk);
if (isOk)
data.set_type(type);
break;
}
case kRsvdMrtCode:
{
uint val = value.toUInt(&isOk);
if (isOk)
data.set_rsvd_code(val);
break;
}
case kChecksum:
{
uint csum = value.toUInt(&isOk);
if (isOk)
data.set_checksum(csum);
break;
}
case kMldMrt:
{
uint mrt = value.toUInt(&isOk);
if (isOk)
data.set_max_response_time(mrt);
break;
}
case kGroupAddress:
// XXX: Handled by subclass
break;
case kRsvd1:
isOk = false;
break;
case kSFlag:
{
bool flag = value.toBool();
data.set_s_flag(flag);
isOk = true;
break;
}
case kQrv:
{
uint qrv = value.toUInt(&isOk);
if (isOk)
data.set_qrv(qrv);
break;
}
case kQqic:
{
uint qqi = value.toUInt(&isOk); // TODO: QQIC or QQI??
if (isOk)
data.set_qqi(qqi);
break;
}
case kSourceCount:
{
uint count = value.toUInt(&isOk);
if (isOk)
data.set_source_count(count);
break;
}
case kSources:
// XXX: Handled by subclass
break;
case kRsvd2:
isOk = false;
break;
case kGroupRecordCount:
{
uint count = value.toUInt(&isOk);
if (isOk)
data.set_group_record_count(count);
break;
}
case kGroupRecords:
// XXX: Handled by subclass
break;
// Meta Fields
case kIsOverrideChecksum:
{
bool ovr = value.toBool();
data.set_is_override_checksum(ovr);
isOk = true;
break;
}
case kGroupMode:
{
uint mode = value.toUInt(&isOk);
if (isOk && data.GroupMode_IsValid(mode))
data.set_group_mode((OstProto::Gmp::GroupMode)mode);
break;
}
case kGroupCount:
{
uint count = value.toUInt(&isOk);
if (isOk)
data.set_group_count(count);
break;
}
case kGroupPrefix:
{
uint prefix = value.toUInt(&isOk);
if (isOk)
data.set_group_prefix(prefix);
break;
}
case kIsOverrideSourceCount:
{
bool ovr = value.toBool();
data.set_is_override_source_count(ovr);
isOk = true;
break;
}
case kIsOverrideGroupRecordCount:
{
bool ovr = value.toBool();
data.set_is_override_group_record_count(ovr);
isOk = true;
break;
}
default:
qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__,
index);
break;
}
_exit:
return isOk;
}
/*!
TODO: Return the protocol frame size in bytes\n
If your protocol has a fixed size - you don't need to reimplement this; the
base class implementation is good enough
*/
int GmpProtocol::protocolFrameSize(int streamIndex) const
{
// TODO: Calculate to reduce processing cost
return AbstractProtocol::protocolFrameValue(streamIndex, true).size();
}
/*!
TODO: If your protocol has any variable fields, return true \n
Otherwise you don't need to reimplement this method - the base class always
returns false
*/
bool GmpProtocol::isProtocolFrameValueVariable() const
{
// TODO: check msg types and then return value appropriately
return true;
}
QWidget* GmpProtocol::configWidget()
{
/* Lazy creation of the configWidget */
if (configForm == NULL)
{
configForm = new GmpConfigForm;
loadConfigWidget();
}
return configForm;
}
void GmpProtocol::loadConfigWidget()
{
configWidget();
configForm->msgTypeCombo->setValue(fieldData(kType, FieldValue).toUInt());
configForm->maxResponseTime->setText(QString("%1").arg(
data.max_response_time()));
configForm->overrideChecksum->setChecked(
fieldData(kIsOverrideChecksum, FieldValue).toBool());
configForm->checksum->setText(uintToHexStr(
fieldData(kChecksum, FieldValue).toUInt(), 2));
configForm->groupAddress->setText(
fieldData(kGroupAddress, FieldValue).toString());
configForm->groupMode->setCurrentIndex(
fieldData(kGroupMode, FieldValue).toUInt());
configForm->groupCount->setText(
fieldData(kGroupCount, FieldValue).toString());
configForm->groupPrefix->setText(
fieldData(kGroupPrefix, FieldValue).toString());
configForm->sFlag->setChecked(fieldData(kSFlag, FieldValue).toBool());
configForm->qrv->setText(fieldData(kQrv, FieldValue).toString());
configForm->qqi->setText(fieldData(kQqic, FieldValue).toString());
configForm->overrideSourceCount->setChecked(
fieldData(kIsOverrideSourceCount, FieldValue).toBool());
configForm->sourceCount->setText(
fieldData(kSourceCount, FieldValue).toString());
// TODO: sources
configForm->overrideGroupRecordCount->setChecked(
fieldData(kIsOverrideGroupRecordCount, FieldValue).toBool());
configForm->groupRecordCount->setText(
fieldData(kGroupRecordCount, FieldValue).toString());
// TODO: groups
}
void GmpProtocol::storeConfigWidget()
{
bool isOk;
configWidget();
setFieldData(kType, configForm->msgTypeCombo->currentValue());
setFieldData(kMldMrt, configForm->maxResponseTime->text());
setFieldData(kIsOverrideChecksum,
configForm->overrideChecksum->isChecked());
setFieldData(kChecksum,
configForm->checksum->text().toUInt(&isOk, BASE_HEX));
setFieldData(kGroupAddress, configForm->groupAddress->text());
setFieldData(kGroupMode, configForm->groupMode->currentIndex());
setFieldData(kGroupCount, configForm->groupCount->text());
setFieldData(kGroupPrefix, configForm->groupPrefix->text());
setFieldData(kSFlag, configForm->sFlag->isChecked());
setFieldData(kQrv, configForm->qrv->text());
setFieldData(kQqic, configForm->qqi->text());
setFieldData(kIsOverrideSourceCount,
configForm->overrideSourceCount->isChecked());
setFieldData(kSourceCount, configForm->sourceCount->text());
// TODO: Sources
setFieldData(kIsOverrideGroupRecordCount,
configForm->overrideGroupRecordCount->isChecked());
setFieldData(kGroupRecordCount, configForm->groupRecordCount->text());
// TODO: Group Records
#if 0
setFieldData(kA, configForm->gmpA->text());
setFieldData(kB, configForm->gmpB->text());
setFieldData(kPayloadLength, configForm->gmpPayloadLength->text());
setFieldData(kIs_override_checksum,
configForm->isChecksumOverride->isChecked());
setFieldData(kChecksum, configForm->gmpChecksum->text().toUInt(&isOk, BASE_HEX));
setFieldData(kX, configForm->gmpX->text());
setFieldData(kY, configForm->gmpY->text());
#endif
}