blob: c4dab298b8d088788d07aca9d6939f6d19dfb9c6 [file] [log] [blame]
#define LOG_MODULE PacketLogModuleIgmpLayer
#include <IgmpLayer.h>
#include <IpUtils.h>
#include <Logger.h>
#include <string.h>
#ifdef WIN32 //for using ntohl, ntohs, etc.
#include <winsock2.h>
#elif LINUX
#include <in.h> //for using ntohl, ntohs, etc.
#elif MAC_OS_X
#include <arpa/inet.h> //for using ntohl, ntohs, etc.
#endif
namespace pcpp
{
/*************
* IgmpLayer
*************/
IgmpLayer::IgmpLayer(IgmpType type, const IPv4Address& groupAddr, uint8_t maxResponseTime, ProtocolType igmpVer)
{
m_DataLen = getHeaderSizeByVerAndType(igmpVer, type);
m_Data = new uint8_t[m_DataLen];
memset(m_Data, 0, m_DataLen);
m_Protocol = igmpVer;
setType(type);
if (groupAddr != IPv4Address::Zero)
setGroupAddress(groupAddr);
getIgmpHeader()->maxResponseTime = maxResponseTime;
}
void IgmpLayer::setGroupAddress(const IPv4Address& groupAddr)
{
igmp_header* hdr = getIgmpHeader();
hdr->groupAddress = groupAddr.toInt();
}
IgmpType IgmpLayer::getType()
{
uint8_t type = getIgmpHeader()->type;
if (type < (uint8_t)IgmpType_MembershipQuery ||
(type > (uint8_t)IgmpType_LeaveGroup && type < (uint8_t)IgmpType_MulticastTracerouteResponse) ||
(type > (uint8_t)IgmpType_MulticastTraceroute && type < (uint8_t)IgmpType_MembershipReportV3) ||
(type > (uint8_t)IgmpType_MembershipReportV3 && type < (uint8_t)IgmpType_MulticastRouterAdvertisement) ||
type > IgmpType_MulticastRouterTermination)
return IgmpType_Unknown;
return (IgmpType)type;
}
void IgmpLayer::setType(IgmpType type)
{
if (type == IgmpType_Unknown)
return;
igmp_header* hdr = getIgmpHeader();
hdr->type = type;
}
ProtocolType IgmpLayer::getIGMPVerFromData(uint8_t* data, size_t dataLen, bool& isQuery)
{
isQuery = false;
if (dataLen < 8 || data == NULL)
return UnknownProtocol;
switch ((int)data[0])
{
case IgmpType_MembershipReportV2:
case IgmpType_LeaveGroup:
return IGMPv2;
case IgmpType_MembershipReportV1:
return IGMPv1;
case IgmpType_MembershipReportV3:
return IGMPv3;
case IgmpType_MembershipQuery:
{
isQuery = true;
if (dataLen >= sizeof(igmpv3_query_header))
return IGMPv3;
if (data[1] == 0)
return IGMPv1;
else
return IGMPv2;
}
default:
return UnknownProtocol;
}
}
uint16_t IgmpLayer::calculateChecksum()
{
ScalarBuffer<uint16_t> buffer;
buffer.buffer = (uint16_t*)getIgmpHeader();
buffer.len = getHeaderLen();
return compute_checksum(&buffer, 1);
}
size_t IgmpLayer::getHeaderSizeByVerAndType(ProtocolType igmpVer, IgmpType igmpType)
{
if (igmpVer == IGMPv1 || igmpVer == IGMPv2)
return sizeof(igmp_header);
if (igmpVer == IGMPv3)
{
if (igmpType == IgmpType_MembershipQuery)
return sizeof(igmpv3_query_header);
else if (igmpType == IgmpType_MembershipReportV3)
return sizeof(igmpv3_report_header);
}
return 0;
}
std::string IgmpLayer::toString()
{
std::string igmpVer = "";
switch (getProtocol())
{
case IGMPv1:
igmpVer = "1";
break;
case IGMPv2:
igmpVer = "2";
break;
default:
igmpVer = "3";
}
std::string msgType;
switch (getType())
{
case IgmpType_MembershipQuery:
msgType = "Membership Query";
break;
case IgmpType_MembershipReportV1:
msgType = "Membership Report";
break;
case IgmpType_DVMRP:
msgType = "DVMRP";
break;
case IgmpType_P1Mv1:
msgType = "PIMv1";
break;
case IgmpType_CiscoTrace:
msgType = "Cisco Trace";
break;
case IgmpType_MembershipReportV2:
msgType = "Membership Report";
break;
case IgmpType_LeaveGroup:
msgType = "Leave Group";
break;
case IgmpType_MulticastTracerouteResponse:
msgType = "Multicast Traceroute Response";
break;
case IgmpType_MulticastTraceroute:
msgType = "Multicast Traceroute";
break;
case IgmpType_MembershipReportV3:
msgType = "Membership Report";
break;
case IgmpType_MulticastRouterAdvertisement:
msgType = "Multicast Router Advertisement";
break;
case IgmpType_MulticastRouterSolicitation:
msgType = "Multicast Router Solicitation";
break;
case IgmpType_MulticastRouterTermination:
msgType = "Multicast Router Termination";
break;
default:
msgType = "Unknown";
break;
}
std::string result = "IGMPv" + igmpVer + " Layer, " + msgType + " message";
return result;
}
/*************
* IgmpV1Layer
*************/
IgmpV1Layer::IgmpV1Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) :
IgmpLayer(data, dataLen, prevLayer, packet, IGMPv1)
{
}
IgmpV1Layer::IgmpV1Layer(IgmpType type, const IPv4Address& groupAddr) :
IgmpLayer(type, groupAddr, 0, IGMPv1)
{
}
void IgmpV1Layer::computeCalculateFields()
{
igmp_header* hdr = getIgmpHeader();
hdr->checksum = 0;
hdr->checksum = htons(calculateChecksum());
hdr->maxResponseTime = 0;
}
/*************
* IgmpV2Layer
*************/
IgmpV2Layer::IgmpV2Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) :
IgmpLayer(data, dataLen, prevLayer, packet, IGMPv2)
{
}
IgmpV2Layer::IgmpV2Layer(IgmpType type, const IPv4Address& groupAddr, uint8_t maxResponseTime) :
IgmpLayer(type, groupAddr, maxResponseTime, IGMPv2)
{
}
void IgmpV2Layer::computeCalculateFields()
{
igmp_header* hdr = getIgmpHeader();
hdr->checksum = 0;
hdr->checksum = htons(calculateChecksum());
}
/******************
* IgmpV3QueryLayer
******************/
IgmpV3QueryLayer::IgmpV3QueryLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) :
IgmpLayer(data, dataLen, prevLayer, packet, IGMPv3)
{
}
IgmpV3QueryLayer::IgmpV3QueryLayer(const IPv4Address& multicastAddr, uint8_t maxResponseTime, uint8_t s_qrv) :
IgmpLayer(IgmpType_MembershipQuery, multicastAddr, maxResponseTime, IGMPv3)
{
getIgmpV3QueryHeader()->s_qrv = s_qrv;
}
uint16_t IgmpV3QueryLayer::getSourceAddressCount()
{
return ntohs(getIgmpV3QueryHeader()->numOfSources);
}
IPv4Address IgmpV3QueryLayer::getSourceAddressAtIndex(int index)
{
uint16_t numOfSources = getSourceAddressCount();
if (index < 0 || index >= numOfSources)
return IPv4Address::Zero;
// verify numOfRecords is a reasonable number that points to data within the packet
int ptrOffset = index * sizeof(uint32_t) + sizeof(igmpv3_query_header);
if (ptrOffset + sizeof(uint32_t) > getDataLen())
return IPv4Address::Zero;
uint8_t* ptr = m_Data + ptrOffset;
return IPv4Address(*(uint32_t*)ptr);
}
size_t IgmpV3QueryLayer::getHeaderLen()
{
uint16_t numOfSources = getSourceAddressCount();
int headerLen = numOfSources * sizeof(uint32_t) + sizeof(igmpv3_query_header);
// verify numOfRecords is a reasonable number that points to data within the packet
if ((size_t)headerLen > getDataLen())
return getDataLen();
return (size_t)headerLen;
}
void IgmpV3QueryLayer::computeCalculateFields()
{
igmpv3_query_header* hdr = getIgmpV3QueryHeader();
hdr->checksum = 0;
hdr->checksum = htons(calculateChecksum());
}
bool IgmpV3QueryLayer::addSourceAddress(const IPv4Address& addr)
{
return addSourceAddressAtIndex(addr, getSourceAddressCount());
}
bool IgmpV3QueryLayer::addSourceAddressAtIndex(const IPv4Address& addr, int index)
{
uint16_t sourceAddrCount = getSourceAddressCount();
if (index < 0 || index > (int)sourceAddrCount)
{
LOG_ERROR("Cannot add source address at index %d, index is out of bounds", index);
return false;
}
size_t offset = sizeof(igmpv3_query_header) + index * sizeof(uint32_t);
if (offset > getHeaderLen())
{
LOG_ERROR("Cannot add source address at index %d, index is out of packet bounds", index);
return false;
}
if (!extendLayer(offset, sizeof(uint32_t)))
{
LOG_ERROR("Cannot add source address at index %d, didn't manage to extend layer", index);
return false;
}
uint32_t addrAsInt = addr.toInt();
memcpy(m_Data + offset, &addrAsInt, sizeof(uint32_t));
getIgmpV3QueryHeader()->numOfSources = htons(sourceAddrCount+1);
return true;
}
bool IgmpV3QueryLayer::removeSourceAddressAtIndex(int index)
{
uint16_t sourceAddrCount = getSourceAddressCount();
if (index < 0 || index > (int)sourceAddrCount-1)
{
LOG_ERROR("Cannot remove source address at index %d, index is out of bounds", index);
return false;
}
size_t offset = sizeof(igmpv3_query_header) + index * sizeof(uint32_t);
if (offset >= getHeaderLen())
{
LOG_ERROR("Cannot remove source address at index %d, index is out of packet bounds", index);
return false;
}
if (!shortenLayer(offset, sizeof(uint32_t)))
{
LOG_ERROR("Cannot remove source address at index %d, didn't manage to shorten layer", index);
return false;
}
getIgmpV3QueryHeader()->numOfSources = htons(sourceAddrCount-1);
return true;
}
bool IgmpV3QueryLayer::removeAllSourceAddresses()
{
size_t offset = sizeof(igmpv3_query_header);
size_t numOfBytesToShorted = getHeaderLen() - offset;
if (!shortenLayer(offset, numOfBytesToShorted))
{
LOG_ERROR("Cannot remove all source addresses, didn't manage to shorten layer");
return false;
}
getIgmpV3QueryHeader()->numOfSources = 0;
return true;
}
/*******************
* IgmpV3ReportLayer
*******************/
IgmpV3ReportLayer::IgmpV3ReportLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) :
IgmpLayer(data, dataLen, prevLayer, packet, IGMPv3)
{
}
IgmpV3ReportLayer::IgmpV3ReportLayer() :
IgmpLayer(IgmpType_MembershipReportV3, IPv4Address::Zero, 0, IGMPv3)
{
}
uint16_t IgmpV3ReportLayer::getGroupRecordCount()
{
return ntohs(getReportHeader()->numOfGroupRecords);
}
igmpv3_group_record* IgmpV3ReportLayer::getFirstGroupRecord()
{
// check if there are group records at all
if (getHeaderLen() <= sizeof(igmpv3_report_header))
return NULL;
uint8_t* curGroupPtr = m_Data + sizeof(igmpv3_report_header);
return (igmpv3_group_record*)curGroupPtr;
}
igmpv3_group_record* IgmpV3ReportLayer::getNextGroupRecord(igmpv3_group_record* groupRecord)
{
if (groupRecord == NULL)
return NULL;
// prev group was the last group
if ((uint8_t*)groupRecord + groupRecord->getRecordLen() - m_Data >= (int)getHeaderLen())
return NULL;
igmpv3_group_record* nextGroup = (igmpv3_group_record*)((uint8_t*)groupRecord + groupRecord->getRecordLen());
return nextGroup;
}
size_t IgmpV3ReportLayer::getHeaderLen()
{
return m_DataLen;
}
void IgmpV3ReportLayer::computeCalculateFields()
{
igmpv3_report_header* hdr = getReportHeader();
hdr->checksum = 0;
hdr->checksum = htons(calculateChecksum());
}
igmpv3_group_record* IgmpV3ReportLayer::addGroupRecordAt(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector<IPv4Address>& sourceAddresses, int offset)
{
if (offset > (int)getHeaderLen())
{
LOG_ERROR("Cannot add group record, offset is out of layer bounds");
return NULL;
}
size_t groupRecordSize = sizeof(igmpv3_group_record) + sizeof(uint32_t)*sourceAddresses.size();
if (!extendLayer(offset, groupRecordSize))
{
LOG_ERROR("Cannot add group record, cannot extend layer");
return NULL;
}
uint8_t* groupRecordBuffer = new uint8_t[groupRecordSize];
memset(groupRecordBuffer, 0, groupRecordSize);
igmpv3_group_record* newGroupRecord = (igmpv3_group_record*)groupRecordBuffer;
newGroupRecord->multicastAddress = multicastAddress.toInt();
newGroupRecord->recordType = recordType;
newGroupRecord->auxDataLen = 0;
newGroupRecord->numOfSources = htons(sourceAddresses.size());
int srcAddrOffset = 0;
for (std::vector<IPv4Address>::const_iterator iter = sourceAddresses.begin(); iter != sourceAddresses.end(); iter++)
{
uint32_t addrAsInt = iter->toInt();
memcpy(newGroupRecord->sourceAddresses + srcAddrOffset, &addrAsInt, sizeof(uint32_t));
srcAddrOffset += sizeof(uint32_t);
}
memcpy(m_Data + offset, groupRecordBuffer, groupRecordSize);
delete[] groupRecordBuffer;
getReportHeader()->numOfGroupRecords = htons(getGroupRecordCount() + 1);
return (igmpv3_group_record*)(m_Data + offset);
}
igmpv3_group_record* IgmpV3ReportLayer::addGroupRecord(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector<IPv4Address>& sourceAddresses)
{
return addGroupRecordAt(recordType, multicastAddress, sourceAddresses, (int)getHeaderLen());
}
igmpv3_group_record* IgmpV3ReportLayer::addGroupRecordAtIndex(uint8_t recordType, const IPv4Address& multicastAddress, const std::vector<IPv4Address>& sourceAddresses, int index)
{
int groupCnt = (int)getGroupRecordCount();
if (index < 0 || index > groupCnt)
{
LOG_ERROR("Cannot add group record, index %d out of bounds", index);
return NULL;
}
size_t offset = sizeof(igmpv3_report_header);
igmpv3_group_record* curRecord = getFirstGroupRecord();
for (int i = 0; i < index; i++)
{
if (curRecord == NULL)
{
LOG_ERROR("Cannot add group record, cannot find group record at index %d", i);
return NULL;
}
offset += curRecord->getRecordLen();
curRecord = getNextGroupRecord(curRecord);
}
return addGroupRecordAt(recordType, multicastAddress, sourceAddresses, (int)offset);
}
bool IgmpV3ReportLayer::removeGroupRecordAtIndex(int index)
{
int groupCnt = (int)getGroupRecordCount();
if (index < 0 || index >= groupCnt)
{
LOG_ERROR("Cannot remove group record, index %d is out of bounds", index);
return false;
}
size_t offset = sizeof(igmpv3_report_header);
igmpv3_group_record* curRecord = getFirstGroupRecord();
for (int i = 0; i < index; i++)
{
if (curRecord == NULL)
{
LOG_ERROR("Cannot remove group record at index %d, cannot find group record at index %d", index, i);
return false;
}
offset += curRecord->getRecordLen();
curRecord = getNextGroupRecord(curRecord);
}
if (!shortenLayer((int)offset, curRecord->getRecordLen()))
{
LOG_ERROR("Cannot remove group record at index %d, cannot shorted layer", index);
return false;
}
getReportHeader()->numOfGroupRecords = htons(groupCnt-1);
return true;
}
bool IgmpV3ReportLayer::removeAllGroupRecords()
{
int offset = (int)sizeof(igmpv3_report_header);
if (!shortenLayer(offset, getHeaderLen()-offset))
{
LOG_ERROR("Cannot remove all group records, cannot shorted layer");
return false;
}
getReportHeader()->numOfGroupRecords = 0;
return true;
}
/*********************
* igmpv3_group_record
*********************/
IPv4Address igmpv3_group_record::getMulticastAddress()
{
return IPv4Address(multicastAddress);
}
uint16_t igmpv3_group_record::getSourceAdressCount()
{
return ntohs(numOfSources);
}
IPv4Address igmpv3_group_record::getSoruceAddressAtIndex(int index)
{
uint16_t numOfRecords = getSourceAdressCount();
if (index < 0 || index >= numOfRecords)
return IPv4Address::Zero;
int offset = index * sizeof(uint32_t);
uint8_t* ptr = sourceAddresses + offset;
return IPv4Address(*(uint32_t*)ptr);
}
size_t igmpv3_group_record::getRecordLen()
{
uint16_t numOfRecords = getSourceAdressCount();
int headerLen = numOfRecords * sizeof(uint32_t) + sizeof(igmpv3_group_record);
return (size_t)headerLen;
}
}