Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | 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:
36
	mov	ebx, [eax + TCP_SOCKET.SND_NXT] 	; calculate offset
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
 
45
	call	TCP_outflags	; in dl
46
 
47
; If in persist timeout with window of 0, send 1 byte.
48
; Otherwise, if window is small but nonzero, and timer expired,
49
; we will send what we can and go to transmit state
50
 
51
	test	[eax + TCP_SOCKET.t_force], -1
52
	jz	.no_persist_timeout
53
 
54
	test	ecx, ecx
55
	jnz	.no_zero_window
56
 
57
	cmp	ebx, [eax + STREAM_SOCKET.snd + RING_BUFFER.size]
58
	jge	@f
59
 
60
	and	dl, not (TH_FIN)	  ; clear the FIN flag    ??? how can it be set before?
61
 
62
       @@:
63
	inc	ecx
64
	jmp	.no_persist_timeout
65
 
66
  .no_zero_window:
67
 
68
	mov	[eax + TCP_SOCKET.timer_persist], 0
69
	mov	[eax + TCP_SOCKET.t_rxtshift], 0
70
 
71
  .no_persist_timeout:
72
 
73
;;;106
74
 
75
	mov	esi, [eax + STREAM_SOCKET.snd + RING_BUFFER.size]
76
	cmp	esi, ecx
77
	jl	@f
78
	mov	esi, ecx
79
       @@:
80
	sub	esi, ebx
81
 
82
	cmp	esi, -1
83
	jne	.not_minus_one
84
 
85
; If FIN has been set, but not ACKed, and we havent been called to retransmit,
86
; len (esi) will be -1
87
; Otherwise, window shrank after we sent into it.
88
; If window shrank to 0, cancel pending retransmit and pull SND_NXT back to (closed) window
89
; We will enter persist state below.
90
; If window didn't close completely, just wait for an ACK
91
 
92
	xor	esi, esi
93
 
94
	test	ecx, ecx
95
	jnz	@f
96
 
97
	mov	[eax + TCP_SOCKET.timer_retransmission], 0   ; cancel retransmit
98
 
99
	push	[eax + TCP_SOCKET.SND_UNA]
100
	pop	[eax + TCP_SOCKET.SND_NXT]
101
       @@:
102
 
103
  .not_minus_one:
104
 
105
;;; 124
106
 
107
	cmp	esi, [eax + TCP_SOCKET.t_maxseg]
108
	jle	@f
109
 
110
	mov	esi, [eax + TCP_SOCKET.t_maxseg]
111
	;sendalot = 1
112
 
113
       @@:
114
 
115
;;; 128
116
 
117
	mov	edi, [eax + TCP_SOCKET.SND_NXT]
118
	add	edi, esi	; len
119
	sub	edi, [eax + TCP_SOCKET.SND_UNA]
120
	add	edi, [eax + STREAM_SOCKET.snd + RING_BUFFER.size]
121
	cmp	edi, 0
122
	jle	@f
123
 
124
	and	dl, not (TH_FIN)	  ; clear the FIN flag
125
 
126
       @@:
127
 
128
 
129
; set ecx to space available in receive buffer
130
; From now on, ecx will be the window we advertise to the other end
131
 
132
	mov	ecx, SOCKET_MAXDATA
133
	sub	ecx, [eax + STREAM_SOCKET.rcv + RING_BUFFER.size]
134
 
135
;------------------------------
136
; Sender silly window avoidance
137
 
138
	cmp	ecx, [eax + TCP_SOCKET.t_maxseg]
139
	je	.send
140
 
141
;;; TODO: 144-145
142
 
143
	test	[eax + TCP_SOCKET.t_force], -1
144
	jnz	.send
145
 
146
	mov	ebx, [eax + TCP_SOCKET.max_sndwnd]
147
	shr	ebx, 1
148
	cmp	ecx, ebx
149
	jge	.send
150
 
151
	mov	ebx, [eax + TCP_SOCKET.SND_NXT]
152
	cmp	ebx, [eax + TCP_SOCKET.SND_MAX]
153
	jl	.send
154
 
155
;----------------------------------------
156
; Check if a window update should be sent
157
 
158
	test	ecx, ecx	; window
