From d9ed8e88eb21c979ab735db9f013d107957b2886 Mon Sep 17 00:00:00 2001
From: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
Date: Tue, 11 Mar 2025 13:37:00 +0100
Subject: [PATCH 1/1] Add client timeout and FIN for L4S UDP mode

---
 include/payloads.h |  1 +
 src/Client.cpp     |  7 +++++--
 src/Server.cpp     | 38 +++++++++++++++++++++++++++++++++++++-
 3 files changed, 43 insertions(+), 3 deletions(-)

diff --git a/include/payloads.h b/include/payloads.h
index c76e9fed..5832cc4b 100644
--- a/include/payloads.h
+++ b/include/payloads.h
@@ -133,6 +133,7 @@ extern "C" {
 
 // L4S Flags
 #define L4S_ECN_ERR 0x0001
+#define L4S_PKT_FIN 0x0002
 
 /*
  * Structures used for test messages which
diff --git a/src/Client.cpp b/src/Client.cpp
index 2e2e41bf..ec795f8e 100644
--- a/src/Client.cpp
+++ b/src/Client.cpp
@@ -1737,6 +1737,7 @@ void Client::RunUDPL4S () {
     initmtu = ((size_tp)mSettings->mBufLen > PRAGUE_MINMTU) ? mSettings->mBufLen : PRAGUE_MINMTU;
     PragueCC l4s_pacer(initmtu, 0, 0, PRAGUE_INITRATE, PRAGUE_INITWIN, PRAGUE_MINRATE, maxrate);
     time_tp nextSend = l4s_pacer.Now();
+    time_tp last_acktime = 0;
     count_tp seqnr = 1;
     count_tp inflight = 0;
     rate_tp pacing_rate;
@@ -1748,8 +1749,6 @@ void Client::RunUDPL4S () {
 
     while (InProgress()) {
 	count_tp inburst = 0;
-        // [TODO] Add timout functionality
-        // time_tp timeout = 0;
         time_tp startSend = 0;
         time_tp pacer_now = l4s_pacer.Now();
         while ((inflight < packet_window) && (inburst < packet_burst) && (nextSend - pacer_now <= 0)) {
@@ -1849,12 +1848,14 @@ void Client::RunUDPL4S () {
 	    // ecn_tp rcv_ecn = ecn_tp(reportstruct->tos & 0x3);
 	    time_tp timestamp;
 	    time_tp echoed_timestamp;
+	    last_acktime = pacer_now;
 	    timestamp = ntohl(UDPAckBuf.rx_ts);
 	    echoed_timestamp = ntohl(UDPAckBuf.echoed_ts);
 	    count_tp pkts_rx = ntohl(UDPAckBuf.rx_cnt);
 	    count_tp pkts_ce = ntohl(UDPAckBuf.CE_cnt);
 	    count_tp pkts_lost = ntohl(UDPAckBuf.lost_cnt);
 	    bool l4s_err = (htons(UDPAckBuf.flags) & L4S_ECN_ERR);
+	    peerclose = (htons(UDPAckBuf.flags) & L4S_PKT_FIN);
 	    if (!UDPAckBuf.reserved1) {
 	        // To distinguish L4S ACK from retrnasmitted client_udp_testhdr (reverse mode)
 	        l4s_pacer.PacketReceived(timestamp, echoed_timestamp);
@@ -1863,6 +1864,8 @@ void Client::RunUDPL4S () {
         }
         else // timeout, reset state
             if (inflight >= packet_window) {
+                if (last_acktime - pacer_now + 1000000 <= 0)
+                    peerclose = true;
                 l4s_pacer.ResetCCInfo();
                 inflight = 0;
             }
diff --git a/src/Server.cpp b/src/Server.cpp
index 58d51112..0115b38c 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -1218,7 +1218,43 @@ void Server::RunUDPL4S () {
                     }
                 } else {
                     ReportPacket(myReport, reportstruct);
-		}
+                }
+            }
+        }
+        if (!isLastPacket) {
+            struct udp_l4s_ack udp_l4s_pkt_fin;
+            memset(&udp_l4s_pkt_fin, 0, sizeof(udp_l4s_pkt_fin));
+            udp_l4s_pkt_fin.flags = htons(L4S_PKT_FIN);
+
+            struct msghdr msg;
+            struct iovec iov[1];
+            unsigned char cmsg[CMSG_SPACE(sizeof(int))];
+            struct cmsghdr *cmsgptr = NULL;
+            memset(&iov, 0, sizeof(iov));
+            memset(&cmsg, 0, sizeof(cmsg));
+            memset(&msg, 0, sizeof (struct msghdr));
+
+            iov[0].iov_base = reinterpret_cast<char *> (&(udp_l4s_pkt_fin));
+            iov[0].iov_len = sizeof(struct udp_l4s_ack);
+            msg.msg_iov = iov;
+            msg.msg_iovlen = 1;
+            msg.msg_control = cmsg;
+            msg.msg_controllen = sizeof(cmsg);
+            cmsgptr = CMSG_FIRSTHDR(&msg);
+            cmsgptr->cmsg_level = IPPROTO_IP;
+            cmsgptr->cmsg_type  = IP_TOS;
+            cmsgptr->cmsg_len  = CMSG_LEN(sizeof(u_char));
+            u_char tos = (mSettings->mTOS | ecn_l4s_id);
+            memcpy(CMSG_DATA(cmsgptr), (u_char*)&tos, sizeof(u_char));
+            msg.msg_controllen = CMSG_SPACE(sizeof(u_char));
+            int ackLen = sendmsg(mySocket, &msg, 0);
+            if (ackLen <= 0) {
+                if (ackLen == 0) {
+                    WARN_errno(1, "write l4s fin timeout");
+                    reportstruct->err_readwrite = WriteTimeo;
+                } else if (FATALUDPWRITERR(errno)) {
+                    WARN_errno(1, "write l4s fin fatal");
+                }
             }
         }
     }
-- 
2.25.1

