1 /*
2     SPDX-FileCopyrightText: 2009 Joris Guisson <joris.guisson@gmail.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "remotewindow.h"
8 #include "connection.h"
9 #include "utpprotocol.h"
10 #include <util/functions.h>
11 #include <util/log.h>
12 
13 using namespace bt;
14 
15 namespace utp
16 {
UnackedPacket(const PacketBuffer & packet,bt::Uint16 seq_nr,bt::TimeStamp send_time)17 UnackedPacket::UnackedPacket(const PacketBuffer &packet, bt::Uint16 seq_nr, bt::TimeStamp send_time)
18     : packet(packet)
19     , seq_nr(seq_nr)
20     , send_time(send_time)
21     , retransmitted(false)
22 {
23 }
24 
~UnackedPacket()25 UnackedPacket::~UnackedPacket()
26 {
27 }
28 
RemoteWindow()29 RemoteWindow::RemoteWindow()
30     : cur_window(0)
31     , max_window(64 * 1024)
32     , wnd_size(0)
33     , last_ack_nr(0)
34     , last_ack_receive_count(0)
35 {
36 }
37 
~RemoteWindow()38 RemoteWindow::~RemoteWindow()
39 {
40     clear();
41 }
42 
packetReceived(const utp::Header * hdr,const SelectiveAck * sack,Retransmitter * conn)43 void RemoteWindow::packetReceived(const utp::Header *hdr, const SelectiveAck *sack, Retransmitter *conn)
44 {
45     if (hdr->ack_nr == last_ack_nr) {
46         if (hdr->type == ST_STATE)
47             last_ack_receive_count++;
48     } else {
49         last_ack_nr = hdr->ack_nr;
50         last_ack_receive_count = 1;
51     }
52 
53     wnd_size = hdr->wnd_size;
54 
55     bt::TimeStamp now = bt::Now();
56     QList<UnackedPacket>::iterator i = unacked_packets.begin();
57     while (i != unacked_packets.end()) {
58         if (SeqNrCmpSE(i->seq_nr, hdr->ack_nr)) {
59             // everything up until the ack_nr in the header is acked
60             conn->updateRTT(hdr, now - i->send_time, i->packet.payloadSize());
61             cur_window -= i->packet.payloadSize();
62             i = unacked_packets.erase(i);
63         } else if (sack) {
64             if (Acked(sack, i->seq_nr - hdr->ack_nr)) {
65                 conn->updateRTT(hdr, now - i->send_time, i->packet.payloadSize());
66                 cur_window -= i->packet.payloadSize();
67                 i = unacked_packets.erase(i);
68             } else
69                 ++i;
70         } else
71             break;
72     }
73 
74     if (!unacked_packets.isEmpty()) {
75         checkLostPackets(hdr, sack, conn);
76     }
77 }
78 
addPacket(const PacketBuffer & packet,bt::Uint16 seq_nr,bt::TimeStamp send_time)79 void RemoteWindow::addPacket(const PacketBuffer &packet, bt::Uint16 seq_nr, bt::TimeStamp send_time)
80 {
81     cur_window += packet.payloadSize();
82     wnd_size -= packet.payloadSize();
83     unacked_packets.append(UnackedPacket(packet, seq_nr, send_time));
84 }
85 
checkLostPackets(const utp::Header * hdr,const utp::SelectiveAck * sack,Retransmitter * conn)86 void RemoteWindow::checkLostPackets(const utp::Header *hdr, const utp::SelectiveAck *sack, Retransmitter *conn)
87 {
88     bt::TimeStamp now = bt::Now();
89     bool lost_packets = false;
90 
91     QList<UnackedPacket>::iterator itr = unacked_packets.begin();
92     UnackedPacket &first_unacked = *itr;
93     if (last_ack_receive_count >= 3 && first_unacked.seq_nr == hdr->ack_nr + 1) {
94         // packet has been lost
95         if (!first_unacked.retransmitted || now - first_unacked.send_time > conn->currentTimeout()) {
96             try {
97                 conn->retransmit(first_unacked.packet, first_unacked.seq_nr);
98                 first_unacked.send_time = now;
99                 first_unacked.retransmitted = true;
100             } catch (utp::Connection::TransmissionError &) {
101             }
102             lost_packets = true;
103         }
104 
105         ++itr;
106     }
107 
108     if (sack) {
109         bt::Uint16 lost_index = lost(sack);
110         while (lost_index > 0 && itr != unacked_packets.end()) {
111             bt::Uint16 d = itr->seq_nr - hdr->ack_nr;
112             if (d < lost_index && (!itr->retransmitted || now - itr->send_time > conn->currentTimeout())) {
113                 try {
114                     conn->retransmit(itr->packet, itr->seq_nr);
115                     itr->send_time = now;
116                     itr->retransmitted = true;
117                 } catch (utp::Connection::TransmissionError &) {
118                 }
119                 lost_packets = true;
120             }
121             ++itr;
122         }
123     }
124 
125     if (lost_packets) {
126         Out(SYS_UTP | LOG_DEBUG) << "UTP: lost packets on connection " << hdr->connection_id << endl;
127         max_window = (bt::Uint32)qRound(0.78 * max_window);
128     }
129 }
130 
lost(const SelectiveAck * sack)131 bt::Uint16 RemoteWindow::lost(const SelectiveAck *sack)
132 {
133     // A packet is lost if 3 packets have been acked after it
134     bt::Uint32 acked = 0;
135     bt::Int16 i = sack->length * 8 - 1;
136     while (i >= 0 && acked < 3) {
137         if (Acked(sack, i)) {
138             acked++;
139             if (acked == 3)
140                 return i;
141         }
142 
143         i--;
144     }
145 
146     return 0;
147 }
148 
timeout(Retransmitter * conn)149 void RemoteWindow::timeout(Retransmitter *conn)
150 {
151     try {
152         max_window = MIN_PACKET_SIZE;
153         bt::TimeStamp now = bt::Now();
154         // When a timeout occurs retransmit packets which are lost longer then the current timeout
155         for (UnackedPacket &pkt : unacked_packets) {
156             if (!pkt.retransmitted || now - pkt.send_time > conn->currentTimeout()) {
157                 conn->retransmit(pkt.packet, pkt.seq_nr);
158                 pkt.send_time = bt::Now();
159                 pkt.retransmitted = true;
160             }
161         }
162     } catch (utp::Connection::TransmissionError &) {
163     }
164 }
165 
updateWindowSize(double scaled_gain)166 void RemoteWindow::updateWindowSize(double scaled_gain)
167 {
168     int d = (int)qRound(scaled_gain);
169     if (max_window + d < MIN_PACKET_SIZE)
170         max_window = MIN_PACKET_SIZE;
171     else
172         max_window += d;
173 
174     // if (scaled_gain > 1000)
175     //  Out(SYS_UTP|LOG_DEBUG) << "RemoteWindow::updateWindowSize " << scaled_gain << " " << max_window << endl;
176 }
177 
clear()178 void RemoteWindow::clear()
179 {
180     unacked_packets.clear();
181 }
182 }
183