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 |