Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(871)

Unified Diff: src/internet/model/tcp-socket-base.cc

Issue 24900043: TCP Window Scale and Timestamps Implementation
Patch Set: Created 10 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/internet/model/tcp-socket-base.cc
===================================================================
--- a/src/internet/model/tcp-socket-base.cc
+++ b/src/internet/model/tcp-socket-base.cc
@@ -2,6 +2,7 @@
/*
* Copyright (c) 2007 Georgia Tech Research Corporation
* Copyright (c) 2010 Adrian Sai-wah Tam
+ * Copyright (c) 2013 ResiliNets, ITTC, University of Kansas
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -19,6 +20,13 @@
* Author: Adrian Sai-wah Tam <adrian.sw.tam@gmail.com>
*/
+/* TCP options processing, TCP window scale and timestamp implementations
+ *
+ * Author: Truc Anh N. Nguyen <annguyen@ittc.ku.edu>
+ * ResiliNets Research Group http://wiki.ittc.ku.edu/resilinets
+ * James P.G. Sterbenz <jpgs@ittc.ku.edu>, director
+ */
+
#define NS_LOG_APPEND_CONTEXT \
if (m_node) { std::clog << Simulator::Now ().GetSeconds () << " [node " << m_node->GetId () << "] "; }
@@ -82,7 +90,19 @@
.AddAttribute ("IcmpCallback6", "Callback invoked whenever an icmpv6 error is received on this socket.",
CallbackValue (),
MakeCallbackAccessor (&TcpSocketBase::m_icmpCallback6),
- MakeCallbackChecker ())
+ MakeCallbackChecker ())
+ .AddAttribute ("RcvWindScale", "Window shift count to be applied to outgoing window field",
+ UintegerValue (8),
+ MakeUintegerAccessor (&TcpSocketBase::m_rcvWindScale),
+ MakeUintegerChecker<uint8_t> ())
+ .AddAttribute ("WindScaleEnabled", "Enable window scale option",
+ BooleanValue (true),
+ MakeBooleanAccessor (&TcpSocketBase::m_windScaleEnabled),
+ MakeBooleanChecker ())
+ .AddAttribute ("TSEnabled", "Enable timestamps option",
+ BooleanValue (false),
+ MakeBooleanAccessor (&TcpSocketBase::m_tsEnabled),
+ MakeBooleanChecker ())
.AddTraceSource ("RTO",
"Retransmission timeout",
MakeTraceSourceAccessor (&TcpSocketBase::m_rto))
@@ -127,7 +147,14 @@
m_connected (false),
m_segmentSize (0),
// For attribute initialization consistency (quiet valgrind)
- m_rWnd (0)
+ m_rWnd (0),
+ m_recentTS (0),
+ m_lastAckSent (0),
+ m_ts (0),
+ m_echoTS (0),
+ m_withEchoTS (false),
+ m_tsEnabled (false),
+ m_tsRcvd (false)
{
NS_LOG_FUNCTION (this);
}
@@ -162,7 +189,18 @@
m_msl (sock.m_msl),
m_segmentSize (sock.m_segmentSize),
m_maxWinSize (sock.m_maxWinSize),
- m_rWnd (sock.m_rWnd)
+ m_rWnd (sock.m_rWnd),
+ m_sndWindScale (sock.m_sndWindScale),
+ m_rcvWindScale (sock.m_rcvWindScale),
+ m_windScaleRcvd (sock.m_windScaleRcvd),
+ m_windScaleEnabled (sock.m_windScaleEnabled),
+ m_recentTS (sock.m_recentTS),
+ m_lastAckSent (sock.m_lastAckSent),
+ m_ts (sock.m_ts),
+ m_echoTS (sock.m_echoTS),
+ m_withEchoTS (sock.m_withEchoTS),
+ m_tsEnabled (sock.m_tsEnabled),
+ m_tsRcvd (sock.m_tsRcvd)
{
NS_LOG_FUNCTION (this);
NS_LOG_LOGIC ("Invoked the copy constructor");
@@ -455,7 +493,7 @@
SendRST ();
return 0;
}
-
+
if (m_txBuffer.SizeFromSequence (m_nextTxSequence) > 0)
{ // App close with pending data must wait until all data transmitted
if (m_closeOnEmpty == false)
@@ -473,7 +511,7 @@
TcpSocketBase::ShutdownSend (void)
{
NS_LOG_FUNCTION (this);
-
+
//this prevents data from being added to the buffer
m_shutdownSend = true;
m_closeOnEmpty = true;
@@ -483,8 +521,8 @@
{
if (m_state == ESTABLISHED || m_state == CLOSE_WAIT)
{
- NS_LOG_INFO("Emtpy tx buffer, send fin");
- SendEmptyPacket (TcpHeader::FIN);
+ NS_LOG_INFO ("Emtpy tx buffer, send fin");
+ SendEmptyPacket (TcpHeader::FIN);
if (m_state == ESTABLISHED)
{ // On active close: I am the first one to send FIN
@@ -495,10 +533,10 @@
{ // On passive close: Peer sent me FIN already
NS_LOG_INFO ("CLOSE_WAIT -> LAST_ACK");
m_state = LAST_ACK;
- }
+ }
}
}
-
+
return 0;
}
@@ -830,8 +868,8 @@
void
TcpSocketBase::ForwardIcmp6 (Ipv6Address icmpSource, uint8_t icmpTtl,
- uint8_t icmpType, uint8_t icmpCode,
- uint32_t icmpInfo)
+ uint8_t icmpType, uint8_t icmpCode,
+ uint32_t icmpInfo)
{
NS_LOG_FUNCTION (this << icmpSource << (uint32_t)icmpTtl << (uint32_t)icmpType <<
(uint32_t)icmpCode << icmpInfo);
@@ -859,11 +897,17 @@
// Peel off TCP header and do validity checking
TcpHeader tcpHeader;
packet->RemoveHeader (tcpHeader);
- if (tcpHeader.GetFlags () & TcpHeader::ACK)
+
+ // Process all options appended to the incoming packet
+ if (tcpHeader.GetLength () * 4 > 20)
Peter Barnes 2013/12/26 21:25:45 It would be simpler just to call ProcessReceivedOp
+ {
+ ProcessReceivedOptions (tcpHeader);
+ }
+
+ if ((tcpHeader.GetFlags () & TcpHeader::ACK) == TcpHeader::ACK)
{
EstimateRtt (tcpHeader);
}
- ReadOptions (tcpHeader);
// Update Rx window size, i.e. the flow control window
if (m_rWnd.Get () == 0 && tcpHeader.GetWindowSize () != 0)
@@ -871,7 +915,18 @@
NS_LOG_LOGIC (this << " Leaving zerowindow persist state");
m_persistEvent.Cancel ();
}
- m_rWnd = tcpHeader.GetWindowSize ();
+
+ if (m_windScaleEnabled)
+ {
+ // Left shift the window by the scale factor
+ m_rWnd = tcpHeader.GetWindowSize () * std::pow (2, m_sndWindScale);
+ NS_LOG_LOGIC ("Receive window after scaling: " << m_rWnd);
+ }
+ else
+ {
+ m_rWnd = tcpHeader.GetWindowSize ();
+ NS_LOG_LOGIC ("Receive window when scaling is not used: " << m_rWnd);
+ }
// Discard fully out of range data packets
if (packet->GetSize ()
@@ -914,7 +969,10 @@
h.SetSourcePort (tcpHeader.GetDestinationPort ());
h.SetDestinationPort (tcpHeader.GetSourcePort ());
h.SetWindowSize (AdvertisedWindowSize ());
- AddOptions (h);
+ if (!m_optionList.empty ())
Peter Barnes 2013/12/26 21:25:45 It would be simpler just to call ProcessSentOption
+ {
+ ProcessSentOptions (h); // Append options to the outgoing packet's header
+ }
m_tcp->SendPacket (Create<Packet> (), h, header.GetDestination (), header.GetSource (), m_boundnetdevice);
}
break;
@@ -954,11 +1012,17 @@
// Peel off TCP header and do validity checking
TcpHeader tcpHeader;
packet->RemoveHeader (tcpHeader);
+
+ // Process all options appended to the incoming packet
+ if (tcpHeader.GetLength () * 4 > 20)
+ {
+ ProcessReceivedOptions (tcpHeader);
+ }
+
if (tcpHeader.GetFlags () & TcpHeader::ACK)
{
EstimateRtt (tcpHeader);
}
- ReadOptions (tcpHeader);
// Update Rx window size, i.e. the flow control window
if (m_rWnd.Get () == 0 && tcpHeader.GetWindowSize () != 0)
@@ -966,7 +1030,18 @@
NS_LOG_LOGIC (this << " Leaving zerowindow persist state");
m_persistEvent.Cancel ();
}
- m_rWnd = tcpHeader.GetWindowSize ();
+
+ if (m_windScaleEnabled)
+ {
+ // Left shift the window by the scale factor
+ m_rWnd = tcpHeader.GetWindowSize () * std::pow (2, m_sndWindScale);
+ NS_LOG_LOGIC ("Receive window after scaling: " << m_rWnd);
+ }
+ else
+ {
+ m_rWnd = tcpHeader.GetWindowSize ();
+ NS_LOG_LOGIC ("Receive window when scaling is not used: " << m_rWnd);
+ }
// Discard fully out of range packets
if (packet->GetSize ()
@@ -1009,7 +1084,10 @@
h.SetSourcePort (tcpHeader.GetDestinationPort ());
h.SetDestinationPort (tcpHeader.GetSourcePort ());
h.SetWindowSize (AdvertisedWindowSize ());
- AddOptions (h);
+ if (!m_optionList.empty ())
+ {
+ ProcessSentOptions (h); //Append options to the outgoing packet's header
+ }
m_tcp->SendPacket (Create<Packet> (), h, header.GetDestinationAddress (), header.GetSourceAddress (), m_boundnetdevice);
}
break;
@@ -1097,7 +1175,7 @@
}
else if (tcpHeader.GetAckNumber () == m_txBuffer.HeadSequence ())
{ // Case 2: Potentially a duplicated ACK
- if (tcpHeader.GetAckNumber () < m_nextTxSequence && packet->GetSize() == 0)
+ if (tcpHeader.GetAckNumber () < m_nextTxSequence && packet->GetSize () == 0)
{
NS_LOG_LOGIC ("Dupack of " << tcpHeader.GetAckNumber ());
DupAck (tcpHeader, ++m_dupAckCount);
@@ -1600,6 +1678,31 @@
NS_LOG_WARN ("Failed to send empty packet due to null endpoint");
return;
}
+
+ if (m_windScaleEnabled)
+ { //if window scale option is enabled
+ if ((flags == TcpHeader::SYN) || ((flags == (TcpHeader::SYN | TcpHeader::ACK)) && m_windScaleRcvd))
+ { //send the scale factor in
+ // 1. SYN packet or
+ // 2. SYN-ACK packet after receiving the remote host's scale factor in SYN
+ AddOption (3);
Peter Barnes 2013/12/26 21:25:45 TcpOption::KIND_WSOPT
+ }
+ }
+
+ if (m_tsEnabled)
+ { //if Timestamp option is enabled
+ if (flags == TcpHeader::SYN)
+ { //send the TSopt in SYN to initiate the Timestamp usage for the connection
+ AddOption (8);
Peter Barnes 2013/12/26 21:25:45 TcpOption::KIND_TSOPT
+ }
+ }
+
+ if (m_tsRcvd && (flags == TcpHeader::ACK))
+ {
+ AddOption (8);
Peter Barnes 2013/12/26 21:25:45 TcpOption::KIND_TSOPT
+ m_withEchoTS = true; //TSecr only valid when ACK bit is set
+ }
+
if (flags & TcpHeader::FIN)
{
flags |= TcpHeader::ACK;
@@ -1612,6 +1715,13 @@
header.SetFlags (flags);
header.SetSequenceNumber (s);
header.SetAckNumber (m_rxBuffer.NextRxSequence ());
+
+ // Update m_lastAckSent
+ if (flags & TcpHeader::ACK)
+ {
+ m_lastAckSent = m_rxBuffer.NextRxSequence ();
+ }
+
if (m_endPoint != 0)
{
header.SetSourcePort (m_endPoint->GetLocalPort ());
@@ -1623,7 +1733,12 @@
header.SetDestinationPort (m_endPoint6->GetPeerPort ());
}
header.SetWindowSize (AdvertisedWindowSize ());
- AddOptions (header);
+
+ if (!m_optionList.empty ())
+ {
+ ProcessSentOptions (header); // Append options to the outgoing empty packet's header
+
+ }
m_rto = m_rtt->RetransmitTimeout ();
bool hasSyn = flags & TcpHeader::SYN;
bool hasFin = flags & TcpHeader::FIN;
@@ -1801,8 +1916,10 @@
m_state = SYN_RCVD;
m_cnCount = m_cnRetries;
SetupCallback ();
+
// Set the sequence number and send SYN+ACK
m_rxBuffer.SetNextRxSequence (h.GetSequenceNumber () + SequenceNumber32 (1));
+
SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK);
}
@@ -1895,7 +2012,17 @@
header.SetDestinationPort (m_endPoint6->GetPeerPort ());
}
header.SetWindowSize (AdvertisedWindowSize ());
- AddOptions (header);
+
+ if (m_tsEnabled)
+ {
+ AddOption (8);
Peter Barnes 2013/12/26 21:25:45 TcpOption::KIND_TSOPT
+ m_withEchoTS = false;
+ }
+
+ if (!m_optionList.empty ())
+ {
+ ProcessSentOptions (header); // Append options to the outgoing data packet's header
+ }
if (m_retxEvent.IsExpired () )
{ // Schedule retransmit
m_rto = m_rtt->RetransmitTimeout ();
@@ -2011,6 +2138,7 @@
uint16_t
TcpSocketBase::AdvertisedWindowSize ()
{
+ NS_LOG_FUNCTION (this);
return std::min (m_rxBuffer.MaxBufferSize () - m_rxBuffer.Size (), (uint32_t)m_maxWinSize);
}
@@ -2023,6 +2151,11 @@
" ack " << tcpHeader.GetAckNumber () <<
" pkt size " << p->GetSize () );
+ // Update m_recentTS
+ if (tcpHeader.GetSequenceNumber () <= m_lastAckSent && m_lastAckSent < tcpHeader.GetSequenceNumber () + p->GetSize ())
+ {
+ m_recentTS = m_ts;
+ }
// Put into Rx buffer
SequenceNumber32 expectedSeq = m_rxBuffer.NextRxSequence ();
if (!m_rxBuffer.Add (p, tcpHeader))
@@ -2075,19 +2208,29 @@
void
TcpSocketBase::EstimateRtt (const TcpHeader& tcpHeader)
{
- // Use m_rtt for the estimation. Note, RTT of duplicated acknowledgement
- // (which should be ignored) is handled by m_rtt. Once timestamp option
- // is implemented, this function would be more elaborated.
- Time nextRtt = m_rtt->AckSeq (tcpHeader.GetAckNumber () );
+ NS_LOG_FUNCTION (this);
+ if (m_tsEnabled)
+ {
+ if (m_echoTS != 0)
+ {
+ m_lastRtt = m_rtt->EstimateRtt (m_echoTS);
+ }
+ }
+ else
+ {
+ // Use m_rtt for the estimation. Note, RTT of duplicated acknowledgement
+ // (which should be ignored) is handled by m_rtt. Once timestamp option
+ // is implemented, this function would be more elaborated.
+ Time nextRtt = m_rtt->AckSeq (tcpHeader.GetAckNumber () );
- //nextRtt will be zero for dup acks. Don't want to update lastRtt in that case
- //but still needed to do list clearing that is done in AckSeq.
- if(nextRtt != 0)
- {
- m_lastRtt = nextRtt;
- NS_LOG_FUNCTION(this << m_lastRtt);
- }
-
+ //nextRtt will be zero for dup acks. Don't want to update lastRtt in that case
+ //but still needed to do list clearing that is done in AckSeq.
+ if (nextRtt != 0)
+ {
+ m_lastRtt = nextRtt;
+ NS_LOG_FUNCTION (this << m_lastRtt);
+ }
+ }
}
// Called by the ReceivedAck() when new ACK received and by ProcessSynRcvd()
@@ -2210,8 +2353,11 @@
tcpHeader.SetSourcePort (m_endPoint6->GetLocalPort ());
tcpHeader.SetDestinationPort (m_endPoint6->GetPeerPort ());
}
- AddOptions (tcpHeader);
+ if (!m_optionList.empty ())
+ {
+ ProcessSentOptions (tcpHeader); // Append options to the outgoing packet's header
+ }
if (m_endPoint != 0)
{
m_tcp->SendPacket (p, tcpHeader, m_endPoint->GetLocalAddress (),
@@ -2418,16 +2564,119 @@
return false;
}
-/** Placeholder function for future extension that reads more from the TCP header */
void
-TcpSocketBase::ReadOptions (const TcpHeader&)
+TcpSocketBase::AddOption (uint8_t kind)
{
+ NS_LOG_FUNCTION (this << uint32_t (kind));
+ m_optionList.push_back (kind);
}
-/** Placeholder function for future extension that changes the TCP header */
void
-TcpSocketBase::AddOptions (TcpHeader&)
+TcpSocketBase::ProcessSentOptions (TcpHeader& header)
{
+ NS_LOG_FUNCTION (this);
+ for (std::list<uint8_t>::iterator i = m_optionList.begin (); i != m_optionList.end (); ++i)
+ {
+ switch (*i)
+ {
+ case 3:
Peter Barnes 2013/12/26 21:25:45 case TcpOption::KIND_WSOPT
+ SetOptionWinScale (header);
+ break;
+ case 8:
Peter Barnes 2013/12/26 21:25:45 case TcpOption::KIND_TSOPT
+ SetOptionTS (header);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Empty the option list
+ m_optionList.clear ();
+}
+
+void
+TcpSocketBase::ProcessReceivedOptions (const TcpHeader& header)
+{
+ NS_LOG_FUNCTION (this);
+ Ptr<TcpOption> option;
+ if (option = header.GetOption (3))
Peter Barnes 2013/12/26 21:25:45 TpcOption::KIND_WSOPT
+ {
+ GetOptionWinScale (option);
+ }
+ if (option = header.GetOption (8))
Peter Barnes 2013/12/26 21:25:45 TpcOption::KIND_TSOPT
+ {
+ GetOptionTS (option);
+ if (header.GetFlags () & TcpHeader::SYN)
+ { //if TSopt received in SYN, TSopt can be sent in other segments
+ m_tsRcvd = true;
+ }
+ }
+}
+
+void
+TcpSocketBase::SetOptionWinScale (TcpHeader& header)
+{
+ NS_LOG_FUNCTION (this);
+ // Create the option
+ Ptr<TcpOption> option;
+ option = option->CreateOption (3);
Peter Barnes 2013/12/26 21:25:45 TpcOption::KIND_WSOPT
+
+ // Set the scale factor
+ Ptr<TcpOptionWinScale> scale;
+ scale = DynamicCast<TcpOptionWinScale> (option);
+ scale->SetScale (m_rcvWindScale);
+
+ // Append the option to the header
+ bool isAppended = header.AppendOption (option);
+ if (!isAppended)
+ {
+ NS_LOG_LOGIC ("Option cannot be appended to the header");
+ }
+}
+
+void
+TcpSocketBase::SetOptionTS (TcpHeader& header)
+{
+ NS_LOG_FUNCTION (this);
+ // Create the option
+ Ptr<TcpOption> option;
+ option = option->CreateOption (8);
Peter Barnes 2013/12/26 21:25:45 TpcOption::KIND_TSOPT
+
+ // Set the TS values
+ Ptr<TcpOptionTS> timestamp;
+ timestamp = DynamicCast<TcpOptionTS> (option);
+ timestamp->SetTimestamp ((uint32_t)Simulator::Now ().GetDouble ());
+ if (m_withEchoTS)
+ { //when ACK bit is set
+ timestamp->SetEcho (m_recentTS);
+ }
+ else
+ { //when ACK bit is not set
+ timestamp->SetEcho (0);
+ }
+
+ // Append the option to the header
+ bool isAppended = header.AppendOption (option);
+ if (!isAppended)
+ {
+ NS_LOG_LOGIC ("Option cannot be appended to the header");
+ }
+}
+
+void
+TcpSocketBase::GetOptionWinScale (Ptr<TcpOption> option)
+{
+ NS_LOG_FUNCTION (this);
+ m_sndWindScale = DynamicCast<TcpOptionWinScale> (option)->GetScale ();
+ m_windScaleRcvd = true;
+}
+
+void
+TcpSocketBase::GetOptionTS (Ptr<TcpOption> option)
+{
+ NS_LOG_FUNCTION (this);
+ m_ts = DynamicCast<TcpOptionTS> (option)->GetTimestamp ();
+ m_echoTS = DynamicCast<TcpOptionTS> (option)->GetEcho ();
}
} // namespace ns3

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b