78,8 → 78,14 |
|
|
align 4 |
TCP_process_input: |
proc TCP_process_input |
|
locals |
dataoffset dd ? |
timestamp dd ? |
temp_bits db ? |
endl |
|
xor esi, esi |
mov ecx, MANUAL_DESTROY |
call create_event |
94,6 → 100,7 |
get_from_queue TCP_queue, TCP_QUEUE_SIZE, sizeof.TCP_queue_entry, .wait |
|
push [esi + TCP_queue_entry.timestamp] |
pop [timestamp] |
push [esi + TCP_queue_entry.buffer_ptr] |
|
mov ebx, [esi + TCP_queue_entry.device_ptr] |
109,8 → 116,8 |
je .checksum_ok |
|
; re-calculate the checksum (if not already done by hw) |
; test [ebx + NET_DEVICE.hwacc], HWACC_TCP_IPv4_IN |
; jnz .checksum_ok |
test [ebx + NET_DEVICE.hwacc], NET_HWACC_TCP_IPv4_IN |
jnz .checksum_ok |
|
push ecx esi |
pushw [esi + TCP_header.Checksum] |
123,12 → 130,13 |
.checksum_ok: |
|
; Verify the data offset |
and [edx + TCP_header.DataOffset], 0xf0 ; Calculate TCP segment header size (throwing away unused reserved bits in TCP header) |
shr [edx + TCP_header.DataOffset], 2 |
cmp [edx + TCP_header.DataOffset], sizeof.TCP_header ; Now see if it's at least the size of a standard TCP header |
movzx eax, [edx + TCP_header.DataOffset] |
and al, 0xf0 ; Calculate TCP segment header size (throwing away unused reserved bits in TCP header) |
shr al, 2 |
cmp al, sizeof.TCP_header ; Now see if it's at least the size of a standard TCP header |
jb .drop_no_socket ; If not, drop the packet |
mov [dataoffset], eax |
|
movzx eax, [edx + TCP_header.DataOffset] |
sub ecx, eax ; substract TCP header size from total segment size |
jb .drop_no_socket ; If total segment size is less then the advertised header size, drop packet |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: %u bytes of data\n", ecx |
211,7 → 219,7 |
;--------------------------- |
; disable all temporary bits |
|
mov [ebx + TCP_SOCKET.temp_bits], 0 |
mov [temp_bits], 0 |
|
;--------------------------------------- |
; unscale the window into a 32 bit value |
245,7 → 253,7 |
|
mov ebx, eax |
|
mov [ebx + TCP_SOCKET.temp_bits], TCP_BIT_DROPSOCKET ;;; FIXME: should we take over bits from previous socket? |
mov [temp_bits], TCP_BIT_DROPSOCKET |
|
push dword [edi + 4] ; Ipv4 destination addres |
pop [ebx + IP_SOCKET.LocalIP] |
267,18 → 275,18 |
;-------------------- |
; Process TCP options |
|
;;; FIXME: for LISTEN, options should be called after we determined route, we need it for MSS |
;;; cmp [ebx + TCP_SOCKET.t_state], TCPS_LISTEN ; no options when in listen state |
;;; jz .not_uni_xfer ; also no header prediction |
|
push ecx |
|
movzx ecx, [edx + TCP_header.DataOffset] |
mov ecx, [dataoffset] |
cmp ecx, sizeof.TCP_header ; Does header contain any options? |
je .no_options |
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Segment has options\n" |
|
;;; FIXME: for LISTEN, options should be called after we determined route, we need it for MSS |
;;; cmp [ebx + TCP_SOCKET.t_state], TCPS_LISTEN ; no options when in listen state |
;;; jz .not_uni_xfer ; also no header prediction |
|
add ecx, edx |
lea esi, [edx + sizeof.TCP_header] |
|
311,9 → 319,10 |
test [edx + TCP_header.Flags], TH_SYN |
jz @f |
|
xor eax, eax |
lodsw |
rol ax, 8 |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Maxseg=%u\n", ax |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Maxseg=%u\n", eax |
call TCP_mss |
@@: |
jmp .opt_loop |
366,10 → 375,11 |
@@: |
|
lodsd |
bswap eax |
mov [ebx + TCP_SOCKET.ts_val], eax |
lodsd ; timestamp echo reply |
mov [ebx + TCP_SOCKET.ts_ecr], eax |
or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP |
or [temp_bits], TCP_BIT_TIMESTAMP |
|
; Since we have a timestamp, lets do the paws test right away! |
|
380,11 → 390,11 |
test eax, eax |
jz .no_paws |
cmp eax, [ebx + TCP_SOCKET.ts_val] |
jge .no_paws |
jbe .no_paws |
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: PAWS: detected an old segment\n" |
|
mov eax, [esp+4+4] ; tcp_now |
mov eax, [timestamp] |
sub eax, [ebx + TCP_SOCKET.ts_recent_age] |
|
pop ecx |
474,9 → 484,9 |
|
; Update RTT estimators |
|
test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP |
test [temp_bits], TCP_BIT_TIMESTAMP |
jz .no_timestamp_rtt |
mov eax, [esp + 4] ; timestamp when this segment was received |
mov eax, [timestamp] |
sub eax, [ebx + TCP_SOCKET.ts_ecr] |
inc eax |
call TCP_xmit_timer |
536,12 → 546,11 |
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Header prediction: we are receiving %u bytes\n", ecx |
|
add [ebx + TCP_SOCKET.RCV_NXT], ecx ; Update sequence number with number of bytes we have copied |
|
movzx esi, [edx + TCP_header.DataOffset] |
mov esi, [dataoffset] |
add esi, edx |
lea eax, [ebx + STREAM_SOCKET.rcv] |
call SOCKET_ring_write ; Add the data to the socket buffer |
add [ebx + TCP_SOCKET.RCV_NXT], ecx ; Update sequence number with number of bytes we have copied |
|
mov eax, ebx |
call SOCKET_notify |
558,12 → 567,13 |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Header prediction failed\n" |
|
; Calculate receive window size |
|
push edx |
mov eax, SOCKETBUFFSIZE |
mov eax, SOCKET_MAXDATA |
sub eax, [ebx + STREAM_SOCKET.rcv.size] |
DEBUGF DEBUG_NETWORK_VERBOSE, "Space in receive buffer=%d\n", eax |
mov edx, [ebx + TCP_SOCKET.RCV_ADV] |
sub edx, [ebx + TCP_SOCKET.RCV_NXT] |
DEBUGF DEBUG_NETWORK_VERBOSE, "Current advertised window=%d\n", edx |
cmp eax, edx |
jg @f |
mov eax, edx |
583,8 → 593,9 |
;---------------------------- |
; trim any data not in window |
|
; check for duplicate data at beginning of segment (635) |
; 1. Check for duplicate data at beginning of segment |
|
; Calculate number of bytes we need to drop |
mov eax, [ebx + TCP_SOCKET.RCV_NXT] |
sub eax, [edx + TCP_header.SequenceNumber] |
jle .no_duplicate |
609,7 → 620,7 |
dec eax |
.no_dup_syn: |
|
; Check for entire duplicate segment (646) |
; 2. Check for entire duplicate segment |
cmp eax, ecx ; eax holds number of bytes to drop, ecx is data size |
jb .duplicate |
jnz @f |
623,16 → 634,19 |
|
; send an ACK and resynchronize and drop any data. |
; But keep on processing for RST or ACK |
DEBUGF DEBUG_NETWORK_VERBOSE, "616\n" |
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
mov eax, ecx |
;TODO: update stats |
|
;;; TODO: update stats |
|
;----------------------------------------------- |
; Remove duplicate data and update urgent offset |
|
.duplicate: |
;;; TODO: 677 |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: trimming duplicate data\n" |
|
; Trim data from left side of window |
add [dataoffset], eax |
add [edx + TCP_header.SequenceNumber], eax |
sub ecx, eax |
|
643,10 → 657,10 |
@@: |
|
;-------------------------------------------------- |
; Handle data that arrives after process terminates (687) |
; Handle data that arrives after process terminates |
|
.no_duplicate: |
cmp [ebx + SOCKET.PID], 0 |
cmp [ebx + SOCKET.PID], 0 ;;; TODO: use socket flags instead?? |
jne .not_terminated |
cmp [ebx + TCP_SOCKET.t_state], TCPS_CLOSE_WAIT |
jbe .not_terminated |
659,7 → 673,7 |
jmp .respond_seg_reset |
|
;---------------------------------------- |
; Remove data beyond right edge of window (700-736) |
; Remove data beyond right edge of window |
|
.not_terminated: |
mov eax, [edx + TCP_header.SequenceNumber] |
688,32 → 702,29 |
jmp .findpcb ; FIXME: skip code for unscaling window, ... |
.no_new_request: |
|
; If window is closed can only take segments at window edge, and have to drop data and PUSH from |
; If window is closed, we can only take segments at window edge, and have to drop data and PUSH from |
; incoming segments. Continue processing, but remember to ACK. Otherwise drop segment and ACK |
|
cmp [ebx + TCP_SOCKET.RCV_WND], 0 |
jne .drop_after_ack |
mov eax, [edx + TCP_header.SequenceNumber] |
cmp eax, [ebx + TCP_SOCKET.RCV_NXT] |
mov esi, [edx + TCP_header.SequenceNumber] |
cmp esi, [ebx + TCP_SOCKET.RCV_NXT] |
jne .drop_after_ack |
|
DEBUGF DEBUG_NETWORK_VERBOSE, "690\n" |
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
;;; TODO: update stats |
jmp .no_excess_data |
.dont_drop_all: |
;;; TODO: update stats |
;;; TODO: 733 |
|
sub ecx, eax |
and [ebx + TCP_SOCKET.t_flags], not (TH_PUSH or TH_FIN) |
DEBUGF DEBUG_NETWORK_VERBOSE, "Trimming %u bytes from the right of the window\n" |
sub ecx, eax ; remove data from the right side of window (decrease data length) |
and [edx + TCP_header.Flags], not (TH_PUSH or TH_FIN) |
.no_excess_data: |
|
;----------------- |
; Record timestamp (737-746) |
; Record timestamp |
|
; If last ACK falls within this segments sequence numbers, record its timestamp |
test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP |
test [temp_bits], TCP_BIT_TIMESTAMP |
jz .no_timestamp |
mov eax, [ebx + TCP_SOCKET.last_ack_sent] |
sub eax, [edx + TCP_header.SequenceNumber] |
727,7 → 738,7 |
|
DEBUGF DEBUG_NETWORK_VERBOSE, "Recording timestamp\n" |
|
mov eax, [esp + 4] ; tcp_now |
mov eax, [timestamp] |
mov [ebx + TCP_SOCKET.ts_recent_age], eax |
mov eax, [ebx + TCP_SOCKET.ts_val] |
mov [ebx + TCP_SOCKET.ts_recent], eax |
882,7 → 893,9 |
|
mov eax, [ebx + TCP_SOCKET.SND_WND] |
cmp eax, [ebx + TCP_SOCKET.SND_CWND] |
cmova eax, [ebx + TCP_SOCKET.SND_CWND] |
jbe @f |
mov eax, [ebx + TCP_SOCKET.SND_CWND] |
@@: |
shr eax, 1 |
push edx |
xor edx, edx |
993,13 → 1006,13 |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: acceptable ACK for %u bytes\n", edi |
|
;------------------------------------------ |
; RTT measurements and retransmission timer (912-926) |
; RTT measurements and retransmission timer |
|
; If we have a timestamp, update smoothed RTT |
|
test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_TIMESTAMP |
test [temp_bits], TCP_BIT_TIMESTAMP |
jz .timestamp_not_present |
mov eax, [esp+4] |
mov eax, [timestamp] |
sub eax, [ebx + TCP_SOCKET.ts_ecr] |
inc eax |
call TCP_xmit_timer |
1028,7 → 1041,7 |
cmp eax, [edx + TCP_header.AckNumber] |
jne .more_data |
and [ebx + TCP_SOCKET.timer_flags], not timer_flag_retransmission |
or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT |
or [temp_bits], TCP_BIT_NEEDOUTPUT |
jmp .no_restart |
.more_data: |
test [ebx + TCP_SOCKET.timer_flags], timer_flag_persist |
1067,7 → 1080,9 |
pop ecx |
|
cmp esi, eax |
cmova esi, eax |
jbe @f |
mov esi, eax |
@@: |
mov [ebx + TCP_SOCKET.SND_CWND], esi |
|
;------------------------------------------ |
1175,13 → 1190,10 |
call mutex_unlock |
pop ebx |
|
push ebx |
mov eax, ebx |
call TCP_disconnect |
pop ebx |
call TCP_close |
jmp .drop_no_socket |
|
jmp .destroy_new_socket |
|
.ack_tw: |
mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL |
or [ebx + TCP_SOCKET.timer_flags], timer_flag_wait |
1228,7 → 1240,7 |
TCP_rcvseqinit ebx |
|
mov [ebx + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED |
mov [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval ;;;; macro |
or [ebx + TCP_SOCKET.timer_flags], timer_flag_keepalive |
|
1238,7 → 1250,7 |
lea eax, [ebx + STREAM_SOCKET.rcv] |
call SOCKET_ring_create |
|
and [ebx + TCP_SOCKET.temp_bits], not TCP_BIT_DROPSOCKET |
and [temp_bits], not TCP_BIT_DROPSOCKET |
|
pusha |
mov eax, ebx |
1355,8 → 1367,17 |
|
inc [edx + TCP_header.SequenceNumber] |
|
;;; TODO: Drop any received data that follows receive window (590) |
; Drop any received data that doesnt fit in the receive window. |
cmp ecx, [ebx + TCP_SOCKET.RCV_WND] |
jbe .dont_trim |
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: received data does not fit in window, trimming %u bytes\n", eax |
mov ecx, [ebx + TCP_SOCKET.RCV_WND] |
and [edx + TCP_header.Flags], not (TH_FIN) |
;;; TODO: update stats |
|
.dont_trim: |
|
mov eax, [edx + TCP_header.SequenceNumber] |
mov [ebx + TCP_SOCKET.RCV_UP], eax |
dec eax |
1409,7 → 1430,7 |
push [edx + TCP_header.AckNumber] |
pop [ebx + TCP_SOCKET.SND_WL2] |
|
or [ebx + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT |
or [temp_bits], TCP_BIT_NEEDOUTPUT |
|
.no_window_update: |
|
1475,7 → 1496,7 |
or [ebx + TCP_SOCKET.t_flags], TF_DELACK |
|
pusha |
movzx esi, [edx + TCP_header.DataOffset] |
mov esi, [dataoffset] |
add esi, edx |
lea eax, [ebx + STREAM_SOCKET.rcv] |
call SOCKET_ring_write ; Add the data to the socket buffer |
1489,16 → 1510,16 |
jmp .data_done |
|
.out_of_order: |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP data is out of order!\nSequencenumber is %u, we expected %u.\n", \ |
[edx + TCP_header.SequenceNumber], [ebx + TCP_SOCKET.RCV_NXT] |
|
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP data is out of order\n" |
|
; Uh-oh, some data is out of order, lets call TCP reassemble for help |
|
call TCP_reassemble |
|
DEBUGF DEBUG_NETWORK_VERBOSE, "1470\n" |
; Generate ACK immediately, to let the other end know that a segment was received out of order, |
; and to tell it what sequence number is expected. This aids the fast-retransmit algorithm. |
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
|
.data_done: |
|
;--------------- |
1517,7 → 1538,7 |
mov eax, ebx |
call SOCKET_cant_recv_more |
|
mov [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
inc [ebx + TCP_SOCKET.RCV_NXT] |
|
.not_first_fin: |
1539,31 → 1560,56 |
dd .fin_timed ; TCPS_TIMED_WAIT |
|
.fin_syn_est: |
|
mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSE_WAIT |
jmp .final_processing |
|
.fin_wait1: |
|
mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSING |
jmp .final_processing |
|
.fin_wait2: |
|
mov [ebx + TCP_SOCKET.t_state], TCPS_TIMED_WAIT |
mov eax, ebx |
call TCP_cancel_timers |
mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL |
or [ebx + TCP_SOCKET.timer_flags], timer_flag_wait |
call SOCKET_is_disconnected |
jmp .final_processing |
|
.fin_timed: |
mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL |
or [ebx + TCP_SOCKET.timer_flags], timer_flag_wait |
jmp .final_processing |
|
;----------------- |
; Final processing |
|
.final_processing: |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Final processing\n" |
|
push ebx |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
pop eax |
|
test [temp_bits], TCP_BIT_NEEDOUTPUT |
jnz .need_output |
|
test [eax + TCP_SOCKET.t_flags], TF_ACKNOW |
jz .dumpit |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: ACK now!\n" |
|
.need_output: |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: need output\n" |
call TCP_output |
|
.dumpit: |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: dumping\n" |
|
call NET_packet_free |
jmp .loop |
|
|
;----------------- |
; Drop the segment |
|
|
.drop_after_ack: |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Drop after ACK\n" |
|
1598,35 → 1644,6 |
jnz .respond_syn |
jmp .dumpit |
|
;----------------- |
; Final processing |
|
.final_processing: |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Final processing\n" |
|
push ebx |
lea ecx, [ebx + SOCKET.mutex] |
call mutex_unlock |
pop eax |
|
test [eax + TCP_SOCKET.temp_bits], TCP_BIT_NEEDOUTPUT |
jnz .need_output |
|
test [eax + TCP_SOCKET.t_flags], TF_ACKNOW |
jz .dumpit |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: ACK now!\n" |
|
.need_output: |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: need output\n" |
call TCP_output |
|
.dumpit: |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: dumping\n" |
|
call NET_packet_free |
add esp, 4 |
jmp .loop |
|
;--------- |
; Respond |
|
1687,7 → 1704,7 |
popa |
|
.destroy_new_socket: |
test [ebx + TCP_SOCKET.temp_bits], TCP_BIT_DROPSOCKET |
test [temp_bits], TCP_BIT_DROPSOCKET |
jz .drop_no_socket |
|
mov eax, ebx |
1697,5 → 1714,6 |
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Drop (no socket)\n" |
|
call NET_packet_free |
add esp, 4 |
jmp .loop |
|
endp |