Subversion Repositories Kolibri OS

Rev

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