Subversion Repositories Kolibri OS

Rev

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