text: #define LOG_MODULE PacketLogModuleVlanLayer
  
  #include "VlanLayer.h"
  #include "IPv4Layer.h"
  #include "IPv6Layer.h"
  #include "PayloadLayer.h"
  #include "ArpLayer.h"
  #include "PPPoELayer.h"
  #include "MplsLayer.h"
  #include <string.h>
  #include <sstream>
  #include "EndianPortable.h"
  
  namespace pcpp
  {
  
  VlanLayer::VlanLayer(const uint16_t vlanID, bool cfi, uint8_t priority, uint16_t etherType)
  {
  	const size_t headerLen = sizeof(vlan_header);
  	m_DataLen = headerLen;
  	m_Data = new uint8_t[headerLen];
  	memset(m_Data, 0, headerLen);
  	m_Protocol = VLAN;
  
  	vlan_header* vlanHeader = getVlanHeader();
  	setVlanID(vlanID);
  	setCFI(cfi);
  	setPriority(priority);
  	vlanHeader->etherType = htobe16(etherType);
  }
  
  uint16_t VlanLayer::getVlanID() const {
  	return htobe16(getVlanHeader()->vlan) & 0xFFF;
  }
  
  uint8_t VlanLayer::getCFI() const {
  	return ((htobe16(getVlanHeader()->vlan) >> 12) & 1);
  }
  
  uint8_t VlanLayer::getPriority() const {
  	return (htobe16(getVlanHeader()->vlan) >> 13) & 7;
  }
  
  void VlanLayer::setVlanID(uint16_t id) {
  	getVlanHeader()->vlan = htobe16((be16toh(getVlanHeader()->vlan) & (~0xFFF)) | (id & 0xFFF));
  }
  
  void VlanLayer::setCFI(bool cfi) {
  	getVlanHeader()->vlan = htobe16((be16toh(getVlanHeader()->vlan) & (~(1 << 12))) | ((cfi & 1) << 12));
  }
  
  void VlanLayer::setPriority(uint8_t priority) {
  	getVlanHeader()->vlan = htobe16((be16toh(getVlanHeader()->vlan) & (~(7 << 13))) | ((priority & 7) << 13));
  }
  
  void VlanLayer::parseNextLayer()
  {
  	if (m_DataLen <= sizeof(vlan_header))
  		return;
  	
  	uint8_t* payload = m_Data + sizeof(vlan_header);
  	size_t payloadLen = m_DataLen - sizeof(vlan_header);
  
  	vlan_header* hdr = getVlanHeader();
  	switch (be16toh(hdr->etherType))
  	{
  	case PCPP_ETHERTYPE_IP:
  		m_NextLayer = IPv4Layer::isDataValid(payload, payloadLen)
  			? static_cast<Layer*>(new IPv4Layer(payload, payloadLen, this, m_Packet))
  			: static_cast<Layer*>(new PayloadLayer(payload, payloadLen, this, m_Packet));
  		break;
  	case PCPP_ETHERTYPE_IPV6:
  		m_NextLayer = IPv6Layer::isDataValid(payload, payloadLen)
  			? static_cast<Layer*>(new IPv6Layer(payload, payloadLen, this, m_Packet))
  			: static_cast<Layer*>(new PayloadLayer(payload, payloadLen, this, m_Packet));
  		break;
  	case PCPP_ETHERTYPE_ARP:
  		m_NextLayer = new ArpLayer(payload, payloadLen, this, m_Packet);
  		break;
  	case PCPP_ETHERTYPE_VLAN:
  	case PCPP_ETHERTYPE_IEEE_802_1AD:
  		m_NextLayer = new VlanLayer(payload, payloadLen, this, m_Packet);
  		break;
  	case PCPP_ETHERTYPE_PPPOES:
  		m_NextLayer = PPPoESessionLayer::isDataValid(payload, payloadLen)
  			? static_cast<Layer*>(new PPPoESessionLayer(payload, payloadLen, this, m_Packet))
  			: static_cast<Layer*>(new PayloadLayer(payload, payloadLen, this, m_Packet));
  		break;
  	case PCPP_ETHERTYPE_PPPOED:
  		m_NextLayer = PPPoEDiscoveryLayer::isDataValid(payload, payloadLen)
  			? static_cast<Layer*>(new PPPoEDiscoveryLayer(payload, payloadLen, this, m_Packet))
  			: static_cast<Layer*>(new PayloadLayer(payload, payloadLen, this, m_Packet));
  		break;
  	case PCPP_ETHERTYPE_MPLS:
  		m_NextLayer = new MplsLayer(payload, payloadLen, this, m_Packet);
  		break;
  	default:
  		m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet);
  	}
  }
  
  void VlanLayer::computeCalculateFields()
  {
  	if (m_NextLayer == NULL)
  		return;
  
  	switch (m_NextLayer->getProtocol())
  	{
  	case IPv4:
  		getVlanHeader()->etherType = htobe16(PCPP_ETHERTYPE_IP);
  		break;
  	case IPv6:
  		getVlanHeader()->etherType = htobe16(PCPP_ETHERTYPE_IPV6);
  		break;
  	case ARP:
  		getVlanHeader()->etherType = htobe16(PCPP_ETHERTYPE_ARP);
  		break;
  	case VLAN:
  		getVlanHeader()->etherType = htobe16(PCPP_ETHERTYPE_VLAN);
  		break;
  	default:
  		return;
  	}
  }
  
  std::string VlanLayer::toString() const
  {
  	std::ostringstream cfiStream;
  	cfiStream << (int)getCFI();
  	std::ostringstream priStream;
  	priStream << (int)getPriority();
  	std::ostringstream idStream;
  	idStream << getVlanID();
  
  	return "VLAN Layer, Priority: " + priStream.str() + ", Vlan ID: " + idStream.str() + ", CFI: " + cfiStream.str();
  }
  
  } // namespace pcpp
  