Files
eden-sim/contrib/ethernet/model/ethernet-net-device.cc

770 lines
25 KiB
C++

#include "ethernet-net-device.h"
#include "ethernet-channel.h"
#include "ethernet-header2.h"
#include "ns3/error-model.h"
#include "ns3/llc-snap-header.h"
#include "ns3/log.h"
#include "ns3/mac48-address.h"
#include "ns3/pointer.h"
#include "ns3/queue.h"
#include "ns3/simulator.h"
#include "ns3/trace-source-accessor.h"
#include "ns3/uinteger.h"
#include "ns3/ethernet-trailer.h"
#include "ns3/timestamp-tag.h"
#include "ns3/names.h"
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("EthernetNetDevice");
NS_OBJECT_ENSURE_REGISTERED(EthernetNetDevice);
TypeId
EthernetNetDevice::GetTypeId()
{
static TypeId tid =
TypeId("ns3::EthernetNetDevice")
.SetParent<NetDevice>()
.SetGroupName("Ethernet")
.AddConstructor<EthernetNetDevice>()
.AddAttribute("Mtu",
"The MAC-level Maximum Transmission Unit",
UintegerValue(DEFAULT_MTU),
MakeUintegerAccessor(&EthernetNetDevice::SetMtu,
&EthernetNetDevice::GetMtu),
MakeUintegerChecker<uint16_t>())
.AddAttribute("Address",
"The MAC address of this device.",
Mac48AddressValue(Mac48Address("00:00:00:00:ED:E1")),
MakeMac48AddressAccessor(&EthernetNetDevice::m_address),
MakeMac48AddressChecker())
.AddAttribute("DataRate",
"The default data rate for point to point links",
DataRateValue(DataRate("1Gb/s")),
MakeDataRateAccessor(&EthernetNetDevice::m_bps),
MakeDataRateChecker())
.AddAttribute("ReceiveErrorModel",
"The receiver error model used to simulate packet loss",
PointerValue(),
MakePointerAccessor(&EthernetNetDevice::m_receiveErrorModel),
MakePointerChecker<ErrorModel>())
.AddAttribute("preambleAndSFDGap",
"Number of byte for the preamble and start of frame delimiter gap",
UintegerValue(8),
MakeUintegerAccessor(&EthernetNetDevice::m_preambleAndSFDGap),
MakeUintegerChecker<uint8_t>())
.AddAttribute("InterframeGap",
"Number of byte for the interframe gap",
UintegerValue(12),
MakeUintegerAccessor(&EthernetNetDevice::m_interframeGap),
MakeUintegerChecker<uint8_t>())
//
// Trace sources at the "top" of the net device, where packets transition
// to/from higher layers.
//
.AddTraceSource("MacTx",
"Trace source indicating a packet has arrived "
"for transmission by this device",
MakeTraceSourceAccessor(&EthernetNetDevice::m_macTxTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("MacTxAnimation",
"Trace source indicating a packet has arrived "
"for transmission by this device",
MakeTraceSourceAccessor(&EthernetNetDevice::m_macTxAnimationTrace),
"ns3::EthernetNetDevice::MacTxAnimationCallback")
.AddTraceSource("MacTxDrop",
"Trace source indicating a packet has been dropped "
"by the device before transmission",
MakeTraceSourceAccessor(&EthernetNetDevice::m_macTxDropTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("MacPromiscRx",
"A packet has been received by this device, "
"has been passed up from the physical layer "
"and is being forwarded up the local protocol stack. "
"This is a promiscuous trace,",
MakeTraceSourceAccessor(&EthernetNetDevice::m_macPromiscRxTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("MacRx",
"A packet has been received by this device, "
"has been passed up from the physical layer "
"and is being forwarded up the local protocol stack. "
"This is a non-promiscuous trace,",
MakeTraceSourceAccessor(&EthernetNetDevice::m_macRxTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("MacRxAnimation",
"Trace source indicating a packet has arrived "
"in this device",
MakeTraceSourceAccessor(&EthernetNetDevice::m_macRxAnimationTrace),
"ns3::EthernetNetDevice::MacRxAnimationCallback")
#if 0
// Not currently implemented for this device
.AddTraceSource ("MacRxDrop",
"Trace source indicating a packet was dropped "
"before being forwarded up the stack",
MakeTraceSourceAccessor (&EthernetNetDevice::m_macRxDropTrace),
"ns3::Packet::TracedCallback")
#endif
//
// Trace sources at the "bottom" of the net device, where packets transition
// to/from the channel.
//
.AddTraceSource("PhyTxBegin",
"Trace source indicating a packet has begun "
"transmitting over the channel",
MakeTraceSourceAccessor(&EthernetNetDevice::m_phyTxBeginTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("PhyTxEnd",
"Trace source indicating a packet has been "
"completely transmitted over the channel",
MakeTraceSourceAccessor(&EthernetNetDevice::m_phyTxEndTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("PhyTxDrop",
"Trace source indicating a packet has been "
"dropped by the device during transmission",
MakeTraceSourceAccessor(&EthernetNetDevice::m_phyTxDropTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("Latency",
"Trace source to compute network latency ",
MakeTraceSourceAccessor(&EthernetNetDevice::m_latencyTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("FIFOState",
"Trace source to study the FIFO state ",
MakeTraceSourceAccessor(&EthernetNetDevice::m_FIFOStateSnifferTrace),
"ns3::EthernetNetDevice::m_FIFOStateSnifferTrace")
#if 0
// Not currently implemented for this device
.AddTraceSource ("PhyRxBegin",
"Trace source indicating a packet has begun "
"being received by the device",
MakeTraceSourceAccessor (&EthernetNetDevice::m_phyRxBeginTrace),
"ns3::Packet::TracedCallback")
#endif
.AddTraceSource("PhyRxEnd",
"Trace source indicating a packet has been "
"completely received by the device",
MakeTraceSourceAccessor(&EthernetNetDevice::m_phyRxEndTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("PhyRxDrop",
"Trace source indicating a packet has been "
"dropped by the device during reception",
MakeTraceSourceAccessor(&EthernetNetDevice::m_phyRxDropTrace),
"ns3::Packet::TracedCallback")
//
// Trace sources designed to simulate a packet sniffer facility (tcpdump).
// Note that there is really no difference between promiscuous and
// non-promiscuous traces in a point-to-point link.
//
.AddTraceSource("Sniffer",
"Trace source simulating a non-promiscuous packet sniffer "
"attached to the device",
MakeTraceSourceAccessor(&EthernetNetDevice::m_snifferTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource("PromiscSniffer",
"Trace source simulating a promiscuous packet sniffer "
"attached to the device",
MakeTraceSourceAccessor(&EthernetNetDevice::m_promiscSnifferTrace),
"ns3::Packet::TracedCallback");
return tid;
}
EthernetNetDevice::EthernetNetDevice()
{
NS_LOG_FUNCTION(this);
m_txMachineState = READY;
m_channel = nullptr;
m_linkUp = false;
m_currentPkt = nullptr;
}
EthernetNetDevice::~EthernetNetDevice()
{
NS_LOG_FUNCTION(this);
}
void
EthernetNetDevice::AddHeader(Ptr<Packet> p, Mac48Address dst, Mac48Address src, uint16_t ethertype)
{
NS_LOG_FUNCTION(this);
EthernetHeader2 ethHeader;
ethHeader.SetDest(dst);
ethHeader.SetSrc(src);
ethHeader.SetEthertype(ethertype);
p->AddHeader(ethHeader);
}
void
EthernetNetDevice::AddHeader(Ptr<Packet> p, Mac48Address dst, Mac48Address src, uint16_t ethertype, uint16_t vid, uint8_t pcp, uint8_t dei)
{
NS_LOG_FUNCTION(this);
EthernetHeader2 ethHeader;
ethHeader.SetDest(dst);
ethHeader.SetSrc(src);
ethHeader.SetEthertype(ethertype);
ethHeader.SetVlanTag(pcp, dei, vid);
p->AddHeader(ethHeader);
}
void
EthernetNetDevice::DoDispose()
{
NS_LOG_FUNCTION(this);
m_node = nullptr;
m_channel = nullptr;
m_receiveErrorModel = nullptr;
m_currentPkt = nullptr;
m_queues.clear();
NetDevice::DoDispose();
}
void
EthernetNetDevice::SetDataRate(DataRate bps)
{
NS_LOG_FUNCTION(this);
m_bps = bps;
}
bool
EthernetNetDevice::TransmitStart(Ptr<Packet> p)
{
NS_LOG_FUNCTION(this << p);
NS_ASSERT_MSG(m_channel != nullptr, "Channel ptr is null");
NS_LOG_INFO(Names::FindName(this) << " PKT #" << p->GetUid() << " at " << Simulator::Now().GetNanoSeconds()<<" Transmit started ");
//Add a tag to compute delay between reception(or FIFO enter if first
//emission port) and transmission start
TimestampTag tag(Simulator::Now());
p->AddByteTag(tag);
//
// This function is called to start the process of transmitting a packet.
// We need to tell the channel that we've started wiggling the wire and
// schedule an event that will be executed when the transmission is complete.
//
NS_ASSERT_MSG(m_txMachineState == READY, "Must be READY to transmit");
m_txMachineState = BUSY;
m_currentPkt = p;
m_phyTxBeginTrace(m_currentPkt);
Time preambleAndSFDGapTime = m_bps.CalculateBytesTxTime(m_preambleAndSFDGap);
Time txTime = m_bps.CalculateBytesTxTime(p->GetSize());
Time interpacketGapTime = m_bps.CalculateBytesTxTime(m_interframeGap);
Time txCompleteTime = preambleAndSFDGapTime + txTime + interpacketGapTime;
//NS_LOG_INFO(Names::FindName(this) << " "<< Simulator::Now().GetNanoSeconds()<<" Transmit pkt #"<<p->GetUid());
//NS_LOG_LOGIC(Names::FindName(this) << " Schedule TransmitCompleteEvent in " << txCompleteTime.As(Time::S) << " for PKT #" << p->GetUid());
Simulator::Schedule(txCompleteTime, &EthernetNetDevice::TransmitComplete, this);
bool result = m_channel->TransmitStart(p, this, txCompleteTime-interpacketGapTime); //Pkt don't need to wait for the end of Interpacket gap to be considered as receive on the other end of the link
if (!result)
{
m_phyTxDropTrace(p);
}
return result;
}
void
EthernetNetDevice::TransmitComplete()
{
NS_LOG_FUNCTION(this);
NS_LOG_INFO(Names::FindName(this) << " PKT #" << m_currentPkt->GetUid() << " at " << Simulator::Now().GetNanoSeconds()<<" Transmit completed ");
// NS_LOG_INFO(Names::FindName(this) << " "<< Simulator::Now().GetNanoSeconds()<<" Transmit completed pkt ");
//
// This function is called to when we're all done transmitting a packet.
// We try and pull another packet off of the transmit queue. If the queue
// is empty, we are done, otherwise we need to start transmitting the
// next packet.
//
NS_ASSERT_MSG(m_txMachineState == BUSY, "Must be BUSY if transmitting");
m_txMachineState = READY;
NS_ASSERT_MSG(m_currentPkt, "EthernetNetDevice::TransmitComplete(): m_currentPkt zero");
m_phyTxEndTrace(m_currentPkt);
m_currentPkt = nullptr;
//Add the event after all the already present at this exact instant to avoid
//the beginning of a transmission if a packet will be ready at the event just
//after but at the same time instant.
Simulator::Schedule(Time(0), &EthernetNetDevice::CheckForReadyPacket, this);
}
void
EthernetNetDevice::CheckForReadyPacket()
{
NS_LOG_FUNCTION(this);
if (m_txMachineState != READY){
return;
}
int next_queue_id = TransmitSelection();
if(next_queue_id==-1)
{
return;
}
Ptr<Packet> p = m_queues[next_queue_id]->Dequeue();
//
// Got another packet off of the queue, so start the transmit process again.
//
m_snifferTrace(p);
m_promiscSnifferTrace(p);
TransmitStart(p);
}
int
EthernetNetDevice::TransmitSelection()
{
NS_LOG_FUNCTION(this);
//For now simple Static Priority Algo
for (int i = m_queues.size(); i --> 0; )
{
if(!m_queues[i]->IsEmpty())
{
return(i);
}
}
return(-1);
}
bool
EthernetNetDevice::Attach(Ptr<EthernetChannel> ch)
{
NS_LOG_FUNCTION(this << &ch);
m_channel = ch;
m_channel->Attach(this);
//
// This device is up whenever it is attached to a channel. A better plan
// would be to have the link come up when both devices are attached, but this
// is not done for now.
//
NotifyLinkUp();
return true;
}
void
EthernetNetDevice::SetQueue(Ptr<Queue<Packet>> q)
{
NS_LOG_FUNCTION(this << q);
m_queues.insert(m_queues.end(),q);
}
void
EthernetNetDevice::SetReceiveErrorModel(Ptr<ErrorModel> em)
{
NS_LOG_FUNCTION(this << em);
m_receiveErrorModel = em;
}
void
EthernetNetDevice::Receive(Ptr<Packet> packet)
{
NS_LOG_FUNCTION(this << packet);
NS_LOG_LOGIC("UID is " << packet->GetUid());
if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt(packet))
{
//
// If we have an error model and it indicates that it is time to lose a
// corrupted packet, don't forward this packet up, let it go.
//
NS_LOG_LOGIC("Dropping pkt due to error model ");
m_phyRxDropTrace(packet);
}
else
{
//Add timestamp to measure delay in the switches
TimestampTag tag(Simulator::Now());
packet->AddByteTag(tag);
//
// Hit the trace hooks. All of these hooks are in the same place in this
// device because it is so simple, but this is not usually the case in
// more complicated devices.
//
m_snifferTrace(packet);
m_promiscSnifferTrace(packet);
m_phyRxEndTrace(packet);
//
// Trace sinks will expect complete packets, not packets without some of the
// headers.
//
Ptr<Packet> originalPacket = packet->Copy();
EthernetTrailer trailer;
packet->RemoveTrailer(trailer);
if (Node::ChecksumEnabled())
{
trailer.EnableFcs(true);
}
bool crcGood = trailer.CheckFcs(packet);
if (!crcGood)
{
NS_LOG_INFO("CRC error on Packet " << packet);
m_phyRxDropTrace(packet);
return;
}
//
// Strip off the ethernet protocol header and forward this packet
// up the protocol stack. Since this is a simple point-to-point link,
// there is no difference in what the promisc callback sees and what the
// normal receive callback sees.
//
EthernetHeader2 header;
packet->RemoveHeader(header);
uint16_t protocol;
protocol = header.GetEthertype();
//
// Classify the packet based on its destination.
//
PacketType packetType;
if (header.GetDest() == m_address)
{
packetType = PACKET_HOST;
}
else if (header.GetDest().IsGroup())
{
packetType = PACKET_MULTICAST;
}
else if (header.GetDest().IsBroadcast())
{
packetType = PACKET_BROADCAST;
}
else
{
packetType = PACKET_OTHERHOST;
}
if (!m_promiscCallback.IsNull())
{
m_macPromiscRxTrace(originalPacket->Copy());
m_promiscCallback(this,
originalPacket,
protocol,
header.GetSrc(),
header.GetDest(),
packetType);
}
//
// If this packet is not destined for some other host, it must be for us
// as either a broadcast, multicast or unicast. We need to hit the mac
// packet received trace hook and forward the packet up the stack.
//
if (packetType != PACKET_OTHERHOST)
{
//NS_LOG_INFO((Simulator::Now()).As(Time::S) << " \t" << Names::FindName(m_node) << "/" << Names::FindName(this) <<" : Pkt #" << originalPacket->GetUid() <<" received by the netDevice ! " << originalPacket->ToString());
m_macRxTrace(originalPacket);
m_macRxAnimationTrace(originalPacket, this);
m_rxCallback(this, packet, protocol, header.GetSrc());
m_latencyTrace(originalPacket);
}
}
}
Ptr<Queue<Packet>>
EthernetNetDevice::GetQueue(uint8_t index) const
{
NS_LOG_FUNCTION(this);
return m_queues[index];
}
void
EthernetNetDevice::NotifyLinkUp()
{
NS_LOG_FUNCTION(this);
m_linkUp = true;
m_linkChangeCallbacks();
}
void
EthernetNetDevice::SetIfIndex(const uint32_t index)
{
NS_LOG_FUNCTION(this);
m_ifIndex = index;
}
uint32_t
EthernetNetDevice::GetIfIndex() const
{
return m_ifIndex;
}
Ptr<Channel>
EthernetNetDevice::GetChannel() const
{
return m_channel;
}
void
EthernetNetDevice::SetAddress(Address address)
{
NS_LOG_FUNCTION(this << address);
m_address = Mac48Address::ConvertFrom(address);
}
Address
EthernetNetDevice::GetAddress() const
{
return m_address;
}
bool
EthernetNetDevice::IsLinkUp() const
{
NS_LOG_FUNCTION(this);
return m_linkUp;
}
void
EthernetNetDevice::AddLinkChangeCallback(Callback<void> callback)
{
NS_LOG_FUNCTION(this);
m_linkChangeCallbacks.ConnectWithoutContext(callback);
}
//
// This is a ethernet device, so every transmission is a broadcast to
// all of the devices on the channel.
//
bool
EthernetNetDevice::IsBroadcast() const
{
NS_LOG_FUNCTION(this);
return true;
}
Address
EthernetNetDevice::GetBroadcast() const
{
NS_LOG_FUNCTION(this);
return Mac48Address("ff:ff:ff:ff:ff:ff");
}
bool
EthernetNetDevice::IsMulticast() const
{
NS_LOG_FUNCTION(this);
return true;
}
Address
EthernetNetDevice::GetMulticast(Ipv4Address multicastGroup) const
{
NS_LOG_FUNCTION(this);
return Mac48Address("01:00:5e:00:00:00");
}
Address
EthernetNetDevice::GetMulticast(Ipv6Address addr) const
{
NS_LOG_FUNCTION(this << addr);
return Mac48Address("33:33:00:00:00:00");
}
bool
EthernetNetDevice::IsPointToPoint() const
{
NS_LOG_FUNCTION(this);
return true;
}
bool
EthernetNetDevice::IsBridge() const
{
NS_LOG_FUNCTION(this);
return false;
}
bool
EthernetNetDevice::Send(Ptr<Packet> packet, const Address& dest, uint16_t ethertype)
{
uint16_t vid = 65535;
uint8_t pcp = 0;
uint8_t dei = 0;
return SendFrom(packet, GetAddress(), dest, ethertype, vid, pcp, dei);
}
bool
EthernetNetDevice::Send(Ptr<Packet> packet, const Address& dest, uint16_t ethertype, uint16_t vid, uint8_t pcp, uint8_t dei)
{
return SendFrom(packet, GetAddress(), dest, ethertype, vid, pcp, dei);
}
bool
EthernetNetDevice::SendFrom(Ptr<Packet> packet,
const Address& source,
const Address& dest,
uint16_t ethertype)
{
//Call by the switch net device with a packet with a EhternetHeader2 and EthernetTrailer
//Or by another SendFrom function that have already have a packet with header and trailer
// /!\ source, dest, and ethertype are not use in this function but are needed in
// the callback define by the netDevice class
//
// If IsLinkUp() is false it means there is no channel to send any packet
// over so we just hit the drop trace on the packet and return an error.
//
if (!IsLinkUp())
{
m_macTxDropTrace(packet);
return false;
}
//Add timestamp if first emission
TimestampTag tag(Simulator::Now());
if (!packet->FindFirstMatchingByteTag(tag))
{
packet->AddByteTag(tag);
}
m_macTxTrace(packet);
m_macTxAnimationTrace(packet, this);
Ptr<Packet> pCopy = packet->Copy();
EthernetHeader2 header;
pCopy->RemoveHeader(header);
uint8_t pcp = header.GetPcp();
NS_ASSERT_MSG(m_queues.size() > pcp, "Not enough fifos ( 0 to "<< m_queues.size()-1 <<") to handle this pcp priority ("<< unsigned(pcp) <<") on Node " <<Names::FindName(GetNode()));
m_FIFOStateSnifferTrace(m_queues, m_txMachineState==BUSY, packet);
//
// We should enqueue and dequeue the packet to hit the tracing hooks.
//
if (m_queues[pcp]->Enqueue(packet))
{
NS_LOG_INFO(Names::FindName(this) << " Enqueue pkt #"<<packet->GetUid() << " OK !");
//Add the event after all the already present at this exact instant to avoid
//the beginning of a transmission if a packet will be ready at the event just
//after but at the same time instant.
Simulator::Schedule(Time(0), &EthernetNetDevice::CheckForReadyPacket, this);
return true;
}
// Enqueue may fail (overflow)
m_macTxDropTrace(packet);
return false;
}
bool
EthernetNetDevice::SendFrom(Ptr<Packet> packet, const Address& source, const Address& dest, uint16_t ethertype, uint16_t vid, uint8_t pcp, uint8_t dei)
{
NS_LOG_FUNCTION(this << packet << dest << ethertype);
NS_LOG_LOGIC(Names::FindName(this) << " p=" << packet->GetUid() << ", dest=" << &dest);
// NS_LOG_INFO(Names::FindName(this) << " SendFrom : p=" << packet->GetUid() << ", dest=" << &dest << " at " << Simulator::Now());
if (vid == 65535){
AddHeader(packet, Mac48Address::ConvertFrom(dest), Mac48Address::ConvertFrom(source), ethertype);
}
else{
AddHeader(packet, Mac48Address::ConvertFrom(dest), Mac48Address::ConvertFrom(source), ethertype, vid, pcp, dei);
}
EthernetTrailer trailer;
trailer.EnableFcs(true);
trailer.CalcFcs(packet);
packet->AddTrailer(trailer);
return SendFrom(packet, source, dest, ethertype);
}
Ptr<Node>
EthernetNetDevice::GetNode() const
{
return m_node;
}
void
EthernetNetDevice::SetNode(Ptr<Node> node)
{
NS_LOG_FUNCTION(this);
m_node = node;
}
bool
EthernetNetDevice::NeedsArp() const
{
NS_LOG_FUNCTION(this);
return false;
}
void
EthernetNetDevice::SetReceiveCallback(NetDevice::ReceiveCallback cb)
{
m_rxCallback = cb;
}
void
EthernetNetDevice::SetPromiscReceiveCallback(NetDevice::PromiscReceiveCallback cb)
{
m_promiscCallback = cb;
}
bool
EthernetNetDevice::SupportsSendFrom() const
{
NS_LOG_FUNCTION(this);
return true;
}
Address
EthernetNetDevice::GetRemote() const
{
NS_LOG_FUNCTION(this);
NS_ASSERT(m_channel->GetNDevices() == 2);
for (std::size_t i = 0; i < m_channel->GetNDevices(); ++i)
{
Ptr<NetDevice> tmp = m_channel->GetDevice(i);
if (tmp != this)
{
return tmp->GetAddress();
}
}
NS_ASSERT(false);
// quiet compiler.
return Address();
}
bool
EthernetNetDevice::SetMtu(uint16_t mtu)
{
NS_LOG_FUNCTION(this << mtu);
m_mtu = mtu;
return true;
}
uint16_t
EthernetNetDevice::GetMtu() const
{
NS_LOG_FUNCTION(this);
return m_mtu;
}
}