Index: src/internet/model/tcp-socket-base.cc |
=================================================================== |
--- a/src/internet/model/tcp-socket-base.cc |
+++ b/src/internet/model/tcp-socket-base.cc |
@@ -135,6 +135,10 @@ |
BooleanValue (true), |
MakeBooleanAccessor (&TcpSocketBase::m_limitedTx), |
MakeBooleanChecker ()) |
+ .AddAttribute ("UseEcn", "True to use ECN functionality", |
+ BooleanValue (false), |
+ MakeBooleanAccessor (&TcpSocketBase::m_ecn), |
+ MakeBooleanChecker ()) |
.AddTraceSource ("RTO", |
"Retransmission timeout", |
MakeTraceSourceAccessor (&TcpSocketBase::m_rto), |
@@ -159,6 +163,10 @@ |
"TCP Congestion machine state", |
MakeTraceSourceAccessor (&TcpSocketBase::m_congStateTrace), |
"ns3::TcpSocketState::TcpCongStatesTracedValueCallback") |
+ .AddTraceSource ("EcnState", |
+ "Current ECN State of TCP Socket", |
+ MakeTraceSourceAccessor (&TcpSocketBase::m_ecnStateTrace), |
+ "ns3::TcpSocketState::EcnStatesTracedValueCallback") |
.AddTraceSource ("AdvWND", |
"Advertised Window Size", |
MakeTraceSourceAccessor (&TcpSocketBase::m_advWnd), |
@@ -195,6 +203,18 @@ |
"Receive tcp packet from IP protocol", |
MakeTraceSourceAccessor (&TcpSocketBase::m_rxTrace), |
"ns3::TcpSocketBase::TcpTxRxTracedCallback") |
+ .AddTraceSource ("EcnEchoSeq", |
+ "Sequence of last received ECN Echo", |
+ MakeTraceSourceAccessor (&TcpSocketBase::m_ecnEchoSeq), |
+ "ns3::SequenceNumber32TracedValueCallback") |
+ .AddTraceSource ("EcnCESeq", |
+ "Sequence of last received CE ", |
+ MakeTraceSourceAccessor (&TcpSocketBase::m_ecnCESeq), |
+ "ns3::SequenceNumber32TracedValueCallback") |
+ .AddTraceSource ("EcnCWRSeq", |
+ "Sequence of last received CWR", |
+ MakeTraceSourceAccessor (&TcpSocketBase::m_ecnCWRSeq), |
+ "ns3::SequenceNumber32TracedValueCallback") |
; |
return tid; |
} |
@@ -225,6 +245,10 @@ |
"TCP Congestion machine state", |
MakeTraceSourceAccessor (&TcpSocketState::m_congState), |
"ns3::TracedValue::TcpCongStatesTracedValueCallback") |
+ .AddTraceSource ("EcnState", |
+ "Current ECN State of TCP Socket", |
+ MakeTraceSourceAccessor (&TcpSocketState::m_ecnState), |
+ "ns3::TracedValue::EcnStatesTracedValueCallback") |
.AddTraceSource ("HighestSequence", |
"Highest sequence number received from peer", |
MakeTraceSourceAccessor (&TcpSocketState::m_highTxMark), |
@@ -246,6 +270,7 @@ |
m_segmentSize (0), |
m_lastAckedSeq (0), |
m_congState (CA_OPEN), |
+ m_ecnState (ECN_DISABLED), |
m_highTxMark (0), |
// Change m_nextTxSequence for non-zero initial sequence number |
m_nextTxSequence (0), |
@@ -263,6 +288,7 @@ |
m_segmentSize (other.m_segmentSize), |
m_lastAckedSeq (other.m_lastAckedSeq), |
m_congState (other.m_congState), |
+ m_ecnState (other.m_ecnState), |
m_highTxMark (other.m_highTxMark), |
m_nextTxSequence (other.m_nextTxSequence), |
m_rcvTimestampValue (other.m_rcvTimestampValue), |
@@ -276,6 +302,13 @@ |
"CA_OPEN", "CA_DISORDER", "CA_CWR", "CA_RECOVERY", "CA_LOSS" |
}; |
+const char* const |
+TcpSocketState::EcnStateName[TcpSocketState::ECN_CWR_SENT+1] = |
+{ |
+ "ECN_DISABLED", "ECN_IDLE", "ECN_CE_RCVD", "ECN_ECE_SENT", "ECN_ECE_RCVD", "ECN_CWR_SENT" |
+}; |
+ |
+ |
TcpSocketBase::TcpSocketBase (void) |
: TcpSocket (), |
m_retxEvent (), |
@@ -332,7 +365,11 @@ |
m_retxThresh (3), |
m_limitedTx (false), |
m_congestionControl (0), |
- m_isFirstPartialAck (true) |
+ m_isFirstPartialAck (true), |
+ m_ecn(false), |
+ m_ecnEchoSeq (0), |
+ m_ecnCESeq (0), |
+ m_ecnCWRSeq (0) |
{ |
NS_LOG_FUNCTION (this); |
m_rxBuffer = CreateObject<TcpRxBuffer> (); |
@@ -353,6 +390,10 @@ |
MakeCallback (&TcpSocketBase::UpdateCongState, this)); |
NS_ASSERT (ok == true); |
+ ok = m_tcb->TraceConnectWithoutContext ("EcnState", |
+ MakeCallback (&TcpSocketBase::UpdateEcnState, this)); |
+ NS_ASSERT (ok == true); |
+ |
ok = m_tcb->TraceConnectWithoutContext ("NextTxSequence", |
MakeCallback (&TcpSocketBase::UpdateNextTxSequence, this)); |
NS_ASSERT (ok == true); |
@@ -409,7 +450,10 @@ |
m_limitedTx (sock.m_limitedTx), |
m_isFirstPartialAck (sock.m_isFirstPartialAck), |
m_txTrace (sock.m_txTrace), |
- m_rxTrace (sock.m_rxTrace) |
+ m_rxTrace (sock.m_rxTrace), |
+ m_ecn (sock.m_ecn), |
+ m_ecnEchoSeq (sock.m_ecnEchoSeq), |
+ m_ecnCWRSeq (sock.m_ecnCWRSeq) |
{ |
NS_LOG_FUNCTION (this); |
NS_LOG_LOGIC ("Invoked the copy constructor"); |
@@ -448,6 +492,10 @@ |
MakeCallback (&TcpSocketBase::UpdateCongState, this)); |
NS_ASSERT (ok == true); |
+ ok = m_tcb->TraceConnectWithoutContext ("EcnState", |
+ MakeCallback (&TcpSocketBase::UpdateEcnState, this)); |
+ NS_ASSERT (ok == true); |
+ |
ok = m_tcb->TraceConnectWithoutContext ("NextTxSequence", |
MakeCallback (&TcpSocketBase::UpdateNextTxSequence, this)); |
NS_ASSERT (ok == true); |
@@ -1051,9 +1099,18 @@ |
// A new connection is allowed only if this socket does not have a connection |
if (m_state == CLOSED || m_state == LISTEN || m_state == SYN_SENT || m_state == LAST_ACK || m_state == CLOSE_WAIT) |
{ // send a SYN packet and change state into SYN_SENT |
- SendEmptyPacket (TcpHeader::SYN); |
+ // send a SYN packet with ECE and CWR flags set if sender is ECN capable |
+ if (m_ecn) |
+ { |
+ SendEmptyPacket (TcpHeader::SYN | TcpHeader::ECE | TcpHeader::CWR); |
+ } |
+ else |
+ { |
+ SendEmptyPacket (TcpHeader::SYN); |
+ } |
NS_LOG_DEBUG (TcpStateName[m_state] << " -> SYN_SENT"); |
m_state = SYN_SENT; |
+ m_tcb->m_ecnState = TcpSocketState::ECN_DISABLED; // because sender is not yet aware about receiver's ECN capability |
} |
else if (m_state != TIME_WAIT) |
{ // In states SYN_RCVD, ESTABLISHED, FIN_WAIT_1, FIN_WAIT_2, and CLOSING, an connection |
@@ -1160,7 +1217,15 @@ |
Address fromAddress = InetSocketAddress (header.GetSource (), port); |
Address toAddress = InetSocketAddress (header.GetDestination (), |
m_endPoint->GetLocalPort ()); |
- |
+ TcpHeader tcpHeader; |
+ packet->PeekHeader (tcpHeader); |
+ if (header.GetEcn() == Ipv4Header::ECN_CE && m_ecnCESeq < tcpHeader.GetAckNumber ()) |
+ { |
+ NS_LOG_INFO ("Received CE flag is valid"); |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_CE_RCVD"); |
+ m_ecnCESeq = tcpHeader.GetAckNumber (); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_CE_RCVD; |
+ } |
DoForwardUp (packet, fromAddress, toAddress); |
} |
@@ -1178,6 +1243,15 @@ |
Address toAddress = Inet6SocketAddress (header.GetDestinationAddress (), |
m_endPoint6->GetLocalPort ()); |
+ TcpHeader tcpHeader; |
+ packet->PeekHeader (tcpHeader); |
+ if (header.GetEcn() == Ipv6Header::ECN_CE && m_ecnCESeq < tcpHeader.GetAckNumber ()) |
+ { |
+ NS_LOG_INFO ("Received CE flag is valid"); |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_CE_RCVD"); |
+ m_ecnCESeq = tcpHeader.GetAckNumber (); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_CE_RCVD; |
+ } |
DoForwardUp (packet, fromAddress, toAddress); |
} |
@@ -1235,7 +1309,29 @@ |
// Acknowledgement should be sent for all unacceptable packets (RFC793, p.69) |
if (m_state == ESTABLISHED && !(tcpHeader.GetFlags () & TcpHeader::RST)) |
{ |
- SendEmptyPacket (TcpHeader::ACK); |
+ // Check if the sender has responded to ECN echo by reducing the Congestion Window |
+ if (tcpHeader.GetFlags () & TcpHeader::CWR ) |
+ { |
+ // Check if a packet with CE bit set is received. If there is no CE bit set, then change the state to ECN_IDLE to |
+ // stop sending ECN Echo messages. If there is CE bit set, the packet should continue sending ECN Echo messages |
+ // |
+ if (m_tcb->m_ecnState != TcpSocketState::ECN_CE_RCVD) |
+ { |
+ m_tcb->m_ecnState = TcpSocketState::ECN_IDLE; |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_IDLE"); |
+ } |
+ } |
+ if (m_tcb->m_ecnState == TcpSocketState::ECN_CE_RCVD || m_tcb->m_ecnState == TcpSocketState::ECN_ECE_SENT) |
+ { |
+ // Receiver sets ECE flags when it receives a packet with CE bit on or sender hasn’t responded to ECN echo sent by receiver |
+ SendEmptyPacket (TcpHeader::ACK | TcpHeader::ECE); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_ECE_SENT; |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_ECE_SENT"); |
+ } |
+ else |
+ { |
+ SendEmptyPacket (TcpHeader::ACK); |
+ } |
} |
return; |
} |
@@ -1416,8 +1512,8 @@ |
{ |
NS_LOG_FUNCTION (this << tcpHeader); |
- // Extract the flags. PSH and URG are not honoured. |
- uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); |
+ // Extract the flags. PSH, URG, CWR and ECE are not honoured. |
+ uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG | TcpHeader::CWR | TcpHeader::ECE); |
// Different flags are different events |
if (tcpflags == TcpHeader::ACK) |
@@ -1439,7 +1535,17 @@ |
NS_LOG_WARN ("Ignored ack of " << tcpHeader.GetAckNumber () << |
" HighTxMark = " << m_tcb->m_highTxMark); |
+ // Receiver sets ECE flags when it receives a packet with CE bit on or sender hasn’t responded to ECN echo sent by receiver |
+ if (m_tcb->m_ecnState == TcpSocketState::ECN_CE_RCVD || m_tcb->m_ecnState == TcpSocketState::ECN_ECE_SENT) |
+ { |
+ SendEmptyPacket (TcpHeader::ACK | TcpHeader::ECE); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_ECE_SENT; |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_ECE_SENT"); |
+ } |
+ else |
+ { |
SendEmptyPacket (TcpHeader::ACK); |
+ } |
} |
else |
{ |
@@ -1656,6 +1762,17 @@ |
SequenceNumber32 ackNumber = tcpHeader.GetAckNumber (); |
+ if (ackNumber > m_txBuffer->HeadSequence () && (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED) && (tcpHeader.GetFlags () & TcpHeader::ECE)) |
+ { |
+ if (m_ecnEchoSeq < tcpHeader.GetAckNumber ()) |
+ { |
+ NS_LOG_INFO ("Received ECN Echo is valid"); |
+ m_ecnEchoSeq = tcpHeader.GetAckNumber (); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_ECE_RCVD; |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_ECE_RCVD"); |
+ } |
+ } |
+ |
// RFC 6675 Section 5: 2nd, 3rd paragraph and point (A), (B) implementation |
// are inside the function ProcessAck |
ProcessAck (ackNumber, scoreboardUpdated); |
@@ -1888,8 +2005,8 @@ |
{ |
NS_LOG_FUNCTION (this << tcpHeader); |
- // Extract the flags. PSH and URG are not honoured. |
- uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); |
+ // Extract the flags. PSH, URG, CWR and ECE are not honoured. |
+ uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG | TcpHeader::CWR | TcpHeader::ECE); |
// Fork a socket if received a SYN. Do nothing otherwise. |
// C.f.: the LISTEN part in tcp_v4_do_rcv() in tcp_ipv4.c in Linux kernel |
@@ -1917,8 +2034,8 @@ |
{ |
NS_LOG_FUNCTION (this << tcpHeader); |
- // Extract the flags. PSH and URG are not honoured. |
- uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); |
+ // Extract the flags. PSH, URG, CWR and ECE are not honoured. |
+ uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG | TcpHeader::CWR | TcpHeader::ECE); |
if (tcpflags == 0) |
{ // Bare data, accept it and move to ESTABLISHED state. This is not a normal behaviour. Remove this? |
@@ -1940,7 +2057,22 @@ |
m_state = SYN_RCVD; |
m_synCount = m_synRetries; |
m_rxBuffer->SetNextRxSequence (tcpHeader.GetSequenceNumber () + SequenceNumber32 (1)); |
- SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK); |
+ |
+ /* Check if we recieved an ECN SYN packet. Change the ECN state of receiver to ECN_IDLE if the traffic is ECN capable and |
+ * sender has sent ECN SYN packet |
+ */ |
+ if (m_ecn && (tcpHeader.GetFlags () & (TcpHeader::CWR | TcpHeader::ECE)) == (TcpHeader::CWR | TcpHeader::ECE)) |
+ { |
+ NS_LOG_INFO ("Received ECN SYN packet"); |
+ SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK | TcpHeader::ECE); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_IDLE; |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_IDLE"); |
+ } |
+ else |
+ { |
+ m_tcb->m_ecnState = TcpSocketState::ECN_DISABLED; |
+ SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK); |
+ } |
} |
else if (tcpflags == (TcpHeader::SYN | TcpHeader::ACK) |
&& m_tcb->m_nextTxSequence + SequenceNumber32 (1) == tcpHeader.GetAckNumber ()) |
@@ -1954,6 +2086,21 @@ |
m_tcb->m_highTxMark = ++m_tcb->m_nextTxSequence; |
m_txBuffer->SetHeadSequence (m_tcb->m_nextTxSequence); |
SendEmptyPacket (TcpHeader::ACK); |
+ |
+ /* Check if we received an ECN SYN-ACK packet. Change the ECN state of sender to ECN_IDLE if receiver has sent an ECN SYN-ACK |
+ * packet and the traffic is ECN Capable |
+ */ |
+ if (m_ecn && (tcpHeader.GetFlags () & (TcpHeader::CWR | TcpHeader::ECE)) == (TcpHeader::ECE)) |
+ { |
+ NS_LOG_INFO ("Received ECN SYN-ACK packet."); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_IDLE; |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_IDLE"); |
+ } |
+ else |
+ { |
+ m_tcb->m_ecnState = TcpSocketState::ECN_DISABLED; |
+ } |
+ |
SendPendingData (m_connected); |
Simulator::ScheduleNow (&TcpSocketBase::ConnectionSucceeded, this); |
// Always respond to first data packet to speed up the connection. |
@@ -1979,8 +2126,8 @@ |
{ |
NS_LOG_FUNCTION (this << tcpHeader); |
- // Extract the flags. PSH and URG are not honoured. |
- uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); |
+ // Extract the flags. PSH, URG, CWR and ECE are not honoured. |
+ uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG | TcpHeader::CWR | TcpHeader::ECE); |
if (tcpflags == 0 |
|| (tcpflags == TcpHeader::ACK |
@@ -2019,7 +2166,22 @@ |
else if (tcpflags == TcpHeader::SYN) |
{ // Probably the peer lost my SYN+ACK |
m_rxBuffer->SetNextRxSequence (tcpHeader.GetSequenceNumber () + SequenceNumber32 (1)); |
- SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK); |
+ |
+ /* Check if we received an ECN SYN packet. Change the ECN state of receiver to ECN_IDLE if sender has sent an ECN SYN |
+ * packet and the traffic is ECN Capable |
+ */ |
+ if (m_ecn && (tcpHeader.GetFlags () & (TcpHeader::CWR | TcpHeader::ECE)) == (TcpHeader::CWR | TcpHeader::ECE)) |
+ { |
+ NS_LOG_INFO ("Received ECN SYN packet"); |
+ SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK |TcpHeader::ECE); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_IDLE; |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_IDLE"); |
+ } |
+ else |
+ { |
+ m_tcb->m_ecnState = TcpSocketState::ECN_DISABLED; |
+ SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK); |
+ } |
} |
else if (tcpflags == (TcpHeader::FIN | TcpHeader::ACK)) |
{ |
@@ -2070,8 +2232,8 @@ |
{ |
NS_LOG_FUNCTION (this << tcpHeader); |
- // Extract the flags. PSH and URG are not honoured. |
- uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); |
+ // Extract the flags. PSH, URG, CWR and ECE are not honoured. |
+ uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG | TcpHeader::CWR | TcpHeader::ECE); |
if (packet->GetSize () > 0 && tcpflags != TcpHeader::ACK) |
{ // Bare data, accept it |
@@ -2142,8 +2304,8 @@ |
{ |
NS_LOG_FUNCTION (this << tcpHeader); |
- // Extract the flags. PSH and URG are not honoured. |
- uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); |
+ // Extract the flags. PSH, URG, CWR and ECE are not honoured. |
+ uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG | TcpHeader::CWR | TcpHeader::ECE); |
if (tcpflags == TcpHeader::ACK) |
{ |
@@ -2174,8 +2336,8 @@ |
{ |
NS_LOG_FUNCTION (this << tcpHeader); |
- // Extract the flags. PSH and URG are not honoured. |
- uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); |
+ // Extract the flags. PSH, URG, CWR and ECE are not honoured. |
+ uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG | TcpHeader::CWR | TcpHeader::ECE); |
if (tcpflags == 0) |
{ |
@@ -2602,7 +2764,20 @@ |
// Set the sequence number and send SYN+ACK |
m_rxBuffer->SetNextRxSequence (h.GetSequenceNumber () + SequenceNumber32 (1)); |
- SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK); |
+ /* Check if we received an ECN SYN packet. Change the ECN state of receiver to ECN_IDLE if sender has sent an ECN SYN |
+ * packet and the traffic is ECN Capable |
+ */ |
+ if (m_ecn && (h.GetFlags () & (TcpHeader::CWR | TcpHeader::ECE)) == (TcpHeader::CWR | TcpHeader::ECE)) |
+ { |
+ SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK | TcpHeader::ECE); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_IDLE; |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_IDLE"); |
+ } |
+ else |
+ { |
+ SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_DISABLED; |
+ } |
} |
void |
@@ -2643,6 +2818,25 @@ |
m_delAckCount = 0; |
} |
+ // Sender should reduce the Congestion Window as a response to receiver's ECN Echo notification only once per window |
+ if (m_tcb->m_ecnState == TcpSocketState::ECN_ECE_RCVD && m_ecnEchoSeq.Get() > m_ecnCWRSeq.Get() && !isRetransmission ) |
+ { |
+ NS_LOG_INFO ("Backoff mechanism by reducing CWND by half because we've received ECN Echo"); |
+ m_tcb->m_ssThresh = m_congestionControl->GetSsThresh (m_tcb, BytesInFlight ()); |
+ m_tcb->m_cWnd = std::max ((uint32_t)m_tcb->m_cWnd/2, m_tcb->m_segmentSize); |
+ flags |= TcpHeader::CWR; |
+ m_ecnCWRSeq = seq; |
+ m_tcb->m_ecnState = TcpSocketState::ECN_CWR_SENT; |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_CWR_SENT"); |
+ NS_LOG_INFO ("CWR flags set"); |
+ NS_LOG_DEBUG (TcpSocketState::TcpCongStateName[m_tcb->m_congState] << " -> CA_CWR"); |
+ if (m_tcb->m_congState == TcpSocketState::CA_OPEN) |
+ { |
+ m_congestionControl->CongestionStateSet (m_tcb, TcpSocketState::CA_CWR); |
+ m_tcb->m_congState = TcpSocketState::CA_CWR; |
+ } |
+ } |
+ |
/* |
* Add tags for each socket option. |
* Note that currently the socket adds both IPv4 tag and IPv6 tag |
@@ -2652,16 +2846,49 @@ |
if (GetIpTos ()) |
{ |
SocketIpTosTag ipTosTag; |
- ipTosTag.SetTos (GetIpTos ()); |
+ NS_LOG_LOGIC (" ECT bits should not be set on retransmitted packets "); |
+ if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && (GetIpTos () & 0x3) == 0 && !isRetransmission) |
+ { |
+ ipTosTag.SetTos (GetIpTos () | 0x2); |
+ } |
+ else |
+ { |
+ ipTosTag.SetTos (GetIpTos ()); |
+ } |
p->AddPacketTag (ipTosTag); |
} |
+ else |
+ { |
+ if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && !isRetransmission) |
+ { |
+ SocketIpTosTag ipTosTag; |
+ ipTosTag.SetTos (0x02); |
+ p->AddPacketTag (ipTosTag); |
+ } |
+ } |
if (IsManualIpv6Tclass ()) |
{ |
SocketIpv6TclassTag ipTclassTag; |
- ipTclassTag.SetTclass (GetIpv6Tclass ()); |
+ if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && (GetIpv6Tclass () & 0x3) == 0 && !isRetransmission) |
+ { |
+ ipTclassTag.SetTclass (GetIpv6Tclass () | 0x2); |
+ } |
+ else |
+ { |
+ ipTclassTag.SetTclass (GetIpv6Tclass ()); |
+ } |
p->AddPacketTag (ipTclassTag); |
} |
+ else |
+ { |
+ if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && !isRetransmission) |
+ { |
+ SocketIpv6TclassTag ipTclassTag; |
+ ipTclassTag.SetTclass (0x02); |
+ p->AddPacketTag (ipTclassTag); |
+ } |
+ } |
if (IsManualIpTtl ()) |
{ |
@@ -2998,7 +3225,16 @@ |
SequenceNumber32 expectedSeq = m_rxBuffer->NextRxSequence (); |
if (!m_rxBuffer->Add (p, tcpHeader)) |
{ // Insert failed: No data or RX buffer full |
- SendEmptyPacket (TcpHeader::ACK); |
+ if (m_tcb->m_ecnState == TcpSocketState::ECN_CE_RCVD || m_tcb->m_ecnState == TcpSocketState::ECN_ECE_SENT) |
+ { |
+ SendEmptyPacket (TcpHeader::ACK | TcpHeader::ECE); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_ECE_SENT; |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_ECE_SENT"); |
+ } |
+ else |
+ { |
+ SendEmptyPacket (TcpHeader::ACK); |
+ } |
return; |
} |
// Notify app to receive if necessary |
@@ -3024,7 +3260,16 @@ |
// Now send a new ACK packet acknowledging all received and delivered data |
if (m_rxBuffer->Size () > m_rxBuffer->Available () || m_rxBuffer->NextRxSequence () > expectedSeq + p->GetSize ()) |
{ // A gap exists in the buffer, or we filled a gap: Always ACK |
- SendEmptyPacket (TcpHeader::ACK); |
+ if (m_tcb->m_ecnState == TcpSocketState::ECN_CE_RCVD || m_tcb->m_ecnState == TcpSocketState::ECN_ECE_SENT) |
+ { |
+ SendEmptyPacket (TcpHeader::ACK | TcpHeader::ECE); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_ECE_SENT; |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_ECE_SENT"); |
+ } |
+ else |
+ { |
+ SendEmptyPacket (TcpHeader::ACK); |
+ } |
} |
else |
{ // In-sequence packet: ACK if delayed ack count allows |
@@ -3032,7 +3277,16 @@ |
{ |
m_delAckEvent.Cancel (); |
m_delAckCount = 0; |
- SendEmptyPacket (TcpHeader::ACK); |
+ if (m_tcb->m_ecnState == TcpSocketState::ECN_CE_RCVD || m_tcb->m_ecnState == TcpSocketState::ECN_ECE_SENT) |
+ { |
+ SendEmptyPacket (TcpHeader::ACK | TcpHeader::ECE); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_ECE_SENT; |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_ECE_SENT"); |
+ } |
+ else |
+ { |
+ SendEmptyPacket (TcpHeader::ACK); |
+ } |
} |
else if (m_delAckEvent.IsExpired ()) |
{ |
@@ -3242,7 +3496,15 @@ |
TcpSocketBase::DelAckTimeout (void) |
{ |
m_delAckCount = 0; |
- SendEmptyPacket (TcpHeader::ACK); |
+ if (m_tcb->m_ecnState == TcpSocketState::ECN_CE_RCVD || m_tcb->m_ecnState == TcpSocketState::ECN_ECE_SENT) |
+ { |
+ SendEmptyPacket (TcpHeader::ACK | TcpHeader::ECE); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_ECE_SENT; |
+ } |
+ else |
+ { |
+ SendEmptyPacket (TcpHeader::ACK); |
+ } |
} |
void |
@@ -3286,7 +3548,17 @@ |
tcpHeader.SetDestinationPort (m_endPoint6->GetPeerPort ()); |
} |
AddOptions (tcpHeader); |
- |
+ //Send a packet tag for setting ECT bits in IP header |
+ if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED) |
+ { |
+ SocketIpTosTag ipTosTag; |
+ ipTosTag.SetTos (0x02); |
+ p->AddPacketTag (ipTosTag); |
+ |
+ SocketIpv6TclassTag ipTclassTag; |
+ ipTclassTag.SetTclass (0x02); |
+ p->AddPacketTag (ipTclassTag); |
+ } |
m_txTrace (p, tcpHeader, this); |
if (m_endPoint != 0) |
@@ -3315,7 +3587,15 @@ |
{ |
if (m_synCount > 0) |
{ |
- SendEmptyPacket (TcpHeader::SYN); |
+ if (m_ecn) |
+ { |
+ SendEmptyPacket (TcpHeader::SYN | TcpHeader::ECE | TcpHeader::CWR); |
+ } |
+ else |
+ { |
+ SendEmptyPacket (TcpHeader::SYN); |
+ } |
+ m_tcb->m_ecnState = TcpSocketState::ECN_DISABLED; |
} |
else |
{ |
@@ -3416,7 +3696,16 @@ |
*/ |
if (oldSize < size && m_connected) |
{ |
- SendEmptyPacket (TcpHeader::ACK); |
+ if (m_tcb->m_ecnState == TcpSocketState::ECN_CE_RCVD || m_tcb->m_ecnState == TcpSocketState::ECN_ECE_SENT) |
+ { |
+ SendEmptyPacket (TcpHeader::ACK | TcpHeader::ECE); |
+ m_tcb->m_ecnState = TcpSocketState::ECN_ECE_SENT; |
+ NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_ECE_SENT"); |
+ } |
+ else |
+ { |
+ SendEmptyPacket (TcpHeader::ACK); |
+ } |
} |
} |
@@ -3821,6 +4110,13 @@ |
} |
void |
+TcpSocketBase::UpdateEcnState (TcpSocketState::EcnState_t oldValue, |
+ TcpSocketState::EcnState_t newValue) |
+{ |
+ m_ecnStateTrace (oldValue, newValue); |
+} |
+ |
+void |
TcpSocketBase::UpdateNextTxSequence (SequenceNumber32 oldValue, |
SequenceNumber32 newValue) |
@@ -3858,6 +4154,12 @@ |
return 0; |
} |
+void |
+TcpSocketBase::SetEcn() |
+{ |
+ m_ecn = true; |
+} |
+ |
//RttHistory methods |
RttHistory::RttHistory (SequenceNumber32 s, uint32_t c, Time t) |
: seq (s), |