Rev 1761 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1733 | hidnplayr | 1 | ;----------------------------------------------------------------- |
2 | ; |
||
3 | ; TCP_output |
||
4 | ; |
||
5 | ; IN: eax = socket pointer |
||
6 | ; |
||
7 | ; OUT: / |
||
8 | ; |
||
9 | ;----------------------------------------------------------------- |
||
10 | align 4 |
||
11 | TCP_output: |
||
12 | |||
13 | DEBUGF 1,"TCP_output, socket: %x\n", eax |
||
14 | |||
15 | |||
16 | ; We'll detect the length of the data to be transmitted, and flags to be used |
||
17 | ; If there is some data, or any critical controls to send (SYN / RST), then transmit |
||
18 | ; Otherwise, investigate further |
||
19 | |||
20 | mov ebx, [eax + TCP_SOCKET.SND_MAX] |
||
21 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
||
22 | jne .not_idle |
||
23 | |||
24 | mov ebx, [eax + TCP_SOCKET.t_idle] |
||
25 | cmp ebx, [eax + TCP_SOCKET.t_rxtcur] |
||
26 | jle .not_idle |
||
27 | |||
28 | ; We have been idle for a while and no ACKS are expected to clock out any data we send.. |
||
29 | ; Slow start to get ack "clock" running again. |
||
30 | |||
31 | mov ebx, [eax + TCP_SOCKET.t_maxseg] |
||
32 | mov [eax + TCP_SOCKET.SND_CWND], ebx |
||
33 | |||
34 | .not_idle: |
||
35 | .again: |
||
1761 | hidnplayr | 36 | mov ebx, [eax + TCP_SOCKET.SND_NXT] ; calculate offset (71) |
1733 | hidnplayr | 37 | sub ebx, [eax + TCP_SOCKET.SND_UNA] ; |
38 | |||
39 | mov ecx, [eax + TCP_SOCKET.SND_WND] ; determine window |
||
40 | cmp ecx, [eax + TCP_SOCKET.SND_CWND] ; |
||
41 | jl @f ; |
||
42 | mov ecx, [eax + TCP_SOCKET.SND_CWND] ; |
||
43 | @@: ; |
||
44 | |||
1761 | hidnplayr | 45 | call TCP_outflags ; flags in dl |
1733 | hidnplayr | 46 | |
1761 | hidnplayr | 47 | ;------------------------ |
48 | ; data being forced out ? |
||
49 | |||
1733 | hidnplayr | 50 | ; If in persist timeout with window of 0, send 1 byte. |
51 | ; Otherwise, if window is small but nonzero, and timer expired, |
||
52 | ; we will send what we can and go to transmit state |
||
53 | |||
54 | test [eax + TCP_SOCKET.t_force], -1 |
||
1761 | hidnplayr | 55 | jz .no_force |
1733 | hidnplayr | 56 | |
57 | test ecx, ecx |
||
58 | jnz .no_zero_window |
||
59 | |||
60 | cmp ebx, [eax + STREAM_SOCKET.snd + RING_BUFFER.size] |
||
61 | jge @f |
||
62 | |||
63 | and dl, not (TH_FIN) ; clear the FIN flag ??? how can it be set before? |
||
64 | |||
65 | @@: |
||
66 | inc ecx |
||
1761 | hidnplayr | 67 | jmp .no_force |
1733 | hidnplayr | 68 | |
69 | .no_zero_window: |
||
70 | mov [eax + TCP_SOCKET.timer_persist], 0 |
||
71 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
||
72 | |||
1761 | hidnplayr | 73 | .no_force: |
1733 | hidnplayr | 74 | |
1761 | hidnplayr | 75 | ;-------------------------------- |
76 | ; Calculate how much data to send (106) |
||
1733 | hidnplayr | 77 | |
78 | mov esi, [eax + STREAM_SOCKET.snd + RING_BUFFER.size] |
||
79 | cmp esi, ecx |
||
80 | jl @f |
||
81 | mov esi, ecx |
||
82 | @@: |
||
83 | sub esi, ebx |
||
84 | |||
1761 | hidnplayr | 85 | ;------------------------ |
86 | ; check for window shrink (107) |
||
1733 | hidnplayr | 87 | |
1761 | hidnplayr | 88 | ; If FIN has been set, but not ACKed, but we havent been called to retransmit, esi will be -1 |
1733 | hidnplayr | 89 | ; Otherwise, window shrank after we sent into it. |
90 | |||
1762 | hidnplayr | 91 | jns .not_negative |
1761 | hidnplayr | 92 | |
93 | ; enter persist state |
||
1733 | hidnplayr | 94 | xor esi, esi |
95 | |||
1761 | hidnplayr | 96 | ; If window shrank to 0 |
1733 | hidnplayr | 97 | test ecx, ecx |
98 | jnz @f |
||
99 | |||
1761 | hidnplayr | 100 | ; cancel pending retransmit |
101 | mov [eax + TCP_SOCKET.timer_retransmission], 0 |
||
1733 | hidnplayr | 102 | |
1761 | hidnplayr | 103 | ; pull SND_NXT back to (closed) window, We will enter persist state below. |
1733 | hidnplayr | 104 | push [eax + TCP_SOCKET.SND_UNA] |
105 | pop [eax + TCP_SOCKET.SND_NXT] |
||
1761 | hidnplayr | 106 | |
1733 | hidnplayr | 107 | @@: |
108 | |||
1761 | hidnplayr | 109 | ; If window didn't close completely, just wait for an ACK |
1733 | hidnplayr | 110 | |
1762 | hidnplayr | 111 | .not_negative: |
1733 | hidnplayr | 112 | |
1761 | hidnplayr | 113 | ;--------------------------- |
114 | ; Send one segment at a time (124) |
||
115 | |||
1733 | hidnplayr | 116 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
117 | jle @f |
||
118 | |||
119 | mov esi, [eax + TCP_SOCKET.t_maxseg] |
||
120 | |||
1761 | hidnplayr | 121 | ;;; sendalot = 1 |
122 | |||
1733 | hidnplayr | 123 | @@: |
124 | |||
1761 | hidnplayr | 125 | ;-------------------------------------------- |
126 | ; Turn of FIN flag if send buffer not emptied (128) |
||
1733 | hidnplayr | 127 | |
128 | mov edi, [eax + TCP_SOCKET.SND_NXT] |
||
1761 | hidnplayr | 129 | add edi, esi |
1733 | hidnplayr | 130 | sub edi, [eax + TCP_SOCKET.SND_UNA] |
1761 | hidnplayr | 131 | sub edi, [eax + STREAM_SOCKET.snd + RING_BUFFER.size] |
1762 | hidnplayr | 132 | jns @f |
1761 | hidnplayr | 133 | |
134 | and dl, not (TH_FIN) |
||
1733 | hidnplayr | 135 | |
136 | @@: |
||
137 | |||
1761 | hidnplayr | 138 | ;------------------------------- |
139 | ; calculate window advertisement (130) |
||
1733 | hidnplayr | 140 | |
141 | mov ecx, SOCKET_MAXDATA |
||
142 | sub ecx, [eax + STREAM_SOCKET.rcv + RING_BUFFER.size] |
||
143 | |||
144 | ;------------------------------ |
||
1761 | hidnplayr | 145 | ; Sender silly window avoidance (131) |
1733 | hidnplayr | 146 | |
1761 | hidnplayr | 147 | test esi, esi |
148 | jz .len_zero |
||
149 | |||
150 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
||
1733 | hidnplayr | 151 | je .send |
152 | |||
1761 | hidnplayr | 153 | ;;; if (idle or TF_NODELAY) && (esi + ebx >= so_snd.sb_cc), send |
1733 | hidnplayr | 154 | |
1761 | hidnplayr | 155 | test [eax + TCP_SOCKET.t_force], -1 ;;; |
1733 | hidnplayr | 156 | jnz .send |
157 | |||
158 | mov ebx, [eax + TCP_SOCKET.max_sndwnd] |
||
159 | shr ebx, 1 |
||
1761 | hidnplayr | 160 | cmp esi, ebx |
1733 | hidnplayr | 161 | jge .send |
162 | |||
163 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
||
164 | cmp ebx, [eax + TCP_SOCKET.SND_MAX] |
||
165 | jl .send |
||
166 | |||
1761 | hidnplayr | 167 | .len_zero: |
168 | |||
1733 | hidnplayr | 169 | ;---------------------------------------- |
1761 | hidnplayr | 170 | ; Check if a window update should be sent (154) |
1733 | hidnplayr | 171 | |
1761 | hidnplayr | 172 | test ecx, ecx |
1733 | hidnplayr | 173 | jz .no_window |
174 | |||
1761 | hidnplayr | 175 | ;;; TODO 167-172 |
1733 | hidnplayr | 176 | |
177 | .no_window: |
||
178 | |||
179 | ;-------------------------- |
||
1761 | hidnplayr | 180 | ; Should a segment be sent? (174) |
1733 | hidnplayr | 181 | |
1761 | hidnplayr | 182 | test [eax + TCP_SOCKET.t_flags], TF_ACKNOW ; we need to ACK |
1733 | hidnplayr | 183 | jnz .send |
184 | |||
1761 | hidnplayr | 185 | test dl, TH_SYN + TH_RST ; we need to send a SYN or RST |
1733 | hidnplayr | 186 | jnz .send |
187 | |||
1761 | hidnplayr | 188 | mov ebx, [eax + TCP_SOCKET.SND_UP] ; when urgent pointer is beyond start of send bufer |
1733 | hidnplayr | 189 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
190 | jg .send |
||
191 | |||
192 | test dl, TH_FIN |
||
1761 | hidnplayr | 193 | jz .enter_persist ; no reason to send, enter persist state |
1733 | hidnplayr | 194 | |
1761 | hidnplayr | 195 | ; FIN was set, only send if not already sent, or on retransmit |
196 | |||
1733 | hidnplayr | 197 | test [eax + TCP_SOCKET.t_flags], TF_SENTFIN |
198 | jnz .send |
||
199 | |||
200 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
||
201 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
||
202 | je .send |
||
203 | |||
204 | ;-------------------- |
||
1761 | hidnplayr | 205 | ; Enter persist state (191) |
1733 | hidnplayr | 206 | |
207 | .enter_persist: |
||
208 | |||
209 | DEBUGF 1,"Entering persist state\n" |
||
210 | |||
211 | |||
1761 | hidnplayr | 212 | ;;; 213 - 217 |
213 | |||
214 | ;---------------------------- |
||
215 | ; No reason to send a segment (219) |
||
216 | |||
1733 | hidnplayr | 217 | DEBUGF 1,"No reason to send a segment\n" |
218 | |||
1761 | hidnplayr | 219 | mov [eax + SOCKET.lock], 0 |
1733 | hidnplayr | 220 | |
221 | ret |
||
222 | |||
223 | |||
1761 | hidnplayr | 224 | |
225 | |||
226 | |||
227 | |||
228 | |||
229 | |||
230 | |||
1733 | hidnplayr | 231 | ;----------------------------------------------- |
232 | ; |
||
1761 | hidnplayr | 233 | ; Send a segment (222) |
1733 | hidnplayr | 234 | ; |
235 | ; eax = socket pointer |
||
1761 | hidnplayr | 236 | ; esi = data len |
1733 | hidnplayr | 237 | ; dl = flags |
238 | ; |
||
239 | ;----------------------------------------------- |
||
240 | |||
241 | .send: |
||
242 | |||
243 | DEBUGF 1,"Preparing to send a segment\n" |
||
244 | |||
245 | mov edi, TCP_segment.Data ; edi will contain headersize |
||
246 | |||
247 | sub esp, 8 ; create some space on stack |
||
1761 | hidnplayr | 248 | push eax ; save socket pointer |
1733 | hidnplayr | 249 | |
250 | ;------------------------------------ |
||
251 | ; Send options with first SYN segment |
||
252 | |||
253 | test dl, TH_SYN |
||
1761 | hidnplayr | 254 | jz .options_done |
1733 | hidnplayr | 255 | |
256 | push [eax + TCP_SOCKET.ISS] |
||
257 | pop [eax + TCP_SOCKET.SND_NXT] |
||
258 | |||
259 | test [eax + TCP_SOCKET.t_flags], TF_NOOPT |
||
1761 | hidnplayr | 260 | jnz .options_done |
1733 | hidnplayr | 261 | |
262 | mov ecx, 1460 |
||
263 | or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16 |
||
264 | bswap ecx |
||
265 | push ecx |
||
266 | add di, 4 |
||
267 | |||
268 | test [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE |
||
269 | jz .no_syn |
||
270 | |||
271 | test dl, TH_ACK |
||
272 | jnz .scale_opt |
||
273 | |||
274 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE |
||
275 | jz .no_syn |
||
276 | |||
277 | .scale_opt: |
||
278 | movzx ecx, byte [eax + TCP_SOCKET.request_r_scale] |
||
279 | or ecx, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP shl 8 |
||
280 | bswap ecx |
||
281 | pushd ecx |
||
282 | add di, 4 |
||
283 | |||
284 | .no_syn: |
||
285 | |||
286 | ;------------------------------------ |
||
287 | ; Make the timestamp option if needed |
||
288 | |||
289 | test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP |
||
290 | jz .no_timestamp |
||
291 | |||
292 | test dl, TH_RST |
||
293 | jnz .no_timestamp |
||
294 | |||
295 | test dl, TH_ACK |
||
296 | jz .timestamp |
||
297 | |||
298 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP |
||
299 | jz .no_timestamp |
||
300 | |||
301 | .timestamp: |
||
1761 | hidnplayr | 302 | mov ebx, [timer_ticks] |
303 | bswap ebx |
||
304 | push ebx |
||
1733 | hidnplayr | 305 | pushw 0 |
306 | pushd TCP_OPT_TIMESTAMP + 10 shl 8 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24 |
||
307 | add di, 10 |
||
308 | |||
309 | .no_timestamp: |
||
310 | |||
1761 | hidnplayr | 311 | ; |
1733 | hidnplayr | 312 | |
313 | |||
1761 | hidnplayr | 314 | |
315 | |||
316 | |||
317 | |||
318 | |||
319 | |||
320 | .options_done: |
||
321 | |||
322 | ; eax = socket ptr |
||
323 | ; edx = flags |
||
324 | ; edi = header size |
||
325 | ; esi = data len |
||
326 | |||
327 | ;--------------------------------------------- |
||
328 | ; check if we dont exceed the max segment size (270) |
||
329 | |||
330 | add esi, edi ; total TCP segment size |
||
331 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
||
332 | jle .no_overflow |
||
333 | |||
334 | mov esi, [eax + TCP_SOCKET.t_maxseg] |
||
335 | |||
336 | ;;; sendalot = 1 |
||
337 | |||
338 | .no_overflow: |
||
339 | |||
340 | ;----------------------------------------------------------------- |
||
1733 | hidnplayr | 341 | ; Start by pushing all TCP header values in reverse order on stack |
1761 | hidnplayr | 342 | ; (essentially, creating the tcp header on the stack!) |
1733 | hidnplayr | 343 | |
344 | pushw 0 ; .UrgentPointer dw ? |
||
345 | pushw 0 ; .Checksum dw ? |
||
346 | pushw 0x00a0 ; .Window dw ? ;;;;;;; |
||
347 | shl edi, 2 ; .DataOffset db ? only 4 left-most bits |
||
348 | shl dx, 8 |
||
349 | or dx, di ; .Flags db ? |
||
350 | pushw dx |
||
351 | shr edi, 2 ; .DataOffset db ? ;;;; |
||
352 | |||
353 | push [eax + TCP_SOCKET.RCV_NXT] ; .AckNumber dd ? |
||
354 | ntohd [esp] |
||
355 | |||
356 | push [eax + TCP_SOCKET.SND_NXT] ; .SequenceNumber dd ? |
||
357 | ntohd [esp] |
||
358 | |||
359 | push [eax + TCP_SOCKET.RemotePort] ; .DestinationPort dw ? |
||
360 | ntohw [esp] |
||
361 | |||
362 | push [eax + TCP_SOCKET.LocalPort] ; .SourcePort dw ? |
||
363 | ntohw [esp] |
||
364 | |||
365 | push edi ; header size |
||
366 | |||
1761 | hidnplayr | 367 | ;--------------------- |
1733 | hidnplayr | 368 | ; Create the IP packet |
1761 | hidnplayr | 369 | |
370 | mov ecx, esi |
||
371 | |||
1733 | hidnplayr | 372 | mov ebx, [eax + IP_SOCKET.LocalIP] ; source ip |
373 | mov eax, [eax + IP_SOCKET.RemoteIP] ; dest ip |
||
374 | mov di, IP_PROTO_TCP shl 8 + 128 |
||
375 | call IPv4_output |
||
376 | jz .fail |
||
377 | |||
378 | ;----------------------------------------- |
||
379 | ; Move TCP header from stack to TCP packet |
||
380 | |||
381 | push ecx |
||
382 | mov ecx, [esp+4] |
||
383 | lea esi, [esp+4+4] |
||
384 | shr ecx, 2 |
||
385 | rep movsd |
||
386 | pop ecx ; full TCP packet size |
||
387 | |||
388 | pop esi ; headersize |
||
389 | add esp, esi |
||
390 | |||
391 | mov [esp + 4], eax ; packet ptr |
||
392 | mov [esp + 4+4], edx ; packet size |
||
393 | |||
394 | mov edx, edi ; begin of data |
||
395 | sub edx, esi ; begin of packet (edi = begin of data) |
||
396 | push ecx |
||
397 | sub ecx, esi ; data size |
||
398 | |||
399 | ;-------------- |
||
400 | ; Copy the data |
||
401 | |||
402 | ; eax = ptr to ring struct |
||
403 | ; ecx = buffer size |
||
404 | ; edi = ptr to buffer |
||
405 | |||
1761 | hidnplayr | 406 | mov eax, [esp+4] ; get socket ptr |
407 | |||
408 | add [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number |
||
409 | |||
1733 | hidnplayr | 410 | add eax, STREAM_SOCKET.snd |
411 | push edx |
||
412 | call SOCKET_ring_read |
||
1761 | hidnplayr | 413 | pop esi ; begin of data |
414 | pop ecx ; full packet size |
||
415 | pop eax ; socket ptr |
||
1733 | hidnplayr | 416 | |
1761 | hidnplayr | 417 | ;---------------------------------- |
418 | ; update sequence number and timers (400) |
||
419 | |||
1733 | hidnplayr | 420 | test [esi + TCP_segment.Flags], TH_SYN + TH_FIN |
421 | jz @f |
||
1761 | hidnplayr | 422 | inc [eax + TCP_SOCKET.SND_NXT] ; syn and fin take a sequence number |
423 | test [esi + TCP_segment.Flags], TH_FIN |
||
424 | jz @f |
||
425 | or [eax + TCP_SOCKET.t_flags], TF_SENTFIN ; if we sent a fin, set the sentfin flag |
||
1733 | hidnplayr | 426 | @@: |
427 | |||
428 | mov edx, [eax + TCP_SOCKET.SND_NXT] |
||
429 | cmp edx, [eax + TCP_SOCKET.SND_MAX] |
||
430 | jle @f |
||
431 | mov [eax + TCP_SOCKET.SND_MAX], edx |
||
432 | |||
433 | ;;;; TODO: time transmission (420) |
||
1761 | hidnplayr | 434 | |
1733 | hidnplayr | 435 | @@: |
436 | |||
1761 | hidnplayr | 437 | ; set retransmission timer if not already set, and not doing an ACK or keepalive probe |
1733 | hidnplayr | 438 | |
1761 | hidnplayr | 439 | cmp [eax + TCP_SOCKET.timer_retransmission], 1000 ;;;; |
440 | jl .retransmit_set |
||
441 | |||
442 | cmp edx, [eax + TCP_SOCKET.SND_UNA] ; edx = [eax + TCP_SOCKET.SND_NXT] |
||
443 | je .retransmit_set |
||
444 | |||
445 | mov edx, [eax + TCP_SOCKET.t_rxtcur] |
||
446 | mov [eax + TCP_SOCKET.timer_retransmission], dx |
||
447 | |||
448 | mov [eax + TCP_SOCKET.timer_persist], 0 |
||
449 | mov [eax + TCP_SOCKET.t_rxtshift], 0 ;;; TODO: only do this if timer_persist was set |
||
450 | |||
451 | |||
452 | .retransmit_set: |
||
453 | |||
1733 | hidnplayr | 454 | ;-------------------- |
455 | ; Create the checksum |
||
456 | |||
457 | DEBUGF 1,"checksum: ptr=%x size=%u\n", esi, ecx |
||
458 | |||
459 | TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP) |
||
460 | mov [esi+TCP_segment.Checksum], dx |
||
461 | |||
1761 | hidnplayr | 462 | ; unlock socket |
463 | |||
464 | mov [eax + SOCKET.lock], 0 |
||
465 | |||
1733 | hidnplayr | 466 | ;---------------- |
467 | ; Send the packet |
||
468 | |||
469 | DEBUGF 1,"Sending TCP Packet to device %x\n", ebx |
||
470 | call [ebx + NET_DEVICE.transmit] |
||
471 | ret |
||
472 | |||
473 | |||
474 | .fail: |
||
475 | pop ecx |
||
476 | add esp, ecx |
||
477 | add esp, 4+8 |
||
1761 | hidnplayr | 478 | mov [eax + SOCKET.lock], 0 |
1733 | hidnplayr | 479 | DEBUGF 1,"TCP_output: failed\n" |
480 | ret |
||
481 |