544 lines
17 KiB
C++
544 lines
17 KiB
C++
/*
|
|
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 "mld.h"
|
|
|
|
#include "iputils.h"
|
|
|
|
#include <QHostAddress>
|
|
#include <QStringList>
|
|
|
|
MldProtocol::MldProtocol(StreamBase *stream, AbstractProtocol *parent)
|
|
: GmpProtocol(stream, parent)
|
|
{
|
|
_hasPayload = false;
|
|
|
|
data.set_type(kMldV1Query);
|
|
}
|
|
|
|
MldProtocol::~MldProtocol()
|
|
{
|
|
}
|
|
|
|
AbstractProtocol* MldProtocol::createInstance(StreamBase *stream,
|
|
AbstractProtocol *parent)
|
|
{
|
|
return new MldProtocol(stream, parent);
|
|
}
|
|
|
|
quint32 MldProtocol::protocolNumber() const
|
|
{
|
|
return OstProto::Protocol::kMldFieldNumber;
|
|
}
|
|
|
|
void MldProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const
|
|
{
|
|
protocol.MutableExtension(OstProto::mld)->CopyFrom(data);
|
|
protocol.mutable_protocol_id()->set_id(protocolNumber());
|
|
}
|
|
|
|
void MldProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol)
|
|
{
|
|
if (protocol.protocol_id().id() == protocolNumber() &&
|
|
protocol.HasExtension(OstProto::mld))
|
|
data.MergeFrom(protocol.GetExtension(OstProto::mld));
|
|
}
|
|
|
|
QString MldProtocol::name() const
|
|
{
|
|
return QString("Multicast Listener Discovery");
|
|
}
|
|
|
|
QString MldProtocol::shortName() const
|
|
{
|
|
return QString("MLD");
|
|
}
|
|
|
|
quint32 MldProtocol::protocolId(ProtocolIdType type) const
|
|
{
|
|
switch(type)
|
|
{
|
|
case ProtocolIdIp: return 0x3a;
|
|
default:break;
|
|
}
|
|
|
|
return AbstractProtocol::protocolId(type);
|
|
}
|
|
|
|
AbstractProtocol::FieldFlags MldProtocol::fieldFlags(int index) const
|
|
{
|
|
AbstractProtocol::FieldFlags flags;
|
|
|
|
flags = GmpProtocol::fieldFlags(index);
|
|
|
|
switch(index)
|
|
{
|
|
case kMldMrt:
|
|
case kMldRsvd:
|
|
if (msgType() != kMldV2Report)
|
|
flags |= FrameField;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
QVariant MldProtocol::fieldData(int index, FieldAttrib attrib,
|
|
int streamIndex) const
|
|
{
|
|
switch (index)
|
|
{
|
|
case kRsvdMrtCode:
|
|
{
|
|
switch(attrib)
|
|
{
|
|
case FieldName: return QString("Code");
|
|
default: break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case kMldMrt:
|
|
{
|
|
quint16 mrt = 0, mrcode = 0;
|
|
|
|
if (msgType() == kMldV2Query)
|
|
{
|
|
mrt = data.max_response_time();
|
|
mrcode = mrc(mrt);
|
|
}
|
|
else if (msgType() == kMldV1Query)
|
|
mrcode = mrt = data.max_response_time() & 0xFFFF;
|
|
|
|
switch(attrib)
|
|
{
|
|
case FieldName:
|
|
if (isQuery())
|
|
return QString("Max Response Time");
|
|
return QString("Reserved");
|
|
case FieldValue:
|
|
return mrt;
|
|
case FieldTextValue:
|
|
return QString("%1 ms").arg(mrt);
|
|
case FieldFrameValue:
|
|
{
|
|
QByteArray fv;
|
|
|
|
fv.resize(2);
|
|
qToBigEndian(mrcode, (uchar*) fv.data());
|
|
return fv;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case kMldRsvd:
|
|
{
|
|
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 kGroupAddress:
|
|
{
|
|
quint64 grpHi = 0, grpLo = 0;
|
|
|
|
ipUtils::ipAddress(
|
|
data.group_address().v6_hi(),
|
|
data.group_address().v6_lo(),
|
|
data.group_prefix(),
|
|
ipUtils::AddrMode(data.group_mode()),
|
|
data.group_count(),
|
|
streamIndex,
|
|
grpHi,
|
|
grpLo);
|
|
|
|
switch(attrib)
|
|
{
|
|
case FieldName:
|
|
return QString("Group Address");
|
|
case FieldValue:
|
|
case FieldTextValue:
|
|
case FieldFrameValue:
|
|
{
|
|
QByteArray fv;
|
|
fv.resize(16);
|
|
qToBigEndian(grpHi, (uchar*) fv.data());
|
|
qToBigEndian(grpLo, (uchar*) (fv.data() + 8));
|
|
if (attrib == FieldFrameValue)
|
|
return fv;
|
|
else
|
|
return QHostAddress((quint8*)fv.constData()).toString();
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case kSources:
|
|
{
|
|
switch(attrib)
|
|
{
|
|
case FieldName:
|
|
return QString("Source List");
|
|
case FieldValue:
|
|
{
|
|
QStringList list;
|
|
QByteArray fv;
|
|
fv.resize(16);
|
|
for (int i = 0; i < data.sources_size(); i++)
|
|
{
|
|
qToBigEndian(data.sources(i).v6_hi(),
|
|
(uchar*)fv.data());
|
|
qToBigEndian(data.sources(i).v6_lo(),
|
|
(uchar*)fv.data()+8);
|
|
|
|
list << QHostAddress((quint8*)fv.constData()).toString();
|
|
}
|
|
return list;
|
|
}
|
|
case FieldFrameValue:
|
|
{
|
|
QByteArray fv;
|
|
fv.resize(16 * data.sources_size());
|
|
for (int i = 0; i < data.sources_size(); i++)
|
|
{
|
|
qToBigEndian(data.sources(i).v6_hi(),
|
|
(uchar*)(fv.data() + i*16));
|
|
qToBigEndian(data.sources(i).v6_lo(),
|
|
(uchar*)(fv.data() + i*16 + 8));
|
|
}
|
|
return fv;
|
|
}
|
|
case FieldTextValue:
|
|
{
|
|
QStringList list;
|
|
QByteArray fv;
|
|
fv.resize(16);
|
|
for (int i = 0; i < data.sources_size(); i++)
|
|
{
|
|
qToBigEndian(data.sources(i).v6_hi(),
|
|
(uchar*)fv.data());
|
|
qToBigEndian(data.sources(i).v6_lo(),
|
|
(uchar*)fv.data()+8);
|
|
|
|
list << QHostAddress((quint8*)fv.constData()).toString();
|
|
}
|
|
return list.join(", ");
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case kGroupRecords:
|
|
{
|
|
switch(attrib)
|
|
{
|
|
case FieldValue:
|
|
{
|
|
QVariantList grpRecords = GmpProtocol::fieldData(
|
|
index, attrib, streamIndex).toList();
|
|
QByteArray ip;
|
|
|
|
ip.resize(16);
|
|
|
|
for (int i = 0; i < data.group_records_size(); i++)
|
|
{
|
|
QVariantMap grpRec = grpRecords.at(i).toMap();
|
|
OstProto::Gmp::GroupRecord rec = data.group_records(i);
|
|
|
|
qToBigEndian(quint64(rec.group_address().v6_hi()),
|
|
(uchar*)(ip.data()));
|
|
qToBigEndian(quint64(rec.group_address().v6_lo()),
|
|
(uchar*)(ip.data() + 8));
|
|
grpRec["groupRecordAddress"] = QHostAddress(
|
|
(quint8*)ip.constData()).toString();
|
|
|
|
QStringList sl;
|
|
for (int j = 0; j < rec.sources_size(); j++)
|
|
{
|
|
qToBigEndian(rec.sources(j).v6_hi(),
|
|
(uchar*)(ip.data()));
|
|
qToBigEndian(rec.sources(j).v6_lo(),
|
|
(uchar*)(ip.data() + 8));
|
|
sl.append(QHostAddress(
|
|
(quint8*)ip.constData()).toString());
|
|
}
|
|
grpRec["groupRecordSourceList"] = sl;
|
|
|
|
grpRecords.replace(i, grpRec);
|
|
}
|
|
return grpRecords;
|
|
}
|
|
case FieldFrameValue:
|
|
{
|
|
QVariantList list = GmpProtocol::fieldData(
|
|
index, attrib, streamIndex).toList();
|
|
QByteArray fv;
|
|
QByteArray ip;
|
|
ip.resize(16);
|
|
|
|
for (int i = 0; i < data.group_records_size(); i++)
|
|
{
|
|
OstProto::Gmp::GroupRecord rec = data.group_records(i);
|
|
QByteArray rv = list.at(i).toByteArray();
|
|
|
|
rv.insert(4, QByteArray(16+16*rec.sources_size(), char(0)));
|
|
qToBigEndian(rec.group_address().v6_hi(),
|
|
(uchar*)(rv.data()+4));
|
|
qToBigEndian(rec.group_address().v6_lo(),
|
|
(uchar*)(rv.data()+4+8));
|
|
for (int j = 0; j < rec.sources_size(); j++)
|
|
{
|
|
qToBigEndian(rec.sources(j).v6_hi(),
|
|
(uchar*)(rv.data()+20+16*j));
|
|
qToBigEndian(rec.sources(j).v6_lo(),
|
|
(uchar*)(rv.data()+20+16*j+8));
|
|
}
|
|
|
|
fv.append(rv);
|
|
}
|
|
return fv;
|
|
}
|
|
case FieldTextValue:
|
|
{
|
|
QStringList list = GmpProtocol::fieldData(
|
|
index, attrib, streamIndex).toStringList();
|
|
QByteArray ip;
|
|
|
|
ip.resize(16);
|
|
|
|
for (int i = 0; i < data.group_records_size(); i++)
|
|
{
|
|
OstProto::Gmp::GroupRecord rec = data.group_records(i);
|
|
QString recStr = list.at(i);
|
|
QString str;
|
|
|
|
qToBigEndian(rec.group_address().v6_hi(),
|
|
(uchar*)(ip.data()));
|
|
qToBigEndian(rec.group_address().v6_lo(),
|
|
(uchar*)(ip.data() + 8));
|
|
str.append(QString("Group: %1").arg(
|
|
QHostAddress((quint8*)ip.constData()).toString()));
|
|
|
|
str.append("; Sources: ");
|
|
QStringList sl;
|
|
for (int j = 0; j < rec.sources_size(); j++)
|
|
{
|
|
qToBigEndian(rec.sources(j).v6_hi(),
|
|
(uchar*)(ip.data()));
|
|
qToBigEndian(rec.sources(j).v6_lo(),
|
|
(uchar*)(ip.data() + 8));
|
|
sl.append(QHostAddress(
|
|
(quint8*)ip.constData()).toString());
|
|
}
|
|
str.append(sl.join(", "));
|
|
|
|
recStr.replace("XXX", str);
|
|
list.replace(i, recStr);
|
|
}
|
|
return list.join("\n").insert(0, "\n");
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return GmpProtocol::fieldData(index, attrib, streamIndex);
|
|
}
|
|
|
|
bool MldProtocol::setFieldData(int index, const QVariant &value,
|
|
FieldAttrib attrib)
|
|
{
|
|
bool isOk = false;
|
|
|
|
if (attrib != FieldValue)
|
|
goto _exit;
|
|
|
|
switch (index)
|
|
{
|
|
case kGroupAddress:
|
|
{
|
|
Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address();
|
|
quint64 x;
|
|
|
|
x = (quint64(addr[0]) << 56)
|
|
| (quint64(addr[1]) << 48)
|
|
| (quint64(addr[2]) << 40)
|
|
| (quint64(addr[3]) << 32)
|
|
| (quint64(addr[4]) << 24)
|
|
| (quint64(addr[5]) << 16)
|
|
| (quint64(addr[6]) << 8)
|
|
| (quint64(addr[7]) << 0);
|
|
data.mutable_group_address()->set_v6_hi(x);
|
|
|
|
x = (quint64(addr[ 8]) << 56)
|
|
| (quint64(addr[ 9]) << 48)
|
|
| (quint64(addr[10]) << 40)
|
|
| (quint64(addr[11]) << 32)
|
|
| (quint64(addr[12]) << 24)
|
|
| (quint64(addr[13]) << 16)
|
|
| (quint64(addr[14]) << 8)
|
|
| (quint64(addr[15]) << 0);
|
|
data.mutable_group_address()->set_v6_lo(x);
|
|
break;
|
|
}
|
|
|
|
case kSources:
|
|
{
|
|
QStringList list = value.toStringList();
|
|
|
|
data.clear_sources();
|
|
foreach(QString str, list)
|
|
{
|
|
OstProto::Gmp::IpAddress *src = data.add_sources();
|
|
Q_IPV6ADDR addr = QHostAddress(str).toIPv6Address();
|
|
quint64 x;
|
|
|
|
x = (quint64(addr[0]) << 56)
|
|
| (quint64(addr[1]) << 48)
|
|
| (quint64(addr[2]) << 40)
|
|
| (quint64(addr[3]) << 32)
|
|
| (quint64(addr[4]) << 24)
|
|
| (quint64(addr[5]) << 16)
|
|
| (quint64(addr[6]) << 8)
|
|
| (quint64(addr[7]) << 0);
|
|
src->set_v6_hi(x);
|
|
|
|
x = (quint64(addr[ 8]) << 56)
|
|
| (quint64(addr[ 9]) << 48)
|
|
| (quint64(addr[10]) << 40)
|
|
| (quint64(addr[11]) << 32)
|
|
| (quint64(addr[12]) << 24)
|
|
| (quint64(addr[13]) << 16)
|
|
| (quint64(addr[14]) << 8)
|
|
| (quint64(addr[15]) << 0);
|
|
src->set_v6_lo(x);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case kGroupRecords:
|
|
{
|
|
GmpProtocol::setFieldData(index, value, attrib);
|
|
QVariantList list = value.toList();
|
|
|
|
for (int i = 0; i < list.count(); i++)
|
|
{
|
|
QVariantMap grpRec = list.at(i).toMap();
|
|
OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i);
|
|
Q_IPV6ADDR addr = QHostAddress(
|
|
grpRec["groupRecordAddress"].toString())
|
|
.toIPv6Address();
|
|
quint64 x;
|
|
|
|
x = (quint64(addr[0]) << 56)
|
|
| (quint64(addr[1]) << 48)
|
|
| (quint64(addr[2]) << 40)
|
|
| (quint64(addr[3]) << 32)
|
|
| (quint64(addr[4]) << 24)
|
|
| (quint64(addr[5]) << 16)
|
|
| (quint64(addr[6]) << 8)
|
|
| (quint64(addr[7]) << 0);
|
|
rec->mutable_group_address()->set_v6_hi(x);
|
|
|
|
x = (quint64(addr[ 8]) << 56)
|
|
| (quint64(addr[ 9]) << 48)
|
|
| (quint64(addr[10]) << 40)
|
|
| (quint64(addr[11]) << 32)
|
|
| (quint64(addr[12]) << 24)
|
|
| (quint64(addr[13]) << 16)
|
|
| (quint64(addr[14]) << 8)
|
|
| (quint64(addr[15]) << 0);
|
|
rec->mutable_group_address()->set_v6_lo(x);
|
|
|
|
QStringList srcList = grpRec["groupRecordSourceList"]
|
|
.toStringList();
|
|
rec->clear_sources();
|
|
foreach (QString str, srcList)
|
|
{
|
|
OstProto::Gmp::IpAddress *src = rec->add_sources();
|
|
Q_IPV6ADDR addr = QHostAddress(str).toIPv6Address();
|
|
quint64 x;
|
|
|
|
x = (quint64(addr[0]) << 56)
|
|
| (quint64(addr[1]) << 48)
|
|
| (quint64(addr[2]) << 40)
|
|
| (quint64(addr[3]) << 32)
|
|
| (quint64(addr[4]) << 24)
|
|
| (quint64(addr[5]) << 16)
|
|
| (quint64(addr[6]) << 8)
|
|
| (quint64(addr[7]) << 0);
|
|
src->set_v6_hi(x);
|
|
|
|
x = (quint64(addr[ 8]) << 56)
|
|
| (quint64(addr[ 9]) << 48)
|
|
| (quint64(addr[10]) << 40)
|
|
| (quint64(addr[11]) << 32)
|
|
| (quint64(addr[12]) << 24)
|
|
| (quint64(addr[13]) << 16)
|
|
| (quint64(addr[14]) << 8)
|
|
| (quint64(addr[15]) << 0);
|
|
src->set_v6_lo(x);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
isOk = GmpProtocol::setFieldData(index, value, attrib);
|
|
break;
|
|
}
|
|
|
|
_exit:
|
|
return isOk;
|
|
}
|
|
|
|
quint16 MldProtocol::checksum(int streamIndex) const
|
|
{
|
|
return AbstractProtocol::protocolFrameCksum(streamIndex, CksumTcpUdp);
|
|
}
|