@@ -708,12 +708,6 @@ INET_ERROR TCPEndPoint::Send(PacketBuffer *data, bool push)
708708
709709#if WEAVE_SYSTEM_CONFIG_USE_LWIP
710710
711- if (mUnsentQueue == NULL )
712- {
713- mUnsentQueue = data;
714- mUnsentOffset = 0 ;
715- }
716-
717711#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
718712 if (!mUserTimeoutTimerRunning )
719713 {
@@ -1199,6 +1193,10 @@ void TCPEndPoint::Init(InetLayer *inetLayer)
11991193#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
12001194
12011195#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
1196+
1197+ #if WEAVE_SYSTEM_CONFIG_USE_LWIP
1198+ mUnackedLength = 0 ;
1199+ #endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
12021200}
12031201
12041202INET_ERROR TCPEndPoint::DriveSending ()
@@ -1219,44 +1217,60 @@ INET_ERROR TCPEndPoint::DriveSending()
12191217 uint16_t sendWindowSize = tcp_sndbuf (mTCP );
12201218
12211219 // If there's data to be sent and the send window is open...
1222- bool canSend = (mUnsentQueue != NULL && sendWindowSize > 0 );
1220+ bool canSend = (RemainingToSend () > 0 && sendWindowSize > 0 );
12231221 if (canSend)
12241222 {
1223+ // Find first packet buffer with remaining data to send by skipping
1224+ // all sent but un-acked data.
1225+ TCPEndPoint::BufferOffset startOfUnsent = FindStartOfUnsent ();
1226+ const Weave::System::PacketBuffer* currentUnsentBuf = startOfUnsent.buffer ;
1227+ uint16_t unsentOffset = startOfUnsent.offset ;
1228+
12251229 // While there's data to be sent and a window to send it in...
12261230 do
12271231 {
1228- uint16_t bufDataLen = mUnsentQueue ->DataLength ();
1232+ VerifyOrDie (currentUnsentBuf != NULL );
1233+
1234+ uint16_t bufDataLen = currentUnsentBuf->DataLength ();
12291235
12301236 // Get a pointer to the start of unsent data within the first buffer on the unsent queue.
1231- uint8_t *sendData = mUnsentQueue ->Start () + mUnsentOffset ;
1237+ const uint8_t *sendData = currentUnsentBuf ->Start () + unsentOffset ;
12321238
12331239 // Determine the amount of data to send from the current buffer.
1234- uint16_t sendLen = bufDataLen - mUnsentOffset ;
1240+ uint16_t sendLen = bufDataLen - unsentOffset ;
12351241 if (sendLen > sendWindowSize)
12361242 sendLen = sendWindowSize;
12371243
1238- // Adjust the unsent data offset by the length of data to be written. If the entire buffer
1239- // has been sent advance to the next one.
1240- mUnsentOffset += sendLen;
1241- if (mUnsentOffset == bufDataLen)
1242- {
1243- mUnsentQueue = mUnsentQueue ->Next ();
1244- mUnsentOffset = 0 ;
1245- }
1246-
1247- // Adjust the remaining window size.
1248- sendWindowSize -= sendLen;
1249-
1250- // Determine if there's more data to be sent after this buffer.
1251- canSend = (mUnsentQueue != NULL && sendWindowSize > 0 );
1252-
12531244 // Call LwIP to queue the data to be sent, telling it if there's more data to come.
1245+ // Data is queued in-place as a reference within the source packet buffer. It is
1246+ // critical that the underlying packet buffer not be freed until the data
1247+ // is acknowledged, otherwise retransmissions could use an invalid
1248+ // backing. Using TCP_WRITE_FLAG_COPY would eliminate this requirement, but overall
1249+ // requires many more memory allocations which may be problematic when very
1250+ // memory-constrained or when using pool-based allocations.
12541251 lwipErr = tcp_write (mTCP , sendData, sendLen, (canSend) ? TCP_WRITE_FLAG_MORE : 0 );
12551252 if (lwipErr != ERR_OK)
12561253 {
12571254 err = Weave::System::MapErrorLwIP (lwipErr);
12581255 break ;
12591256 }
1257+ // Start accounting for the data sent as yet-to-be-acked.
1258+ mUnackedLength += sendLen;
1259+
1260+ // Adjust the unsent data offset by the length of data that was written.
1261+ // If the entire buffer has been sent advance to the next one.
1262+ unsentOffset += sendLen;
1263+ if (unsentOffset == bufDataLen)
1264+ {
1265+ currentUnsentBuf = currentUnsentBuf->Next ();
1266+ unsentOffset = 0 ;
1267+ }
1268+
1269+ // Adjust the remaining window size.
1270+ sendWindowSize -= sendLen;
1271+
1272+ // Determine if there's more data to be sent after this buffer.
1273+ canSend = (RemainingToSend () > 0 && sendWindowSize > 0 );
12601274 } while (canSend);
12611275
12621276 // Call LwIP to send the queued data.
@@ -1274,7 +1288,7 @@ INET_ERROR TCPEndPoint::DriveSending()
12741288 if (err == INET_NO_ERROR)
12751289 {
12761290 // If in the SendShutdown state and the unsent queue is now empty, shutdown the PCB for sending.
1277- if (State == kState_SendShutdown && mUnsentQueue == NULL )
1291+ if (State == kState_SendShutdown && ( RemainingToSend () == 0 ) )
12781292 {
12791293 lwipErr = tcp_shutdown (mTCP , 0 , 1 );
12801294 if (lwipErr != ERR_OK)
@@ -1571,6 +1585,9 @@ INET_ERROR TCPEndPoint::DoClose(INET_ERROR err, bool suppressCallback)
15711585 mSendQueue = NULL ;
15721586 PacketBuffer::Free (mRcvQueue );
15731587 mRcvQueue = NULL ;
1588+ #if WEAVE_SYSTEM_CONFIG_USE_LWIP
1589+ mUnackedLength = 0 ;
1590+ #endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
15741591
15751592 // Call the appropriate app callback if allowed.
15761593 if (!suppressCallback)
@@ -1752,6 +1769,67 @@ void TCPEndPoint::RestartTCPUserTimeoutTimer()
17521769
17531770#if WEAVE_SYSTEM_CONFIG_USE_LWIP
17541771
1772+ uint32_t TCPEndPoint::RemainingToSend ()
1773+ {
1774+ if (mSendQueue == NULL )
1775+ {
1776+ return 0 ;
1777+ }
1778+ else
1779+ {
1780+ // We can never have reported more unacked data than there is pending
1781+ // in the send queue! This would indicate a critical accounting bug.
1782+ VerifyOrDie (mUnackedLength <= mSendQueue ->TotalLength ());
1783+
1784+ return mSendQueue ->TotalLength () - mUnackedLength ;
1785+ }
1786+ }
1787+
1788+ TCPEndPoint::BufferOffset TCPEndPoint::FindStartOfUnsent ()
1789+ {
1790+ // Find first packet buffer with remaining data to send by skipping
1791+ // all sent but un-acked data. This is necessary because of the Consume()
1792+ // call in HandleDataSent(), which potentially releases backing memory for
1793+ // fully-sent packet buffers, causing an invalidation of all possible
1794+ // offsets one might have cached. The TCP acnowledgements may come back
1795+ // with a variety of sizes depending on prior activity, and size of the
1796+ // send window. The only way to ensure we get the correct offsets into
1797+ // unsent data while retaining the buffers that have un-acked data is to
1798+ // traverse all sent-but-unacked data in the chain to reach the beginning
1799+ // of ready-to-send data.
1800+ Weave::System::PacketBuffer* currentUnsentBuf = mSendQueue ;
1801+ uint16_t unsentOffset = 0 ;
1802+ uint32_t leftToSkip = mUnackedLength ;
1803+
1804+ VerifyOrDie (leftToSkip < mSendQueue ->TotalLength ());
1805+
1806+ while (leftToSkip > 0 )
1807+ {
1808+ VerifyOrDie (currentUnsentBuf != NULL );
1809+ uint16_t bufDataLen = currentUnsentBuf->DataLength ();
1810+ if (leftToSkip >= bufDataLen)
1811+ {
1812+ // We have more to skip than current packet buffer size.
1813+ // Follow the chain to continue.
1814+ currentUnsentBuf = currentUnsentBuf->Next ();
1815+ leftToSkip -= bufDataLen;
1816+ }
1817+ else
1818+ {
1819+ // Done skipping all data, currentUnsentBuf is first packet buffer
1820+ // containing unsent data.
1821+ unsentOffset = leftToSkip;
1822+ leftToSkip = 0 ;
1823+ }
1824+ }
1825+
1826+ TCPEndPoint::BufferOffset startOfUnsent;
1827+ startOfUnsent.buffer = currentUnsentBuf;
1828+ startOfUnsent.offset = unsentOffset;
1829+
1830+ return startOfUnsent;
1831+ }
1832+
17551833INET_ERROR TCPEndPoint::GetPCB (IPAddressType addrType)
17561834{
17571835 // IMMPORTANT: This method MUST be called with the LwIP stack LOCKED!
@@ -1843,16 +1921,32 @@ void TCPEndPoint::HandleDataSent(uint16_t lenSent)
18431921{
18441922 if (IsConnected ())
18451923 {
1924+ // Ensure we do not have internal inconsistency in the lwIP, which
1925+ // could cause invalid pointer accesses.
1926+ if (lenSent > mUnackedLength )
1927+ {
1928+ WeaveLogError (Inet, " Got more ACKed bytes (%d) than were pending (%d)" , (int )lenSent, (int )mUnackedLength );
1929+ DoClose (INET_ERROR_UNEXPECTED_EVENT, false );
1930+ return ;
1931+ }
1932+ else if (mSendQueue == NULL )
1933+ {
1934+ WeaveLogError (Inet, " Got ACK for %d bytes but data backing gone" , (int )lenSent);
1935+ DoClose (INET_ERROR_UNEXPECTED_EVENT, false );
1936+ return ;
1937+ }
1938+
18461939 // Consume data off the head of the send queue equal to the amount of data being acknowledged.
18471940 mSendQueue = mSendQueue ->Consume (lenSent);
1941+ mUnackedLength -= lenSent;
18481942
18491943#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
18501944 // Only change the UserTimeout timer if lenSent > 0,
18511945 // indicating progress being made in sending data
18521946 // across.
18531947 if (lenSent > 0 )
18541948 {
1855- if (mSendQueue == NULL )
1949+ if (RemainingToSend () == 0 )
18561950 {
18571951 // If the output queue has been flushed then stop the timer.
18581952
@@ -1880,12 +1974,12 @@ void TCPEndPoint::HandleDataSent(uint16_t lenSent)
18801974 if (OnDataSent != NULL )
18811975 OnDataSent (this , lenSent);
18821976
1883- // If unsent data exists, attempt to sent it now...
1884- if (mUnsentQueue != NULL )
1977+ // If unsent data exists, attempt to send it now...
1978+ if (RemainingToSend () > 0 )
18851979 DriveSending ();
18861980
18871981 // If in the closing state and the send queue is now empty, attempt to transition to closed.
1888- if (State == kState_Closing && mSendQueue == NULL )
1982+ if (( State == kState_Closing ) && ( RemainingToSend () == 0 ) )
18891983 DoClose (INET_NO_ERROR, false );
18901984 }
18911985}
0 commit comments