Rev 1733 | 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 | |||
1761 | hidnplayr | 91 | jnc .bigger_than_zero |
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 | |
1761 | hidnplayr | 111 | .bigger_than_zero: |
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] |
132 | |||
1733 | hidnplayr | 133 | cmp edi, 0 |
1761 | hidnplayr | 134 | jge @f |
1733 | hidnplayr | 135 | |
1761 | hidnplayr | 136 | and dl, not (TH_FIN) |
1733 | hidnplayr | 137 | |
138 | @@: |
||
139 | |||
1761 | hidnplayr | 140 | ;------------------------------- |
141 | ; calculate window advertisement (130) |
||
1733 | hidnplayr | 142 | |
143 | mov ecx, SOCKET_MAXDATA |
||
144 | sub ecx, [eax + STREAM_SOCKET.rcv + RING_BUFFER.size] |
||
145 | |||
146 | ;------------------------------ |
||
1761 | hidnplayr | 147 | ; Sender silly window avoidance (131) |
1733 | hidnplayr | 148 | |
1761 | hidnplayr | 149 | test esi, esi |
150 | jz .len_zero |
||
151 | |||
152 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
||
1733 | hidnplayr | 153 | je .send |
154 | |||
1761 | hidnplayr | 155 | ;;; if (idle or TF_NODELAY) && (esi + ebx >= so_snd.sb_cc), send |
1733 | hidnplayr | 156 | |
1761 | hidnplayr | 157 | test [eax + TCP_SOCKET.t_force], -1 ;;; |
1733 | hidnplayr | 158 | jnz .send |
159 | |||
160 | mov ebx, [eax + TCP_SOCKET.max_sndwnd] |
||
161 | shr ebx, 1 |
||
1761 | hidnplayr | 162 | cmp esi, ebx |
1733 | hidnplayr | 163 | jge .send |
164 | |||
165 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
||
166 | cmp ebx, [eax + TCP_SOCKET.SND_MAX] |
||
167 | jl .send |
||
168 | |||
1761 | hidnplayr | 169 | .len_zero: |
170 | |||
1733 | hidnplayr | 171 | ;---------------------------------------- |
1761 | hidnplayr | 172 | ; Check if a window update should be sent (154) |
1733 | hidnplayr | 173 | |
1761 | hidnplayr | 174 | test ecx, ecx |
1733 | hidnplayr | 175 | jz .no_window |
176 | |||
1761 | hidnplayr | 177 | ;;; TODO 167-172 |
1733 | hidnplayr | 178 | |
179 | .no_window: |
||
180 | |||
181 | ;-------------------------- |
||
1761 | hidnplayr | 182 | ; Should a segment be sent? (174) |
1733 | hidnplayr | 183 | |
1761 | hidnplayr | 184 | test [eax + TCP_SOCKET.t_flags], TF_ACKNOW ; we need to ACK |
1733 | hidnplayr | 185 | jnz .send |
186 | |||
1761 | hidnplayr | 187 | test dl, TH_SYN + TH_RST ; we need to send a SYN or RST |
1733 | hidnplayr | 188 | jnz .send |
189 | |||
1761 | hidnplayr | 190 | mov ebx, [eax + TCP_SOCKET.SND_UP] ; when urgent pointer is beyond start of send bufer |
1733 | hidnplayr | 191 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
192 | jg .send |
||
193 | |||
194 | test dl, TH_FIN |
||
1761 | hidnplayr | 195 | jz .enter_persist ; no reason to send, enter persist state |
1733 | hidnplayr | 196 | |
1761 | hidnplayr | 197 | ; FIN was set, only send if not already sent, or on retransmit |
198 | |||
1733 | hidnplayr | 199 | test [eax + TCP_SOCKET.t_flags], TF_SENTFIN |
200 | jnz .send |
||
201 | |||
202 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
||
203 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
||
204 | je .send |
||
205 | |||
206 | ;-------------------- |
||
1761 | hidnplayr | 207 | ; Enter persist state (191) |
1733 | hidnplayr | 208 | |
209 | .enter_persist: |
||
210 | |||
211 | DEBUGF 1,"Entering persist state\n" |
||
212 | |||
213 | |||
1761 | hidnplayr | 214 | ;;; 213 - 217 |
215 | |||
216 | ;---------------------------- |
||
217 | ; No reason to send a segment (219) |
||
218 | |||
1733 | hidnplayr | 219 | DEBUGF 1,"No reason to send a segment\n" |
220 | |||
1761 | hidnplayr | 221 | mov [eax + SOCKET.lock], 0 |
1733 | hidnplayr | 222 | |
223 | ret |
||
224 | |||
225 | |||
1761 | hidnplayr | 226 | |
227 | |||
228 | |||
229 | |||
230 | |||
231 | |||
232 | |||
1733 | hidnplayr | 233 | ;----------------------------------------------- |
234 | ; |
||
1761 | hidnplayr | 235 | ; Send a segment (222) |
1733 | hidnplayr | 236 | ; |
237 | ; eax = socket pointer |
||
1761 | hidnplayr | 238 | ; esi = data len |
1733 | hidnplayr | 239 | ; dl = flags |
240 | ; |
||
241 | ;----------------------------------------------- |
||
242 | |||
243 | .send: |
||
244 | |||
245 | DEBUGF 1,"Preparing to send a segment\n" |
||
246 | |||
247 | mov edi, TCP_segment.Data ; edi will contain headersize |
||
248 | |||
249 | sub esp, 8 ; create some space on stack |
||
1761 | hidnplayr | 250 | push eax ; save socket pointer |
1733 | hidnplayr | 251 | |
252 | ;------------------------------------ |
||
253 | ; Send options with first SYN segment |
||
254 | |||
255 | test dl, TH_SYN |
||
1761 | hidnplayr | 256 | jz .options_done |
1733 | hidnplayr | 257 | |
258 | push [eax + TCP_SOCKET.ISS] |
||
259 | pop [eax + TCP_SOCKET.SND_NXT] |
||
260 | |||
261 | test [eax + TCP_SOCKET.t_flags], TF_NOOPT |
||
1761 | hidnplayr | 262 | jnz .options_done |
1733 | hidnplayr | 263 | |
264 | mov ecx, 1460 |
||
265 | or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16 |
||
266 | bswap ecx |
||
267 | push ecx |
||
268 | add di, 4 |
||
269 | |||
270 | test [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE |
||
271 | jz .no_syn |
||
272 | |||
273 | test dl, TH_ACK |
||
274 | jnz .scale_opt |
||
275 | |||
276 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE |
||
277 | jz .no_syn |
||
278 | |||
279 | .scale_opt: |
||
280 | movzx ecx, byte [eax + TCP_SOCKET.request_r_scale] |
||
281 | or ecx, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP shl 8 |
||
282 | bswap ecx |
||
283 | pushd ecx |
||
284 | add di, 4 |
||
285 | |||
286 | .no_syn: |
||
287 | |||
288 | ;------------------------------------ |
||
289 | ; Make the timestamp option if needed |
||
290 | |||
291 | test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP |
||
292 | jz .no_timestamp |
||
293 | |||
294 | test dl, TH_RST |
||
295 | jnz .no_timestamp |
||
296 | |||
297 | test dl, TH_ACK |
||
298 | jz .timestamp |
||
299 | |||
300 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP |
||
301 | jz .no_timestamp |
||
302 | |||
303 | .timestamp: |
||
1761 | hidnplayr | 304 | mov ebx, [timer_ticks] |
305 | bswap ebx |
||
306 | push ebx |
||
1733 | hidnplayr | 307 | pushw 0 |
308 | pushd TCP_OPT_TIMESTAMP + 10 shl 8 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24 |
||
309 | add di, 10 |
||
310 | |||
311 | .no_timestamp: |
||
312 | |||
1761 | hidnplayr | 313 | ; |
1733 | hidnplayr | 314 | |
315 | |||
1761 | hidnplayr | 316 | |
317 | |||
318 | |||
319 | |||
320 | |||
321 | |||
322 | .options_done: |
||
323 | |||
324 | ; eax = socket ptr |
||
325 | ; edx = flags |
||
326 | ; edi = header size |
||
327 | ; esi = data len |
||
328 | |||
329 | ;--------------------------------------------- |
||
330 | ; check if we dont exceed the max segment size (270) |
||
331 | |||
332 | add esi, edi ; total TCP segment size |
||
333 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
||
334 | jle .no_overflow |
||
335 | |||
336 | mov esi, [eax + TCP_SOCKET.t_maxseg] |
||
337 | |||
338 | ;;; sendalot = 1 |
||
339 | |||
340 | .no_overflow: |
||
341 | |||
342 | ;----------------------------------------------------------------- |
||
1733 | hidnplayr | 343 | ; Start by pushing all TCP header values in reverse order on stack |
1761 | hidnplayr | 344 | ; (essentially, creating the tcp header on the stack!) |
1733 | hidnplayr | 345 | |
346 | pushw 0 ; .UrgentPointer dw ? |
||
347 | pushw 0 ; .Checksum dw ? |
||
348 | pushw 0x00a0 ; .Window dw ? ;;;;;;; |
||
349 | shl edi, 2 ; .DataOffset db ? only 4 left-most bits |
||
350 | shl dx, 8 |
||
351 | or dx, di ; .Flags db ? |
||
352 | pushw dx |
||
353 | shr edi, 2 ; .DataOffset db ? ;;;; |
||
354 | |||
355 | push [eax + TCP_SOCKET.RCV_NXT] ; .AckNumber dd ? |
||
356 | ntohd [esp] |
||
357 | |||
358 | push [eax + TCP_SOCKET.SND_NXT] ; .SequenceNumber dd ? |
||
359 | ntohd [esp] |
||
360 | |||
361 | push [eax + TCP_SOCKET.RemotePort] ; .DestinationPort dw ? |
||
362 | ntohw [esp] |
||
363 | |||
364 | push [eax + TCP_SOCKET.LocalPort] ; .SourcePort dw ? |
||
365 | ntohw [esp] |
||
366 | |||
367 | push edi ; header size |
||
368 | |||
1761 | hidnplayr | 369 | ;--------------------- |
1733 | hidnplayr | 370 | ; Create the IP packet |
1761 | hidnplayr | 371 | |
372 | mov ecx, esi |
||
373 | |||
1733 | hidnplayr | 374 | mov ebx, [eax + IP_SOCKET.LocalIP] ; source ip |
375 | mov eax, [eax + IP_SOCKET.RemoteIP] ; dest ip |
||
376 | mov di, IP_PROTO_TCP shl 8 + 128 |
||
377 | call IPv4_output |
||
378 | jz .fail |
||
379 | |||
380 | ;----------------------------------------- |
||
381 | ; Move TCP header from stack to TCP packet |
||
382 | |||
383 | push ecx |
||
384 | mov ecx, [esp+4] |
||
385 | lea esi, [esp+4+4] |
||
386 | shr ecx, 2 |
||
387 | rep movsd |
||
388 | pop ecx ; full TCP packet size |
||
389 | |||
390 | pop esi ; headersize |
||
391 | add esp, esi |
||
392 | |||
393 | mov [esp + 4], eax ; packet ptr |
||
394 | mov [esp + 4+4], edx ; packet size |
||
395 | |||
396 | mov edx, edi ; begin of data |
||
397 | sub edx, esi ; begin of packet (edi = begin of data) |
||
398 | push ecx |
||
399 | sub ecx, esi ; data size |
||
400 | |||
401 | ;-------------- |
||
402 | ; Copy the data |
||
403 | |||
404 | ; eax = ptr to ring struct |
||
405 | ; ecx = buffer size |
||
406 | ; edi = ptr to buffer |
||
407 | |||
1761 | hidnplayr | 408 | mov eax, [esp+4] ; get socket ptr |
409 | |||
410 | add [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number |
||
411 | |||
1733 | hidnplayr | 412 | add eax, STREAM_SOCKET.snd |
413 | push edx |
||
414 | call SOCKET_ring_read |
||
1761 | hidnplayr | 415 | pop esi ; begin of data |
416 | pop ecx ; full packet size |
||
417 | pop eax ; socket ptr |
||
1733 | hidnplayr | 418 | |
1761 | hidnplayr | 419 | ;---------------------------------- |
420 | ; update sequence number and timers (400) |
||
421 | |||
1733 | hidnplayr | 422 | test [esi + TCP_segment.Flags], TH_SYN + TH_FIN |
423 | jz @f |
||
1761 | hidnplayr | 424 | inc [eax + TCP_SOCKET.SND_NXT] ; syn and fin take a sequence number |
425 | test [esi + TCP_segment.Flags], TH_FIN |
||
426 | jz @f |
||
427 | or [eax + TCP_SOCKET.t_flags], TF_SENTFIN ; if we sent a fin, set the sentfin flag |
||
1733 | hidnplayr | 428 | @@: |
429 | |||
430 | mov edx, [eax + TCP_SOCKET.SND_NXT] |
||
431 | cmp edx, [eax + TCP_SOCKET.SND_MAX] |
||
432 | jle @f |
||
433 | mov [eax + TCP_SOCKET.SND_MAX], edx |
||
434 | |||
435 | ;;;; TODO: time transmission (420) |
||
1761 | hidnplayr | 436 | |
1733 | hidnplayr | 437 | @@: |
438 | |||
1761 | hidnplayr | 439 | ; set retransmission timer if not already set, and not doing an ACK or keepalive probe |
1733 | hidnplayr | 440 | |
1761 | hidnplayr | 441 | cmp [eax + TCP_SOCKET.timer_retransmission], 1000 ;;;; |
442 | jl .retransmit_set |
||
443 | |||
444 | cmp edx, [eax + TCP_SOCKET.SND_UNA] ; edx = [eax + TCP_SOCKET.SND_NXT] |
||
445 | je .retransmit_set |
||
446 | |||
447 | mov edx, [eax + TCP_SOCKET.t_rxtcur] |
||
448 | mov [eax + TCP_SOCKET.timer_retransmission], dx |
||
449 | |||
450 | mov [eax + TCP_SOCKET.timer_persist], 0 |
||
451 | mov [eax + TCP_SOCKET.t_rxtshift], 0 ;;; TODO: only do this if timer_persist was set |
||
452 | |||
453 | |||
454 | .retransmit_set: |
||
455 | |||
1733 | hidnplayr | 456 | ;-------------------- |
457 | ; Create the checksum |
||
458 | |||
459 | DEBUGF 1,"checksum: ptr=%x size=%u\n", esi, ecx |
||
460 | |||
461 | TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP) |
||
462 | mov [esi+TCP_segment.Checksum], dx |
||
463 | |||
1761 | hidnplayr | 464 | ; unlock socket |
465 | |||
466 | mov [eax + SOCKET.lock], 0 |
||
467 | |||
1733 | hidnplayr | 468 | ;---------------- |
469 | ; Send the packet |
||
470 | |||
471 | DEBUGF 1,"Sending TCP Packet to device %x\n", ebx |
||
472 | call [ebx + NET_DEVICE.transmit] |
||
473 | ret |
||
474 | |||
475 | |||
476 | .fail: |
||
477 | pop ecx |
||
478 | add esp, ecx |
||
479 | add esp, 4+8 |
||
1761 | hidnplayr | 480 | mov [eax + SOCKET.lock], 0 |
1733 | hidnplayr | 481 | DEBUGF 1,"TCP_output: failed\n" |
482 | ret |
||
483 |