Subversion Repositories Kolibri OS

Rev

Rev 1830 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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