#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() .SetGroupName("Ethernet") .AddConstructor() .AddAttribute("Mtu", "The MAC-level Maximum Transmission Unit", UintegerValue(DEFAULT_MTU), MakeUintegerAccessor(&EthernetNetDevice::SetMtu, &EthernetNetDevice::GetMtu), MakeUintegerChecker()) .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()) .AddAttribute("preambleAndSFDGap", "Number of byte for the preamble and start of frame delimiter gap", UintegerValue(8), MakeUintegerAccessor(&EthernetNetDevice::m_preambleAndSFDGap), MakeUintegerChecker()) .AddAttribute("InterframeGap", "Number of byte for the interframe gap", UintegerValue(12), MakeUintegerAccessor(&EthernetNetDevice::m_interframeGap), MakeUintegerChecker()) // // 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 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 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 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 #"<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 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 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> q) { NS_LOG_FUNCTION(this << q); m_queues.insert(m_queues.end(),q); } void EthernetNetDevice::SetReceiveErrorModel(Ptr em) { NS_LOG_FUNCTION(this << em); m_receiveErrorModel = em; } void EthernetNetDevice::Receive(Ptr 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 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> 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 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 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, 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, 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, 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 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 " <Enqueue(packet)) { NS_LOG_INFO(Names::FindName(this) << " Enqueue pkt #"<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, 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 EthernetNetDevice::GetNode() const { return m_node; } void EthernetNetDevice::SetNode(Ptr 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 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; } }