#define LOG_MODULE PacketLogModuleDhcpV6Layer
#include "DhcpV6Layer.h"
#include "Logger.h"
#include "GeneralUtils.h"
#include "EndianPortable.h"
namespace pcpp
{
DhcpV6OptionType DhcpV6Option::getType() const
{
if (m_Data == nullptr)
return DhcpV6OptionType::DHCPV6_OPT_UNKNOWN;
uint16_t optionType = be16toh(m_Data->recordType);
if (optionType <= 62 && optionType != 10 && optionType != 35 && optionType != 57 && optionType != 58)
{
return static_cast<DhcpV6OptionType>(optionType);
}
if (optionType == 65 || optionType == 66 || optionType == 68 || optionType == 79 || optionType == 112)
{
return static_cast<DhcpV6OptionType>(optionType);
}
return DHCPV6_OPT_UNKNOWN;
}
std::string DhcpV6Option::getValueAsHexString() const
{
if (m_Data == nullptr)
return "";
return byteArrayToHexString(m_Data->recordValue, getDataSize());
}
size_t DhcpV6Option::getTotalSize() const
{
if (m_Data == nullptr)
return 0;
return 2*sizeof(uint16_t) + be16toh(m_Data->recordLen);
}
size_t DhcpV6Option::getDataSize() const
{
if (m_Data == nullptr)
return 0;
return static_cast<size_t>(be16toh(m_Data->recordLen));
}
DhcpV6Option DhcpV6OptionBuilder::build() const
{
if (m_RecType == 0)
return DhcpV6Option(nullptr);
size_t optionSize = 2 * sizeof(uint16_t) + m_RecValueLen;
uint8_t* recordBuffer = new uint8_t[optionSize];
uint16_t optionTypeVal = htobe16(static_cast<uint16_t>(m_RecType));
uint16_t optionLength = htobe16(static_cast<uint16_t>(m_RecValueLen));
memcpy(recordBuffer, &optionTypeVal, sizeof(uint16_t));
memcpy(recordBuffer + sizeof(uint16_t), &optionLength, sizeof(uint16_t));
if (optionSize > 0 && m_RecValue != nullptr)
memcpy(recordBuffer + 2*sizeof(uint16_t), m_RecValue, m_RecValueLen);
return DhcpV6Option(recordBuffer);
}
DhcpV6Layer::DhcpV6Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet)
{
m_Protocol = DHCPv6;
}
DhcpV6Layer::DhcpV6Layer(DhcpV6MessageType messageType, uint32_t transactionId)
{
m_DataLen = sizeof(dhcpv6_header);
m_Data = new uint8_t[m_DataLen];
memset(m_Data, 0, m_DataLen);
m_Protocol = DHCPv6;
setMessageType(messageType);
setTransactionID(transactionId);
}
DhcpV6MessageType DhcpV6Layer::getMessageType() const
{
uint8_t messageType = getDhcpHeader()->messageType;
if (messageType > 13)
{
return DHCPV6_UNKNOWN_MSG_TYPE;
}
return static_cast<DhcpV6MessageType>(messageType);
}
std::string DhcpV6Layer::getMessageTypeAsString() const
{
DhcpV6MessageType messageType = getMessageType();
switch (messageType)
{
case DHCPV6_SOLICIT:
return "Solicit";
case DHCPV6_ADVERTISE:
return "Advertise";
case DHCPV6_REQUEST:
return "Request";
case DHCPV6_CONFIRM:
return "Confirm";
case DHCPV6_RENEW:
return "Renew";
case DHCPV6_REBIND:
return "Rebind";
case DHCPV6_REPLY:
return "Reply";
case DHCPV6_RELEASE:
return "Release";
case DHCPV6_DECLINE:
return "Decline";
case DHCPV6_RECONFIGURE:
return "Reconfigure";
case DHCPV6_INFORMATION_REQUEST:
return "Information-Request";
case DHCPV6_RELAY_FORWARD:
return "Relay-Forward";
case DHCPV6_RELAY_REPLY:
return "Relay-Reply";
default:
return "Unknown";
}
}
void DhcpV6Layer::setMessageType(DhcpV6MessageType messageType)
{
getDhcpHeader()->messageType = static_cast<uint8_t>(messageType);
}
uint32_t DhcpV6Layer::getTransactionID() const
{
dhcpv6_header* hdr = getDhcpHeader();
uint32_t result = hdr->transactionId1 << 16 | hdr->transactionId2 << 8 | hdr->transactionId3;
return result;
}
void DhcpV6Layer::setTransactionID(uint32_t transactionId) const
{
dhcpv6_header* hdr = getDhcpHeader();
hdr->transactionId1 = (transactionId >> 16) & 0xff;
hdr->transactionId2 = (transactionId >> 8) & 0xff;
hdr->transactionId3 = transactionId & 0xff;
}
DhcpV6Option DhcpV6Layer::getFirstOptionData() const
{
return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), getHeaderLen() - sizeof(dhcpv6_header));
}
DhcpV6Option DhcpV6Layer::getNextOptionData(DhcpV6Option dhcpv6Option) const
{
return m_OptionReader.getNextTLVRecord(dhcpv6Option, getOptionsBasePtr(), getHeaderLen() - sizeof(dhcpv6_header));
}
DhcpV6Option DhcpV6Layer::getOptionData(DhcpV6OptionType option) const
{
return m_OptionReader.getTLVRecord(static_cast<uint32_t>(option), getOptionsBasePtr(), getHeaderLen() - sizeof(dhcpv6_header));
}
size_t DhcpV6Layer::getOptionCount() const
{
return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), getHeaderLen() - sizeof(dhcpv6_header));
}
DhcpV6Option DhcpV6Layer::addOptionAt(const DhcpV6OptionBuilder& optionBuilder, int offset)
{
DhcpV6Option newOpt = optionBuilder.build();
if (newOpt.isNull())
{
PCPP_LOG_ERROR("Cannot build new option");
return DhcpV6Option(nullptr);
}
size_t sizeToExtend = newOpt.getTotalSize();
if (!extendLayer(offset, sizeToExtend))
{
PCPP_LOG_ERROR("Could not extend DhcpLayer in [" << newOpt.getTotalSize() << "] bytes");
newOpt.purgeRecordData();
return DhcpV6Option(nullptr);
}
memcpy(m_Data + offset, newOpt.getRecordBasePtr(), newOpt.getTotalSize());
uint8_t* newOptPtr = m_Data + offset;
m_OptionReader.changeTLVRecordCount(1);
newOpt.purgeRecordData();
return DhcpV6Option(newOptPtr);
}
DhcpV6Option DhcpV6Layer::addOption(const DhcpV6OptionBuilder& optionBuilder)
{
return addOptionAt(optionBuilder, getHeaderLen());
}
DhcpV6Option DhcpV6Layer::addOptionAfter(const DhcpV6OptionBuilder& optionBuilder, DhcpV6OptionType optionType)
{
int offset = 0;
DhcpV6Option prevOpt = getOptionData(optionType);
if (prevOpt.isNull())
{
PCPP_LOG_ERROR("Option type " << optionType << " doesn't exist in layer");
return DhcpV6Option(nullptr);
}
offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data;
return addOptionAt(optionBuilder, offset);
}
DhcpV6Option DhcpV6Layer::addOptionBefore(const DhcpV6OptionBuilder& optionBuilder, DhcpV6OptionType optionType)
{
int offset = 0;
DhcpV6Option nextOpt = getOptionData(optionType);
if (nextOpt.isNull())
{
PCPP_LOG_ERROR("Option type " << optionType << " doesn't exist in layer");
return DhcpV6Option(nullptr);
}
offset = nextOpt.getRecordBasePtr() - m_Data;
return addOptionAt(optionBuilder, offset);
}
bool DhcpV6Layer::removeOption(DhcpV6OptionType optionType)
{
DhcpV6Option optToRemove = getOptionData(optionType);
if (optToRemove.isNull())
{
return false;
}
int offset = optToRemove.getRecordBasePtr() - m_Data;
if (!shortenLayer(offset, optToRemove.getTotalSize()))
{
return false;
}
m_OptionReader.changeTLVRecordCount(-1);
return true;
}
bool DhcpV6Layer::removeAllOptions()
{
int offset = sizeof(dhcpv6_header);
if (!shortenLayer(offset, getHeaderLen()-offset))
return false;
m_OptionReader.changeTLVRecordCount(0-getOptionCount());
return true;
}
std::string DhcpV6Layer::toString() const
{
return "DHCPv6 Layer, " + getMessageTypeAsString() + " message";
}
}