159
	jz	.no_window
160
 
161
;;; TODO 154-172
162
 
163
  .no_window:
164
 
165
;--------------------------
166
; Should a segment be sent?
167
 
168
	test	[eax + TCP_SOCKET.t_flags], TF_ACKNOW
169
	jnz	.send
170
 
171
	test	dl, TH_SYN + TH_RST
172
	jnz	.send
173
 
174
	mov	ebx, [eax + TCP_SOCKET.SND_UP]
175
	cmp	ebx, [eax + TCP_SOCKET.SND_UNA]
176
	jg	.send
177
 
178
	test	dl, TH_FIN
179
	jz	.enter_persist
180
 
181
	test	[eax + TCP_SOCKET.t_flags], TF_SENTFIN
182
	jnz	.send
183
 
184
	mov	ebx, [eax + TCP_SOCKET.SND_NXT]
185
	cmp	ebx, [eax + TCP_SOCKET.SND_UNA]
186
	je	.send
187
 
188
;--------------------
189
; Enter persist state
190
 
191
  .enter_persist:
192
 
193
	DEBUGF	1,"Entering persist state\n"
194
 
195
;--------------------------------------
196
; No reason to send a segment, just ret
197
 
198
	DEBUGF	1,"No reason to send a segment\n"
199
 
200
	mov	[ebx + SOCKET.lock], 0
201
 
202
	ret
203
 
204
 
205
;-----------------------------------------------
206
;
207
; Send a segment
208
;
209
; eax = socket pointer
210
;  dl = flags
211
;
212
;-----------------------------------------------
213
 
214
  .send:
215
 
216
	DEBUGF	1,"Preparing to send a segment\n"
217
 
218
	mov	edi, TCP_segment.Data	; edi will contain headersize
219
 
220
	sub	esp, 8			; create some space on stack
221
	push	eax			; save this too..
222
 
223
;------------------------------------
224
; Send options with first SYN segment
225
 
226
	test	dl, TH_SYN
227
	jz	.no_options
228
 
229
	push	[eax + TCP_SOCKET.ISS]
230
	pop	[eax + TCP_SOCKET.SND_NXT]
231
 
232
	test	[eax + TCP_SOCKET.t_flags], TF_NOOPT
233
	jnz	.no_options
234
 
235
	mov	ecx, 1460
236
	or	ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16
237
	bswap	ecx
238
	push	ecx
239
	add	di, 4
240
 
241
	test	[eax + TCP_SOCKET.t_flags], TF_REQ_SCALE
242
	jz	.no_syn
243
 
244
	test	dl, TH_ACK
245
	jnz	.scale_opt
246
 
