Rev 5594 | Rev 5984 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3555 | Serge | 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
2 | ;; ;; |
||
5565 | serge | 3 | ;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; |
3555 | Serge | 4 | ;; Distributed under terms of the GNU General Public License ;; |
5 | ;; ;; |
||
6 | ;; Part of the TCP/IP network stack for KolibriOS ;; |
||
7 | ;; ;; |
||
8 | ;; Written by hidnplayr@kolibrios.org ;; |
||
9 | ;; ;; |
||
10 | ;; Based on the code of 4.4BSD ;; |
||
11 | ;; ;; |
||
12 | ;; GNU GENERAL PUBLIC LICENSE ;; |
||
13 | ;; Version 2, June 1991 ;; |
||
14 | ;; ;; |
||
15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
||
16 | |||
5596 | serge | 17 | $Revision: 5584 $ |
3555 | Serge | 18 | |
19 | ;----------------------------------------------------------------- |
||
20 | ; |
||
21 | ; TCP_output |
||
22 | ; |
||
23 | ; IN: eax = socket pointer |
||
4423 | Serge | 24 | ; OUT: eax = 0 on success/errorcode |
3555 | Serge | 25 | ; |
26 | ;----------------------------------------------------------------- |
||
27 | align 4 |
||
4423 | Serge | 28 | proc TCP_output |
3555 | Serge | 29 | |
4423 | Serge | 30 | locals |
31 | temp_bits db ? |
||
32 | endl |
||
3555 | Serge | 33 | |
4423 | Serge | 34 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: socket=%x state=%u\n", eax, [eax + TCP_SOCKET.t_state] |
35 | |||
3555 | Serge | 36 | push eax |
37 | lea ecx, [eax + SOCKET.mutex] |
||
38 | call mutex_lock |
||
39 | pop eax |
||
40 | |||
3725 | Serge | 41 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: socket locked\n" |
42 | |||
3555 | Serge | 43 | ; We'll detect the length of the data to be transmitted, and flags to be used |
44 | ; If there is some data, or any critical controls to send (SYN / RST), then transmit |
||
45 | ; Otherwise, investigate further |
||
46 | |||
47 | mov ebx, [eax + TCP_SOCKET.SND_MAX] |
||
48 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
||
49 | jbe .not_idle |
||
50 | |||
51 | mov ebx, [eax + TCP_SOCKET.t_idle] |
||
52 | cmp ebx, [eax + TCP_SOCKET.t_rxtcur] |
||
53 | jbe .not_idle |
||
54 | |||
55 | ; We have been idle for a while and no ACKS are expected to clock out any data we send.. |
||
56 | ; Slow start to get ack "clock" running again. |
||
57 | |||
58 | mov ebx, [eax + TCP_SOCKET.t_maxseg] |
||
59 | mov [eax + TCP_SOCKET.SND_CWND], ebx |
||
60 | |||
61 | .not_idle: |
||
62 | .again: |
||
4423 | Serge | 63 | mov [temp_bits], 0 |
3555 | Serge | 64 | |
65 | mov ebx, [eax + TCP_SOCKET.SND_NXT] ; calculate offset (71) |
||
66 | sub ebx, [eax + TCP_SOCKET.SND_UNA] ; |
||
67 | |||
68 | mov ecx, [eax + TCP_SOCKET.SND_WND] ; determine window |
||
69 | cmp ecx, [eax + TCP_SOCKET.SND_CWND] ; |
||
70 | jb @f ; |
||
71 | mov ecx, [eax + TCP_SOCKET.SND_CWND] ; |
||
72 | @@: ; |
||
73 | |||
74 | call TCP_outflags ; flags in dl |
||
75 | |||
76 | ;------------------------ |
||
77 | ; data being forced out ? |
||
78 | |||
79 | ; If in persist timeout with window of 0, send 1 byte. |
||
80 | ; Otherwise, if window is small but nonzero, and timer expired, |
||
81 | ; we will send what we can and go to transmit state |
||
82 | |||
83 | cmp [eax + TCP_SOCKET.t_force], 0 |
||
84 | je .no_force |
||
85 | |||
3589 | Serge | 86 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: forcing data out\n" |
3555 | Serge | 87 | |
88 | test ecx, ecx |
||
89 | jnz .no_zero_window |
||
90 | |||
91 | cmp ebx, [eax + STREAM_SOCKET.snd.size] |
||
92 | jae @f |
||
93 | and dl, not (TH_FIN) |
||
5565 | serge | 94 | @@: |
3555 | Serge | 95 | |
96 | inc ecx |
||
97 | jmp .no_force |
||
98 | |||
99 | .no_zero_window: |
||
3626 | Serge | 100 | and [eax + TCP_SOCKET.timer_flags], not timer_flag_persist |
3555 | Serge | 101 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
102 | |||
103 | .no_force: |
||
104 | |||
105 | ;-------------------------------- |
||
106 | ; Calculate how much data to send (106) |
||
107 | |||
108 | mov esi, [eax + STREAM_SOCKET.snd.size] |
||
109 | cmp esi, ecx |
||
110 | jb @f |
||
111 | mov esi, ecx |
||
112 | @@: |
||
113 | sub esi, ebx |
||
114 | |||
115 | ;------------------------ |
||
116 | ; check for window shrink (107) |
||
117 | |||
118 | ; If FIN has been set, but not ACKed, but we havent been called to retransmit, esi will be -1 |
||
119 | ; Otherwise, window shrank after we sent into it. |
||
120 | |||
121 | jae .not_persist |
||
122 | |||
123 | ; enter persist state |
||
124 | xor esi, esi |
||
125 | |||
126 | ; If window shrank to 0 |
||
127 | test ecx, ecx |
||
128 | jnz @f |
||
129 | |||
130 | ; cancel pending retransmit |
||
3626 | Serge | 131 | and [eax + TCP_SOCKET.timer_flags], not timer_flag_retransmission |
3555 | Serge | 132 | |
133 | ; pull SND_NXT back to (closed) window, We will enter persist state below. |
||
134 | push [eax + TCP_SOCKET.SND_UNA] |
||
135 | pop [eax + TCP_SOCKET.SND_NXT] |
||
136 | @@: |
||
137 | |||
138 | ; If window didn't close completely, just wait for an ACK |
||
139 | |||
140 | .not_persist: |
||
141 | |||
142 | ;--------------------------- |
||
143 | ; Send one segment at a time (124) |
||
144 | |||
145 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
||
146 | jbe @f |
||
147 | mov esi, [eax + TCP_SOCKET.t_maxseg] |
||
4423 | Serge | 148 | or [temp_bits], TCP_BIT_SENDALOT |
3555 | Serge | 149 | @@: |
150 | |||
151 | ;-------------------------------------------- |
||
152 | ; Turn of FIN flag if send buffer not emptied (128) |
||
153 | |||
154 | mov edi, [eax + TCP_SOCKET.SND_NXT] |
||
155 | add edi, esi |
||
156 | sub edi, [eax + TCP_SOCKET.SND_UNA] |
||
157 | cmp edi, [eax + STREAM_SOCKET.snd.size] |
||
158 | jae @f |
||
159 | and dl, not (TH_FIN) |
||
160 | @@: |
||
161 | |||
162 | ;------------------------------- |
||
163 | ; calculate window advertisement (130) |
||
164 | |||
165 | mov ecx, SOCKET_MAXDATA |
||
166 | sub ecx, [eax + STREAM_SOCKET.rcv.size] |
||
167 | |||
168 | ;------------------------------ |
||
169 | ; Sender silly window avoidance (131) |
||
170 | |||
171 | test esi, esi |
||
172 | jz .len_zero |
||
173 | |||
174 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
||
4423 | Serge | 175 | je .send |
3555 | Serge | 176 | |
177 | add ebx, esi ; offset + length |
||
178 | cmp ebx, [eax + STREAM_SOCKET.snd.size] |
||
179 | jb @f |
||
180 | |||
181 | test [eax + TCP_SOCKET.t_flags], TF_NODELAY |
||
4423 | Serge | 182 | jnz .send |
3555 | Serge | 183 | |
184 | mov ebx, [eax + TCP_SOCKET.SND_MAX] |
||
185 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
||
4423 | Serge | 186 | je .send |
3555 | Serge | 187 | @@: |
188 | |||
189 | test [eax + TCP_SOCKET.t_force], -1 ;;; |
||
4423 | Serge | 190 | jnz .send |
3555 | Serge | 191 | |
192 | mov ebx, [eax + TCP_SOCKET.max_sndwnd] |
||
193 | shr ebx, 1 |
||
194 | cmp esi, ebx |
||
4423 | Serge | 195 | jae .send |
3555 | Serge | 196 | |
197 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
||
198 | cmp ebx, [eax + TCP_SOCKET.SND_MAX] |
||
4423 | Serge | 199 | jb .send |
3555 | Serge | 200 | |
201 | .len_zero: |
||
202 | |||
203 | ;---------------------------------------- |
||
204 | ; Check if a window update should be sent (154) |
||
205 | |||
3589 | Serge | 206 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: window=%d\n", ecx |
3555 | Serge | 207 | |
208 | ; Compare available window to amount of window known to peer (as advertised window less next expected input) |
||
209 | ; If the difference is at least two max size segments, or at least 50% of the maximum possible window, |
||
210 | ; Then we want to send a window update to the peer. |
||
211 | |||
212 | test ecx, ecx |
||
213 | jz .no_window |
||
214 | |||
215 | push ecx |
||
216 | mov cl, [eax + TCP_SOCKET.RCV_SCALE] |
||
217 | mov ebx, TCP_max_win |
||
218 | shl ebx, cl |
||
219 | pop ecx |
||
3725 | Serge | 220 | |
3555 | Serge | 221 | cmp ebx, ecx |
222 | jb @f |
||
223 | mov ebx, ecx |
||
224 | @@: |
||
225 | sub ebx, [eax + TCP_SOCKET.RCV_ADV] |
||
226 | add ebx, [eax + TCP_SOCKET.RCV_NXT] |
||
227 | |||
228 | mov edi, [eax + TCP_SOCKET.t_maxseg] |
||
229 | shl edi, 1 |
||
230 | |||
4423 | Serge | 231 | cmp ebx, edi |
232 | jae .send |
||
3555 | Serge | 233 | |
4423 | Serge | 234 | shl ebx, 1 |
3555 | Serge | 235 | ; cmp ebx, [eax + TCP_SOCKET.] ;;; TODO: check with receive buffer high water mark |
236 | ; jae TCP_send |
||
237 | |||
238 | .no_window: |
||
239 | |||
240 | ;-------------------------- |
||
241 | ; Should a segment be sent? (174) |
||
242 | |||
243 | test [eax + TCP_SOCKET.t_flags], TF_ACKNOW ; we need to ACK |
||
4423 | Serge | 244 | jnz .send |
3555 | Serge | 245 | |
246 | test dl, TH_SYN + TH_RST ; we need to send a SYN or RST |
||
4423 | Serge | 247 | jnz .send |
3555 | Serge | 248 | |
249 | mov ebx, [eax + TCP_SOCKET.SND_UP] ; when urgent pointer is beyond start of send bufer |
||
250 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
||
4423 | Serge | 251 | ja .send |
3555 | Serge | 252 | |
253 | test dl, TH_FIN |
||
254 | jz .enter_persist ; no reason to send, enter persist state |
||
255 | |||
256 | ; FIN was set, only send if not already sent, or on retransmit |
||
257 | |||
258 | test [eax + TCP_SOCKET.t_flags], TF_SENTFIN |
||
4423 | Serge | 259 | jz .send |
3555 | Serge | 260 | |
261 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
||
262 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
||
4423 | Serge | 263 | je .send |
3555 | Serge | 264 | |
265 | ;-------------------- |
||
266 | ; Enter persist state (191) |
||
267 | |||
268 | .enter_persist: |
||
269 | |||
3626 | Serge | 270 | cmp [eax + STREAM_SOCKET.snd.size], 0 ; Data ready to send? |
3555 | Serge | 271 | jne @f |
3626 | Serge | 272 | and [eax + TCP_SOCKET.timer_flags], not timer_flag_retransmission |
3555 | Serge | 273 | jne @f |
274 | |||
3626 | Serge | 275 | test [eax + TCP_SOCKET.timer_flags], timer_flag_persist ; Persist timer already expired? |
276 | jnz @f |
||
277 | |||
3589 | Serge | 278 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: Entering persist state\n" |
3555 | Serge | 279 | |
280 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
||
281 | call TCP_set_persist |
||
282 | @@: |
||
283 | |||
284 | ;---------------------------- |
||
285 | ; No reason to send a segment (219) |
||
286 | |||
3589 | Serge | 287 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: No reason to send a segment\n" |
3555 | Serge | 288 | |
289 | pusha |
||
290 | lea ecx, [eax + SOCKET.mutex] |
||
291 | call mutex_unlock |
||
292 | popa |
||
293 | |||
294 | ; Fixme: returnvalue? |
||
295 | |||
296 | ret |
||
297 | |||
298 | |||
299 | ;----------------------------------------------- |
||
300 | ; |
||
301 | ; Send a segment (222) |
||
302 | ; |
||
303 | ; eax = socket pointer |
||
304 | ; esi = data len |
||
305 | ; dl = flags |
||
306 | ; |
||
307 | ;----------------------------------------------- |
||
4423 | Serge | 308 | .send: |
3555 | Serge | 309 | |
3589 | Serge | 310 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: socket=%x length=%u flags=%x\n", eax, esi, dl |
3555 | Serge | 311 | |
312 | push eax ; save socket ptr |
||
313 | push esi ; and data length too |
||
314 | mov edi, sizeof.TCP_header ; edi will contain headersize |
||
315 | |||
316 | ;------------------------------------ |
||
317 | ; Send options with first SYN segment |
||
318 | |||
319 | test dl, TH_SYN |
||
320 | jz .options_done |
||
321 | |||
322 | push [eax + TCP_SOCKET.ISS] |
||
323 | pop [eax + TCP_SOCKET.SND_NXT] |
||
324 | |||
325 | test [eax + TCP_SOCKET.t_flags], TF_NOOPT |
||
326 | jnz .options_done |
||
327 | |||
328 | mov ecx, 1460 ;;;; FIXME: use routing blablabla to determine MSS |
||
329 | or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16 |
||
330 | bswap ecx |
||
331 | push ecx |
||
332 | add di, 4 |
||
333 | |||
3589 | Serge | 334 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added maxseg option\n" |
3555 | Serge | 335 | |
336 | test [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE |
||
337 | jz .no_scale |
||
338 | |||
339 | test dl, TH_ACK |
||
340 | jz .scale_opt |
||
341 | |||
342 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE |
||
343 | jz .no_scale |
||
344 | |||
345 | .scale_opt: |
||
346 | mov cl, [eax + TCP_SOCKET.request_r_scale] |
||
347 | mov ch, TCP_OPT_NOP |
||
348 | pushw cx |
||
349 | pushw TCP_OPT_WINDOW + 3 shl 8 |
||
350 | add di, 4 |
||
351 | |||
3589 | Serge | 352 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added scale option\n" |
3555 | Serge | 353 | |
354 | .no_scale: |
||
355 | .no_syn: |
||
356 | |||
357 | ;------------------------------------ |
||
358 | ; Make the timestamp option if needed |
||
359 | |||
360 | test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP |
||
361 | jz .no_timestamp |
||
362 | |||
363 | test dl, TH_RST |
||
364 | jnz .no_timestamp |
||
365 | |||
366 | test dl, TH_ACK |
||
367 | jz .timestamp |
||
368 | |||
369 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP |
||
370 | jz .no_timestamp |
||
371 | |||
372 | .timestamp: |
||
373 | pushd 0 |
||
374 | pushd [timer_ticks] |
||
375 | pushd TCP_OPT_NOP + TCP_OPT_NOP shl 8 + TCP_OPT_TIMESTAMP shl 16 + 10 shl 24 |
||
376 | add di, 12 |
||
377 | |||
3589 | Serge | 378 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added timestamp\n" |
3555 | Serge | 379 | |
380 | .no_timestamp: |
||
381 | |||
382 | ; |
||
383 | |||
384 | .options_done: |
||
385 | |||
386 | ; eax = socket ptr |
||
387 | ; edx = flags |
||
388 | ; edi = header size |
||
389 | ; esi = data len |
||
390 | |||
391 | ;--------------------------------------------- |
||
392 | ; check if we dont exceed the max segment size (270) |
||
393 | |||
394 | add esi, edi ; total TCP segment size |
||
395 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
||
396 | jbe .no_overflow |
||
397 | |||
398 | mov esi, [eax + TCP_SOCKET.t_maxseg] |
||
4423 | Serge | 399 | or [temp_bits], TCP_BIT_SENDALOT |
3555 | Serge | 400 | .no_overflow: |
401 | |||
5565 | serge | 402 | ; Update stats |
403 | test esi, esi |
||
404 | jz .zero_data |
||
405 | |||
406 | cmp [eax + TCP_SOCKET.t_force], 1 |
||
407 | jne @f |
||
408 | cmp esi, 1 |
||
409 | jne @f |
||
410 | inc [TCPS_sndprobe] |
||
411 | jmp .eos |
||
412 | @@: |
||
413 | |||
414 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
||
415 | cmp ebx, [eax + TCP_SOCKET.SND_MAX] |
||
416 | jae @f |
||
417 | inc [TCPS_sndrexmitpack] |
||
418 | add [TCPS_sndrexmitbyte], esi |
||
419 | jmp .eos |
||
420 | @@: |
||
421 | inc [TCPS_sndpack] |
||
422 | add [TCPS_sndbyte], esi |
||
423 | jmp .eos |
||
424 | |||
425 | .zero_data: |
||
426 | test [eax + TCP_SOCKET.t_flags], TF_ACKNOW |
||
427 | jz @f |
||
428 | inc [TCPS_sndacks] |
||
429 | jmp .eos |
||
430 | @@: |
||
431 | test dl, TH_SYN + TH_FIN + TH_RST |
||
432 | jz @f |
||
433 | inc [TCPS_sndctrl] |
||
434 | jmp .eos |
||
435 | @@: |
||
436 | mov ebx, [eax + TCP_SOCKET.SND_UP] |
||
437 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
||
438 | jb @f |
||
439 | inc [TCPS_sndurg] |
||
440 | jmp .eos |
||
441 | @@: |
||
442 | inc [TCPS_sndwinup] |
||
443 | |||
444 | .eos: |
||
445 | |||
4423 | Serge | 446 | ;---------------------------------------------------- |
447 | ; Calculate the receive window. |
||
448 | ; Dont shrink window, but avoid silly window syndrome |
||
449 | |||
450 | mov ebx, SOCKET_MAXDATA |
||
451 | sub ebx, [eax + STREAM_SOCKET.rcv.size] |
||
452 | |||
453 | cmp ebx, SOCKET_MAXDATA/4 |
||
454 | jae @f |
||
455 | cmp ebx, [eax + TCP_SOCKET.t_maxseg] |
||
456 | jae @f |
||
457 | xor ebx, ebx |
||
458 | @@: |
||
459 | |||
460 | cmp ebx, TCP_max_win |
||
461 | jbe @f |
||
462 | mov ebx, TCP_max_win |
||
463 | @@: |
||
464 | |||
465 | mov ecx, [eax + TCP_SOCKET.RCV_ADV] |
||
466 | sub ecx, [eax + TCP_SOCKET.RCV_NXT] |
||
467 | cmp ebx, ecx |
||
468 | ja @f |
||
469 | mov ebx, ecx |
||
470 | @@: |
||
471 | |||
472 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: window = %u\n", ebx |
||
473 | |||
474 | mov cl, [eax + TCP_SOCKET.RCV_SCALE] |
||
475 | shr ebx, cl |
||
476 | xchg bl, bh |
||
477 | |||
3555 | Serge | 478 | ;----------------------------------------------------------------- |
479 | ; Start by pushing all TCP header values in reverse order on stack |
||
480 | ; (essentially, creating the tcp header on the stack!) |
||
481 | |||
482 | pushw 0 ; .UrgentPointer dw ? |
||
483 | pushw 0 ; .Checksum dw ? |
||
4423 | Serge | 484 | pushw bx ; .Window dw ? |
3555 | Serge | 485 | shl edi, 2 ; .DataOffset db ? only 4 left-most bits |
486 | shl dx, 8 |
||
487 | or dx, di ; .Flags db ? |
||
488 | pushw dx |
||
489 | shr edi, 2 ; .DataOffset db ? |
||
490 | |||
491 | push [eax + TCP_SOCKET.RCV_NXT] ; .AckNumber dd ? |
||
492 | ntohd [esp] |
||
493 | |||
494 | push [eax + TCP_SOCKET.SND_NXT] ; .SequenceNumber dd ? |
||
495 | ntohd [esp] |
||
496 | |||
497 | push [eax + TCP_SOCKET.RemotePort] ; .DestinationPort dw ? |
||
498 | push [eax + TCP_SOCKET.LocalPort] ; .SourcePort dw ? |
||
499 | |||
500 | push edi ; header size |
||
501 | |||
502 | ;--------------------- |
||
503 | ; Create the IP packet |
||
504 | |||
505 | mov ecx, esi |
||
506 | mov edx, [eax + IP_SOCKET.LocalIP] ; source ip |
||
5594 | serge | 507 | mov ebx, [eax + IP_SOCKET.device] |
3555 | Serge | 508 | mov eax, [eax + IP_SOCKET.RemoteIP] ; dest ip |
509 | mov di, IP_PROTO_TCP shl 8 + 128 |
||
510 | call IPv4_output |
||
511 | jz .ip_error |
||
512 | |||
5565 | serge | 513 | ;------------------------------------------ |
514 | ; Move TCP header from stack to TCP segment |
||
3555 | Serge | 515 | |
516 | push ecx |
||
517 | mov ecx, [esp + 4] |
||
518 | lea esi, [esp + 8] |
||
519 | shr ecx, 2 ; count is in bytes, we will work with dwords |
||
3725 | Serge | 520 | rep movsd |
3555 | Serge | 521 | pop ecx ; full TCP packet size |
522 | |||
523 | pop esi ; headersize |
||
524 | add esp, esi ; remove it from stack |
||
525 | |||
526 | push eax ; packet ptr for send proc |
||
527 | |||
528 | mov edx, edi ; begin of data |
||
529 | sub edx, esi ; begin of packet (edi = begin of data) |
||
530 | push ecx |
||
531 | sub ecx, esi ; data size |
||
532 | |||
533 | ;-------------- |
||
534 | ; Copy the data |
||
535 | |||
536 | ; eax = ptr to ring struct |
||
537 | ; ecx = buffer size |
||
538 | ; edi = ptr to buffer |
||
539 | |||
5565 | serge | 540 | mov eax, [esp + 12] ; get socket ptr |
3555 | Serge | 541 | |
542 | push edx |
||
543 | push [eax + TCP_SOCKET.SND_NXT] ; we'll need this for timing the transmission |
||
544 | test ecx, ecx |
||
545 | jz .nodata |
||
546 | mov edx, [eax + TCP_SOCKET.SND_NXT] |
||
5565 | serge | 547 | add [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number |
3555 | Serge | 548 | sub edx, [eax + TCP_SOCKET.SND_UNA] ; offset |
549 | add eax, STREAM_SOCKET.snd |
||
550 | call SOCKET_ring_read |
||
551 | .nodata: |
||
552 | pop edi |
||
553 | pop esi ; begin of data |
||
554 | pop ecx ; full packet size |
||
5565 | serge | 555 | mov eax, [esp + 8] ; socket ptr |
3555 | Serge | 556 | |
557 | ;---------------------------------- |
||
558 | ; initialize retransmit timer (400) |
||
559 | |||
560 | ;TODO: check t_force and persist |
||
561 | |||
562 | test [esi + TCP_header.Flags], TH_SYN + TH_FIN ; syn and fin take a sequence number |
||
563 | jz @f |
||
564 | inc [eax + TCP_SOCKET.SND_NXT] |
||
565 | test [esi + TCP_header.Flags], TH_FIN |
||
566 | jz @f |
||
567 | or [eax + TCP_SOCKET.t_flags], TF_SENTFIN ; if we sent a fin, set the sentfin flag |
||
568 | @@: |
||
569 | |||
570 | mov edx, [eax + TCP_SOCKET.SND_NXT] |
||
571 | cmp edx, [eax + TCP_SOCKET.SND_MAX] ; is this a retransmission? |
||
572 | jbe @f |
||
573 | mov [eax + TCP_SOCKET.SND_MAX], edx ; [eax + TCP_SOCKET.SND_NXT] from before we updated it |
||
574 | |||
575 | cmp [eax + TCP_SOCKET.t_rtt], 0 ; are we currently timing anything? |
||
576 | je @f |
||
577 | mov [eax + TCP_SOCKET.t_rtt], 1 ; nope, start transmission timer |
||
578 | mov [eax + TCP_SOCKET.t_rtseq], edi |
||
5565 | serge | 579 | inc [TCPS_segstimed] |
3555 | Serge | 580 | @@: |
581 | |||
582 | ; set retransmission timer if not already set, and not doing an ACK or keepalive probe |
||
3626 | Serge | 583 | test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission |
584 | jnz .retransmit_set |
||
3555 | Serge | 585 | |
586 | cmp edx, [eax + TCP_SOCKET.SND_UNA] ; edx is still [eax + TCP_SOCKET.SND_NXT] |
||
587 | je .retransmit_set |
||
588 | |||
589 | mov edx, [eax + TCP_SOCKET.t_rxtcur] |
||
590 | mov [eax + TCP_SOCKET.timer_retransmission], edx |
||
3626 | Serge | 591 | or [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission |
3555 | Serge | 592 | |
3626 | Serge | 593 | test [eax + TCP_SOCKET.timer_flags], timer_flag_persist |
594 | jz .retransmit_set |
||
595 | and [eax + TCP_SOCKET.timer_flags], not timer_flag_persist |
||
3555 | Serge | 596 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
597 | |||
598 | .retransmit_set: |
||
599 | |||
600 | ;-------------------- |
||
601 | ; Create the checksum |
||
602 | |||
4423 | Serge | 603 | xor dx, dx |
604 | test [ebx + NET_DEVICE.hwacc], NET_HWACC_TCP_IPv4_OUT |
||
605 | jnz .checksum_ok |
||
606 | |||
3555 | Serge | 607 | TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP) |
4423 | Serge | 608 | |
609 | .checksum_ok: |
||
3555 | Serge | 610 | mov [esi + TCP_header.Checksum], dx |
611 | |||
612 | ;---------------- |
||
613 | ; Send the packet |
||
614 | |||
3589 | Serge | 615 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: Sending with device %x\n", ebx |
3555 | Serge | 616 | call [ebx + NET_DEVICE.transmit] |
617 | jnz .send_error |
||
618 | |||
619 | ;--------------- |
||
620 | ; Ok, data sent! |
||
621 | |||
622 | pop ecx |
||
623 | pop eax |
||
624 | |||
3725 | Serge | 625 | call NET_ptr_to_num4 |
626 | inc [TCP_segments_tx + edi] |
||
5565 | serge | 627 | inc [TCPS_sndtotal] |
3555 | Serge | 628 | |
629 | ; update advertised receive window |
||
630 | test ecx, ecx |
||
631 | jz @f |
||
632 | add ecx, [eax + TCP_SOCKET.RCV_NXT] |
||
633 | cmp ecx, [eax + TCP_SOCKET.RCV_ADV] |
||
634 | jbe @f |
||
635 | mov [eax + TCP_SOCKET.RCV_ADV], ecx |
||
636 | @@: |
||
637 | |||
638 | ; update last ack sent |
||
639 | push [eax + TCP_SOCKET.RCV_NXT] |
||
640 | pop [eax + TCP_SOCKET.last_ack_sent] |
||
641 | |||
4423 | Serge | 642 | ; clear the ACK flags |
3555 | Serge | 643 | and [eax + TCP_SOCKET.t_flags], not (TF_ACKNOW + TF_DELACK) |
644 | |||
645 | ;-------------- |
||
646 | ; unlock socket |
||
647 | |||
3725 | Serge | 648 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: unlocking socket 0x%x\n", eax |
649 | |||
3555 | Serge | 650 | push eax |
651 | lea ecx, [eax + SOCKET.mutex] |
||
652 | call mutex_unlock |
||
653 | pop eax |
||
654 | |||
655 | ;----------------------------- |
||
656 | ; Check if we need more output |
||
657 | |||
4423 | Serge | 658 | test [temp_bits], TCP_BIT_SENDALOT |
3555 | Serge | 659 | jnz TCP_output.again |
660 | |||
3589 | Serge | 661 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: success!\n" |
3555 | Serge | 662 | |
663 | xor eax, eax |
||
664 | ret |
||
665 | |||
666 | |||
667 | .ip_error: |
||
668 | pop ecx |
||
669 | add esp, ecx |
||
670 | add esp, 4 |
||
671 | pop eax |
||
672 | |||
673 | mov [eax + TCP_SOCKET.timer_retransmission], TCP_time_re_min |
||
3626 | Serge | 674 | or [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission |
3555 | Serge | 675 | |
676 | lea ecx, [eax + SOCKET.mutex] |
||
677 | call mutex_unlock |
||
678 | |||
3626 | Serge | 679 | DEBUGF DEBUG_NETWORK_ERROR, "TCP_send: IP error\n" |
3555 | Serge | 680 | |
681 | or eax, -1 |
||
682 | ret |
||
683 | |||
684 | |||
685 | .send_error: |
||
3725 | Serge | 686 | add esp, 4 |
687 | pop eax |
||
3555 | Serge | 688 | |
689 | lea ecx, [eax + SOCKET.mutex] |
||
690 | call mutex_unlock |
||
691 | |||
3626 | Serge | 692 | DEBUGF DEBUG_NETWORK_ERROR, "TCP_send: sending failed\n" |
3555 | Serge | 693 | |
694 | or eax, -2 |
||
695 | ret |
||
696 | |||
697 | |||
4423 | Serge | 698 | endp |