diff --git a/RELEASE_NOTES b/RELEASE_NOTES index ba564f525..2fddf40f5 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -35,6 +35,7 @@ New user-visible features Bugs fixed ---------- - Bug 1745 - There can be only one Ipv6AddressHelper in a script +- Bug 2277 - lte: EpcTftClassifier::Classify blindly assumes that a packet has a L4 header - Bug 2505 - network: Avoid asserts in Header/Trailer deserialization - Bug 2656 - wifi: Minstrel and MinstrelHt provide different results for 802.11a/b/g - Bug 2653 - tcp: Avoid saving smaller TS in case of packet reordering diff --git a/src/lte/model/epc-tft-classifier.cc b/src/lte/model/epc-tft-classifier.cc index 28e7e3c81..d1e2ac7d0 100644 --- a/src/lte/model/epc-tft-classifier.cc +++ b/src/lte/model/epc-tft-classifier.cc @@ -48,10 +48,9 @@ EpcTftClassifier::EpcTftClassifier () void EpcTftClassifier::Add (Ptr tft, uint32_t id) { - NS_LOG_FUNCTION (this << tft); - - m_tftMap[id] = tft; - + NS_LOG_FUNCTION (this << tft << id); + m_tftMap[id] = tft; + // simple sanity check: there shouldn't be more than 16 bearers (hence TFTs) per UE NS_ASSERT (m_tftMap.size () <= 16); } @@ -67,7 +66,7 @@ EpcTftClassifier::Delete (uint32_t id) uint32_t EpcTftClassifier::Classify (Ptr p, EpcTft::Direction direction) { - NS_LOG_FUNCTION (this << p << direction); + NS_LOG_FUNCTION (this << p << p->GetSize () << direction); Ptr pCopy = p->Copy (); @@ -105,8 +104,103 @@ EpcTftClassifier::Classify (Ptr p, EpcTft::Direction direction) } NS_LOG_INFO ("local address: " << localAddressIpv4 << " remote address: " << remoteAddressIpv4); + uint16_t payloadSize = ipv4Header.GetPayloadSize (); + uint16_t fragmentOffset = ipv4Header.GetFragmentOffset (); + bool isLastFragment = ipv4Header.IsLastFragment (); + + // NS_LOG_DEBUG ("PayloadSize = " << payloadSize); + // NS_LOG_DEBUG ("fragmentOffset " << fragmentOffset << " isLastFragment " << isLastFragment); + protocol = ipv4Header.GetProtocol (); tos = ipv4Header.GetTos (); + + // Port info only can be get if it is the first fragment and + // there is enough data in the payload + // We keep the port info for fragmented packets, + // i.e. it is the first one but it is not the last one + if (fragmentOffset == 0) + { + if (protocol == UdpL4Protocol::PROT_NUMBER && payloadSize >= 8) + { + UdpHeader udpHeader; + pCopy->RemoveHeader (udpHeader); + if (direction == EpcTft::UPLINK) + { + localPort = udpHeader.GetSourcePort (); + remotePort = udpHeader.GetDestinationPort (); + } + else + { + remotePort = udpHeader.GetSourcePort (); + localPort = udpHeader.GetDestinationPort (); + } + if (!isLastFragment) + { + std::tuple fragmentKey = + std::make_tuple (ipv4Header.GetSource ().Get (), + ipv4Header.GetDestination ().Get (), + protocol, + ipv4Header.GetIdentification ()); + + m_classifiedIpv4Fragments[fragmentKey] = std::make_pair (localPort, remotePort); + } + } + else if (protocol == TcpL4Protocol::PROT_NUMBER && payloadSize >= 20) + { + TcpHeader tcpHeader; + pCopy->RemoveHeader (tcpHeader); + if (direction == EpcTft::UPLINK) + { + localPort = tcpHeader.GetSourcePort (); + remotePort = tcpHeader.GetDestinationPort (); + } + else + { + remotePort = tcpHeader.GetSourcePort (); + localPort = tcpHeader.GetDestinationPort (); + } + + if (!isLastFragment) + { + std::tuple fragmentKey = + std::make_tuple (ipv4Header.GetSource ().Get (), + ipv4Header.GetDestination ().Get (), + protocol, + ipv4Header.GetIdentification ()); + + m_classifiedIpv4Fragments[fragmentKey] = std::make_pair (localPort, remotePort); + } + } + + // else + // First fragment but not enough data for port info or not UDP/TCP protocol. + // Nothing can be done, i.e. we cannot get port info from packet. + } + else + { + // Not first fragment, so port info is not available but + // port info should already be known (if there is not fragment reordering) + std::tuple fragmentKey = + std::make_tuple (ipv4Header.GetSource ().Get (), + ipv4Header.GetDestination ().Get (), + protocol, + ipv4Header.GetIdentification ()); + + std::map< std::tuple, + std::pair >::iterator it = + m_classifiedIpv4Fragments.find (fragmentKey); + + if (it != m_classifiedIpv4Fragments.end ()) + { + localPort = it->second.first; + remotePort = it->second.second; + + if (isLastFragment) + { + m_classifiedIpv4Fragments.erase (fragmentKey); + } + } + } } else if (ipType == 0x06) { @@ -128,53 +222,44 @@ EpcTftClassifier::Classify (Ptr p, EpcTft::Direction direction) protocol = ipv6Header.GetNextHeader (); tos = ipv6Header.GetTrafficClass (); + + if (protocol == UdpL4Protocol::PROT_NUMBER) + { + UdpHeader udpHeader; + pCopy->RemoveHeader (udpHeader); + + if (direction == EpcTft::UPLINK) + { + localPort = udpHeader.GetSourcePort (); + remotePort = udpHeader.GetDestinationPort (); + } + else + { + remotePort = udpHeader.GetSourcePort (); + localPort = udpHeader.GetDestinationPort (); + } + } + else if (protocol == TcpL4Protocol::PROT_NUMBER) + { + TcpHeader tcpHeader; + pCopy->RemoveHeader (tcpHeader); + if (direction == EpcTft::UPLINK) + { + localPort = tcpHeader.GetSourcePort (); + remotePort = tcpHeader.GetDestinationPort (); + } + else + { + remotePort = tcpHeader.GetSourcePort (); + localPort = tcpHeader.GetDestinationPort (); + } + } } else { NS_ABORT_MSG ("EpcTftClassifier::Classify - Unknown IP type..."); } - if (protocol == UdpL4Protocol::PROT_NUMBER) - { - UdpHeader udpHeader; - pCopy->RemoveHeader (udpHeader); - - if (direction == EpcTft::UPLINK) - { - localPort = udpHeader.GetSourcePort (); - remotePort = udpHeader.GetDestinationPort (); - } - else - { - remotePort = udpHeader.GetSourcePort (); - localPort = udpHeader.GetDestinationPort (); - } - } - else if (protocol == TcpL4Protocol::PROT_NUMBER) - { - TcpHeader tcpHeader; - pCopy->RemoveHeader (tcpHeader); - if (direction == EpcTft::UPLINK) - { - localPort = tcpHeader.GetSourcePort (); - remotePort = tcpHeader.GetDestinationPort (); - } - else - { - remotePort = tcpHeader.GetSourcePort (); - localPort = tcpHeader.GetDestinationPort (); - } - } - else if (protocol == Icmpv6L4Protocol::PROT_NUMBER || protocol == Icmpv4L4Protocol::PROT_NUMBER) - { - remotePort = 0; - localPort = 0; - } - else - { - NS_LOG_INFO ("Unknown protocol: " << protocol); - return 0; // no match - } if (ipType == 0x04) { diff --git a/src/lte/model/epc-tft-classifier.h b/src/lte/model/epc-tft-classifier.h index 1da79b15c..5e0aef84d 100644 --- a/src/lte/model/epc-tft-classifier.h +++ b/src/lte/model/epc-tft-classifier.h @@ -36,9 +36,20 @@ class EpcTft; class Packet; /** - * \brief classifies IP packets accoding to Traffic Flow Templates (TFTs) - * - * \note this implementation works with IPv4 only. + * \brief classifies IP packets according to Traffic Flow Templates (TFTs) + * + * \note this implementation works with IPv4 and IPv6. + * When there is fragmentation of IP packets, UDP/TCP ports maybe missing. + * + * The following actions are performed to use the port info present in the first segment with + * the next fragments: + * - Port info is stored if it is available, i.e. it is the first fragment with UDP/TCP protocol + * and there is enough data in the payload of the IP packet for the port numbers. + * - Port info is used for the next fragments. + * - Port info is deleted, when the last fragment is processed. + * + * When we cannot cache the port info, the TFT of the default bearer is used. This may happen + * if there is reordering or losses of IP packets. */ class EpcTftClassifier : public SimpleRefCount { @@ -76,7 +87,16 @@ public: protected: std::map > m_tftMap; ///< TFT map - + + std::map < std::tuple, + std::pair > + m_classifiedIpv4Fragments; ///< Map with already classified IPv4 Fragments + ///< An entry is added when the port info is available, i.e. + ///< first fragment, UDP/TCP protocols and enough payload data + ///< An entry is used if port info is not available, i.e. + ///< not first fragment or not enough payload data for TCP/UDP + ///< An entry is removed when the last fragment is classified + ///< Note: If last fragment is lost, entry is not removed }; diff --git a/src/lte/test/test-epc-tft-classifier.cc b/src/lte/test/test-epc-tft-classifier.cc index 163bf5a51..9717331b1 100644 --- a/src/lte/test/test-epc-tft-classifier.cc +++ b/src/lte/test/test-epc-tft-classifier.cc @@ -116,14 +116,15 @@ EpcTftClassifierTestCase::EpcTftClassifierTestCase (Ptr c, m_d (d), m_tftId (tftId) { - NS_LOG_FUNCTION (this); + NS_LOG_FUNCTION (this << c << d << sa << da << sp << dp << tos << tftId); m_ipHeader.SetSource (sa); m_ipHeader.SetDestination (da); - m_ipHeader.SetTos (tos); + m_ipHeader.SetTos (tos); + m_ipHeader.SetPayloadSize (8); // Full UDP header m_udpHeader.SetSourcePort (sp); - m_udpHeader.SetDestinationPort (dp); + m_udpHeader.SetDestinationPort (dp); } EpcTftClassifierTestCase::~EpcTftClassifierTestCase () @@ -163,7 +164,7 @@ EpcTftClassifierTestCase::DoRun (void) udpPacket->AddHeader (m_ipHeader); NS_LOG_LOGIC (this << *udpPacket); uint32_t obtainedTftId = m_c ->Classify (udpPacket, m_d); - NS_TEST_ASSERT_MSG_EQ (obtainedTftId, m_tftId, "bad classification of UDP packet"); + NS_TEST_ASSERT_MSG_EQ (obtainedTftId, (uint16_t) m_tftId, "bad classification of UDP packet"); }