Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
1763 hidnplayr 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                                 ;;
2362 hidnplayr 3
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved.    ;;
1763 hidnplayr 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: 2362 $
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]
2300 hidnplayr 44
	jbe	.not_idle
1733 hidnplayr 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]	;
2300 hidnplayr 59
	jb	@f					;
1733 hidnplayr 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
 
2362 hidnplayr 78
	cmp	ebx, [eax + STREAM_SOCKET.snd.size]
2300 hidnplayr 79
	jae	@f
1733 hidnplayr 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
 
2362 hidnplayr 96
	mov	esi, [eax + STREAM_SOCKET.snd.size]
1733 hidnplayr 97
	cmp	esi, ecx
2300 hidnplayr 98
	jb	@f
1733 hidnplayr 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]
2300 hidnplayr 135
	jbe	@f
1733 hidnplayr 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]
2362 hidnplayr 149
	sub	edi, [eax + STREAM_SOCKET.snd.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
2362 hidnplayr 160
	sub	ecx, [eax + STREAM_SOCKET.rcv.size]
1733 hidnplayr 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
 
2362 hidnplayr 171
	test	[eax + TCP_SOCKET.t_flags], TF_NODELAY
172
	jnz	@f
173
	; TODO: if not 'idle', skip to next codeblock
174
       @@:
175
	add	ebx, esi
176
	cmp	ebx, [eax + STREAM_SOCKET.snd.size]
177
	jae	.send
1733 hidnplayr 178
 
1761 hidnplayr 179
	test	[eax + TCP_SOCKET.t_force], -1	;;;
1733 hidnplayr 180
	jnz	.send
181
 
182
	mov	ebx, [eax + TCP_SOCKET.max_sndwnd]
183
	shr	ebx, 1
1761 hidnplayr 184
	cmp	esi, ebx
2300 hidnplayr 185
	jae	.send
1733 hidnplayr 186
 
187
	mov	ebx, [eax + TCP_SOCKET.SND_NXT]
188
	cmp	ebx, [eax + TCP_SOCKET.SND_MAX]
2300 hidnplayr 189
	jb	.send
1733 hidnplayr 190
 
1761 hidnplayr 191
  .len_zero:
192
 
1733 hidnplayr 193
;----------------------------------------
1761 hidnplayr 194
; Check if a window update should be sent (154)
1733 hidnplayr 195
 
1761 hidnplayr 196
	test	ecx, ecx
1733 hidnplayr 197
	jz	.no_window
198
 
2362 hidnplayr 199
	push	ecx
200
	mov	cl, [eax + TCP_SOCKET.RCV_SCALE]
201
	inc	cl					; we want it *2
202
	mov	ebx, TCP_max_win
203
	shl	ebx, cl
204
	pop	ecx
205
	cmp	ebx, ecx
206
	cmovb	ebx, ecx
1733 hidnplayr 207
 
2362 hidnplayr 208
	; now ebx is TWICE the amount we can increase the window
209
	; (with TCP_max_win shl rcv_scale as the maximum)
210
 
211
	cmp	ebx, [eax + TCP_SOCKET.t_maxseg]
212
	jae	.send
213
 
214
  ;;;      cmp     ebx, [eax + ]                        ;;; TODO: check receive buffer high water mark
215
    ;;;    jae     .send
216
 
1733 hidnplayr 217
  .no_window:
218
 
219
;--------------------------
1761 hidnplayr 220
; Should a segment be sent? (174)
1733 hidnplayr 221
 
1761 hidnplayr 222
	test	[eax + TCP_SOCKET.t_flags], TF_ACKNOW	; we need to ACK
1733 hidnplayr 223
	jnz	.send
224
 
1761 hidnplayr 225
	test	dl, TH_SYN + TH_RST			; we need to send a SYN or RST
1733 hidnplayr 226
	jnz	.send
227
 
1761 hidnplayr 228
	mov	ebx, [eax + TCP_SOCKET.SND_UP]		; when urgent pointer is beyond start of send bufer
1733 hidnplayr 229
	cmp	ebx, [eax + TCP_SOCKET.SND_UNA]
2300 hidnplayr 230
	ja	.send
1733 hidnplayr 231
 
232
	test	dl, TH_FIN
1761 hidnplayr 233
	jz	.enter_persist	; no reason to send, enter persist state
1733 hidnplayr 234
 
1761 hidnplayr 235
; FIN was set, only send if not already sent, or on retransmit
236
 
1733 hidnplayr 237
	test	[eax + TCP_SOCKET.t_flags], TF_SENTFIN
238
	jnz	.send
239
 
240
	mov	ebx, [eax + TCP_SOCKET.SND_NXT]
241
	cmp	ebx, [eax + TCP_SOCKET.SND_UNA]
242
	je	.send
243
 
244
;--------------------
1761 hidnplayr 245
; Enter persist state (191)
1733 hidnplayr 246
 
247
  .enter_persist:
248
 
2362 hidnplayr 249
	cmp	[eax + STREAM_SOCKET.snd.size], 0	; Data ready to send?
250
	jne	@f
251
	cmp	[eax + TCP_SOCKET.timer_retransmission], 0
252
	jne	@f
253
	cmp	[eax + TCP_SOCKET.timer_persist], 0	; Persist timer already expired?
254
	jne	@f
255
 
1733 hidnplayr 256
	DEBUGF	1,"Entering persist state\n"
257
 
2362 hidnplayr 258
	mov	[eax + TCP_SOCKET.t_rxtshift], 0
259
	TCP_set_persist eax
260
       @@:
1733 hidnplayr 261
 
1761 hidnplayr 262
;----------------------------
263
; No reason to send a segment (219)
264
 
1733 hidnplayr 265
	DEBUGF	1,"No reason to send a segment\n"
266
 
1761 hidnplayr 267
	mov	[eax + SOCKET.lock], 0
1733 hidnplayr 268
 
269
	ret
270
 
271
 
1761 hidnplayr 272
 
273
 
274
 
275
 
276
 
277
 
278
 
1733 hidnplayr 279
;-----------------------------------------------
280
;
1761 hidnplayr 281
; Send a segment (222)
1733 hidnplayr 282
;
283
; eax = socket pointer
1761 hidnplayr 284
; esi = data len
1733 hidnplayr 285
;  dl = flags
286
;
287
;-----------------------------------------------
288
 
289
  .send:
290
 
291
	DEBUGF	1,"Preparing to send a segment\n"
292
 
2362 hidnplayr 293
	mov	edi, sizeof.TCP_header	; edi will contain headersize
1733 hidnplayr 294
 
295
	sub	esp, 8			; create some space on stack
1761 hidnplayr 296
	push	eax			; save socket pointer
1733 hidnplayr 297
 
298
;------------------------------------
299
; Send options with first SYN segment
300
 
301
	test	dl, TH_SYN
1761 hidnplayr 302
	jz	.options_done
1733 hidnplayr 303
 
304
	push	[eax + TCP_SOCKET.ISS]
305
	pop	[eax + TCP_SOCKET.SND_NXT]
306
 
307
	test	[eax + TCP_SOCKET.t_flags], TF_NOOPT
1761 hidnplayr 308
	jnz	.options_done
1733 hidnplayr 309
 
2301 hidnplayr 310
	mov	ecx, 1460			       ;;;; FIXME
1733 hidnplayr 311
	or	ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16
312
	bswap	ecx
313
	push	ecx
314
	add	di, 4
315
 
316
	test	[eax + TCP_SOCKET.t_flags], TF_REQ_SCALE
317
	jz	.no_syn
318
 
319
	test	dl, TH_ACK
320
	jnz	.scale_opt
321
 
322
	test	[eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE
323
	jz	.no_syn
324
 
325
  .scale_opt:
326
	movzx	ecx, byte [eax + TCP_SOCKET.request_r_scale]
327
	or	ecx, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP shl 8
328
	bswap	ecx
329
	pushd	ecx
330
	add	di, 4
331
 
332
  .no_syn:
333
 
334
;------------------------------------
335
; Make the timestamp option if needed
336
 
337
	test	[eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP
338
	jz	.no_timestamp
339
 
340
	test	dl, TH_RST
341
	jnz	.no_timestamp
342
 
343
	test	dl, TH_ACK
344
	jz	.timestamp
345
 
346
	test	[eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
347
	jz	.no_timestamp
348
 
349
  .timestamp:
1761 hidnplayr 350
	mov	ebx, [timer_ticks]
351
	bswap	ebx
352
	push	ebx
1733 hidnplayr 353
	pushw	0
354
	pushd	TCP_OPT_TIMESTAMP + 10 shl 8 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24
355
	add	di, 10
356
 
357
  .no_timestamp:
358
 
1761 hidnplayr 359
	; 
1733 hidnplayr 360
 
361
 
1761 hidnplayr 362
 
363
 
364
 
365
 
366
 
367
 
368
  .options_done:
369
 
370
; eax = socket ptr
371
; edx = flags
372
; edi = header size
373
; esi = data len
374
 
375
;---------------------------------------------
376
; check if we dont exceed the max segment size (270)
377
 
378
	add	esi, edi			; total TCP segment size
379
	cmp	esi, [eax + TCP_SOCKET.t_maxseg]
2300 hidnplayr 380
	jbe	.no_overflow
1761 hidnplayr 381
 
382
	mov	esi, [eax + TCP_SOCKET.t_maxseg]
383
 
384
;;; sendalot = 1
385
 
386
  .no_overflow:
387
 
388
;-----------------------------------------------------------------
1733 hidnplayr 389
; Start by pushing all TCP header values in reverse order on stack
1761 hidnplayr 390
; (essentially, creating the tcp header on the stack!)
1733 hidnplayr 391
 
392
	pushw	0	;        .UrgentPointer          dw ?
393
	pushw	0	;        .Checksum               dw ?
1830 hidnplayr 394
	pushw	0x00a0	;        .Window                 dw ?    ;;;;;;; FIXME
1733 hidnplayr 395
	shl	edi, 2	;        .DataOffset             db ?  only 4 left-most bits
396
	shl	dx, 8
397
	or	dx, di	;        .Flags                  db ?
398
	pushw	dx
399
	shr	edi, 2	;        .DataOffset             db ? ;;;;
400
 
401
	push	[eax + TCP_SOCKET.RCV_NXT]	;        .AckNumber              dd ?
402
	ntohd	[esp]
403
 
404
	push	[eax + TCP_SOCKET.SND_NXT]	;        .SequenceNumber         dd ?
405
	ntohd	[esp]
406
 
407
	push	[eax + TCP_SOCKET.RemotePort]	;        .DestinationPort        dw ?
408
	ntohw	[esp]
409
 
410
	push	[eax + TCP_SOCKET.LocalPort]	;        .SourcePort             dw ?
411
	ntohw	[esp]
412
 
413
	push	edi		; header size
414
 
1761 hidnplayr 415
;---------------------
1733 hidnplayr 416
; Create the IP packet
1761 hidnplayr 417
 
418
	mov	ecx, esi
419
 
1733 hidnplayr 420
	mov	ebx, [eax + IP_SOCKET.LocalIP]	; source ip
421
	mov	eax, [eax + IP_SOCKET.RemoteIP] ; dest ip
422
	mov	di, IP_PROTO_TCP shl 8 + 128
423
	call	IPv4_output
424
	jz	.fail
425
 
426
;-----------------------------------------
427
; Move TCP header from stack to TCP packet
428
 
429
	push	ecx
430
	mov	ecx, [esp+4]
431
	lea	esi, [esp+4+4]
432
	shr	ecx, 2
433
	rep	movsd
434
	pop	ecx		; full TCP packet size
435
 
436
	pop	esi		; headersize
437
	add	esp, esi
438
 
439
	mov	[esp + 4], eax		; packet ptr
440
	mov	[esp + 4+4], edx	; packet size
441
 
442
	mov	edx, edi		; begin of data
443
	sub	edx, esi		; begin of packet (edi = begin of data)
444
	push	ecx
445
	sub	ecx, esi		; data size
446
 
447
;--------------
448
; Copy the data
449
 
450
; eax = ptr to ring struct
451
; ecx = buffer size
452
; edi = ptr to buffer
453
 
1761 hidnplayr 454
	mov	eax, [esp+4]			; get socket ptr
455
 
456
	add	[eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number
457
 
1733 hidnplayr 458
	add	eax, STREAM_SOCKET.snd
459
	push	edx
460
	call	SOCKET_ring_read
1761 hidnplayr 461
	pop	esi				; begin of data
462
	pop	ecx				; full packet size
463
	pop	eax				; socket ptr
1733 hidnplayr 464
 
1761 hidnplayr 465
;----------------------------------
466
; update sequence number and timers  (400)
467
 
2305 hidnplayr 468
	test	[esi + TCP_header.Flags], TH_SYN + TH_FIN
1733 hidnplayr 469
	jz	@f
1761 hidnplayr 470
	inc	[eax + TCP_SOCKET.SND_NXT]	; syn and fin take a sequence number
2305 hidnplayr 471
	test	[esi + TCP_header.Flags], TH_FIN
1761 hidnplayr 472
	jz	@f
473
	or	[eax + TCP_SOCKET.t_flags], TF_SENTFIN	; if we sent a fin, set the sentfin flag
1733 hidnplayr 474
       @@:
475
 
476
	mov	edx, [eax + TCP_SOCKET.SND_NXT]
477
	cmp	edx, [eax + TCP_SOCKET.SND_MAX]
2300 hidnplayr 478
	jbe	@f
1733 hidnplayr 479
	mov	[eax + TCP_SOCKET.SND_MAX], edx
480
 
481
	;;;; TODO: time transmission (420)
1761 hidnplayr 482
 
1733 hidnplayr 483
       @@:
484
 
1761 hidnplayr 485
; set retransmission timer if not already set, and not doing an ACK or keepalive probe
1733 hidnplayr 486
 
2301 hidnplayr 487
	cmp	[eax + TCP_SOCKET.timer_retransmission], 1000 ;;;; FIXME
2300 hidnplayr 488
	jb	.retransmit_set
1761 hidnplayr 489
 
490
	cmp	edx, [eax + TCP_SOCKET.SND_UNA] 	; edx = [eax + TCP_SOCKET.SND_NXT]
491
	je	.retransmit_set
492
 
493
	mov	edx, [eax + TCP_SOCKET.t_rxtcur]
494
	mov	[eax + TCP_SOCKET.timer_retransmission], dx
495
 
2362 hidnplayr 496
	cmp	[eax + TCP_SOCKET.timer_persist], 0
497
	jne	@f
1761 hidnplayr 498
	mov	[eax + TCP_SOCKET.timer_persist], 0
2362 hidnplayr 499
	mov	[eax + TCP_SOCKET.t_rxtshift], 0
500
       @@:
1761 hidnplayr 501
 
502
  .retransmit_set:
503
 
1733 hidnplayr 504
;--------------------
505
; Create the checksum
506
 
507
	DEBUGF	1,"checksum: ptr=%x size=%u\n", esi, ecx
508
 
509
	TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP)
2305 hidnplayr 510
	mov	[esi + TCP_header.Checksum], dx
1733 hidnplayr 511
 
1761 hidnplayr 512
; unlock socket
513
 
514
	mov	[eax + SOCKET.lock], 0
515
 
1733 hidnplayr 516
;----------------
517
; Send the packet
518
 
519
	DEBUGF	1,"Sending TCP Packet to device %x\n", ebx
520
	call	[ebx + NET_DEVICE.transmit]
521
	ret
522
 
523
 
524
  .fail:
525
	pop	ecx
526
	add	esp, ecx
1838 hidnplayr 527
	pop	eax
528
	add	esp, 8
1761 hidnplayr 529
	mov	[eax + SOCKET.lock], 0
1733 hidnplayr 530
	DEBUGF 1,"TCP_output: failed\n"
531
	ret
532