Rev 3545 | Rev 3600 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 3545 | Rev 3556 | ||
---|---|---|---|
1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
2 | ;; ;; |
2 | ;; ;; |
3 | ;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
3 | ;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
4 | ;; Distributed under terms of the GNU General Public License ;; |
4 | ;; Distributed under terms of the GNU General Public License ;; |
5 | ;; ;; |
5 | ;; ;; |
6 | ;; Part of the TCP/IP network stack for KolibriOS ;; |
6 | ;; Part of the TCP/IP network stack for KolibriOS ;; |
7 | ;; ;; |
7 | ;; ;; |
8 | ;; Written by hidnplayr@kolibrios.org ;; |
8 | ;; Written by hidnplayr@kolibrios.org ;; |
9 | ;; ;; |
9 | ;; ;; |
10 | ;; Based on the code of 4.4BSD ;; |
10 | ;; Based on the code of 4.4BSD ;; |
11 | ;; ;; |
11 | ;; ;; |
12 | ;; GNU GENERAL PUBLIC LICENSE ;; |
12 | ;; GNU GENERAL PUBLIC LICENSE ;; |
13 | ;; Version 2, June 1991 ;; |
13 | ;; Version 2, June 1991 ;; |
14 | ;; ;; |
14 | ;; ;; |
15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
16 | 16 | ||
17 | $Revision: 3289 $ |
17 | $Revision: 3289 $ |
18 | 18 | ||
19 | ;----------------------------------------------------------------- |
19 | ;----------------------------------------------------------------- |
20 | ; |
20 | ; |
21 | ; TCP_output |
21 | ; TCP_output |
22 | ; |
22 | ; |
23 | ; IN: eax = socket pointer |
23 | ; IN: eax = socket pointer |
24 | ; |
24 | ; |
25 | ; OUT: / |
25 | ; OUT: / |
26 | ; |
26 | ; |
27 | ;----------------------------------------------------------------- |
27 | ;----------------------------------------------------------------- |
28 | align 4 |
28 | align 4 |
29 | TCP_output: |
29 | TCP_output: |
30 | 30 | ||
31 | DEBUGF 1,"TCP_output: socket=%x\n", eax |
31 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: socket=%x\n", eax |
32 | 32 | ||
33 | push eax |
33 | push eax |
34 | lea ecx, [eax + SOCKET.mutex] |
34 | lea ecx, [eax + SOCKET.mutex] |
35 | call mutex_lock |
35 | call mutex_lock |
36 | pop eax |
36 | pop eax |
37 | 37 | ||
38 | ; We'll detect the length of the data to be transmitted, and flags to be used |
38 | ; We'll detect the length of the data to be transmitted, and flags to be used |
39 | ; If there is some data, or any critical controls to send (SYN / RST), then transmit |
39 | ; If there is some data, or any critical controls to send (SYN / RST), then transmit |
40 | ; Otherwise, investigate further |
40 | ; Otherwise, investigate further |
41 | 41 | ||
42 | mov ebx, [eax + TCP_SOCKET.SND_MAX] |
42 | mov ebx, [eax + TCP_SOCKET.SND_MAX] |
43 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
43 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
44 | jbe .not_idle |
44 | jbe .not_idle |
45 | 45 | ||
46 | mov ebx, [eax + TCP_SOCKET.t_idle] |
46 | mov ebx, [eax + TCP_SOCKET.t_idle] |
47 | cmp ebx, [eax + TCP_SOCKET.t_rxtcur] |
47 | cmp ebx, [eax + TCP_SOCKET.t_rxtcur] |
48 | jbe .not_idle |
48 | jbe .not_idle |
49 | 49 | ||
50 | ; We have been idle for a while and no ACKS are expected to clock out any data we send.. |
50 | ; We have been idle for a while and no ACKS are expected to clock out any data we send.. |
51 | ; Slow start to get ack "clock" running again. |
51 | ; Slow start to get ack "clock" running again. |
52 | 52 | ||
53 | mov ebx, [eax + TCP_SOCKET.t_maxseg] |
53 | mov ebx, [eax + TCP_SOCKET.t_maxseg] |
54 | mov [eax + TCP_SOCKET.SND_CWND], ebx |
54 | mov [eax + TCP_SOCKET.SND_CWND], ebx |
55 | 55 | ||
56 | .not_idle: |
56 | .not_idle: |
57 | .again: |
57 | .again: |
58 | mov [eax + TCP_SOCKET.temp_bits], 0 |
58 | mov [eax + TCP_SOCKET.temp_bits], 0 |
59 | 59 | ||
60 | mov ebx, [eax + TCP_SOCKET.SND_NXT] ; calculate offset (71) |
60 | mov ebx, [eax + TCP_SOCKET.SND_NXT] ; calculate offset (71) |
61 | sub ebx, [eax + TCP_SOCKET.SND_UNA] ; |
61 | sub ebx, [eax + TCP_SOCKET.SND_UNA] ; |
62 | 62 | ||
63 | mov ecx, [eax + TCP_SOCKET.SND_WND] ; determine window |
63 | mov ecx, [eax + TCP_SOCKET.SND_WND] ; determine window |
64 | cmp ecx, [eax + TCP_SOCKET.SND_CWND] ; |
64 | cmp ecx, [eax + TCP_SOCKET.SND_CWND] ; |
65 | jb @f ; |
65 | jb @f ; |
66 | mov ecx, [eax + TCP_SOCKET.SND_CWND] ; |
66 | mov ecx, [eax + TCP_SOCKET.SND_CWND] ; |
67 | @@: ; |
67 | @@: ; |
68 | 68 | ||
69 | call TCP_outflags ; flags in dl |
69 | call TCP_outflags ; flags in dl |
70 | 70 | ||
71 | ;------------------------ |
71 | ;------------------------ |
72 | ; data being forced out ? |
72 | ; data being forced out ? |
73 | 73 | ||
74 | ; If in persist timeout with window of 0, send 1 byte. |
74 | ; If in persist timeout with window of 0, send 1 byte. |
75 | ; Otherwise, if window is small but nonzero, and timer expired, |
75 | ; Otherwise, if window is small but nonzero, and timer expired, |
76 | ; we will send what we can and go to transmit state |
76 | ; we will send what we can and go to transmit state |
77 | 77 | ||
78 | cmp [eax + TCP_SOCKET.t_force], 0 |
78 | cmp [eax + TCP_SOCKET.t_force], 0 |
79 | je .no_force |
79 | je .no_force |
80 | 80 | ||
81 | DEBUGF 1,"TCP_output: forcing data out\n" |
81 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: forcing data out\n" |
82 | 82 | ||
83 | test ecx, ecx |
83 | test ecx, ecx |
84 | jnz .no_zero_window |
84 | jnz .no_zero_window |
85 | 85 | ||
86 | cmp ebx, [eax + STREAM_SOCKET.snd.size] |
86 | cmp ebx, [eax + STREAM_SOCKET.snd.size] |
87 | jae @f |
87 | jae @f |
88 | 88 | ||
89 | and dl, not (TH_FIN) |
89 | and dl, not (TH_FIN) |
90 | 90 | ||
91 | @@: |
91 | @@: |
92 | inc ecx |
92 | inc ecx |
93 | jmp .no_force |
93 | jmp .no_force |
94 | 94 | ||
95 | .no_zero_window: |
95 | .no_zero_window: |
96 | mov [eax + TCP_SOCKET.timer_persist], 0 |
96 | mov [eax + TCP_SOCKET.timer_persist], 0 |
97 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
97 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
98 | 98 | ||
99 | .no_force: |
99 | .no_force: |
100 | 100 | ||
101 | ;-------------------------------- |
101 | ;-------------------------------- |
102 | ; Calculate how much data to send (106) |
102 | ; Calculate how much data to send (106) |
103 | 103 | ||
104 | mov esi, [eax + STREAM_SOCKET.snd.size] |
104 | mov esi, [eax + STREAM_SOCKET.snd.size] |
105 | cmp esi, ecx |
105 | cmp esi, ecx |
106 | jb @f |
106 | jb @f |
107 | mov esi, ecx |
107 | mov esi, ecx |
108 | @@: |
108 | @@: |
109 | sub esi, ebx |
109 | sub esi, ebx |
110 | 110 | ||
111 | 111 | ||
112 | ;------------------------ |
112 | ;------------------------ |
113 | ; check for window shrink (107) |
113 | ; check for window shrink (107) |
114 | 114 | ||
115 | ; If FIN has been set, but not ACKed, but we havent been called to retransmit, esi will be -1 |
115 | ; If FIN has been set, but not ACKed, but we havent been called to retransmit, esi will be -1 |
116 | ; Otherwise, window shrank after we sent into it. |
116 | ; Otherwise, window shrank after we sent into it. |
117 | 117 | ||
118 | jae .not_persist |
118 | jae .not_persist |
119 | 119 | ||
120 | ; enter persist state |
120 | ; enter persist state |
121 | xor esi, esi |
121 | xor esi, esi |
122 | 122 | ||
123 | ; If window shrank to 0 |
123 | ; If window shrank to 0 |
124 | test ecx, ecx |
124 | test ecx, ecx |
125 | jnz @f |
125 | jnz @f |
126 | 126 | ||
127 | ; cancel pending retransmit |
127 | ; cancel pending retransmit |
128 | mov [eax + TCP_SOCKET.timer_retransmission], 0 |
128 | mov [eax + TCP_SOCKET.timer_retransmission], 0 |
129 | 129 | ||
130 | ; pull SND_NXT back to (closed) window, We will enter persist state below. |
130 | ; pull SND_NXT back to (closed) window, We will enter persist state below. |
131 | push [eax + TCP_SOCKET.SND_UNA] |
131 | push [eax + TCP_SOCKET.SND_UNA] |
132 | pop [eax + TCP_SOCKET.SND_NXT] |
132 | pop [eax + TCP_SOCKET.SND_NXT] |
133 | @@: |
133 | @@: |
134 | 134 | ||
135 | ; If window didn't close completely, just wait for an ACK |
135 | ; If window didn't close completely, just wait for an ACK |
136 | 136 | ||
137 | .not_persist: |
137 | .not_persist: |
138 | 138 | ||
139 | ;--------------------------- |
139 | ;--------------------------- |
140 | ; Send one segment at a time (124) |
140 | ; Send one segment at a time (124) |
141 | 141 | ||
142 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
142 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
143 | jbe @f |
143 | jbe @f |
144 | 144 | ||
145 | mov esi, [eax + TCP_SOCKET.t_maxseg] |
145 | mov esi, [eax + TCP_SOCKET.t_maxseg] |
146 | or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
146 | or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
147 | @@: |
147 | @@: |
148 | 148 | ||
149 | ;-------------------------------------------- |
149 | ;-------------------------------------------- |
150 | ; Turn of FIN flag if send buffer not emptied (128) |
150 | ; Turn of FIN flag if send buffer not emptied (128) |
151 | 151 | ||
152 | mov edi, [eax + TCP_SOCKET.SND_NXT] |
152 | mov edi, [eax + TCP_SOCKET.SND_NXT] |
153 | add edi, esi |
153 | add edi, esi |
154 | sub edi, [eax + TCP_SOCKET.SND_UNA] |
154 | sub edi, [eax + TCP_SOCKET.SND_UNA] |
155 | cmp edi, [eax + STREAM_SOCKET.snd.size] |
155 | cmp edi, [eax + STREAM_SOCKET.snd.size] |
156 | jae @f |
156 | jae @f |
157 | and dl, not (TH_FIN) |
157 | and dl, not (TH_FIN) |
158 | 158 | ||
159 | @@: |
159 | @@: |
160 | 160 | ||
161 | ;------------------------------- |
161 | ;------------------------------- |
162 | ; calculate window advertisement (130) |
162 | ; calculate window advertisement (130) |
163 | 163 | ||
164 | mov ecx, SOCKET_MAXDATA |
164 | mov ecx, SOCKET_MAXDATA |
165 | sub ecx, [eax + STREAM_SOCKET.rcv.size] |
165 | sub ecx, [eax + STREAM_SOCKET.rcv.size] |
166 | 166 | ||
167 | ;------------------------------ |
167 | ;------------------------------ |
168 | ; Sender silly window avoidance (131) |
168 | ; Sender silly window avoidance (131) |
169 | 169 | ||
170 | test esi, esi |
170 | test esi, esi |
171 | jz .len_zero |
171 | jz .len_zero |
172 | 172 | ||
173 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
173 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
174 | je TCP_send |
174 | je TCP_send |
175 | 175 | ||
176 | add ebx, esi ; offset + length |
176 | add ebx, esi ; offset + length |
177 | cmp ebx, [eax + STREAM_SOCKET.snd.size] |
177 | cmp ebx, [eax + STREAM_SOCKET.snd.size] |
178 | jb @f |
178 | jb @f |
179 | 179 | ||
180 | test [eax + TCP_SOCKET.t_flags], TF_NODELAY |
180 | test [eax + TCP_SOCKET.t_flags], TF_NODELAY |
181 | jnz TCP_send |
181 | jnz TCP_send |
182 | 182 | ||
183 | mov ebx, [eax + TCP_SOCKET.SND_MAX] |
183 | mov ebx, [eax + TCP_SOCKET.SND_MAX] |
184 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
184 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
185 | je TCP_send |
185 | je TCP_send |
186 | @@: |
186 | @@: |
187 | 187 | ||
188 | test [eax + TCP_SOCKET.t_force], -1 ;;; |
188 | test [eax + TCP_SOCKET.t_force], -1 ;;; |
189 | jnz TCP_send |
189 | jnz TCP_send |
190 | 190 | ||
191 | mov ebx, [eax + TCP_SOCKET.max_sndwnd] |
191 | mov ebx, [eax + TCP_SOCKET.max_sndwnd] |
192 | shr ebx, 1 |
192 | shr ebx, 1 |
193 | cmp esi, ebx |
193 | cmp esi, ebx |
194 | jae TCP_send |
194 | jae TCP_send |
195 | 195 | ||
196 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
196 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
197 | cmp ebx, [eax + TCP_SOCKET.SND_MAX] |
197 | cmp ebx, [eax + TCP_SOCKET.SND_MAX] |
198 | jb TCP_send |
198 | jb TCP_send |
199 | 199 | ||
200 | .len_zero: |
200 | .len_zero: |
201 | 201 | ||
202 | ;---------------------------------------- |
202 | ;---------------------------------------- |
203 | ; Check if a window update should be sent (154) |
203 | ; Check if a window update should be sent (154) |
204 | 204 | ||
205 | DEBUGF 1,"TCP_output: window=%d\n", ecx |
205 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: window=%d\n", ecx |
206 | 206 | ||
207 | ; Compare available window to amount of window known to peer (as advertised window less next expected input) |
207 | ; Compare available window to amount of window known to peer (as advertised window less next expected input) |
208 | ; If the difference is at least two max size segments, or at least 50% of the maximum possible window, |
208 | ; If the difference is at least two max size segments, or at least 50% of the maximum possible window, |
209 | ; Then we want to send a window update to the peer. |
209 | ; Then we want to send a window update to the peer. |
210 | 210 | ||
211 | test ecx, ecx |
211 | test ecx, ecx |
212 | jz .no_window |
212 | jz .no_window |
213 | 213 | ||
214 | push ecx |
214 | push ecx |
215 | mov cl, [eax + TCP_SOCKET.RCV_SCALE] |
215 | mov cl, [eax + TCP_SOCKET.RCV_SCALE] |
216 | mov ebx, TCP_max_win |
216 | mov ebx, TCP_max_win |
217 | shl ebx, cl |
217 | shl ebx, cl |
218 | pop ecx |
218 | pop ecx |
219 | cmp ebx, ecx |
219 | cmp ebx, ecx |
220 | jb @f |
220 | jb @f |
221 | mov ebx, ecx |
221 | mov ebx, ecx |
222 | @@: |
222 | @@: |
223 | sub ebx, [eax + TCP_SOCKET.RCV_ADV] |
223 | sub ebx, [eax + TCP_SOCKET.RCV_ADV] |
224 | add ebx, [eax + TCP_SOCKET.RCV_NXT] |
224 | add ebx, [eax + TCP_SOCKET.RCV_NXT] |
225 | 225 | ||
226 | mov edi, [eax + TCP_SOCKET.t_maxseg] |
226 | mov edi, [eax + TCP_SOCKET.t_maxseg] |
227 | shl edi, 1 |
227 | shl edi, 1 |
228 | 228 | ||
229 | ; cmp ebx, edi |
229 | ; cmp ebx, edi |
230 | ; jae TCP_send |
230 | ; jae TCP_send |
231 | 231 | ||
232 | ; cmp ebx, [eax + TCP_SOCKET.] ;;; TODO: check with receive buffer high water mark |
232 | ; cmp ebx, [eax + TCP_SOCKET.] ;;; TODO: check with receive buffer high water mark |
233 | ; jae TCP_send |
233 | ; jae TCP_send |
234 | 234 | ||
235 | .no_window: |
235 | .no_window: |
236 | 236 | ||
237 | ;-------------------------- |
237 | ;-------------------------- |
238 | ; Should a segment be sent? (174) |
238 | ; Should a segment be sent? (174) |
239 | 239 | ||
240 | DEBUGF 1,"TCP_output: 174\n" |
240 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: 174\n" |
241 | 241 | ||
242 | test [eax + TCP_SOCKET.t_flags], TF_ACKNOW ; we need to ACK |
242 | test [eax + TCP_SOCKET.t_flags], TF_ACKNOW ; we need to ACK |
243 | jnz TCP_send |
243 | jnz TCP_send |
244 | 244 | ||
245 | test dl, TH_SYN + TH_RST ; we need to send a SYN or RST |
245 | test dl, TH_SYN + TH_RST ; we need to send a SYN or RST |
246 | jnz TCP_send |
246 | jnz TCP_send |
247 | 247 | ||
248 | mov ebx, [eax + TCP_SOCKET.SND_UP] ; when urgent pointer is beyond start of send bufer |
248 | mov ebx, [eax + TCP_SOCKET.SND_UP] ; when urgent pointer is beyond start of send bufer |
249 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
249 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
250 | ja TCP_send |
250 | ja TCP_send |
251 | 251 | ||
252 | test dl, TH_FIN |
252 | test dl, TH_FIN |
253 | jz .enter_persist ; no reason to send, enter persist state |
253 | jz .enter_persist ; no reason to send, enter persist state |
254 | 254 | ||
255 | ; FIN was set, only send if not already sent, or on retransmit |
255 | ; FIN was set, only send if not already sent, or on retransmit |
256 | 256 | ||
257 | test [eax + TCP_SOCKET.t_flags], TF_SENTFIN |
257 | test [eax + TCP_SOCKET.t_flags], TF_SENTFIN |
258 | jz TCP_send |
258 | jz TCP_send |
259 | 259 | ||
260 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
260 | mov ebx, [eax + TCP_SOCKET.SND_NXT] |
261 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
261 | cmp ebx, [eax + TCP_SOCKET.SND_UNA] |
262 | je TCP_send |
262 | je TCP_send |
263 | 263 | ||
264 | ;-------------------- |
264 | ;-------------------- |
265 | ; Enter persist state (191) |
265 | ; Enter persist state (191) |
266 | 266 | ||
267 | .enter_persist: |
267 | .enter_persist: |
268 | 268 | ||
269 | cmp [eax + STREAM_SOCKET.snd.size], 0 ; Data ready to send? |
269 | cmp [eax + STREAM_SOCKET.snd.size], 0 ; Data ready to send? |
270 | jne @f |
270 | jne @f |
271 | cmp [eax + TCP_SOCKET.timer_retransmission], 0 |
271 | cmp [eax + TCP_SOCKET.timer_retransmission], 0 |
272 | jne @f |
272 | jne @f |
273 | cmp [eax + TCP_SOCKET.timer_persist], 0 ; Persist timer already expired? |
273 | cmp [eax + TCP_SOCKET.timer_persist], 0 ; Persist timer already expired? |
274 | jne @f |
274 | jne @f |
275 | 275 | ||
276 | DEBUGF 1,"TCP_output: Entering persist state\n" |
276 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: Entering persist state\n" |
277 | 277 | ||
278 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
278 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
279 | call TCP_set_persist |
279 | call TCP_set_persist |
280 | @@: |
280 | @@: |
281 | 281 | ||
282 | ;---------------------------- |
282 | ;---------------------------- |
283 | ; No reason to send a segment (219) |
283 | ; No reason to send a segment (219) |
284 | 284 | ||
285 | DEBUGF 1,"TCP_output: No reason to send a segment\n" |
285 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: No reason to send a segment\n" |
286 | 286 | ||
287 | pusha |
287 | pusha |
288 | lea ecx, [eax + SOCKET.mutex] |
288 | lea ecx, [eax + SOCKET.mutex] |
289 | call mutex_unlock |
289 | call mutex_unlock |
290 | popa |
290 | popa |
291 | 291 | ||
292 | ; Fixme: returnvalue? |
292 | ; Fixme: returnvalue? |
293 | 293 | ||
294 | ret |
294 | ret |
295 | 295 | ||
296 | 296 | ||
297 | 297 | ||
298 | 298 | ||
299 | 299 | ||
300 | 300 | ||
301 | 301 | ||
302 | 302 | ||
303 | 303 | ||
304 | ;----------------------------------------------- |
304 | ;----------------------------------------------- |
305 | ; |
305 | ; |
306 | ; Send a segment (222) |
306 | ; Send a segment (222) |
307 | ; |
307 | ; |
308 | ; eax = socket pointer |
308 | ; eax = socket pointer |
309 | ; esi = data len |
309 | ; esi = data len |
310 | ; dl = flags |
310 | ; dl = flags |
311 | ; |
311 | ; |
312 | ;----------------------------------------------- |
312 | ;----------------------------------------------- |
313 | align 4 |
313 | align 4 |
314 | TCP_send: |
314 | TCP_send: |
315 | 315 | ||
316 | DEBUGF 1,"TCP_send: socket=%x length=%u flags=%x\n", eax, esi, dl |
316 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: socket=%x length=%u flags=%x\n", eax, esi, dl |
317 | 317 | ||
318 | push eax ; save socket ptr |
318 | push eax ; save socket ptr |
319 | push esi ; and data length too |
319 | push esi ; and data length too |
320 | mov edi, sizeof.TCP_header ; edi will contain headersize |
320 | mov edi, sizeof.TCP_header ; edi will contain headersize |
321 | 321 | ||
322 | ;------------------------------------ |
322 | ;------------------------------------ |
323 | ; Send options with first SYN segment |
323 | ; Send options with first SYN segment |
324 | 324 | ||
325 | test dl, TH_SYN |
325 | test dl, TH_SYN |
326 | jz .options_done |
326 | jz .options_done |
327 | 327 | ||
328 | push [eax + TCP_SOCKET.ISS] |
328 | push [eax + TCP_SOCKET.ISS] |
329 | pop [eax + TCP_SOCKET.SND_NXT] |
329 | pop [eax + TCP_SOCKET.SND_NXT] |
330 | 330 | ||
331 | test [eax + TCP_SOCKET.t_flags], TF_NOOPT |
331 | test [eax + TCP_SOCKET.t_flags], TF_NOOPT |
332 | jnz .options_done |
332 | jnz .options_done |
333 | 333 | ||
334 | mov ecx, 1460 ;;;; FIXME: use routing blablabla to determine MSS |
334 | mov ecx, 1460 ;;;; FIXME: use routing blablabla to determine MSS |
335 | or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16 |
335 | or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16 |
336 | bswap ecx |
336 | bswap ecx |
337 | push ecx |
337 | push ecx |
338 | add di, 4 |
338 | add di, 4 |
339 | 339 | ||
340 | DEBUGF 1,"TCP_send: added maxseg option\n" |
340 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added maxseg option\n" |
341 | 341 | ||
342 | test [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE |
342 | test [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE |
343 | jz .no_scale |
343 | jz .no_scale |
344 | 344 | ||
345 | test dl, TH_ACK |
345 | test dl, TH_ACK |
346 | jz .scale_opt |
346 | jz .scale_opt |
347 | 347 | ||
348 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE |
348 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE |
349 | jz .no_scale |
349 | jz .no_scale |
350 | 350 | ||
351 | .scale_opt: |
351 | .scale_opt: |
352 | mov cl, [eax + TCP_SOCKET.request_r_scale] |
352 | mov cl, [eax + TCP_SOCKET.request_r_scale] |
353 | mov ch, TCP_OPT_NOP |
353 | mov ch, TCP_OPT_NOP |
354 | pushw cx |
354 | pushw cx |
355 | pushw TCP_OPT_WINDOW + 3 shl 8 |
355 | pushw TCP_OPT_WINDOW + 3 shl 8 |
356 | add di, 4 |
356 | add di, 4 |
357 | 357 | ||
358 | DEBUGF 1,"TCP_send: added scale option\n" |
358 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added scale option\n" |
359 | 359 | ||
360 | .no_scale: |
360 | .no_scale: |
361 | .no_syn: |
361 | .no_syn: |
362 | 362 | ||
363 | ;------------------------------------ |
363 | ;------------------------------------ |
364 | ; Make the timestamp option if needed |
364 | ; Make the timestamp option if needed |
365 | 365 | ||
366 | test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP |
366 | test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP |
367 | jz .no_timestamp |
367 | jz .no_timestamp |
368 | 368 | ||
369 | test dl, TH_RST |
369 | test dl, TH_RST |
370 | jnz .no_timestamp |
370 | jnz .no_timestamp |
371 | 371 | ||
372 | test dl, TH_ACK |
372 | test dl, TH_ACK |
373 | jz .timestamp |
373 | jz .timestamp |
374 | 374 | ||
375 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP |
375 | test [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP |
376 | jz .no_timestamp |
376 | jz .no_timestamp |
377 | 377 | ||
378 | .timestamp: |
378 | .timestamp: |
379 | pushd 0 |
379 | pushd 0 |
380 | pushd [timer_ticks] |
380 | pushd [timer_ticks] |
381 | pushd TCP_OPT_NOP + TCP_OPT_NOP shl 8 + TCP_OPT_TIMESTAMP shl 16 + 10 shl 24 |
381 | pushd TCP_OPT_NOP + TCP_OPT_NOP shl 8 + TCP_OPT_TIMESTAMP shl 16 + 10 shl 24 |
382 | add di, 12 |
382 | add di, 12 |
383 | 383 | ||
384 | DEBUGF 1,"TCP_send: added timestamp\n" |
384 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added timestamp\n" |
385 | 385 | ||
386 | .no_timestamp: |
386 | .no_timestamp: |
387 | 387 | ||
388 | ; |
388 | ; |
389 | 389 | ||
390 | .options_done: |
390 | .options_done: |
391 | 391 | ||
392 | ; eax = socket ptr |
392 | ; eax = socket ptr |
393 | ; edx = flags |
393 | ; edx = flags |
394 | ; edi = header size |
394 | ; edi = header size |
395 | ; esi = data len |
395 | ; esi = data len |
396 | 396 | ||
397 | ;--------------------------------------------- |
397 | ;--------------------------------------------- |
398 | ; check if we dont exceed the max segment size (270) |
398 | ; check if we dont exceed the max segment size (270) |
399 | 399 | ||
400 | add esi, edi ; total TCP segment size |
400 | add esi, edi ; total TCP segment size |
401 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
401 | cmp esi, [eax + TCP_SOCKET.t_maxseg] |
402 | jbe .no_overflow |
402 | jbe .no_overflow |
403 | 403 | ||
404 | mov esi, [eax + TCP_SOCKET.t_maxseg] |
404 | mov esi, [eax + TCP_SOCKET.t_maxseg] |
405 | or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
405 | or [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
406 | .no_overflow: |
406 | .no_overflow: |
407 | 407 | ||
408 | ;----------------------------------------------------------------- |
408 | ;----------------------------------------------------------------- |
409 | ; Start by pushing all TCP header values in reverse order on stack |
409 | ; Start by pushing all TCP header values in reverse order on stack |
410 | ; (essentially, creating the tcp header on the stack!) |
410 | ; (essentially, creating the tcp header on the stack!) |
411 | 411 | ||
412 | pushw 0 ; .UrgentPointer dw ? |
412 | pushw 0 ; .UrgentPointer dw ? |
413 | pushw 0 ; .Checksum dw ? |
413 | pushw 0 ; .Checksum dw ? |
414 | pushw 0x00a0 ; .Window dw ? ;;;;;;; FIXME (370) |
414 | pushw 0x00a0 ; .Window dw ? ;;;;;;; FIXME (370) |
415 | shl edi, 2 ; .DataOffset db ? only 4 left-most bits |
415 | shl edi, 2 ; .DataOffset db ? only 4 left-most bits |
416 | shl dx, 8 |
416 | shl dx, 8 |
417 | or dx, di ; .Flags db ? |
417 | or dx, di ; .Flags db ? |
418 | pushw dx |
418 | pushw dx |
419 | shr edi, 2 ; .DataOffset db ? |
419 | shr edi, 2 ; .DataOffset db ? |
420 | 420 | ||
421 | push [eax + TCP_SOCKET.RCV_NXT] ; .AckNumber dd ? |
421 | push [eax + TCP_SOCKET.RCV_NXT] ; .AckNumber dd ? |
422 | ntohd [esp] |
422 | ntohd [esp] |
423 | 423 | ||
424 | push [eax + TCP_SOCKET.SND_NXT] ; .SequenceNumber dd ? |
424 | push [eax + TCP_SOCKET.SND_NXT] ; .SequenceNumber dd ? |
425 | ntohd [esp] |
425 | ntohd [esp] |
426 | 426 | ||
427 | push [eax + TCP_SOCKET.RemotePort] ; .DestinationPort dw ? |
427 | push [eax + TCP_SOCKET.RemotePort] ; .DestinationPort dw ? |
428 | push [eax + TCP_SOCKET.LocalPort] ; .SourcePort dw ? |
428 | push [eax + TCP_SOCKET.LocalPort] ; .SourcePort dw ? |
429 | 429 | ||
430 | push edi ; header size |
430 | push edi ; header size |
431 | 431 | ||
432 | ;--------------------- |
432 | ;--------------------- |
433 | ; Create the IP packet |
433 | ; Create the IP packet |
434 | 434 | ||
435 | mov ecx, esi |
435 | mov ecx, esi |
436 | 436 | ||
437 | mov ebx, [eax + SOCKET.device] |
437 | mov ebx, [eax + SOCKET.device] |
438 | mov edx, [eax + IP_SOCKET.LocalIP] ; source ip |
438 | mov edx, [eax + IP_SOCKET.LocalIP] ; source ip |
439 | mov eax, [eax + IP_SOCKET.RemoteIP] ; dest ip |
439 | mov eax, [eax + IP_SOCKET.RemoteIP] ; dest ip |
440 | mov di, IP_PROTO_TCP shl 8 + 128 |
440 | mov di, IP_PROTO_TCP shl 8 + 128 |
441 | call IPv4_output |
441 | call IPv4_output |
442 | jz .ip_error |
442 | jz .ip_error |
443 | 443 | ||
444 | ;----------------------------------------- |
444 | ;----------------------------------------- |
445 | ; Move TCP header from stack to TCP packet |
445 | ; Move TCP header from stack to TCP packet |
446 | 446 | ||
447 | push ecx |
447 | push ecx |
448 | mov ecx, [esp + 4] |
448 | mov ecx, [esp + 4] |
449 | lea esi, [esp + 8] |
449 | lea esi, [esp + 8] |
450 | shr ecx, 2 ; count is in bytes, we will work with dwords |
450 | shr ecx, 2 ; count is in bytes, we will work with dwords |
451 | rep movsd |
451 | rep movsd |
452 | pop ecx ; full TCP packet size |
452 | pop ecx ; full TCP packet size |
453 | 453 | ||
454 | pop esi ; headersize |
454 | pop esi ; headersize |
455 | add esp, esi ; remove it from stack |
455 | add esp, esi ; remove it from stack |
456 | 456 | ||
457 | push edx ; packet size for send proc |
457 | push edx ; packet size for send proc |
458 | push eax ; packet ptr for send proc |
458 | push eax ; packet ptr for send proc |
459 | 459 | ||
460 | mov edx, edi ; begin of data |
460 | mov edx, edi ; begin of data |
461 | sub edx, esi ; begin of packet (edi = begin of data) |
461 | sub edx, esi ; begin of packet (edi = begin of data) |
462 | push ecx |
462 | push ecx |
463 | sub ecx, esi ; data size |
463 | sub ecx, esi ; data size |
464 | 464 | ||
465 | ;-------------- |
465 | ;-------------- |
466 | ; Copy the data |
466 | ; Copy the data |
467 | 467 | ||
468 | ; eax = ptr to ring struct |
468 | ; eax = ptr to ring struct |
469 | ; ecx = buffer size |
469 | ; ecx = buffer size |
470 | ; edi = ptr to buffer |
470 | ; edi = ptr to buffer |
471 | 471 | ||
472 | mov eax, [esp + 16] ; get socket ptr |
472 | mov eax, [esp + 16] ; get socket ptr |
473 | 473 | ||
474 | push edx |
474 | push edx |
475 | push [eax + TCP_SOCKET.SND_NXT] ; we'll need this for timing the transmission |
475 | push [eax + TCP_SOCKET.SND_NXT] ; we'll need this for timing the transmission |
476 | test ecx, ecx |
476 | test ecx, ecx |
477 | jz .nodata |
477 | jz .nodata |
478 | mov edx, [eax + TCP_SOCKET.SND_NXT] |
478 | mov edx, [eax + TCP_SOCKET.SND_NXT] |
479 | add [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number <<< CHECKME |
479 | add [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number <<< CHECKME |
480 | sub edx, [eax + TCP_SOCKET.SND_UNA] ; offset |
480 | sub edx, [eax + TCP_SOCKET.SND_UNA] ; offset |
481 | add eax, STREAM_SOCKET.snd |
481 | add eax, STREAM_SOCKET.snd |
482 | call SOCKET_ring_read |
482 | call SOCKET_ring_read |
483 | .nodata: |
483 | .nodata: |
484 | pop edi |
484 | pop edi |
485 | pop esi ; begin of data |
485 | pop esi ; begin of data |
486 | pop ecx ; full packet size |
486 | pop ecx ; full packet size |
487 | mov eax, [esp + 12] ; socket ptr |
487 | mov eax, [esp + 12] ; socket ptr |
488 | 488 | ||
489 | ;---------------------------------- |
489 | ;---------------------------------- |
490 | ; initialize retransmit timer (400) |
490 | ; initialize retransmit timer (400) |
491 | 491 | ||
492 | ;TODO: check t_force and persist |
492 | ;TODO: check t_force and persist |
493 | 493 | ||
494 | test [esi + TCP_header.Flags], TH_SYN + TH_FIN ; syn and fin take a sequence number |
494 | test [esi + TCP_header.Flags], TH_SYN + TH_FIN ; syn and fin take a sequence number |
495 | jz @f |
495 | jz @f |
496 | inc [eax + TCP_SOCKET.SND_NXT] |
496 | inc [eax + TCP_SOCKET.SND_NXT] |
497 | test [esi + TCP_header.Flags], TH_FIN |
497 | test [esi + TCP_header.Flags], TH_FIN |
498 | jz @f |
498 | jz @f |
499 | or [eax + TCP_SOCKET.t_flags], TF_SENTFIN ; if we sent a fin, set the sentfin flag |
499 | or [eax + TCP_SOCKET.t_flags], TF_SENTFIN ; if we sent a fin, set the sentfin flag |
500 | @@: |
500 | @@: |
501 | 501 | ||
502 | mov edx, [eax + TCP_SOCKET.SND_NXT] |
502 | mov edx, [eax + TCP_SOCKET.SND_NXT] |
503 | cmp edx, [eax + TCP_SOCKET.SND_MAX] ; is this a retransmission? |
503 | cmp edx, [eax + TCP_SOCKET.SND_MAX] ; is this a retransmission? |
504 | jbe @f |
504 | jbe @f |
505 | mov [eax + TCP_SOCKET.SND_MAX], edx ; [eax + TCP_SOCKET.SND_NXT] from before we updated it |
505 | mov [eax + TCP_SOCKET.SND_MAX], edx ; [eax + TCP_SOCKET.SND_NXT] from before we updated it |
506 | 506 | ||
507 | cmp [eax + TCP_SOCKET.t_rtt], 0 ; are we currently timing anything? |
507 | cmp [eax + TCP_SOCKET.t_rtt], 0 ; are we currently timing anything? |
508 | je @f |
508 | je @f |
509 | mov [eax + TCP_SOCKET.t_rtt], 1 ; nope, start transmission timer |
509 | mov [eax + TCP_SOCKET.t_rtt], 1 ; nope, start transmission timer |
510 | mov [eax + TCP_SOCKET.t_rtseq], edi |
510 | mov [eax + TCP_SOCKET.t_rtseq], edi |
511 | ;TODO: update stats |
511 | ;TODO: update stats |
512 | @@: |
512 | @@: |
513 | 513 | ||
514 | ; set retransmission timer if not already set, and not doing an ACK or keepalive probe |
514 | ; set retransmission timer if not already set, and not doing an ACK or keepalive probe |
515 | 515 | ||
516 | cmp [eax + TCP_SOCKET.timer_retransmission], 0 ;;;; FIXME |
516 | cmp [eax + TCP_SOCKET.timer_retransmission], 0 ;;;; FIXME |
517 | ja .retransmit_set |
517 | ja .retransmit_set |
518 | 518 | ||
519 | cmp edx, [eax + TCP_SOCKET.SND_UNA] ; edx is still [eax + TCP_SOCKET.SND_NXT] |
519 | cmp edx, [eax + TCP_SOCKET.SND_UNA] ; edx is still [eax + TCP_SOCKET.SND_NXT] |
520 | je .retransmit_set |
520 | je .retransmit_set |
521 | 521 | ||
522 | mov edx, [eax + TCP_SOCKET.t_rxtcur] |
522 | mov edx, [eax + TCP_SOCKET.t_rxtcur] |
523 | mov [eax + TCP_SOCKET.timer_retransmission], edx |
523 | mov [eax + TCP_SOCKET.timer_retransmission], edx |
524 | 524 | ||
525 | cmp [eax + TCP_SOCKET.timer_persist], 0 |
525 | cmp [eax + TCP_SOCKET.timer_persist], 0 |
526 | jne .retransmit_set |
526 | jne .retransmit_set |
527 | mov [eax + TCP_SOCKET.timer_persist], 0 |
527 | mov [eax + TCP_SOCKET.timer_persist], 0 |
528 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
528 | mov [eax + TCP_SOCKET.t_rxtshift], 0 |
529 | 529 | ||
530 | .retransmit_set: |
530 | .retransmit_set: |
531 | 531 | ||
532 | ;-------------------- |
532 | ;-------------------- |
533 | ; Create the checksum |
533 | ; Create the checksum |
534 | 534 | ||
535 | TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP) |
535 | TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP) |
536 | mov [esi + TCP_header.Checksum], dx |
536 | mov [esi + TCP_header.Checksum], dx |
537 | 537 | ||
538 | ;---------------- |
538 | ;---------------- |
539 | ; Send the packet |
539 | ; Send the packet |
540 | 540 | ||
541 | DEBUGF 1,"TCP_send: Sending with device %x\n", ebx |
541 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: Sending with device %x\n", ebx |
542 | call [ebx + NET_DEVICE.transmit] |
542 | call [ebx + NET_DEVICE.transmit] |
543 | jnz .send_error |
543 | jnz .send_error |
544 | 544 | ||
545 | ;--------------- |
545 | ;--------------- |
546 | ; Ok, data sent! |
546 | ; Ok, data sent! |
547 | 547 | ||
548 | pop ecx |
548 | pop ecx |
549 | pop eax |
549 | pop eax |
550 | 550 | ||
551 | inc [TCP_segments_tx] ; FIXME: correct interface? |
551 | inc [TCP_segments_tx] ; FIXME: correct interface? |
552 | 552 | ||
553 | ; update advertised receive window |
553 | ; update advertised receive window |
554 | test ecx, ecx |
554 | test ecx, ecx |
555 | jz @f |
555 | jz @f |
556 | add ecx, [eax + TCP_SOCKET.RCV_NXT] |
556 | add ecx, [eax + TCP_SOCKET.RCV_NXT] |
557 | cmp ecx, [eax + TCP_SOCKET.RCV_ADV] |
557 | cmp ecx, [eax + TCP_SOCKET.RCV_ADV] |
558 | jbe @f |
558 | jbe @f |
559 | mov [eax + TCP_SOCKET.RCV_ADV], ecx |
559 | mov [eax + TCP_SOCKET.RCV_ADV], ecx |
560 | @@: |
560 | @@: |
561 | 561 | ||
562 | ; update last ack sent |
562 | ; update last ack sent |
563 | push [eax + TCP_SOCKET.RCV_NXT] |
563 | push [eax + TCP_SOCKET.RCV_NXT] |
564 | pop [eax + TCP_SOCKET.last_ack_sent] |
564 | pop [eax + TCP_SOCKET.last_ack_sent] |
565 | 565 | ||
566 | ; and flags |
566 | ; and flags |
567 | and [eax + TCP_SOCKET.t_flags], not (TF_ACKNOW + TF_DELACK) |
567 | and [eax + TCP_SOCKET.t_flags], not (TF_ACKNOW + TF_DELACK) |
568 | 568 | ||
569 | ;-------------- |
569 | ;-------------- |
570 | ; unlock socket |
570 | ; unlock socket |
571 | 571 | ||
572 | push eax |
572 | push eax |
573 | lea ecx, [eax + SOCKET.mutex] |
573 | lea ecx, [eax + SOCKET.mutex] |
574 | call mutex_unlock |
574 | call mutex_unlock |
575 | pop eax |
575 | pop eax |
576 | 576 | ||
577 | ;----------------------------- |
577 | ;----------------------------- |
578 | ; Check if we need more output |
578 | ; Check if we need more output |
579 | 579 | ||
580 | test [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
580 | test [eax + TCP_SOCKET.temp_bits], TCP_BIT_SENDALOT |
581 | jnz TCP_output.again |
581 | jnz TCP_output.again |
582 | 582 | ||
583 | DEBUGF 1,"TCP_send: success!\n" |
583 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: success!\n" |
584 | 584 | ||
585 | xor eax, eax |
585 | xor eax, eax |
586 | ret |
586 | ret |
587 | 587 | ||
588 | 588 | ||
589 | .ip_error: |
589 | .ip_error: |
590 | pop ecx |
590 | pop ecx |
591 | add esp, ecx |
591 | add esp, ecx |
592 | add esp, 4 |
592 | add esp, 4 |
593 | pop eax |
593 | pop eax |
594 | 594 | ||
595 | mov [eax + TCP_SOCKET.timer_retransmission], TCP_time_re_min |
595 | mov [eax + TCP_SOCKET.timer_retransmission], TCP_time_re_min |
596 | 596 | ||
597 | lea ecx, [eax + SOCKET.mutex] |
597 | lea ecx, [eax + SOCKET.mutex] |
598 | call mutex_unlock |
598 | call mutex_unlock |
599 | 599 | ||
600 | DEBUGF 1,"TCP_send: IP error\n" |
600 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: IP error\n" |
601 | 601 | ||
602 | or eax, -1 |
602 | or eax, -1 |
603 | ret |
603 | ret |
604 | 604 | ||
605 | 605 | ||
606 | .send_error: |
606 | .send_error: |
607 | add esp, 8 |
607 | add esp, 8 |
608 | 608 | ||
609 | lea ecx, [eax + SOCKET.mutex] |
609 | lea ecx, [eax + SOCKET.mutex] |
610 | call mutex_unlock |
610 | call mutex_unlock |
611 | 611 | ||
612 | DEBUGF 1,"TCP_send: sending failed\n" |
612 | DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: sending failed\n" |
613 | 613 | ||
614 | or eax, -2 |
614 | or eax, -2 |
615 | ret><><<> |
615 | ret><><<> |