From 83ed5e7bb62a258b5f5e7db666aeebcb6f4f6a70 Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Thu, 14 Jul 2016 15:59:55 +0200 Subject: [PATCH] network: Support socket priorities --- doc/models/source/internet-models.rst | 2 +- src/internet/model/ipv4-l3-protocol.cc | 11 ++ src/internet/model/ipv6-l3-protocol.cc | 3 + src/internet/model/tcp-socket-base.cc | 20 +++ src/internet/model/tcp-socket-base.h | 4 + src/internet/model/udp-socket-impl.cc | 25 +++ src/internet/model/udp-socket-impl.h | 7 + src/internet/test/udp-test.cc | 111 +++++++++--- src/network/doc/sockets-api.rst | 75 +++++++- src/network/model/socket.cc | 121 +++++++++++++ src/network/model/socket.h | 167 +++++++++++++++++- src/traffic-control/model/queue-disc.cc | 7 + .../model/traffic-control-layer.cc | 7 + 13 files changed, 534 insertions(+), 26 deletions(-) diff --git a/doc/models/source/internet-models.rst b/doc/models/source/internet-models.rst index 59ec4fbe0..274c77e1e 100644 --- a/doc/models/source/internet-models.rst +++ b/doc/models/source/internet-models.rst @@ -1,4 +1,4 @@ -Internet Models (IP, TCP, Routing, UDP, Internet Applications, Codel) +Internet Models (IP, TCP, Routing, UDP, Internet Applications) --------------------------------------------------------------------- .. toctree:: diff --git a/src/internet/model/ipv4-l3-protocol.cc b/src/internet/model/ipv4-l3-protocol.cc index 9bfa2672a..9b41f8a12 100644 --- a/src/internet/model/ipv4-l3-protocol.cc +++ b/src/internet/model/ipv4-l3-protocol.cc @@ -1048,6 +1048,17 @@ Ipv4L3Protocol::IpForward (Ptr rtentry, Ptr p, const Ip m_dropTrace (header, packet, DROP_TTL_EXPIRED, m_node->GetObject (), interface); return; } + // in case the packet still has a priority tag attached, remove it + SocketPriorityTag priorityTag; + packet->RemovePacketTag (priorityTag); + uint8_t priority = Socket::IpTos2Priority (ipHeader.GetTos ()); + // add a priority tag if the priority is not null + if (priority) + { + priorityTag.SetPriority (priority); + packet->AddPacketTag (priorityTag); + } + m_unicastForwardTrace (ipHeader, packet, interface); SendRealOut (rtentry, packet, ipHeader); } diff --git a/src/internet/model/ipv6-l3-protocol.cc b/src/internet/model/ipv6-l3-protocol.cc index 9a2f52f9b..449d1911a 100644 --- a/src/internet/model/ipv6-l3-protocol.cc +++ b/src/internet/model/ipv6-l3-protocol.cc @@ -1300,6 +1300,9 @@ void Ipv6L3Protocol::IpForward (Ptr idev, Ptr rtentr icmpv6->SendRedirection (copy, linkLocal, src, target, dst, Address ()); } } + // in case the packet still has a priority tag attached, remove it + SocketPriorityTag priorityTag; + packet->RemovePacketTag (priorityTag); int32_t interface = GetInterfaceForDevice (rtentry->GetOutputDevice ()); m_unicastForwardTrace (ipHeader, packet, interface); SendRealOut (rtentry, packet, ipHeader); diff --git a/src/internet/model/tcp-socket-base.cc b/src/internet/model/tcp-socket-base.cc index ec8b4454e..f35ae87ab 100644 --- a/src/internet/model/tcp-socket-base.cc +++ b/src/internet/model/tcp-socket-base.cc @@ -1186,6 +1186,10 @@ void TcpSocketBase::DoForwardUp (Ptr packet, const Address &fromAddress, const Address &toAddress) { + // in case the packet still has a priority tag attached, remove it + SocketPriorityTag priorityTag; + packet->RemovePacketTag (priorityTag); + // Peel off TCP header and do validity checking TcpHeader tcpHeader; uint32_t bytesRemoved = packet->RemoveHeader (tcpHeader); @@ -2165,6 +2169,14 @@ TcpSocketBase::SendEmptyPacket (uint8_t flags) p->AddPacketTag (ipHopLimitTag); } + uint8_t priority = GetPriority (); + if (priority) + { + SocketPriorityTag priorityTag; + priorityTag.SetPriority (priority); + p->ReplacePacketTag (priorityTag); + } + if (m_endPoint == 0 && m_endPoint6 == 0) { NS_LOG_WARN ("Failed to send empty packet due to null endpoint"); @@ -2469,6 +2481,14 @@ TcpSocketBase::SendDataPacket (SequenceNumber32 seq, uint32_t maxSize, bool with p->AddPacketTag (ipHopLimitTag); } + uint8_t priority = GetPriority (); + if (priority) + { + SocketPriorityTag priorityTag; + priorityTag.SetPriority (priority); + p->ReplacePacketTag (priorityTag); + } + if (m_closeOnEmpty && (remainingData == 0)) { flags |= TcpHeader::FIN; diff --git a/src/internet/model/tcp-socket-base.h b/src/internet/model/tcp-socket-base.h index 42cb97121..de4b1ad6d 100644 --- a/src/internet/model/tcp-socket-base.h +++ b/src/internet/model/tcp-socket-base.h @@ -202,6 +202,10 @@ public: * operations set the TOS for the socket to the value specified in the provided * address. A SocketIpTos tag is only added to the packet if the resulting * TOS is non-null. + * Each packet is assigned the priority set for the socket. Setting a TOS + * for a socket also sets a priority for the socket (according to the + * Socket::IpTos2Priority function). A SocketPriority tag is only added to the + * packet if the priority is non-null. * * Congestion state machine * --------------------------- diff --git a/src/internet/model/udp-socket-impl.cc b/src/internet/model/udp-socket-impl.cc index 3ff11c047..79806c21c 100644 --- a/src/internet/model/udp-socket-impl.cc +++ b/src/internet/model/udp-socket-impl.cc @@ -487,12 +487,21 @@ UdpSocketImpl::DoSendTo (Ptr p, Ipv4Address dest, uint16_t port, uint8_t return -1; } + uint8_t priority = GetPriority (); if (tos) { SocketIpTosTag ipTosTag; ipTosTag.SetTos (tos); // This packet may already have a SocketIpTosTag (see BUG 2440) p->ReplacePacketTag (ipTosTag); + priority = IpTos2Priority (tos); + } + + if (priority) + { + SocketPriorityTag priorityTag; + priorityTag.SetPriority (priority); + p->ReplacePacketTag (priorityTag); } Ptr ipv4 = m_node->GetObject (); @@ -690,6 +699,14 @@ UdpSocketImpl::DoSendTo (Ptr p, Ipv6Address dest, uint16_t port) p->AddPacketTag (ipTclassTag); } + uint8_t priority = GetPriority (); + if (priority) + { + SocketPriorityTag priorityTag; + priorityTag.SetPriority (priority); + p->ReplacePacketTag (priorityTag); + } + Ptr ipv6 = m_node->GetObject (); // Locally override the IP TTL for this socket @@ -1000,6 +1017,10 @@ UdpSocketImpl::ForwardUp (Ptr packet, Ipv4Header header, uint16_t port, packet->AddPacketTag (ipTtlTag); } + // in case the packet still has a priority tag attached, remove it + SocketPriorityTag priorityTag; + packet->RemovePacketTag (priorityTag); + if ((m_rxAvailable + packet->GetSize ()) <= m_rcvBufSize) { Address address = InetSocketAddress (header.GetSource (), port); @@ -1053,6 +1074,10 @@ UdpSocketImpl::ForwardUp6 (Ptr packet, Ipv6Header header, uint16_t port, packet->AddPacketTag (ipHopLimitTag); } + // in case the packet still has a priority tag attached, remove it + SocketPriorityTag priorityTag; + packet->RemovePacketTag (priorityTag); + if ((m_rxAvailable + packet->GetSize ()) <= m_rcvBufSize) { Address address = Inet6SocketAddress (header.GetSourceAddress (), port); diff --git a/src/internet/model/udp-socket-impl.h b/src/internet/model/udp-socket-impl.h index 6773226e2..c1a5a915d 100644 --- a/src/internet/model/udp-socket-impl.h +++ b/src/internet/model/udp-socket-impl.h @@ -57,6 +57,13 @@ class Ipv6Interface; * In both cases, a SocketIpTos tag is only added to the packet if the resulting * TOS is non-null. The Bind and Connect operations set the TOS for the * socket to the value specified in the provided address. + * If the TOS determined for a packet (as described above) is not null, the + * packet is assigned a priority based on that TOS value (according to the + * Socket::IpTos2Priority function). Otherwise, the priority set for the + * socket is assigned to the packet. Setting a TOS for a socket also sets a + * priority for the socket (according to the Socket::IpTos2Priority function). + * A SocketPriority tag is only added to the packet if the resulting priority + * is non-null. */ class UdpSocketImpl : public UdpSocket diff --git a/src/internet/test/udp-test.cc b/src/internet/test/udp-test.cc index 8981172f6..373dac884 100644 --- a/src/internet/test/udp-test.cc +++ b/src/internet/test/udp-test.cc @@ -31,6 +31,7 @@ #include "ns3/simple-net-device-helper.h" #include "ns3/drop-tail-queue.h" #include "ns3/socket.h" +#include "ns3/traffic-control-helper.h" #include "ns3/boolean.h" #include "ns3/log.h" @@ -41,6 +42,7 @@ #include "ns3/arp-l3-protocol.h" #include "ns3/ipv4-l3-protocol.h" +#include "ns3/ipv4-queue-disc-item.h" #include "ns3/ipv6-l3-protocol.h" #include "ns3/icmpv4-l4-protocol.h" #include "ns3/icmpv6-l4-protocol.h" @@ -149,17 +151,21 @@ class UdpSocketImplTest : public TestCase { Ptr m_receivedPacket; Ptr m_receivedPacket2; - void DoSendData (Ptr socket, std::string to); - void SendData (Ptr socket, std::string to); + Ptr m_sentPacket; + uint32_t GetTos (void); + uint32_t GetPriority (void); + void DoSendDataTo (Ptr socket, std::string to); + void SendDataTo (Ptr socket, std::string to); + void DoSendData (Ptr socket); + void SendData (Ptr socket); public: virtual void DoRun (void); UdpSocketImplTest (); - void ReceivePacket (Ptr socket, Ptr packet, const Address &from); - void ReceivePacket2 (Ptr socket, Ptr packet, const Address &from); void ReceivePkt (Ptr socket); void ReceivePkt2 (Ptr socket); + void SentPkt (Ptr item); }; UdpSocketImplTest::UdpSocketImplTest () @@ -167,16 +173,6 @@ UdpSocketImplTest::UdpSocketImplTest () { } -void UdpSocketImplTest::ReceivePacket (Ptr socket, Ptr packet, const Address &from) -{ - m_receivedPacket = packet; -} - -void UdpSocketImplTest::ReceivePacket2 (Ptr socket, Ptr packet, const Address &from) -{ - m_receivedPacket2 = packet; -} - void UdpSocketImplTest::ReceivePkt (Ptr socket) { uint32_t availableData; @@ -193,8 +189,29 @@ void UdpSocketImplTest::ReceivePkt2 (Ptr socket) NS_ASSERT (availableData == m_receivedPacket2->GetSize ()); } +void UdpSocketImplTest::SentPkt (Ptr item) +{ + Ptr ipv4Item = DynamicCast (item); + NS_TEST_EXPECT_MSG_NE (ipv4Item, 0, "no IPv4 packet"); + Address addr; + m_sentPacket = Create (ipv4Item->GetPacket ()->Copy (), addr, 0, ipv4Item->GetHeader ()); +} + +uint32_t UdpSocketImplTest::GetTos (void) +{ + return static_cast (m_sentPacket->GetHeader ().GetTos ()); +} + +uint32_t UdpSocketImplTest::GetPriority (void) +{ + SocketPriorityTag priorityTag; + bool found = m_sentPacket->GetPacket ()->PeekPacketTag (priorityTag); + NS_TEST_EXPECT_MSG_EQ (found, true, "the packet should carry a SocketPriorityTag"); + return static_cast (priorityTag.GetPriority ()); +} + void -UdpSocketImplTest::DoSendData (Ptr socket, std::string to) +UdpSocketImplTest::DoSendDataTo (Ptr socket, std::string to) { Address realTo = InetSocketAddress (Ipv4Address (to.c_str ()), 1234); NS_TEST_EXPECT_MSG_EQ (socket->SendTo (Create (123), 0, realTo), @@ -202,12 +219,26 @@ UdpSocketImplTest::DoSendData (Ptr socket, std::string to) } void -UdpSocketImplTest::SendData (Ptr socket, std::string to) +UdpSocketImplTest::SendDataTo (Ptr socket, std::string to) { m_receivedPacket = Create (); m_receivedPacket2 = Create (); Simulator::ScheduleWithContext (socket->GetNode ()->GetId (), Seconds (0), - &UdpSocketImplTest::DoSendData, this, socket, to); + &UdpSocketImplTest::DoSendDataTo, this, socket, to); + Simulator::Run (); +} + +void +UdpSocketImplTest::DoSendData (Ptr socket) +{ + NS_TEST_EXPECT_MSG_EQ (socket->Send (Create (123), 0), 123, "100"); +} + +void +UdpSocketImplTest::SendData (Ptr socket) +{ + Simulator::ScheduleWithContext (socket->GetNode ()->GetId (), Seconds (0), + &UdpSocketImplTest::DoSendData, this, socket); Simulator::Run (); } @@ -234,6 +265,9 @@ UdpSocketImplTest::DoRun (void) InternetStackHelper internet; internet.Install (nodes); + TrafficControlHelper tch = TrafficControlHelper::Default (); + QueueDiscContainer qdiscs = tch.Install (net1.Get (1)); + Ptr ipv4; uint32_t netdev_idx; Ipv4InterfaceAddress ipv4Addr; @@ -279,7 +313,7 @@ UdpSocketImplTest::DoRun (void) // ------ Now the tests ------------ // Unicast test - SendData (txSocket, "10.0.0.1"); + SendDataTo (txSocket, "10.0.0.1"); NS_TEST_EXPECT_MSG_EQ (m_receivedPacket->GetSize (), 123, "trivial"); NS_TEST_EXPECT_MSG_EQ (m_receivedPacket2->GetSize (), 0, "second interface should receive it"); @@ -288,7 +322,7 @@ UdpSocketImplTest::DoRun (void) // Simple broadcast test - SendData (txSocket, "255.255.255.255"); + SendDataTo (txSocket, "255.255.255.255"); NS_TEST_EXPECT_MSG_EQ (m_receivedPacket->GetSize (), 123, "trivial"); NS_TEST_EXPECT_MSG_EQ (m_receivedPacket2->GetSize (), 0, "second socket should not receive it (it is bound specifically to the second interface's address"); @@ -305,7 +339,7 @@ UdpSocketImplTest::DoRun (void) rxSocket2->SetRecvCallback (MakeCallback (&UdpSocketImplTest::ReceivePkt2, this)); NS_TEST_EXPECT_MSG_EQ (rxSocket2->Bind (InetSocketAddress (Ipv4Address ("0.0.0.0"), 1234)), 0, "trivial"); - SendData (txSocket, "255.255.255.255"); + SendDataTo (txSocket, "255.255.255.255"); NS_TEST_EXPECT_MSG_EQ (m_receivedPacket->GetSize (), 123, "trivial"); NS_TEST_EXPECT_MSG_EQ (m_receivedPacket2->GetSize (), 123, "trivial"); @@ -315,7 +349,7 @@ UdpSocketImplTest::DoRun (void) // Simple Link-local multicast test txSocket->BindToNetDevice (net1.Get (1)); - SendData (txSocket, "224.0.0.9"); + SendDataTo (txSocket, "224.0.0.9"); NS_TEST_EXPECT_MSG_EQ (m_receivedPacket->GetSize (), 0, "first socket should not receive it (it is bound specifically to the second interface's address"); NS_TEST_EXPECT_MSG_EQ (m_receivedPacket2->GetSize (), 123, "recv2: 224.0.0.9"); @@ -337,6 +371,41 @@ UdpSocketImplTest::DoRun (void) NS_TEST_EXPECT_MSG_EQ (err, 0, "socket GetPeerName() should succeed when socket is connected"); NS_TEST_EXPECT_MSG_EQ (peerAddress, peer, "address from socket GetPeerName() should equal the connected address"); + m_receivedPacket->RemoveAllByteTags (); + m_receivedPacket2->RemoveAllByteTags (); + + // TOS and priority tests + + // Intercept the packets dequeued by the queue disc on the sender node + qdiscs.Get (0)->TraceConnectWithoutContext ("Dequeue", MakeCallback (&UdpSocketImplTest::SentPkt, this)); + + // The socket is not connected. + txSocket->SetIpTos (0x28); // AF11 + txSocket->SetPriority (6); // Interactive + // Send a packet to a specified destination: + // - for not connected sockets, the tos specified in the destination address (0) is used + // - since the tos is zero, the priority set for the socket is used + SendDataTo (txSocket, "10.0.0.1"); + NS_TEST_EXPECT_MSG_EQ (m_receivedPacket->GetSize (), 123, "trivial"); + + NS_TEST_EXPECT_MSG_EQ (GetTos (), 0, "the TOS should be set to 0"); + NS_TEST_EXPECT_MSG_EQ (GetPriority (), 6, "Interactive (6)"); + + m_receivedPacket->RemoveAllByteTags (); + + InetSocketAddress dest ("10.0.0.1", 1234); + dest.SetTos (0xb8); // EF + // the connect operation sets the tos (and priority) for the socket + NS_TEST_EXPECT_MSG_EQ (txSocket->Connect (dest), 0, "the connect operation failed"); + + SendData (txSocket); + NS_TEST_EXPECT_MSG_EQ (m_receivedPacket->GetSize (), 123, "trivial"); + + NS_TEST_EXPECT_MSG_EQ (GetTos (), 0xb8, "the TOS should be set to 0xb8"); + NS_TEST_EXPECT_MSG_EQ (GetPriority (), 4, "Interactive bulk (4)"); + + m_receivedPacket->RemoveAllByteTags (); + Simulator::Destroy (); } diff --git a/src/network/doc/sockets-api.rst b/src/network/doc/sockets-api.rst index 28b5ffff0..6abe41337 100644 --- a/src/network/doc/sockets-api.rst +++ b/src/network/doc/sockets-api.rst @@ -192,7 +192,80 @@ a real (zeroed) buffer on the spot, and the efficiency will be lost there. Socket options ************** -*to be completed* +ToS (Type of Service) +====================== + +The type of service associated with a socket can be set/read through public +methods of the Socket base class:: + + void SetIpTos (uint8_t ipTos); + uint8_t GetIpTos (void) const; + +This option is equivalent to the IP_TOS option of BSD sockets. It only applies +to sockets using the IPv4 protocol. The socket types that support setting +the ToS field of the IPv4 header are :cpp:class:`ns3::UdpSocketImpl` and +:cpp:class:`ns3::TcpSocketBase`. + +Setting the ToS with UDP sockets +################################# + +For IPv4 packets, the ToS field is set according to the following rules: + +* If the socket is connected, the ToS field is set to the ToS value associated + with the socket. + +* If the socket is not connected, the ToS field is set to the value specified + in the destination address (of type :cpp:class:`ns3::InetSockAddress`) passed + to :cpp:func:`ns3::Socket::SendTo`, and the ToS value associated with the + socket is ignored. + +It has to be noted that the ToS value associated with the socket can also be set +by the Bind and Connect operations, which use the ToS value specified in the +provided address (of type :cpp:class:`ns3::InetSockAddress`). + +Setting the ToS with TCP sockets +################################# + +For IPv4 packets, the ToS field is set to the ToS value associated with the +socket. It has to be noted that the ToS value associated with the socket can +also be set by the Bind and Connect operations, which use the ToS value specified +in the provided address (of type :cpp:class:`ns3::InetSockAddress`). + +Priority +========= + +The priority associated with a socket can be set/read through public methods of +the Socket base class:: + + void SetPriority (uint8_t priority); + uint8_t GetPriority (void) const; + +This option is equivalent to the SO_PRIORITY option of BSD sockets. Only values +in the range 0..6 can be set through the above method. Note that setting a ToS +value for the socket also sets a priority for the socket (according to +the :cpp:func:`ns3::Socket::IpTos2Priority` function). The socket types that +support setting the priority for a packet are :cpp:class:`ns3::UdpSocketImpl`, +:cpp:class:`ns3::TcpSocketBase` and :cpp:class:`ns3::PacketSocket`. + +Setting the priority with UDP sockets +###################################### + +If the packet is an IPv4 packet and the value to be inserted in the ToS field +is not null, then the packet is assigned a priority based on such ToS value +(according to the :cpp:func:`ns3::Socket::IpTos2Priority` function). Otherwise, +the priority associated with the socket is assigned to the packet. + +Setting the priority with TCP sockets +###################################### + +Every packet is assigned a priority equal to the priority associated with the +socket. + +Setting the priority with packet sockets +######################################### + +Every packet is assigned a priority equal to the priority associated with the +socket. Socket errno ************ diff --git a/src/network/model/socket.cc b/src/network/model/socket.cc index 85222904f..d228c80f7 100644 --- a/src/network/model/socket.cc +++ b/src/network/model/socket.cc @@ -56,6 +56,7 @@ Socket::Socket (void) m_boundnetdevice = 0; m_recvPktInfo = false; + m_priority = 0; m_ipTos = 0; m_ipTtl = 0; m_ipv6Tclass = 0; @@ -391,13 +392,75 @@ Socket::IsManualIpv6HopLimit (void) const return m_manualIpv6HopLimit; } +void +Socket::SetPriority (uint8_t priority) +{ + if (priority <= 6) + { + m_priority = priority; + } + else + { + NS_LOG_ERROR ("Cannot set a priority higher than 6"); + } +} + +uint8_t +Socket::GetPriority (void) const +{ + return m_priority; +} + +uint8_t +Socket::IpTos2Priority (uint8_t ipTos) +{ + uint8_t prio = NS3_PRIO_BESTEFFORT; + ipTos &= 0x1e; + switch (ipTos >> 1) + { + case 0: + case 1: + case 2: + case 3: + prio = NS3_PRIO_BESTEFFORT; + break; + case 4: + case 5: + case 6: + case 7: + prio = NS3_PRIO_BULK; + break; + case 8: + case 9: + case 10: + case 11: + prio = NS3_PRIO_INTERACTIVE; + break; + case 12: + case 13: + case 14: + case 15: + prio = NS3_PRIO_INTERACTIVE_BULK; + break; + } + return prio; +} + void Socket::SetIpTos (uint8_t tos) { Address address; GetSockName (address); m_manualIpTos = true; + if (GetSocketType () == NS3_SOCK_STREAM) + { + // preserve the least two significant bits of the current TOS + // value, which are used for ECN + tos &= 0xfc; + tos |= m_ipTos & 0x3; + } m_ipTos = tos; + m_priority = IpTos2Priority (tos); } uint8_t @@ -785,6 +848,64 @@ SocketIpTosTag::Print (std::ostream &os) const } +SocketPriorityTag::SocketPriorityTag () +{ +} + +void +SocketPriorityTag::SetPriority (uint8_t priority) +{ + m_priority = priority; +} + +uint8_t +SocketPriorityTag::GetPriority (void) const +{ + return m_priority; +} + +TypeId +SocketPriorityTag::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::SocketPriorityTag") + .SetParent () + .SetGroupName("Network") + .AddConstructor () + ; + return tid; +} + +TypeId +SocketPriorityTag::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +uint32_t +SocketPriorityTag::GetSerializedSize (void) const +{ + return sizeof (uint8_t); +} + +void +SocketPriorityTag::Serialize (TagBuffer i) const +{ + i.WriteU8 (m_priority); +} + +void +SocketPriorityTag::Deserialize (TagBuffer i) +{ + m_priority = i.ReadU8(); +} + +void +SocketPriorityTag::Print (std::ostream &os) const +{ + os << "SO_PRIORITY = " << m_priority; +} + + SocketIpv6TclassTag::SocketIpv6TclassTag () { } diff --git a/src/network/model/socket.h b/src/network/model/socket.h index 8ec01a7a7..c73e840f9 100644 --- a/src/network/model/socket.h +++ b/src/network/model/socket.h @@ -108,6 +108,22 @@ public: NS3_SOCK_RAW }; + /** + * \enum SocketPriority + * \brief Enumeration of the possible socket priorities. + * + * Names and corresponding values are derived from + * the Linux TC_PRIO_* macros + */ + enum SocketPriority { + NS3_PRIO_BESTEFFORT = 0, + NS3_PRIO_FILLER = 1, + NS3_PRIO_BULK = 2, + NS3_PRIO_INTERACTIVE_BULK = 4, + NS3_PRIO_INTERACTIVE = 6, + NS3_PRIO_CONTROL = 7 + }; + /** * \enum Ipv6MulticastFilterMode * \brief Enumeration of the possible filter of a socket. @@ -664,14 +680,111 @@ public: */ bool IsRecvPktInfo () const; + /** + * \brief Manually set the socket priority + * + * This method corresponds to using setsockopt () SO_PRIORITY of + * real network or BSD sockets. + * + * \param priority The socket priority (in the range 0..6) + */ + void SetPriority (uint8_t priority); + + /** + * \brief Query the priority value of this socket + * + * This method corresponds to using getsockopt () SO_PRIORITY of real network + * or BSD sockets. + * + * \return The priority value + */ + uint8_t GetPriority (void) const; + + /** + * \brief Return the priority corresponding to a given TOS value + * + * This function is implemented after the Linux rt_tos2priority + * function. The usage of the TOS byte has been originally defined by + * RFC 1349 (http://www.ietf.org/rfc/rfc1349.txt): + * + * 0 1 2 3 4 5 6 7 + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * | PRECEDENCE | TOS | MBZ | + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * + * where MBZ stands for 'must be zero'. + * + * The Linux rt_tos2priority function ignores the precedence bits and + * maps each of the 16 values coded in bits 3-6 as follows: + * + * Bits 3-6 | Means | Linux Priority + * ---------|-------------------------|---------------- + * 0 | Normal Service | Best Effort (0) + * 1 | Minimize Monetary Cost | Best Effort (0) + * 2 | Maximize Reliability | Best Effort (0) + * 3 | mmc+mr | Best Effort (0) + * 4 | Maximize Throughput | Bulk (2) + * 5 | mmc+mt | Bulk (2) + * 6 | mr+mt | Bulk (2) + * 7 | mmc+mr+mt | Bulk (2) + * 8 | Minimize Delay | Interactive (6) + * 9 | mmc+md | Interactive (6) + * 10 | mr+md | Interactive (6) + * 11 | mmc+mr+md | Interactive (6) + * 12 | mt+md | Int. Bulk (4) + * 13 | mmc+mt+md | Int. Bulk (4) + * 14 | mr+mt+md | Int. Bulk (4) + * 15 | mmc+mr+mt+md | Int. Bulk (4) + * + * RFC 2474 (http://www.ietf.org/rfc/rfc1349.txt) redefines the TOS byte: + * + * 0 1 2 3 4 5 6 7 + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * | DSCP | CU | + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * + * where DSCP is the Differentiated Services Code Point and CU stands for + * 'currently unused' (actually, RFC 3168 proposes to use these two bits for + * ECN purposes). The table above allows to determine how the Linux + * rt_tos2priority function maps each DSCP value to a priority value. Such a + * mapping is shown below. + * + * DSCP | Hex | TOS (binary) | bits 3-6 | Linux Priority + * -----|------|--------------|----------|---------------- + * EF | 0x2E | 101110xx | 12-13 | Int. Bulk (4) + * AF11 | 0x0A | 001010xx | 4-5 | Bulk (2) + * AF21 | 0x12 | 010010xx | 4-5 | Bulk (2) + * AF31 | 0x1A | 011010xx | 4-5 | Bulk (2) + * AF41 | 0x22 | 100010xx | 4-5 | Bulk (2) + * AF12 | 0x0C | 001100xx | 8-9 | Interactive (6) + * AF22 | 0x14 | 010100xx | 8-9 | Interactive (6) + * AF32 | 0x1C | 011100xx | 8-9 | Interactive (6) + * AF42 | 0x24 | 100100xx | 8-9 | Interactive (6) + * AF13 | 0x0E | 001110xx | 12-13 | Int. Bulk (4) + * AF23 | 0x16 | 010110xx | 12-13 | Int. Bulk (4) + * AF33 | 0x1E | 011110xx | 12-13 | Int. Bulk (4) + * AF43 | 0x26 | 100110xx | 12-13 | Int. Bulk (4) + * CS0 | 0x00 | 000000xx | 0-1 | Best Effort (0) + * CS1 | 0x08 | 001000xx | 0-1 | Best Effort (0) + * CS2 | 0x10 | 010000xx | 0-1 | Best Effort (0) + * CS3 | 0x18 | 011000xx | 0-1 | Best Effort (0) + * CS4 | 0x20 | 100000xx | 0-1 | Best Effort (0) + * CS5 | 0x28 | 101000xx | 0-1 | Best Effort (0) + * CS6 | 0x30 | 110000xx | 0-1 | Best Effort (0) + * CS7 | 0x38 | 111000xx | 0-1 | Best Effort (0) + * + * \param ipTos the TOS value (in the range 0..255) + * \return The priority value corresponding to the given TOS value + */ + static uint8_t IpTos2Priority (uint8_t ipTos); + /** * \brief Manually set IP Type of Service field * * This method corresponds to using setsockopt () IP_TOS of * real network or BSD sockets. This option is for IPv4 only. - * Setting the IP TOS should also change the socket queueing - * priority as stated in the man page. However, socket priority - * is not yet supported. + * Setting the IP TOS also changes the socket priority as + * stated in the man page. * * \param ipTos The desired TOS value for IP headers */ @@ -979,6 +1092,8 @@ private: Callback, uint32_t > m_sendCb; //!< packet sent callback Callback > m_receivedData; //!< data received callback + uint8_t m_priority; //!< the socket priority + //IPv4 options bool m_manualIpTos; //!< socket has IPv4 TOS set bool m_manualIpTtl; //!< socket has IPv4 TTL set @@ -1191,6 +1306,52 @@ private: uint8_t m_ipTos; //!< the TOS carried by the tag }; +/** + * \brief indicates whether the socket has a priority set. + */ +class SocketPriorityTag : public Tag +{ +public: + SocketPriorityTag (); + + /** + * \brief Set the tag's priority + * + * \param priority the priority + */ + void SetPriority (uint8_t priority); + + /** + * \brief Get the tag's priority + * + * \returns the priority + */ + uint8_t GetPriority (void) const; + + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + + // inherited function, no need to doc. + virtual TypeId GetInstanceTypeId (void) const; + + // inherited function, no need to doc. + virtual uint32_t GetSerializedSize (void) const; + + // inherited function, no need to doc. + virtual void Serialize (TagBuffer i) const; + + // inherited function, no need to doc. + virtual void Deserialize (TagBuffer i); + + // inherited function, no need to doc. + virtual void Print (std::ostream &os) const; +private: + uint8_t m_priority; //!< the priority carried by the tag +}; + /** * \brief indicates whether the socket has IPV6_TCLASS set. * This tag is for IPv6 socket. diff --git a/src/traffic-control/model/queue-disc.cc b/src/traffic-control/model/queue-disc.cc index 007d15efa..763d4f9ee 100644 --- a/src/traffic-control/model/queue-disc.cc +++ b/src/traffic-control/model/queue-disc.cc @@ -23,6 +23,7 @@ #include "ns3/pointer.h" #include "ns3/object-vector.h" #include "ns3/packet.h" +#include "ns3/socket.h" #include "ns3/unused.h" #include "queue-disc.h" @@ -621,6 +622,12 @@ QueueDisc::Transmit (Ptr item) return false; } + // a single queue device makes no use of the priority tag + if (m_devQueueIface->GetTxQueuesN () == 1) + { + SocketPriorityTag priorityTag; + item->GetPacket ()->RemovePacketTag (priorityTag); + } m_device->Send (item->GetPacket (), item->GetAddress (), item->GetProtocol ()); // the behavior here slightly diverges from Linux. In Linux, it is advised that diff --git a/src/traffic-control/model/traffic-control-layer.cc b/src/traffic-control/model/traffic-control-layer.cc index 06d2e9694..e94a80e89 100644 --- a/src/traffic-control/model/traffic-control-layer.cc +++ b/src/traffic-control/model/traffic-control-layer.cc @@ -21,6 +21,7 @@ #include "ns3/log.h" #include "ns3/object-map.h" #include "ns3/packet.h" +#include "ns3/socket.h" #include "ns3/queue-disc.h" namespace ns3 { @@ -301,6 +302,12 @@ TrafficControlLayer::Send (Ptr device, Ptr item) if (!devQueueIface->GetTxQueue (txq)->IsStopped ()) { item->AddHeader (); + // a single queue device makes no use of the priority tag + if (devQueueIface->GetTxQueuesN () == 1) + { + SocketPriorityTag priorityTag; + item->GetPacket ()->RemovePacketTag (priorityTag); + } device->Send (item->GetPacket (), item->GetAddress (), item->GetProtocol ()); } }