247
	test	[eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE
248
	jz	.no_syn
249
 
250
  .scale_opt:
251
	movzx	ecx, byte [eax + TCP_SOCKET.request_r_scale]
252
	or	ecx, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP shl 8
253
	bswap	ecx
254
	pushd	ecx
255
	add	di, 4
256
 
257
  .no_syn:
258
 
259
;------------------------------------
260
; Make the timestamp option if needed
261
 
262
	test	[eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP
263
	jz	.no_timestamp
264
 
265
	test	dl, TH_RST
266
	jnz	.no_timestamp
267
 
268
	test	dl, TH_ACK
269
	jz	.timestamp
270
 
271
	test	[eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
272
	jz	.no_timestamp
273
 
274
  .timestamp:
275
	mov	esi, [timer_ticks]
276
	bswap	esi
277
	push	esi
278
	pushw	0
279
	pushd	TCP_OPT_TIMESTAMP + 10 shl 8 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24
280
	add	di, 10
281
 
282
  .no_timestamp:
283
	;; TODO: check if we dont exceed the max segment size
284
 
285
  .no_options:
286
	; eax = socket ptr
287
	; edx = flags
288
	; ecx = data size
289
	; edi = header size
290
	; esi = snd ring buff ptr
291
 
292
	mov	ecx, [eax + STREAM_SOCKET.snd + RING_BUFFER.size]
293
	cmp	ecx, [eax + TCP_SOCKET.t_maxseg]			;;; right?
294
	jle	@f
295
	mov	ecx, [eax + TCP_SOCKET.t_maxseg]
296
       @@:
297
	add	ecx, edi	; total TCP segment size
298
 
299
; Start by pushing all TCP header values in reverse order on stack
300
; (essentially, creating the tcp header!)
301
 
302
	pushw	0	;        .UrgentPointer          dw ?
303
	pushw	0	;        .Checksum               dw ?
304
	pushw	0x00a0	;        .Window                 dw ?    ;;;;;;;
305
	shl	edi, 2	;        .DataOffset             db ?  only 4 left-most bits
306
	shl	dx, 8
307
	or	dx, di	;        .Flags                  db ?
308
	pushw	dx
309
	shr	edi, 2	;        .DataOffset             db ? ;;;;
310
 
311
	push	[eax + TCP_SOCKET.RCV_NXT]	;        .AckNumber              dd ?
312
	ntohd	[esp]
313
 
314
	push	[eax + TCP_SOCKET.SND_NXT]	;        .SequenceNumber         dd ?
315
	ntohd	[esp]
316
 
317
	push	[eax + TCP_SOCKET.RemotePort]	;        .DestinationPort        dw ?
318
	ntohw	[esp]
319
 
320
	push	[eax + TCP_SOCKET.LocalPort]	;        .SourcePort             dw ?
321
	ntohw	[esp]
322
 
323
	push	edi		; header size
324
 
325
; Create the IP packet
326
	mov	ebx, [eax + IP_SOCKET.LocalIP]	; source ip
327
	mov	eax, [eax + IP_SOCKET.RemoteIP] ; dest ip
328
	mov	di, IP_PROTO_TCP shl 8 + 128
329
	call	IPv4_output
330
	jz	.fail
331
 
332
;-----------------------------------------
333
; Move TCP header from stack to TCP packet
334
 
335
	push	ecx
336
	mov	ecx, [esp+4]
337
	lea	esi, [esp+4+4]
338
	shr	ecx, 2
339
	rep	movsd
340
	pop	ecx		; full TCP packet size
341
 
342
	pop	esi		; headersize
343
	add	esp, esi
344
 
345
	mov	[esp + 4], eax		; packet ptr
346
	mov	[esp + 4+4], edx	; packet size
347
 
348
	mov	edx, edi		; begin of data
349
	sub	edx, esi		; begin of packet (edi = begin of data)
350
	push	ecx
351
	sub	ecx, esi		; data size
352
 
353
;--------------
354
; Copy the data
355
 
356
; eax = ptr to ring struct
357
; ecx = buffer size
358
; edi = ptr to buffer
359
 
360
;        test    ecx, ecx
361
	mov	eax, [esp+4]		  ; socket ptr
362
	add	[eax + TCP_SOCKET.SND_NXT], ecx
363
	add	eax, STREAM_SOCKET.snd
364
	push	edx
365
	call	SOCKET_ring_read
366
	pop	esi
367
	pop	ecx
368
	pop	eax
369
 
370
	test	[esi + TCP_segment.Flags], TH_SYN + TH_FIN
371
	jz	@f
372
	inc	[eax + TCP_SOCKET.SND_NXT]
373
	;;; TODO: update sentfin flag
374
       @@:
375
 
376
	mov	edx, [eax + TCP_SOCKET.SND_NXT]
377
	cmp	edx, [eax + TCP_SOCKET.SND_MAX]
378
	jle	@f
379
	mov	[eax + TCP_SOCKET.SND_MAX], edx
380
 
381
	;;;; TODO: time transmission (420)
382
       @@:
383
 
384
	;;; TODO: set retransmission timer
385
 
386
;--------------------
387
; Create the checksum
388
 
389
	DEBUGF	1,"checksum: ptr=%x size=%u\n", esi, ecx
390
 
391
	TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP)
392
	mov	[esi+TCP_segment.Checksum], dx
393
 
394
;----------------
395
; Send the packet
396
 
397
	DEBUGF	1,"Sending TCP Packet to device %x\n", ebx
398
	call	[ebx + NET_DEVICE.transmit]
399
	ret
400
 
401
 
402
  .fail:
403
	pop	ecx
404
	add	esp, ecx
405
	add	esp, 4+8
406
	DEBUGF 1,"TCP_output: failed\n"
407
	ret
408