NetSim Source Code Help v14.4
All 13 Components
 
Loading...
Searching...
No Matches
SACK.c
1/************************************************************************************
2* Copyright (C) 2023 *
3* TETCOS, Bangalore. India *
4* *
5* Tetcos owns the intellectual property rights in the Product and its content. *
6* The copying, redistribution, reselling or publication of any or all of the *
7* Product or its content without express prior written consent of Tetcos is *
8* prohibited. Ownership and / or any other right relating to the software and all *
9* intellectual property rights therein shall remain at all times with Tetcos. *
10* *
11* Author: Shashi Kant Suman *
12* *
13* ---------------------------------------------------------------------------------*/
14#include "main.h"
15#include "TCP.h"
16#include "TCP_Header.h"
17
18static UINT get_max_sack_data(PTCP_SEGMENT_HDR hdr)
19{
20 return (MAX_TCP_HDR_SIZE - hdr->HdrLength - SACK_OPTION_FIX_LEN) / SACKDATA_LEN;
21}
22
23UINT32 get_highAck(PNETSIM_SOCKET s)
24{
25 return s->tcb->SND.UNA;
26}
27
28static UINT32 get_highData(PNETSIM_SOCKET s)
29{
30 return s->tcb->SND.NXT;
31}
32
33static UINT32 get_highRxt(PNETSIM_SOCKET s)
34{
35 return s->tcb->HighRxt;
36}
37
38void set_highRxt(PNETSIM_SOCKET s, UINT32 seq)
39{
40 s->tcb->HighRxt = max(s->tcb->HighRxt, seq);
41}
42
43static UINT32 get_rescueRxt(PNETSIM_SOCKET s)
44{
45 return s->tcb->RescueRxt;
46}
47
48void set_rescueRxt(PNETSIM_SOCKET s, UINT32 seq)
49{
50 s->tcb->RescueRxt = max(s->tcb->RescueRxt, seq);
51}
52
53void set_recoveryPoint(PNETSIM_SOCKET s, UINT32 val)
54{
55 s->tcb->recoveryPoint = val;
56}
57
58UINT32 get_recoveryPoint(PNETSIM_SOCKET s)
59{
60 return s->tcb->recoveryPoint;
61}
62
63UINT32 get_pipe(PNETSIM_SOCKET s)
64{
65 return s->tcb->pipe;
66}
67
68void set_sack_option(PNETSIM_SOCKET s, PTCP_SEGMENT_HDR hdr, double time)
69{
70 time;
71 UINT maxData;
72 UINT i=0;
73
74 if (!s->tcb->isSackPermitted)
75 return; // Sack is not permitted
76
77 if (!s->tcb->outOfOrderSegment.size)
78 return; // Sack option is not required as no out of order segment.
79
80 maxData = get_max_sack_data(hdr);
81
82 if (!maxData)
83 return; // No space is left in TCP header
84
85 PSACK_OPTION sack = (PSACK_OPTION)calloc(1, sizeof* sack);
86 sack->type = TCP_OPTION_SACK;
87
88 PQueueInfo queue = s->tcb->outOfOrderSegment.queue;
89
90 PSACKDATA* sackData = calloc(1, sizeof *sackData);;
91 UINT len = 0;
92 UINT32 lastSeq = 0;
93 while (queue)
94 {
95 PTCP_SEGMENT_HDR h = TCP_GET_SEGMENT_HDR(queue->packet);
96 if (lastSeq)
97 {
98 if (h->SeqNum == lastSeq + len)
99 {
100 len = get_seg_len(queue->packet);
101 sackData[i]->rightEdge = h->SeqNum + len;
102 lastSeq = h->SeqNum;
103 }
104 else if (i < maxData - 1)
105 {
106 i++;
107 len = get_seg_len(queue->packet);
108 sackData = (PSACKDATA*)realloc(sackData, (i + 1) * sizeof* sackData);
109 sackData[i] = (PSACKDATA)calloc(1, sizeof* sackData[i]);
110 sackData[i]->leftEdge = h->SeqNum;
111 sackData[i]->rightEdge = h->SeqNum + len;
112 lastSeq = h->SeqNum;
113 }
114 else
115 {
116 break; // Reached Max Sack Data count
117 }
118 }
119 else if (i < maxData)
120 {
121 len = get_seg_len(queue->packet);
122 sackData[i] = (PSACKDATA)calloc(1, sizeof* sackData[i]);
123 sackData[i]->leftEdge = h->SeqNum;
124 sackData[i]->rightEdge = h->SeqNum + len;
125 lastSeq = h->SeqNum;
126 }
127 else
128 {
129 break; // Reached Max Sack Data count
130 }
131
132 queue = queue->next;
133 }
134
135 sack->sackData = sackData;
136 sack->len = SACK_OPTION_LEN(i + 1);
137
138 set_tcp_option(hdr, sack, TCP_OPTION_SACK, sack->len);
139}
140
141static void update_sack_flag(PNETSIM_SOCKET s, UINT32 left, UINT32 right)
142{
143 PQueueInfo queue = s->tcb->retransmissionQueue.queue;
144 while (queue)
145 {
146 PTCP_SEGMENT_HDR hdr = TCP_GET_SEGMENT_HDR(queue->packet);
147 if (hdr->SeqNum >= left && hdr->SeqNum <= right)
148 queue->isSacked = true;
149 queue = queue->next;
150 }
151}
152
153static void update_scoreboard(PNETSIM_SOCKET s, UINT left, UINT right, UINT index)
154{
155 //Store current to prev
156 if (!index)
157 memcpy(&s->tcb->prevScoreboard, &s->tcb->scoreboard, sizeof s->tcb->prevScoreboard);
158
159 s->tcb->scoreboard.leftEdge[index] = left;
160 s->tcb->scoreboard.rightEdge[index] = right;
161}
162
163/*
164This routine returns whether the given sequence number is
165considered to be lost. The routine returns true when either
166DupThresh discontiguous SACKed sequences have arrived above
167'SeqNum' or more than (DupThresh - 1) * SMSS bytes with sequence
168numbers greater than 'SeqNum' have been SACKed. Otherwise, the
169routine returns false.
170*/
171bool tcp_sack_isLost(PNETSIM_SOCKET s, UINT seqNum)
172{
173 UINT i;
174 UINT SMSS = s->tcb->get_MSS(s);
175 UINT check = seqNum + SMSS;
176
177 if (s->tcb->scoreboard.leftEdge[0] >= check)
178 {
179 UINT c = 0;
180 //Discontinuous
181 for (i = 0; i < 4; i++)
182 {
183 c += (s->tcb->scoreboard.rightEdge[i] - s->tcb->scoreboard.leftEdge[i]) / SMSS;
184 if (c >= TCP_DupThresh)
185 return true;
186 if (s->tcb->scoreboard.rightEdge[i] > (TCP_DupThresh - 1)*SMSS + seqNum)
187 return true;
188 }
189 }
190 return false;
191}
192
193void tcp_sack_setPipe(PNETSIM_SOCKET s)
194{
195 UINT32 highAck = get_highAck(s);
196 UINT32 highData = get_highData(s);
197 UINT32 highRxt = get_highRxt(s);
198 s->tcb->pipe = 0;
199 UINT SMSS = s->tcb->get_MSS(s);
200 UINT32 s1;
201
202 for (s1 = highAck; s1 < highData; s1 += SMSS)
203 {
204 if (!tcp_sack_isLost(s, s1))
205 s->tcb->pipe += SMSS;
206
207 if (s1 <= highRxt)
208 s->tcb->pipe += SMSS;
209 }
210}
211
212UINT32 tcp_sack_nextSeg(PNETSIM_SOCKET s)
213{
214 NetSim_PACKET* p;
215 UINT32 s2;
216 UINT32 highRxt = get_highRxt(s);
217 PQueueInfo queue = s->tcb->retransmissionQueue.queue;
218 while (queue)
219 {
220 p = queue->packet;
221
222 //Rule 1
223 if (!queue->isSacked)
224 {
225 PTCP_SEGMENT_HDR hdr = TCP_GET_SEGMENT_HDR(p);
226 s2 = hdr->SeqNum;
227 if (s2 > highRxt &&
228 tcp_sack_isLost(s, s2))
229 {
230 PQueueInfo q = queue->next;
231 while (q)
232 {
233 if (q->isSacked)
234 return s2;
235 q = q->next;
236 }
237 }
238 }
239 queue = queue->next;
240 }
241
242 //Rule 2
243 UINT32 seq = s->tcb->SND.NXT;
244 p = fn_NetSim_Socket_GetPacketFromInterface(s->sId, 0);
245 s->tcb->SND.WND = s->tcb->get_WND(s);
246 if (p)
247 {
248 UINT32 l = (UINT32)p->pstruAppData->dPacketSize;
249 if (seq + l <= s->tcb->SND.WND + s->tcb->SND.UNA)
250 return s->tcb->SND.NXT;
251 }
252
253 //Rule 3
254 queue = s->tcb->retransmissionQueue.queue;
255 while (queue)
256 {
257 p = queue->packet;
258
259 //Rule 1
260 if (!queue->isSacked)
261 {
262 PTCP_SEGMENT_HDR hdr = TCP_GET_SEGMENT_HDR(p);
263 s2 = hdr->SeqNum;
264 if (s2 > highRxt)
265 {
266 PQueueInfo q = queue->next;
267 while (q)
268 {
269 if (q->isSacked)
270 return s2;
271 q = q->next;
272 }
273 }
274 }
275 queue = queue->next;
276 }
277
278 return 0;
279}
280
281void receive_sack_option(PNETSIM_SOCKET s, PTCP_SEGMENT_HDR hdr)
282{
283 s->tcb->isSackOption = false;
284
285 if (!s->tcb->isSackPermitted)
286 return; // Sack is not permitted
287
288 PSACK_OPTION sack = get_tcp_option(hdr, TCP_OPTION_SACK);
289 if (!sack)
290 return; // No sack option is present
291
292 UINT c = get_sack_data_count(sack);
293
294 if (!c)
295 return; //No sack data present
296
297 s->tcb->isSackOption = true;
298
299 UINT i;
300 for (i = 0; i < c; i++)
301 {
302 PSACKDATA data = sack->sackData[i];
303 UINT32 left = data->leftEdge;
304 UINT32 right = data->rightEdge;
305 update_sack_flag(s, left, right);
306 update_scoreboard(s, left, right, i);
307 }
308
309 for(;i<4;i++)
310 update_scoreboard(s, 0, 0, i);
311}
312
313void tcp_sack_fastRetransmit(PNETSIM_SOCKET s)
314{
315 if (s->tcb->isSackOption)
316 {
317 set_recoveryPoint(s, get_highData(s));
318 tcp_sack_setPipe(s);
319 }
320}
321
322bool tcp_sack_lossRecoveryPhase(PNETSIM_SOCKET s)
323{
324 if (s->tcb->SEG.ACK > s->tcb->recoveryPoint)
325 return false; //End loss recovery phase
326 else
327 {
328 tcp_sack_setPipe(s);
329
330 UINT32 wnd = s->tcb->get_WND(s);
331 UINT32 pipe = get_pipe(s);
332 UINT32 mss = s->tcb->get_MSS(s);
333
334 while (wnd >= mss + pipe)
335 {
336 UINT32 nextSeg = tcp_sack_nextSeg(s);
337 if (nextSeg)
338 {
339 UINT32 highData = get_highData(s);
340 if (nextSeg < highData)
341 {
342 resend_segment_without_timeout(s, nextSeg);
343 set_highRxt(s, nextSeg);
344 }
345 else
346 {
347 UINT32 len;
348 UINT32 prev = s->tcb->SND.NXT;
349 send_segment(s);
350 len = s->tcb->SND.NXT - prev;
351 pipe += len;
352 s->tcb->pipe += len;
353 }
354 }
355 else
356 {
357 return true;
358 }
359 }
360 return true; //Continue in Loss recovery phase
361 }
362}