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 |