/* 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 */ #include "mld.h" #include "iputils.h" #include #include 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: return QVariant::fromValue(UInt128(grpHi, grpLo)); 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(quint64(data.sources(i).v6_hi()), (uchar*)fv.data()); qToBigEndian(quint64(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(quint64(data.sources(i).v6_hi()), (uchar*)(fv.data() + i*16)); qToBigEndian(quint64(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(quint64(data.sources(i).v6_hi()), (uchar*)fv.data()); qToBigEndian(quint64(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(quint64(rec.sources(j).v6_hi()), (uchar*)(ip.data())); qToBigEndian(quint64(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(quint64(rec.group_address().v6_hi()), (uchar*)(rv.data()+4)); qToBigEndian(quint64(rec.group_address().v6_lo()), (uchar*)(rv.data()+4+8)); for (int j = 0; j < rec.sources_size(); j++) { qToBigEndian(quint64(rec.sources(j).v6_hi()), (uchar*)(rv.data()+20+16*j)); qToBigEndian(quint64(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(quint64(rec.group_address().v6_hi()), (uchar*)(ip.data())); qToBigEndian(quint64(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(quint64(rec.sources(j).v6_hi()), (uchar*)(ip.data())); qToBigEndian(quint64(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: { if (value.typeName() == QString("UInt128")) { UInt128 addr = value.value(); data.mutable_group_address()->set_v6_hi(addr.hi64()); data.mutable_group_address()->set_v6_lo(addr.lo64()); isOk = true; break; } 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); isOk = true; 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); } isOk = true; 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); } } isOk = true; break; } default: isOk = GmpProtocol::setFieldData(index, value, attrib); break; } _exit: return isOk; } quint16 MldProtocol::checksum(int streamIndex) const { return AbstractProtocol::protocolFrameCksum(streamIndex, CksumTcpUdp); }