Review and rework of GMP and IGMP

This commit is contained in:
Srivats P. 2010-11-06 23:29:44 +05:30
parent d4408f0fc1
commit dbc7409616
10 changed files with 339 additions and 223 deletions

View File

@ -5,7 +5,6 @@ win32:RC_FILE = ostinato.rc
macx:ICON = icons/logo.icns macx:ICON = icons/logo.icns
QT += network script QT += network script
INCLUDEPATH += "../rpc/" "../common/" INCLUDEPATH += "../rpc/" "../common/"
LIBS += -lprotobuf
win32 { win32 {
CONFIG(debug, debug|release) { CONFIG(debug, debug|release) {
LIBS += -L"../common/debug" -lostproto LIBS += -L"../common/debug" -lostproto
@ -25,6 +24,7 @@ win32 {
LIBS += -L"../rpc" -lpbrpc LIBS += -L"../rpc" -lpbrpc
POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a"
} }
LIBS += -lprotobuf
RESOURCES += ostinato.qrc RESOURCES += ostinato.qrc
HEADERS += \ HEADERS += \
dumpview.h \ dumpview.h \

View File

@ -22,11 +22,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include <QHeaderView> #include <QHeaderView>
#include <qendian.h> #include <qendian.h>
QHash<int, int> GmpProtocol::frameFieldCountMap;
GmpConfigForm::GmpConfigForm(QWidget *parent) GmpConfigForm::GmpConfigForm(QWidget *parent)
: QWidget(parent) : QWidget(parent)
{ {
setupUi(this); setupUi(this);
// TODO: this should be in subclass
msgTypeCombo->setValueMask(0xFF); msgTypeCombo->setValueMask(0xFF);
msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query");
msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report");
@ -70,14 +73,14 @@ void GmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/)
case kIgmpV3Query: case kIgmpV3Query:
case kMldV2Query: case kMldV2Query:
asmGroup->hide(); asmGroup->hide();
ssmWidget->setCurrentIndex(0); ssmWidget->setCurrentIndex(kSsmQueryPage);
ssmWidget->show(); ssmWidget->show();
break; break;
case kIgmpV3Report: case kIgmpV3Report:
case kMldV2Report: case kMldV2Report:
asmGroup->hide(); asmGroup->hide();
ssmWidget->setCurrentIndex(1); ssmWidget->setCurrentIndex(kSsmReportPage);
ssmWidget->show(); ssmWidget->show();
break; break;
@ -88,6 +91,14 @@ void GmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/)
} }
} }
void GmpConfigForm::on_groupMode_currentIndexChanged(int index)
{
bool disabled = (index == 0);
groupCount->setDisabled(disabled);
groupPrefix->setDisabled(disabled);
}
void GmpConfigForm::on_addSource_clicked() void GmpConfigForm::on_addSource_clicked()
{ {
QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp);
@ -113,13 +124,14 @@ void GmpConfigForm::on_addGroupRecord_clicked()
QListWidgetItem *item = new QListWidgetItem; QListWidgetItem *item = new QListWidgetItem;
grpRec["groupRecordType"] = defRec.type()-1; grpRec["groupRecordType"] = defRec.type()-1;
grpRec["groupRecordAddress"] = _defaultSourceIp; grpRec["groupRecordAddress"] = _defaultGroupIp;
grpRec["overrideGroupRecordSourceCount"] = defRec.is_override_source_count(); grpRec["overrideGroupRecordSourceCount"] =defRec.is_override_source_count();
grpRec["groupRecordSourceCount"] = defRec.source_count(); grpRec["groupRecordSourceCount"] = defRec.source_count();
grpRec["groupRecordSourceList"] = QStringList(); grpRec["groupRecordSourceList"] = QStringList();
grpRec["overrideAuxDataLength"] = defRec.is_override_aux_data_length(); grpRec["overrideAuxDataLength"] = defRec.is_override_aux_data_length();
grpRec["auxDataLength"] = defRec.aux_data_length(); grpRec["auxDataLength"] = defRec.aux_data_length();
grpRec["auxData"] = QString().fromStdString(defRec.aux_data()); grpRec["auxData"] = QByteArray().append(
QString().fromStdString(defRec.aux_data()));
item->setData(Qt::UserRole, grpRec); item->setData(Qt::UserRole, grpRec);
item->setText(QString("%1: %2") item->setText(QString("%1: %2")
@ -167,7 +179,7 @@ void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current,
rec["groupRecordSourceCount"] = groupRecordSourceCount->text().toUInt(); rec["groupRecordSourceCount"] = groupRecordSourceCount->text().toUInt();
rec["overrideAuxDataLength"] = overrideAuxDataLength->isChecked(); rec["overrideAuxDataLength"] = overrideAuxDataLength->isChecked();
rec["auxDataLength"] = auxDataLength->text().toUInt(); rec["auxDataLength"] = auxDataLength->text().toUInt();
rec["auxData"] = auxData->text(); rec["auxData"] = QByteArray().fromHex(QByteArray().append(auxData->text()));
previous->setData(Qt::UserRole, rec); previous->setData(Qt::UserRole, rec);
previous->setText(QString("%1: %2") previous->setText(QString("%1: %2")
@ -196,7 +208,7 @@ _load_current_record:
rec["groupRecordSourceCount"].toUInt())); rec["groupRecordSourceCount"].toUInt()));
overrideAuxDataLength->setChecked(rec["overrideAuxDataLength"].toBool()); overrideAuxDataLength->setChecked(rec["overrideAuxDataLength"].toBool());
auxDataLength->setText(QString().setNum(rec["auxDataLength"].toUInt())); auxDataLength->setText(QString().setNum(rec["auxDataLength"].toUInt()));
auxData->setText(rec["auxData"].toString()); auxData->setText(QString(rec["auxData"].toByteArray().toHex()));
_exit: _exit:
groupRecord->setEnabled(current != NULL); groupRecord->setEnabled(current != NULL);
@ -254,46 +266,24 @@ int GmpProtocol::fieldCount() const
int GmpProtocol::frameFieldCount() const int GmpProtocol::frameFieldCount() const
{ {
int count = 0; int type = msgType();
// TODO: optimize!!!!! // frameFieldCountMap contains the frameFieldCounts for each
// msgType - this is built on demand and cached for subsequent use
// lookup if we have already cached ...
if (frameFieldCountMap.contains(type))
return frameFieldCountMap.value(type);
// ... otherwise calculate and cache
int count = 0;
for (int i = 0; i < FIELD_COUNT; i++) for (int i = 0; i < FIELD_COUNT; i++)
{ {
if (fieldFlags(i).testFlag(AbstractProtocol::FrameField)) if (fieldFlags(i).testFlag(AbstractProtocol::FrameField))
count++; count++;
} }
frameFieldCountMap.insert(type, count);
return count; return count;
#if 0
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;
}
#endif
} }
AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const
@ -316,6 +306,7 @@ AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const
break; break;
case kMldMrt: case kMldMrt:
case kMldRsvd: case kMldRsvd:
// MLD subclass should handle suitably
break; break;
case kGroupAddress: case kGroupAddress:
@ -404,13 +395,21 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib,
} }
case kChecksum: case kChecksum:
{ {
switch(attrib)
{
case FieldName:
return QString("Checksum");
case FieldBitSize:
return 16;
default:
break;
}
quint16 cksum = data.is_override_checksum() ? quint16 cksum = data.is_override_checksum() ?
data.checksum() : checksum(streamIndex); data.checksum() : checksum(streamIndex);
switch(attrib) switch(attrib)
{ {
case FieldName:
return QString("Checksum");
case FieldValue: case FieldValue:
return cksum; return cksum;
case FieldFrameValue: case FieldFrameValue:
@ -423,14 +422,13 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib,
} }
case FieldTextValue: case FieldTextValue:
return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0'));
case FieldBitSize:
return 16;
default: default:
break; break;
} }
break; break;
} }
case kMldMrt: case kMldMrt:
case kMldRsvd:
// XXX: Present only in MLD - hence handled by the mld subclass // XXX: Present only in MLD - hence handled by the mld subclass
break; break;
@ -440,7 +438,7 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib,
case kRsvd1: case kRsvd1:
{ {
int rsvd = 0; quint8 rsvd = 0;
switch(attrib) switch(attrib)
{ {
@ -513,8 +511,8 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib,
return QString("%1").arg(qqi); return QString("%1").arg(qqi);
case FieldFrameValue: case FieldFrameValue:
{ {
quint8 qqic = qqi; // TODO: derive code from qqi char qqicode = char(qqic(qqi));
return QByteArray(1, char(qqic)); return QByteArray(1, qqicode);
} }
default: default:
break; break;
@ -628,7 +626,7 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib,
rec.is_override_aux_data_length(); rec.is_override_aux_data_length();
grpRec["auxDataLength"] = rec.aux_data_length(); grpRec["auxDataLength"] = rec.aux_data_length();
grpRec["auxData"] = QByteArray().append( grpRec["auxData"] = QByteArray().append(
QString::fromStdString(rec.aux_data())).toHex(); QString::fromStdString(rec.aux_data()));
grpRecords.append(grpRec); grpRecords.append(grpRec);
} }
@ -641,26 +639,23 @@ QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib,
{ {
OstProto::Gmp::GroupRecord rec = data.group_records(i); OstProto::Gmp::GroupRecord rec = data.group_records(i);
QByteArray rv; QByteArray rv;
quint16 srcCount;
rv.resize(4); rv.resize(4);
rv[0] = rec.type(); rv[0] = rec.type();
rv[1] = rec.is_override_aux_data_length() ? rv[1] = rec.is_override_aux_data_length() ?
rec.aux_data_length() : rec.aux_data().size()/4; rec.aux_data_length() : rec.aux_data().size()/4;
if (rec.is_override_source_count()) if (rec.is_override_source_count())
{ srcCount = rec.source_count();
qToBigEndian(quint16(rec.source_count()),
(uchar*)(rv.data()+2));
}
else else
{ srcCount = rec.sources_size();
qToBigEndian(quint16(rec.sources_size()), qToBigEndian(srcCount, (uchar*)(rv.data()+2));
(uchar*)(rv.data()+2));
}
// group_address => subclass responsibility // group_address => subclass responsibility
// source list => subclass responsibility // source list => subclass responsibility
rv.append(QString().fromStdString(rec.aux_data()).toUtf8()); rv.append(QString().fromStdString(rec.aux_data()));
fv.append(rv); fv.append(rv);
} }
@ -823,6 +818,7 @@ bool GmpProtocol::setFieldData(int index, const QVariant &value,
} }
case kGroupAddress: case kGroupAddress:
// XXX: Handled by subclass // XXX: Handled by subclass
isOk = false;
break; break;
case kRsvd1: case kRsvd1:
isOk = false; isOk = false;
@ -843,7 +839,7 @@ bool GmpProtocol::setFieldData(int index, const QVariant &value,
} }
case kQqic: case kQqic:
{ {
uint qqi = value.toUInt(&isOk); // TODO: QQIC or QQI?? uint qqi = value.toUInt(&isOk);
if (isOk) if (isOk)
data.set_qqi(qqi); data.set_qqi(qqi);
break; break;
@ -857,6 +853,7 @@ bool GmpProtocol::setFieldData(int index, const QVariant &value,
} }
case kSources: case kSources:
// XXX: Handled by subclass // XXX: Handled by subclass
isOk = false;
break; break;
case kRsvd2: case kRsvd2:
isOk = false; isOk = false;
@ -889,8 +886,7 @@ bool GmpProtocol::setFieldData(int index, const QVariant &value,
rec->set_is_override_aux_data_length( rec->set_is_override_aux_data_length(
grpRec["overrideAuxDataLength"].toBool()); grpRec["overrideAuxDataLength"].toBool());
rec->set_aux_data_length(grpRec["auxDataLength"].toUInt()); rec->set_aux_data_length(grpRec["auxDataLength"].toUInt());
QByteArray ba = QByteArray::fromHex( QByteArray ba = grpRec["auxData"].toByteArray();
grpRec["auxData"].toByteArray());
// pad to word boundary // pad to word boundary
if (ba.size() % 4) if (ba.size() % 4)
ba.append(QByteArray(4 - (ba.size() % 4), char(0))); ba.append(QByteArray(4 - (ba.size() % 4), char(0)));
@ -956,12 +952,6 @@ _exit:
return isOk; 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 int GmpProtocol::protocolFrameSize(int streamIndex) const
{ {
// TODO: Calculate to reduce processing cost // TODO: Calculate to reduce processing cost
@ -987,8 +977,7 @@ void GmpProtocol::loadConfigWidget()
configWidget(); configWidget();
configForm->msgTypeCombo->setValue(fieldData(kType, FieldValue).toUInt()); configForm->msgTypeCombo->setValue(fieldData(kType, FieldValue).toUInt());
configForm->maxResponseTime->setText(QString("%1").arg( // XXX: configForm->maxResponseTime set by subclass
data.max_response_time()));
configForm->overrideChecksum->setChecked( configForm->overrideChecksum->setChecked(
fieldData(kIsOverrideChecksum, FieldValue).toBool()); fieldData(kIsOverrideChecksum, FieldValue).toBool());
configForm->checksum->setText(uintToHexStr( configForm->checksum->setText(uintToHexStr(
@ -1049,7 +1038,7 @@ void GmpProtocol::storeConfigWidget()
configForm->update(); configForm->update();
setFieldData(kType, configForm->msgTypeCombo->currentValue()); setFieldData(kType, configForm->msgTypeCombo->currentValue());
setFieldData(kMldMrt, configForm->maxResponseTime->text()); // XXX: configForm->maxResponseTime handled by subclass
setFieldData(kIsOverrideChecksum, setFieldData(kIsOverrideChecksum,
configForm->overrideChecksum->isChecked()); configForm->overrideChecksum->isChecked());
setFieldData(kChecksum, setFieldData(kChecksum,
@ -1077,8 +1066,8 @@ void GmpProtocol::storeConfigWidget()
QVariantList grpList; QVariantList grpList;
for (int i = 0; i < configForm->groupList->count(); i++) for (int i = 0; i < configForm->groupList->count(); i++)
{ {
grpList.append(configForm->groupList->item(i)->data(Qt::UserRole) QVariant grp = configForm->groupList->item(i)->data(Qt::UserRole);
.toMap()); grpList.append(grp.toMap());
} }
setFieldData(kGroupRecords, grpList); setFieldData(kGroupRecords, grpList);

View File

@ -25,34 +25,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "abstractprotocol.h" #include "abstractprotocol.h"
enum GmpMsgType #include <QHash>
{
kIgmpV1Query = 0x11,
kIgmpV1Report = 0x12,
kIgmpV2Query = 0xFF11, // Both IGMP and MLD use the same msg type value for 'Query' message
kIgmpV2Report = 0x16, // across versions despite the fields being different. To distinguish
kIgmpV2Leave = 0x17, // Query messages of different versions, we use an additional upper byte
enum GmpMsgType
{
// IGMP
kIgmpV1Query = 0x11,
kIgmpV1Report = 0x12,
kIgmpV3Query = 0xFE11, kIgmpV2Query = 0xFF11,
kIgmpV3Report = 0x22, kIgmpV2Report = 0x16,
kIgmpV2Leave = 0x17,
kMldV1Query = 0x82, kIgmpV3Query = 0xFE11,
kMldV1Report = 0x83, kIgmpV3Report = 0x22,
kMldV1Done = 0x84,
kMldV2Query = 0xFF82, // MLD
kMldV2Report = 0x8F kMldV1Query = 0x82,
}; kMldV1Report = 0x83,
kMldV1Done = 0x84,
kMldV2Query = 0xFF82,
kMldV2Report = 0x8F
};
/* /*
TODO:FIXME Gmp Protocol Frame Format - TODO: for now see the respective RFCs
Gmp Protocol Frame Format -
+-----+------+------+------+------+------+
| A | B | LEN | CSUM | X | Y |
| (3) | (13) | (16) | (16) | (32) | (32) |
+-----+------+------+------+------+------+
Figures in brackets represent field width in bits
*/ */
class GmpProtocol; class GmpProtocol;
@ -64,9 +65,16 @@ public:
~GmpConfigForm(); ~GmpConfigForm();
void update(); void update();
protected: protected:
QString _defaultGroupIp;
QString _defaultSourceIp; QString _defaultSourceIp;
private:
enum {
kSsmQueryPage = 0,
kSsmReportPage = 1
};
private slots: private slots:
void on_msgTypeCombo_currentIndexChanged(int index); void on_msgTypeCombo_currentIndexChanged(int index);
void on_groupMode_currentIndexChanged(int index);
void on_addSource_clicked(); void on_addSource_clicked();
void on_deleteSource_clicked(); void on_deleteSource_clicked();
@ -109,24 +117,18 @@ protected:
// ------------ // ------------
// Frame Fields // Frame Fields
// ------------ // ------------
// Fields used in all ASM and SSM messages, unless otherwise specified
kType = 0, kType = 0,
kRsvdMrtCode, kRsvdMrtCode,
kChecksum, kChecksum,
kMldMrt, // MLD Only (except MLDv2 Report) kMldMrt, // MLD Only (except MLDv2 Report)
kMldRsvd, // MLD Only (except MLDv2 Report) kMldRsvd, // MLD Only (except MLDv2 Report)
// Used ONLY in - // Field used in ASM messages
// IGMPv1: Query, Report
// IGMPv2: Report, Leave (v2 uses v1 Query only)
// IGMPv3: Query
// MLDv1: Query, Report, Done
// MLDv2: Query
kGroupAddress, kGroupAddress,
FIELD_COUNT_ASM_ALL, FIELD_COUNT_ASM_ALL,
// Used ONLY in - // Fields used in SSM Query
// IGMPv3: Query
// MLDv2: Query
kRsvd1 = FIELD_COUNT_ASM_ALL, kRsvd1 = FIELD_COUNT_ASM_ALL,
kSFlag, kSFlag,
kQrv, kQrv,
@ -135,9 +137,7 @@ protected:
kSources, kSources,
FIELD_COUNT_SSM_QUERY, FIELD_COUNT_SSM_QUERY,
// Used ONLY in - // Fields used in SSM Report
// IGMPv3: Report
// MLDv2: Report
kRsvd2 = FIELD_COUNT_SSM_QUERY, kRsvd2 = FIELD_COUNT_SSM_QUERY,
kGroupRecordCount, kGroupRecordCount,
kGroupRecords, kGroupRecords,
@ -163,30 +163,48 @@ protected:
OstProto::Gmp data; OstProto::Gmp data;
GmpConfigForm *configForm; GmpConfigForm *configForm;
GmpMsgType msgType() const GmpMsgType msgType() const;
{
return GmpMsgType(fieldData(kType, FieldValue).toUInt()); virtual bool isSsmReport() const;
} virtual bool isQuery() const;
bool isSsmReport() const virtual bool isSsmQuery() const;
{
return ((msgType() == kIgmpV3Report) int qqic(int value) const;
|| (msgType() == kMldV2Report ));
}
bool isQuery() const
{
return ((msgType() == kIgmpV1Query)
|| (msgType() == kIgmpV2Query)
|| (msgType() == kIgmpV3Query)
|| (msgType() == kMldV1Query )
|| (msgType() == kMldV2Query ));
}
bool isSsmQuery() const
{
return ((msgType() == kIgmpV3Query)
|| (msgType() == kMldV2Query ));
}
virtual quint16 checksum(int streamIndex) const = 0; virtual quint16 checksum(int streamIndex) const = 0;
private:
static QHash<int, int> frameFieldCountMap;
}; };
inline GmpMsgType GmpProtocol::msgType() const
{
return GmpMsgType(fieldData(kType, FieldValue).toUInt());
}
inline bool GmpProtocol::isSsmReport() const
{
return ((msgType() == kIgmpV3Report)
|| (msgType() == kMldV2Report ));
}
inline bool GmpProtocol::isQuery() const
{
return ((msgType() == kIgmpV1Query)
|| (msgType() == kIgmpV2Query)
|| (msgType() == kIgmpV3Query)
|| (msgType() == kMldV1Query )
|| (msgType() == kMldV2Query ));
}
inline bool GmpProtocol::isSsmQuery() const
{
return ((msgType() == kIgmpV3Query)
|| (msgType() == kMldV2Query ));
}
inline int GmpProtocol::qqic(int value) const
{
return quint8(value); // TODO: if value > 128 convert to mantissa/exp form
}
#endif #endif

View File

@ -23,13 +23,14 @@ package OstProto;
// Group Management Protocol (i.e. IGMP and MLD) // Group Management Protocol (i.e. IGMP and MLD)
message Gmp { message Gmp {
// TODO: field numbers and default values //
// Common fields for both ASM and SSM messages
optional uint32 type = 1; // TODO: default value //
optional uint32 type = 1;
optional bool is_override_rsvd_code = 2; optional bool is_override_rsvd_code = 2;
optional uint32 rsvd_code = 3 [default = 0]; optional uint32 rsvd_code = 3;
// MaxRespTime is in milliseconds - MaxRespCode will be derived // MaxRespTime is in milliseconds - MaxRespCode will be derived
optional uint32 max_response_time = 4[default = 100]; optional uint32 max_response_time = 4 [default = 100];
optional bool is_override_checksum = 5; optional bool is_override_checksum = 5;
optional uint32 checksum = 6; optional uint32 checksum = 6;
@ -39,35 +40,34 @@ message Gmp {
optional fixed64 v6_lo = 3; optional fixed64 v6_lo = 3;
} }
// used by //
// IGMPv1: Query, Report // Fields used in ASM messages
// IGMPv2: Report, Leave (v2 uses v1 Query only) //
// MLDv1: Query, Report, Done
enum GroupMode { enum GroupMode {
kFixed = 0; kFixed = 0;
kIncrementGroup = 1; kIncrementGroup = 1;
kDecrementGroup = 2; kDecrementGroup = 2;
kRandomGroup = 3; kRandomGroup = 3;
} }
optional IpAddress group_address = 10; // TODO: default value optional IpAddress group_address = 10;
optional GroupMode group_mode = 11 [default = kFixed]; optional GroupMode group_mode = 11 [default = kFixed];
optional uint32 group_count = 12 [default = 16]; optional uint32 group_count = 12 [default = 16];
optional uint32 group_prefix = 13 [default = 24]; // TODO: verify mcast ip range optional uint32 group_prefix = 13 [default = 24];
// used by //
// IGMPv3: Query // Fields used in SSM Query
// MLDv2: Query //
optional bool s_flag = 20; optional bool s_flag = 20;
optional uint32 qrv = 21 [default = 2]; optional uint32 qrv = 21 [default = 2];
// QuerierQueryInterval is in seconds - QQIC will be derived // QuerierQueryInterval is in seconds - QQIC will be derived
optional uint32 qqi = 22; optional uint32 qqi = 22 [default = 125];
repeated IpAddress sources = 23; repeated IpAddress sources = 23;
optional bool is_override_source_count = 24; optional bool is_override_source_count = 24;
optional uint32 source_count = 25; optional uint32 source_count = 25;
// used by //
// IGMPv3: Report // Fields used in SSM Reports
// MLDv2: Report //
message GroupRecord { message GroupRecord {
enum RecordType { enum RecordType {
kIsInclude = 1; kIsInclude = 1;
@ -79,7 +79,7 @@ message Gmp {
} }
optional RecordType type = 1 [default = kIsInclude]; optional RecordType type = 1 [default = kIsInclude];
optional IpAddress group_address = 2; // TODO: default optional IpAddress group_address = 2;
repeated IpAddress sources = 3; repeated IpAddress sources = 3;
optional bool is_override_source_count = 4; optional bool is_override_source_count = 4;
optional uint32 source_count = 5; optional uint32 source_count = 5;

View File

@ -5,7 +5,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>497</width> <width>509</width>
<height>355</height> <height>355</height>
</rect> </rect>
</property> </property>
@ -31,7 +31,7 @@
<item row="1" column="0" > <item row="1" column="0" >
<widget class="QLabel" name="label_2" > <widget class="QLabel" name="label_2" >
<property name="text" > <property name="text" >
<string>Max Response Time</string> <string>Max Response Time (1/10s)</string>
</property> </property>
<property name="buddy" > <property name="buddy" >
<cstring>maxResponseTime</cstring> <cstring>maxResponseTime</cstring>
@ -155,6 +155,9 @@
</item> </item>
<item row="1" column="2" > <item row="1" column="2" >
<widget class="QLineEdit" name="groupCount" > <widget class="QLineEdit" name="groupCount" >
<property name="enabled" >
<bool>false</bool>
</property>
<property name="sizePolicy" > <property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Fixed" > <sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -165,6 +168,9 @@
</item> </item>
<item row="1" column="3" > <item row="1" column="3" >
<widget class="QLineEdit" name="groupPrefix" > <widget class="QLineEdit" name="groupPrefix" >
<property name="enabled" >
<bool>false</bool>
</property>
<property name="sizePolicy" > <property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Fixed" > <sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -304,7 +310,7 @@
<item> <item>
<widget class="QToolButton" name="deleteSource" > <widget class="QToolButton" name="deleteSource" >
<property name="text" > <property name="text" >
<string>--</string> <string></string>
</property> </property>
</widget> </widget>
</item> </item>
@ -428,7 +434,7 @@
<item> <item>
<widget class="QToolButton" name="deleteGroupRecord" > <widget class="QToolButton" name="deleteGroupRecord" >
<property name="text" > <property name="text" >
<string>--</string> <string></string>
</property> </property>
</widget> </widget>
</item> </item>
@ -580,7 +586,7 @@
<item> <item>
<widget class="QToolButton" name="deleteGroupRecordSource" > <widget class="QToolButton" name="deleteGroupRecordSource" >
<property name="text" > <property name="text" >
<string>--</string> <string></string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -19,37 +19,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "igmp.h" #include "igmp.h"
#include "ipv4addressdelegate.h"
#include "iputils.h"
#include <QHostAddress> #include <QHostAddress>
#include <QItemDelegate>
#include <qendian.h> #include <qendian.h>
class IpAddressDelegate : public QItemDelegate
{
public:
IpAddressDelegate(QObject *parent = 0)
: QItemDelegate(parent) { }
~IpAddressDelegate() {}
QWidget* createEditor(QWidget *parent,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QLineEdit *ipEdit;
ipEdit = static_cast<QLineEdit*>(QItemDelegate::createEditor(
parent, option, index));
ipEdit->setInputMask("009.009.009.009;"); // FIXME: use validator
return ipEdit;
}
};
IgmpConfigForm::IgmpConfigForm(QWidget *parent) IgmpConfigForm::IgmpConfigForm(QWidget *parent)
: GmpConfigForm(parent) : GmpConfigForm(parent)
{ {
_defaultGroupIp = "0.0.0.0";
_defaultSourceIp = "0.0.0.0"; _defaultSourceIp = "0.0.0.0";
sourceList->setItemDelegate(new IpAddressDelegate(this)); groupAddress->setInputMask("009.009.009.009;"); // FIXME: use validator
groupRecordSourceList->setItemDelegate(new IpAddressDelegate(this)); groupRecordAddress->setInputMask("009.009.009.009;"); // FIXME:use validator
sourceList->setItemDelegate(new IPv4AddressDelegate(this));
groupRecordSourceList->setItemDelegate(new IPv4AddressDelegate(this));
} }
IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent) IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent)
@ -114,15 +99,19 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib,
{ {
case kRsvdMrtCode: case kRsvdMrtCode:
{ {
quint8 mrt = 0, mrc = 0; uint mrt = 0;
quint8 mrcode = 0;
if (msgType() == kIgmpV3Query) if (msgType() == kIgmpV3Query)
{ {
mrt = data.max_response_time(); mrt = data.max_response_time();
mrc = mrt; // TODO: MR Code mrcode = quint8(mrc(mrt));
} }
else if (msgType() == kIgmpV2Query) else if (msgType() == kIgmpV2Query)
mrc = mrt = data.max_response_time() & 0xFF; {
mrt = data.max_response_time();
mrcode = mrt & 0xFF;
}
switch(attrib) switch(attrib)
@ -137,7 +126,7 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib,
case FieldTextValue: case FieldTextValue:
return QString("%1").arg(mrt); return QString("%1").arg(mrt);
case FieldFrameValue: case FieldFrameValue:
return QByteArray(1, mrc); return QByteArray(1, mrcode);
default: default:
break; break;
} }
@ -145,14 +134,12 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib,
} }
case kGroupAddress: case kGroupAddress:
{ {
quint32 grpIp = data.group_address().v4(); // FIXME quint32 grpIp = ipUtils::ipAddress(
#if 0 // TODO data.group_address().v4(),
getip( data.group_prefix(),
data.group_address().v4(), ipUtils::AddrMode(data.group_mode()),
data.group_mode(), data.group_count(),
data.group_count(), streamIndex);
data.group_prefix());
#endif
switch(attrib) switch(attrib)
{ {
@ -225,10 +212,10 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib,
grpRec["groupRecordAddress"] = QHostAddress( grpRec["groupRecordAddress"] = QHostAddress(
rec.group_address().v4()).toString(); rec.group_address().v4()).toString();
QStringList l; QStringList sl;
for (int j = 0; j < rec.sources_size(); j++) for (int j = 0; j < rec.sources_size(); j++)
l.append(QHostAddress(rec.sources(j).v4()).toString()); sl.append(QHostAddress(rec.sources(j).v4()).toString());
grpRec["groupRecordSourceList"] = l; grpRec["groupRecordSourceList"] = sl;
grpRecords.replace(i, grpRec); grpRecords.replace(i, grpRec);
} }
@ -273,10 +260,10 @@ QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib,
QHostAddress(rec.group_address().v4()).toString())); QHostAddress(rec.group_address().v4()).toString()));
str.append("; Sources: "); str.append("; Sources: ");
QStringList l; QStringList sl;
for (int j = 0; j < rec.sources_size(); j++) for (int j = 0; j < rec.sources_size(); j++)
l.append(QHostAddress(rec.sources(j).v4()).toString()); sl.append(QHostAddress(rec.sources(j).v4()).toString());
str.append(l.join(", ")); str.append(sl.join(", "));
recStr.replace("XXX", str); recStr.replace("XXX", str);
list.replace(i, recStr); list.replace(i, recStr);
@ -305,6 +292,13 @@ bool IgmpProtocol::setFieldData(int index, const QVariant &value,
switch (index) switch (index)
{ {
case kRsvdMrtCode:
{
uint mrt = value.toUInt(&isOk);
if (isOk)
data.set_max_response_time(mrt);
break;
}
case kGroupAddress: case kGroupAddress:
{ {
QHostAddress addr(value.toString()); QHostAddress addr(value.toString());
@ -343,6 +337,7 @@ bool IgmpProtocol::setFieldData(int index, const QVariant &value,
QStringList srcList = grpRec["groupRecordSourceList"] QStringList srcList = grpRec["groupRecordSourceList"]
.toStringList(); .toStringList();
rec->clear_sources();
foreach (QString src, srcList) foreach (QString src, srcList)
{ {
rec->add_sources()->set_v4( rec->add_sources()->set_v4(
@ -380,48 +375,20 @@ void IgmpProtocol::loadConfigWidget()
configForm->maxResponseTime->setText( configForm->maxResponseTime->setText(
fieldData(kRsvdMrtCode, FieldValue).toString()); fieldData(kRsvdMrtCode, FieldValue).toString());
#if 0
configForm->igmpA->setText(fieldData(igmp_a, FieldValue).toString());
configForm->igmpB->setText(fieldData(igmp_b, FieldValue).toString());
configForm->igmpPayloadLength->setText(
fieldData(igmp_payloadLength, FieldValue).toString());
configForm->isChecksumOverride->setChecked(
fieldData(igmp_is_override_checksum, FieldValue).toBool());
configForm->igmpChecksum->setText(uintToHexStr(
fieldData(igmp_checksum, FieldValue).toUInt(), 2));
configForm->igmpX->setText(fieldData(igmp_x, FieldValue).toString());
configForm->igmpY->setText(fieldData(igmp_y, FieldValue).toString());
#endif
} }
void IgmpProtocol::storeConfigWidget() void IgmpProtocol::storeConfigWidget()
{ {
bool isOk;
GmpProtocol::storeConfigWidget(); GmpProtocol::storeConfigWidget();
#if 0 setFieldData(kRsvdMrtCode, configForm->maxResponseTime->text());
setFieldData(igmp_a, configForm->igmpA->text());
setFieldData(igmp_b, configForm->igmpB->text());
setFieldData(igmp_payloadLength, configForm->igmpPayloadLength->text());
setFieldData(igmp_is_override_checksum,
configForm->isChecksumOverride->isChecked());
setFieldData(igmp_checksum, configForm->igmpChecksum->text().toUInt(&isOk, BASE_HEX));
setFieldData(igmp_x, configForm->igmpX->text());
setFieldData(igmp_y, configForm->igmpY->text());
#endif
} }
quint16 IgmpProtocol::checksum(int streamIndex) const quint16 IgmpProtocol::checksum(int streamIndex) const
{ {
quint16 cks; quint16 cks;
quint32 sum = 0; quint32 sum = 0;
#if 0 // FIXME #if 1 // FIXME
// TODO: add as a new CksumType (CksumIgmp?) and implement in AbsProto // TODO: add as a new CksumType (CksumIgmp?) and implement in AbsProto
cks = protocolFrameCksum(streamIndex, CksumIp); cks = protocolFrameCksum(streamIndex, CksumIp);
sum += (quint16) ~cks; sum += (quint16) ~cks;

View File

@ -58,6 +58,13 @@ public:
protected: protected:
virtual quint16 checksum(int streamIndex) const; virtual quint16 checksum(int streamIndex) const;
private:
int mrc(int value) const;
}; };
inline int IgmpProtocol::mrc(int value) const
{
return quint8(value); // TODO: if value > 128, convert to mantissa/exp form
}
#endif #endif

70
common/iputils.h Normal file
View File

@ -0,0 +1,70 @@
/*
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/>
*/
#ifndef _IP_UTILS_H
#define _IP_UTILS_H
namespace ipUtils {
enum AddrMode {
kFixed = 0,
kIncrement = 1,
kDecrement = 2,
kRandom = 3
};
quint32 ipAddress(quint32 baseIp, int prefix, AddrMode mode, int count,
int index)
{
int u;
quint32 mask = ((1<<prefix) - 1) << (32 - prefix);
quint32 subnet, host, ip;
switch(mode)
{
case kFixed:
ip = baseIp;
break;
case kIncrement:
u = index % count;
subnet = baseIp & mask;
host = (((baseIp & ~mask) + u) &
~mask);
ip = subnet | host;
break;
case kDecrement:
u = index % count;
subnet = baseIp & mask;
host = (((baseIp & ~mask) - u) &
~mask);
ip = subnet | host;
break;
case kRandom:
subnet = baseIp & mask;
host = (qrand() & ~mask);
ip = subnet | host;
break;
default:
qWarning("Unhandled mode = %d", mode);
}
return ip;
}
} // namespace ipUtils
#endif

View File

@ -0,0 +1,58 @@
/*
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/>
*/
#ifndef _IPV4_ADDRESS_DELEGATE
#define _IPV4_ADDRESS_DELEGATE
#include <QItemDelegate>
#include <QLineEdit>
class IPv4AddressDelegate : public QItemDelegate
{
Q_OBJECT
public:
IPv4AddressDelegate(QObject *parent = 0);
~IPv4AddressDelegate();
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
};
inline IPv4AddressDelegate::IPv4AddressDelegate(QObject *parent)
: QItemDelegate(parent)
{
}
inline IPv4AddressDelegate::~IPv4AddressDelegate()
{
}
inline QWidget* IPv4AddressDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QLineEdit *ipEdit;
ipEdit = static_cast<QLineEdit*>(QItemDelegate::createEditor(
parent, option, index));
ipEdit->setInputMask("009.009.009.009;"); // FIXME: use validator
return ipEdit;
}
#endif

View File

@ -73,6 +73,7 @@ HEADERS += \
arp.h \ arp.h \
ip4.h \ ip4.h \
ip6.h \ ip6.h \
ipv4addressdelegate.h \
ip6over4.h \ ip6over4.h \
ip4over6.h \ ip4over6.h \
ip4over4.h \ ip4over4.h \