Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 1528 → Rev 1529

/kernel/branches/net/network/ARP.inc
16,20 → 16,16
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
$Revision$
 
 
ARP_NO_ENTRY equ 0
ARP_VALID_MAPPING equ 1
ARP_AWAITING_RESPONSE equ 2
ARP_RESPONSE_TIMEOUT equ 3
 
ARP_REQUEST_TTL = 20 ; in seconds
ARP_ENTRY_TTL = 600 ; in seconds
ARP_REQUEST_TTL equ 31 ; 20 s
ARP_ENTRY_TTL equ 937 ; 600 s
 
ETHER_ARP equ 0x0608
 
ARP_REQ_OPCODE equ 0x0100 ; request
ARP_REP_OPCODE equ 0x0200 ; reply
 
39,7 → 35,7
.IP dd ?
.MAC dp ?
.Status dw ?
.TTL dw ? ; in seconds
.TTL dw ?
.size:
ends
 
53,26 → 49,16
.SenderIP dd ?
.TargetMAC dp ?
.TargetIP dd ?
.size:
ends
 
 
; The TTL field is decremented every second, and is deleted when it
; reaches 0. It is refreshed every time a packet is received
; If the TTL field is 0xFFFF it is a static entry and is never deleted
; The status field can be the following values:
; 0x0000 entry not used
; 0x0001 entry holds a valid mapping
; 0x0002 entry contains an IP address, awaiting ARP response
; 0x0003 No response received to ARP request.
; The last status value is provided to allow the network layer to delete
; a packet that is queued awaiting an ARP response
 
align 4
uglobal
 
NumARP dd ?
 
ARPTable rb ARP_ENTRY.size * ARP_TABLE_SIZE
ARP_table rb ARP_ENTRY.size * ARP_TABLE_SIZE
 
ARP_PACKETS_TX rd MAX_NET_DEVICES
ARP_PACKETS_RX rd MAX_NET_DEVICES
88,15 → 74,10
;
; This function resets all ARP variables
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
ARP_init:
macro ARP_init {
 
xor eax, eax
 
mov [NumARP], eax
 
mov edi, ARP_PACKETS_TX
103,152 → 84,217
mov ecx, 2*MAX_NET_DEVICES
rep stosd
 
ret
}
 
;---------------------------------------------------------------------------
;
; ARP_decrease_entry_ttls
;
;---------------------------------------------------------------------------
 
macro ARP_decrease_entry_ttls {
 
local .loop
local .exit
 
; The TTL field is decremented every second, and is deleted when it reaches 0.
; It is refreshed every time a packet is received.
; If the TTL field is 0xFFFF it is a static entry and is never deleted.
; The status field can be the following values:
; 0x0000 entry not used
; 0x0001 entry holds a valid mapping
; 0x0002 entry contains an IP address, awaiting ARP response
; 0x0003 No response received to ARP request.
; The last status value is provided to allow the network layer to delete
; a packet that is queued awaiting an ARP response
 
mov ecx, [NumARP]
test ecx, ecx
jz .exit
 
mov esi, ARP_table
.loop:
cmp [esi + ARP_ENTRY.TTL], 0xffff ; 0xffff = static entry
je .next
 
dec [esi + ARP_ENTRY.TTL]
jz .time_out
 
.next:
add esi, ARP_ENTRY.size
loop .loop
jmp .exit
 
.time_out:
cmp [esi + ARP_ENTRY.Status], ARP_AWAITING_RESPONSE
jz .response_timeout
 
push esi ecx
call ARP_del_entry
pop ecx esi
 
jmp .next
 
.response_timeout:
mov [esi + ARP_ENTRY.Status], ARP_RESPONSE_TIMEOUT
mov [esi + ARP_ENTRY.TTL], 10
 
jmp .next
 
.exit:
 
}
 
 
;-----------------------------------------------------------------
;
; ARP_IP_to_MAC
; ARP_input
;
; This function resets all ARP variables
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; packet size (without ethernet header) in ecx
; OUT: /
;
; IN: eax = IPv4 address
; OUT: eax = -1 on error, else eax = first two bytes of mac
; ( high 16 bits are zero)
; ebx = last four bytes of mac ; TODO: special eax value for 'request send'
;
;-----------------------------------------------------------------
align 4
ARP_IP_to_MAC:
ARP_input:
 
DEBUGF 1,"ARP_IP_to_MAC\n"
DEBUGF 1,"ARP_Handler - start\n"
cmp ecx, 28
jl .exit
 
; first, check destination IP to see if it is on 'this' network.
; The test is:
; if ( destIP & subnet_mask == stack_ip & subnet_mask )
; destination is local
; else
; destination is remote, so pass to gateway
;---------------------
; Handle Reply packets
 
xor edx, edx ;;; TODO: find device num in edx
cmp word [edx + ARP_Packet.Opcode], ARP_REP_OPCODE
jne .maybe_request
 
mov ebx, [IP_LIST + edx]
and ebx, [SUBNET_LIST + edx]
DEBUGF 1,"ARP_Handler - it's a reply packet from %u.%u.%u.%u\n",\
[edx + ARP_Packet.SenderIP]:1,[edx + ARP_Packet.SenderIP+1]:1,[edx + ARP_Packet.SenderIP+2]:1,[edx + ARP_Packet.SenderIP+3]:1,
 
mov ecx, eax
and ecx, [SUBNET_LIST + edx]
mov ecx, [NumARP]
test ecx, ecx
jz .exit
 
cmp ecx, ebx
je .local
mov eax, [edx + ARP_Packet.SenderIP]
mov esi, ARP_table
 
mov eax, [GATEWAY_LIST + edx]
DEBUGF 1,"requested IP is not on subnet, using gateway\n"
 
.local:
; try to find it on the list
mov ecx, [NumARP]
test ecx, ecx
jz .not_in_list
mov esi, ARPTable + ARP_ENTRY.IP
.scan_loop:
cmp [esi], eax
je .found_it
.loop:
cmp [esi + ARP_ENTRY.IP], eax
je .gotit
add esi, ARP_ENTRY.size
loop .scan_loop
.not_in_list:
loop .loop
 
DEBUGF 1,"IP not found on list, preparing for ARP request\n"
jmp .exit
 
; if not, reserve an entry in list and send an ARP request packet
.gotit:
DEBUGF 1,"ARP_Handler - found matching entry\n"
 
cmp [esi+ARP_ENTRY.TTL], 0xffff ; if it is a static entry, dont touch it
je .exit
 
DEBUGF 1,"ARP_Handler - updating entry\n"
 
mov [esi+ARP_ENTRY.Status], ARP_VALID_MAPPING
mov [esi+ARP_ENTRY.TTL], ARP_ENTRY_TTL
 
mov eax, dword [edx + ARP_Packet.SenderMAC]
mov dword [esi+ARP_ENTRY.MAC], eax
mov ax , word [edx + ARP_Packet.SenderMAC + 4]
mov word [esi+ARP_ENTRY.MAC+4], ax
 
jmp .exit
 
 
;-----------------------
; Handle Request packets
 
.maybe_request:
cmp word [edx + ARP_Packet.Opcode], ARP_REQ_OPCODE
jne .exit
 
call NET_ptr_to_num
cmp edi, -1
jz .exit
DEBUGF 1,"ARP Request packet through device: %u\n", edi
inc [ARP_PACKETS_RX+4*edi]
 
mov eax, [IP_LIST+4*edi]
cmp eax, [edx + ARP_Packet.TargetIP] ; Is it looking for my IP address?
jne .exit ; TODO: instead of quitting, update local entrys with matching IP's ?
 
push eax
push edi
 
pushw ARP_REQUEST_TTL
pushw ARP_AWAITING_RESPONSE
pushd 0
pushw 0
pushd eax
call ARP_add_entry
cmp eax, -1
je .full
; OK, it is a request for one of our MAC addresses.
; Build the frame and send it. We can reuse the buffer. (faster then using ARP_create_packet)
 
; <Some dirty test code>
lea esi, [edx + ARP_Packet.SenderMAC]
lea edi, [edx + ARP_Packet.TargetMAC]
movsd ; Move Sender Mac to Dest MAC
movsw ;
movsd ; Move sender IP to Dest IP
 
; This piece of code waits for an ARP reply
 
mov ebx, eax
pop esi
mov esi, [NET_DRV_LIST + 4*esi]
lea esi, [esi + ETH_DEVICE.mac]
lea edi, [edx + ARP_Packet.SenderMAC]
movsd ; Copy MAC address from in MAC_LIST
movsw ;
pop eax
push ebx
call ARP_create_request
stosd ; Write our IP
 
push [timer_ticks]
add dword[esp], 100*ARP_REQUEST_TTL
DEBUGF 1,"Waiting for ARP reply, time: %x, entry:%u\n",[timer_ticks], [esp + 4]
.dirty_loop:
mov word [edx + ARP_Packet.Opcode], ARP_REP_OPCODE
 
call change_task ; The ARP reply hasnt been received yet, tell the processor to do some other stuff first
; Now, Fill in ETHERNET header
 
mov eax, [esp + 4]
imul eax, ARP_ENTRY.size
add eax, ARPTable
cmp [eax + ARP_ENTRY.Status], ARP_VALID_MAPPING
je .gogogo
mov edi, [esp]
lea esi, [edx + ARP_Packet.TargetMAC]
movsd
movsw
lea esi, [edx + ARP_Packet.SenderMAC]
movsd
movsw
; mov ax , ETHER_ARP
; stosw
 
mov eax, [esp] ; Check if the reply hasnt timed-out yet
cmp [timer_ticks], eax
jl .dirty_loop
DEBUGF 1,"ARP_Handler - Sending reply \n"
 
; </Some dirty test code>
or eax, -1
add esp, 8
call [ebx + NET_DEVICE.transmit]
ret
 
.found_it:
DEBUGF 1,"found MAC in ARPTable\n"
movzx eax, word [esi+ARP_ENTRY.MAC]
mov ebx, dword[esi+ARP_ENTRY.MAC+2]
ret
.exit:
call kernel_free
add esp, 4 ; pop (balance stack)
 
.full:
add esp, 4
mov eax, -1
DEBUGF 1,"ARP_Handler - exiting\n"
ret
 
.gogogo:
DEBUGF 1,"got ARP reply, time: %x\n",[timer_ticks]
mov ebx, dword[eax+ARP_ENTRY.MAC+2]
movzx eax, word [eax+ARP_ENTRY.MAC]
add esp, 8
ret
 
 
;---------------------------------------------------------------------------
;
; ARP_create_request
; ARP_output_request
;
; IN: ip in eax
;
; OUT: /
;
;---------------------------------------------------------------------------
align 4
ARP_create_request:
ARP_output_request:
 
DEBUGF 1,"Create ARP Packet\n"
 
call IPv4_dest_to_dev
 
push eax ; DestIP
mov eax, [IP_LIST+4*edi] ; senderIP
push eax
pushd [IP_LIST+edi] ; SenderIP
 
mov edi, [NET_DRV_LIST + 4*edi]
lea eax, [edi + ETH_DEVICE.mac]
mov ebx, ETH_BROADCAST
mov ecx, 60 ; minimum packet size
mov edx, edi ;;;
mov ebx, [NET_DRV_LIST+edi] ; device ptr
 
lea eax, [ebx + ETH_DEVICE.mac] ; local device mac
mov edx, ETH_BROADCAST ; broadcast mac
mov ecx, ARP_Packet.size
mov di , ETHER_ARP
call ETH_create_packet
call ETH_output
jz .exit
 
mov ecx, eax
259,16 → 305,18
mov [edi + ARP_Packet.ProtocolSize], 4 ;IP-addr length
mov [edi + ARP_Packet.Opcode], ARP_REQ_OPCODE ;Request
 
add edi, ARP_Packet.SenderMAC ; sendermac
lea esi, [ebx + ETH_DEVICE.mac] ;
add edi, ARP_Packet.SenderMAC
 
lea esi, [ebx + ETH_DEVICE.mac] ; SenderMac
movsw ;
movsd ;
pop eax ;
pop eax ; SenderIP
stosd ;
mov eax, -1 ; destmac
 
mov eax, -1 ; DestMac
stosd ;
stosw ;
pop eax
pop eax ; DestIP
stosd ;
 
DEBUGF 1,"ARP Packet for device %x created successfully\n", ebx
278,84 → 326,17
ret
 
.exit:
add esp, 8
add esp, 4+4
DEBUGF 1,"Create ARP Packet - failed\n"
mov eax, -1
sub eax, eax
ret
 
 
 
;---------------------------------------------------------------------------
;
; ARP_decrease_entry_ttls
;
; IN: /
; OUT: /
;
;---------------------------------------------------------------------------
align 4
ARP_decrease_entry_ttls:
 
mov ecx, [NumARP]
test ecx, ecx
jz .exit
 
mov ebx, ARPTable
 
.timer_loop:
 
cmp [ebx + ARP_ENTRY.TTL], 0xFFFF
je .timer_loop_end ;if TTL==0xFFFF then it's static entry
 
cmp [ebx + ARP_ENTRY.TTL], 0
jnz .timer_loop_end_with_dec ;if TTL!=0
 
; Ok, TTL is 0
;if Status==AWAITING_RESPONSE and TTL==0
;then we have to change it to ARP_RESPONSE_TIMEOUT
cmp [ebx + ARP_ENTRY.Status], ARP_AWAITING_RESPONSE
jne @f
 
mov [ebx + ARP_ENTRY.Status], ARP_RESPONSE_TIMEOUT
mov [ebx + ARP_ENTRY.TTL], word 0x000A ;10 sec
jmp .timer_loop_end
 
@@:
;if TTL==0 and Status==VALID_MAPPING, we have to delete it
;if TTL==0 and Status==RESPONSE_TIMEOUT, delete too
mov esi, [NumARP]
sub esi, ecx ;esi=index of entry, will be deleted
 
push ebx ecx
call ARP_del_entry
pop ecx ebx
 
jmp .timer_loop_end
 
 
.timer_loop_end_with_dec:
 
dec [ebx + ARP_ENTRY.TTL] ;decrease TTL
 
.timer_loop_end:
 
add ebx, ARP_ENTRY.size
loop .timer_loop
 
.exit:
 
ret
 
;-----------------------------------------------------------------
;
; ARP_add_entry (or update)
;
; IN: arp entry in stack: esp .IP
; esp+4 .MAC
; esp+10 .Status
; esp+12 .TTL
; esp+14
;
; IN: esi = ptr to entry (can easily be made on the stack)
; OUT: eax = entry #, -1 on error
;
;----------------------------------------------------------------- ; TODO: use a mutex
365,28 → 346,29
DEBUGF 1,"ARP add entry: "
 
mov ecx, [NumARP]
test ecx, ecx
test ecx, ecx ; first entry?
jz .add
cmp ecx, ARP_TABLE_SIZE ; list full ?
jge .error
 
mov eax, dword[esp + 4 + ARP_ENTRY.MAC]
mov bx , word[esp + 4 + ARP_ENTRY.MAC + 4]
mov esi, ARPTable
mov eax, dword[esi + ARP_ENTRY.MAC]
mov bx , word[esi + ARP_ENTRY.MAC + 4]
mov edi, ARP_table
 
.loop:
cmp dword [esi + ARP_ENTRY.MAC], eax
jne .maybe_next
cmp word [esi + ARP_ENTRY.MAC + 4], bx
jne .maybe_next
cmp dword [edi + ARP_ENTRY.MAC], eax ; Check for duplicate MAC's
jne .maybe_next ;
cmp word [edi + ARP_ENTRY.MAC + 4], bx ;
jne .maybe_next ;
 
cmp dword[esi + ARP_ENTRY.TTL], 0xFFFF ; static entry
cmp dword[edi + ARP_ENTRY.TTL], 0xFFFF ; static entry
jne .notstatic
cmp dword[esp + 4 + ARP_ENTRY.TTL], 0xFFFF
cmp dword[esi + ARP_ENTRY.TTL], 0xFFFF
jne .error
.notstatic:
 
mov ebx, [NumARP]
xchg ebx, ecx
sub ecx, ebx
neg ecx
add ecx, [NumARP]
jmp .add
 
.maybe_next:
394,30 → 376,26
loop .loop
 
mov ecx, [NumARP]
cmp ecx, ARP_TABLE_SIZE
jge .error
 
.add:
push ecx
imul ecx, ARP_ENTRY.size
lea edi, [ecx + ARPTable]
lea esi, [esp + 8]
lea edi, [ecx + ARP_table]
mov ecx, ARP_ENTRY.size/2
repz movsw
rep movsw
 
lea esi, [edi - ARP_ENTRY.size]
inc [NumARP]
pop eax
DEBUGF 1,"New entry created: %u\n", eax
 
.exit:
DEBUGF 1,"Exiting\n"
ret ARP_ENTRY.size
ret
 
.error:
 
DEBUGF 1,"error! \n"
 
mov eax, -1
jmp .exit
ret
 
 
;-----------------------------------------------------------------
424,7 → 402,7
;
; ARP_del_entry
;
; IN: entry # in esi
; IN: esi = ptr to arp entry
; OUT: /
;
;-----------------------------------------------------------------
431,161 → 409,129
align 4
ARP_del_entry:
 
DEBUGF 1,"ARP del entry %u, total entrys: %u\n", esi, [NumARP]
DEBUGF 1,"ARP del entry %x, total entrys: %u\n", esi, [NumARP]
 
cmp esi, [NumARP]
jge .error
 
imul esi, ARP_ENTRY.size
 
mov ecx, (ARP_TABLE_SIZE - 1) * ARP_ENTRY.size
mov ecx, ARP_table + (ARP_TABLE_SIZE - 1) * ARP_ENTRY.size
sub ecx, esi
shr ecx, 1
 
lea edi, [ARPTable + esi] ;edi=ptr to entry that should be deleted
lea esi, [edi + ARP_ENTRY.size] ;esi=ptr to next entry
 
shr ecx,1 ;ecx/2 => ARP_ENTRY_SIZE MUST BE EVEN NUMBER!
mov edi, esi
lea esi, [edi + ARP_ENTRY.size]
rep movsw
 
dec [NumARP] ;decrease arp-entries counter
dec [NumARP]
DEBUGF 1,"ARP entry deleted\n"
.error:
 
ret
 
 
 
 
 
;-----------------------------------------------------------------
;
; ARP_Handler:
; ARP_IP_to_MAC
;
; This function handles ARP protocol over ethernet
; (other protocols may follow in the future)
; This function translates an IP address to a MAC address
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; packet size (without ethernet header) in ecx
; OUT: /
; IN: eax = IPv4 address
; OUT: eax = -1 on error, -2 means request send
; else, ax = first two bytes of mac (high 16 bits of eax will be 0)
; ebx = last four bytes of mac
;
;-----------------------------------------------------------------
align 4
ARP_handler:
ARP_IP_to_MAC:
 
DEBUGF 1,"ARP_Handler - start\n"
cmp ecx, 28
jl .exit
DEBUGF 1,"ARP_IP_to_MAC\n"
 
cmp word [edx + ARP_Packet.Opcode], ARP_REP_OPCODE ; Is this a reply packet?
jne .maybe_request
cmp eax, 0xffffffff
je .broadcast
 
DEBUGF 1,"ARP_Handler - it's a reply packet from %u.%u.%u.%u\n",\
[edx + ARP_Packet.SenderIP]:1,[edx + ARP_Packet.SenderIP+1]:1,[edx + ARP_Packet.SenderIP+2]:1,[edx + ARP_Packet.SenderIP+3]:1,
; if ((Remote IP & subnet_mask) == (local IP & subnet_mask ))
; destination is on same subnet
; else, destination is remote and must use a gateway
 
mov ecx, [NumARP]
test ecx, ecx
jz .exit
call IPv4_dest_to_dev
mov ebx, [IP_LIST + edi]
and ebx, [SUBNET_LIST + edi]
 
mov eax, [edx + ARP_Packet.SenderIP]
mov esi, ARPTable+ARP_ENTRY.IP
mov ecx, eax
and ecx, [SUBNET_LIST + edi]
 
.loop:
cmp [esi], eax
je .gotit
add esi, ARP_ENTRY.size
loop .loop
cmp ecx, ebx
je .local
 
jmp .exit
mov eax, [GATEWAY_LIST + edi]
DEBUGF 1,"requested IP is not on subnet, using default gateway\n"
 
.gotit:
;--------------------------------
; Try to find the IP in ARP_table
 
DEBUGF 1,"ARP_Handler - found matching entry\n"
.local:
mov ecx, [NumARP]
test ecx, ecx
jz .not_in_list
mov esi, ARP_table + ARP_ENTRY.IP
.scan_loop:
cmp [esi], eax
je .found_it
add esi, ARP_ENTRY.size
loop .scan_loop
 
cmp [esi+ARP_ENTRY.Status], 0x0300 ;if it is a static entry, dont touch it
je .exit
.not_in_list:
DEBUGF 1,"IP not found on list, preparing for ARP request\n"
 
DEBUGF 1,"ARP_Handler - updating entry\n"
;--------------------
; Send an ARP request
 
mov [esi+ARP_ENTRY.Status], ARP_VALID_MAPPING
mov [esi+ARP_ENTRY.TTL], ARP_ENTRY_TTL
 
mov eax, dword [edx + ARP_Packet.SenderMAC]
mov dword [esi+ARP_ENTRY.MAC], eax
mov ax , word [edx + ARP_Packet.SenderMAC + 4]
mov word [esi+ARP_ENTRY.MAC+4], ax
 
jmp .exit
 
 
;------
 
 
.maybe_request:
cmp word [edx + ARP_Packet.Opcode], ARP_REQ_OPCODE ; Is this a request packet?
jne .exit
 
call NET_ptr_to_num
DEBUGF 1,"ARP Request packet through device: %u\n", edi
inc [ARP_PACKETS_RX+4*edi]
cmp edi, -1
jz .exit
 
mov eax, edi
shl eax, 2
add eax, IP_LIST
mov eax, [eax]
cmp eax, [edx + ARP_Packet.TargetIP] ; Is it looking for my IP address?
jnz .exit
push eax
push edi
 
; OK, it is a request for one of our MAC addresses. Build the frame and send it
; We can reuse the buffer. (faster then using ARP_create_packet)
pushw ARP_REQUEST_TTL
pushw ARP_AWAITING_RESPONSE
pushd 0
pushw 0
pushd eax
mov esi, esp
call ARP_add_entry
add esp, ARP_ENTRY.size
 
cld
lea esi, [edx + ARP_Packet.SenderMAC]
lea edi, [edx + ARP_Packet.TargetMAC]
movsd ; Move Sender Mac to Dest MAC
movsw ;
movsd ; Move sender IP to Dest IP
cmp eax, -1
je .full
 
pop esi
mov esi, [NET_DRV_LIST + 4*esi]
lea esi, [esi + ETH_DEVICE.mac]
lea edi, [edx + ARP_Packet.SenderMAC]
movsd ; Copy MAC address from in MAC_LIST
movsw ;
pop eax
stosd ; Write our IP
 
mov word [edx + ARP_Packet.Opcode], ARP_REP_OPCODE
call ARP_output_request
 
; Now, Fill in ETHERNET header
mov eax, -2 ; request send
ret
 
mov edi, [esp]
lea esi, [edx + ARP_Packet.TargetMAC]
movsd
movsw
lea esi, [edx + ARP_Packet.SenderMAC]
movsd
movsw
; mov ax , ETHER_ARP
; stosw
.found_it:
DEBUGF 1,"found IP in ARPTable\n"
cmp [esi + ARP_ENTRY.Status], 1
jne .invalid
 
DEBUGF 1,"ARP_Handler - Sending reply \n"
movzx eax, word [esi+ARP_ENTRY.MAC]
mov ebx, dword[esi+ARP_ENTRY.MAC+2]
ret
 
call [ebx + NET_DEVICE.transmit]
.invalid:
mov eax, -1
ret
 
.exit:
call kernel_free
add esp, 4 ; pop (balance stack)
.full:
DEBUGF 1,"ARP table is full!\n"
pop eax
mov eax, -1
ret
 
DEBUGF 1,"ARP_Handler - exiting\n"
.broadcast:
mov eax, 0x0000ffff
mov ebx, 0xffffffff
ret
 
 
 
 
;-----------------------------------------------------------------
;
; ARP_API
643,7 → 589,7
; edi = pointer to buffer
; ecx = # entry
imul ecx, ARP_ENTRY.size
add ecx, ARPTable
add ecx, ARP_table
mov esi, ecx
mov ecx, ARP_ENTRY.size/2
rep movsw
653,16 → 599,15
 
.write:
; esi = pointer to buffer
sub esp, ARP_ENTRY.size
mov edi, esp
mov ecx, ARP_ENTRY.size/2
rep movsw
call ARP_add_entry ;out: eax = entry number, -1 on error
ret
 
.remove:
; ecx = # entry
mov esi, ecx
cmp ecx, [NumARP]
jge .error
imul ecx, ARP_ENTRY.size
lea esi, [ARP_table + ecx]
call ARP_del_entry
ret
 
/kernel/branches/net/network/IPv4.inc
53,6 → 53,73
.Data: ; Ip header begins here (we will need the IP header to re-construct the complete packet)
ends
 
 
 
 
macro IPv4_checksum ptr {
 
; This is the fast procedure to create or check a IP header without options
; To create a new checksum, the checksum field must be set to 0 before computation
; To check an existing checksum, leave the checksum as is, and it will be 0 after this procedure, if it was correct
 
push ebx
xor ebx, ebx
add bl, [ptr+1]
adc bh, [ptr+0]
 
adc bl, [ptr+3]
adc bh, [ptr+2]
 
adc bl, [ptr+5]
adc bh, [ptr+4]
 
adc bl, [ptr+7]
adc bh, [ptr+6]
 
adc bl, [ptr+9]
adc bh, [ptr+8]
 
; we skip 11th and 12th byte, they are the checksum bytes and should be 0 for re-calculation
 
adc bl, [ptr+13]
adc bh, [ptr+12]
 
adc bl, [ptr+15]
adc bh, [ptr+14]
 
adc bl, [ptr+17]
adc bh, [ptr+16]
 
adc bl, [ptr+19]
adc bh, [ptr+18]
 
adc ebx, 0
 
push ecx
mov ecx, ebx
shr ecx, 16
and ebx, 0xffff
add ebx, ecx
 
mov ecx, ebx
shr ecx, 16
add ebx, ecx
 
not bx
jnz .not_zero
dec bx
.not_zero:
xchg bl, bh
pop ecx
 
neg word [ptr+10] ; zero will stay zero so we just get the checksum
add word [ptr+10], bx ; , else we will get (new checksum - old checksum) in the end, wich should be 0 :)
pop ebx
 
}
 
 
 
align 4
uglobal
 
74,30 → 141,49
;
; This function resets all IP variables
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
IPv4_init:
macro IPv4_init {
 
or eax, -1
xor eax, eax
mov edi, IP_LIST
mov ecx, 4*MAX_IP
rep stosd
 
inc eax
mov edi, FRAGMENT_LIST
mov ecx, FRAGMENT_slot.size*MAX_FRAGMENTS/4 + 2*MAX_IP
rep stosd
 
ret
}
 
 
;-----------------------------------------------------------------
;
; Decrease TimeToLive of all fragment slots
;
;-----------------------------------------------------------------
macro IPv4_decrease_fragment_ttls {
 
local .loop
 
mov esi, FRAGMENT_LIST
mov ecx, MAX_FRAGMENTS
.loop:
cmp [esi + FRAGMENT_slot.ttl], 0
je .try_next
dec [esi + FRAGMENT_slot.ttl]
jnz .try_next
DEBUGF 1,"Fragment slot timed-out!\n"
;;; TODO: clear all entry's of timed-out slot
.try_next:
add esi, 4
loop .loop
}
 
 
 
;-----------------------------------------------------------------
;
; IPv4_Handler:
; IPv4_input:
;
; Will check if IP Packet isnt damaged
; and call appropriate handler. (TCP/UDP/ICMP/..)
112,7 → 198,7
;
;-----------------------------------------------------------------
align 4
IPv4_handler: ; TODO: implement handler for IP options
IPv4_input: ; TODO: implement handler for IP options
; TODO2: add code for raw sockets
 
DEBUGF 1,"IPv4_Handler, packet from: %u.%u.%u.%u ",\
137,14 → 223,9
;-------------------------------
; Now, re-calculate the checksum
 
push edx ebx
mov esi, edx
call IPv4_checksum
pop ebx edx
IPv4_checksum edx
jnz .dump ; if checksum isn't valid then dump packet
 
cmp [edx + IPv4_Packet.HeaderChecksum], 0
jne .dump ; if checksum isn't valid then dump packet
 
DEBUGF 1,"IPv4 Checksum is correct\n"
 
;-----------------------------------
435,10 → 516,7
 
; mov esi, edx ; This prints the IP packet to the debug board (usefull when using serial output debug..)
; ;
; @@: ;
; lodsb ;
; DEBUGF 1,"%x ", eax:2 ;
; loop @r ;
; packet_to_debug
 
jmp .handle_it ; edx = buf ptr, ecx = size, [esp] buf ptr, [esp+4], total size, ebx=device ptr
 
460,9 → 538,6
 
 
 
 
 
 
;-----------------------------------------------------------------
;
; find fragment slot
501,70 → 576,15
ret
 
 
;-----------------------------------------------------------------
;
; Decrease TimeToLive of all fragment slots
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
IPv4_decrease_fragment_ttls:
 
mov esi, FRAGMENT_LIST
mov ecx, MAX_FRAGMENTS
.loop:
cmp [esi + FRAGMENT_slot.ttl], 0
je .try_next
dec [esi + FRAGMENT_slot.ttl]
jnz .try_next
DEBUGF 1,"Fragment slot timed-out!\n"
;;; TODO: clear all entry's of timed-out slot
.try_next:
add esi, 4
loop .loop
ret
 
 
 
 
 
;------------------------------------------------------------------
;
; IPv4_output
;
; IN: dword [esp] = pointer to packet to be fragmented
; dword [esp+4] = buffer size
; edx = pointer to IPv4 header in that packet
; ecx = data length
; ebx = device structure
;
; OUT: /
;
;------------------------------------------------------------------
align 4
IPv4_fragment:
 
;;; TODO: write code here
 
 
call kernel_free
add esp, 4
 
ret
 
 
 
 
;------------------------------------------------------------------
;
; Create_IPv4_Packet
;
; IN: eax = dest ip
; ebx = source ip
; ecx = data length
; dx = fragment id ;;;;
; di = protocol
; dx = fragment id
; di = TTL shl 8 + protocol
;
; OUT: eax = pointer to buffer start
; ebx = pointer to device struct (needed for sending procedure)
574,185 → 594,225
;
;------------------------------------------------------------------
align 4
IPv4_create_packet:
IPv4_output:
 
DEBUGF 1,"Create IPv4 Packet (size=%u)\n", ecx
DEBUGF 1,"IPv4_create_packet: size=%u\n", ecx
 
cmp ecx, 65500 ; Max IPv4 packet size
jg .exit_
jg .too_large
 
test ebx, ebx ; if source ip = 0
jnz .ip_ok ; and local ip is valid
; use local ip instead
cmp [IP_LIST],0xffffffff ;
je .ip_ok ; TODO: find solution to send broadcast
; on device other then device 0
mov ebx, [IP_LIST] ;
;
.ip_ok: ;
 
push ecx eax ebx dx di
 
cmp eax, -1
je .broadcast ; If it is broadcast, just send
 
call ARP_IP_to_MAC
 
cmp eax, -1
je .not_found
test eax, 0xffff0000 ; error bits
jnz .arp_error
 
push ebx
push ebx ; push the mac
push ax
 
jmp .send
 
.broadcast:
push word -1
push dword -1
 
.send:
call IPv4_dest_to_dev
inc [IP_PACKETS_TX+4*edi]
mov edx, [NET_DRV_LIST + 4*edi]
lea eax, [edx + ETH_DEVICE.mac]
mov ebx, esp
mov ecx, [esp+18] ;; 18 or 22 ??
inc [IP_PACKETS_TX+edi]
mov ebx, [NET_DRV_LIST+edi]
lea eax, [ebx + ETH_DEVICE.mac]
mov edx, esp
mov ecx, [esp + 18]
add ecx, IPv4_Packet.DataOrOptional
mov di , ETHER_IPv4
;;; TODO: detect if packet is too large for ethernet, if so, call IPv4_fragment
call ETH_create_packet ;;; TODO: figure out a way to make this work with other protocols too
add esp, 6
test edi, edi
jz .exit
call ETH_output
jz .error
 
add esp, 6 ; pop the mac
 
mov [edi + IPv4_Packet.VersionAndIHL], 0x45 ; IPv4, normal length (no Optional header)
mov [edi + IPv4_Packet.TypeOfService], 0
xchg ch, cl
mov [edi + IPv4_Packet.TypeOfService], 0 ; nothing special, just plain ip packet
mov [edi + IPv4_Packet.TotalLength], cx
rol [edi + IPv4_Packet.TotalLength], 8 ; internet byte order
mov [edi + IPv4_Packet.FlagsAndFragmentOffset], 0x0000
mov [edi + IPv4_Packet.TimeToLive], 128
mov [edi + IPv4_Packet.HeaderChecksum], 0
pop cx
mov [edi + IPv4_Packet.Protocol], cl
pop cx
mov [edi + IPv4_Packet.Identification], cx
popw word [edi + IPv4_Packet.TimeToLive] ; ttl shl 8 + protocol
; [edi + IPv4_Packet.Protocol]
popw [edi + IPv4_Packet.Identification] ; fragment id
popd [edi + IPv4_Packet.SourceAddress]
popd [edi + IPv4_Packet.DestinationAddress]
 
pop ecx
mov [edi + IPv4_Packet.SourceAddress], ecx
pop ecx
mov [edi + IPv4_Packet.DestinationAddress], ecx
 
push eax edx esi
mov esi, edi
call IPv4_checksum
pop esi edx eax ecx
IPv4_checksum edi
add edi, IPv4_Packet.DataOrOptional
 
DEBUGF 1,"IPv4 Packet for device %x created successfully\n", ebx
 
ret
 
 
.not_found:
DEBUGF 1,"Create IPv4 Packet - ARP entry not found!\n"
;;;;;;
.exit:
add esp, 16
.exit_:
DEBUGF 1,"Create IPv4 Packet - failed\n"
and edi, 0
.error:
add esp, 6
.arp_error:
add esp, 4+4+4+2+2
.too_large:
DEBUGF 1,"IPv4_create_packet: Failed\n"
sub edi, edi
ret
 
 
;--------------------------------------------------------
;
;
; IN: dword [esp] = pointer to buffer containing ipv4 packet to be fragmented
; dword [esp+4] = buffer size
; esi = pointer to ip header in that buffer
; ecx = max size of fragments
;
; OUT: /
;
;--------------------------------------------------------
 
align 4
IPv4_checksum:
IPv4_fragment:
 
; This is the fast procedure to create or check a IP header without options
;
; To create a new checksum, the checksum field must be set to 0 before computation
;
; To check an existing checksum, leave the checksum as is, and it will be 0 after this procedure, if it was correct
DEBUGF 1,"IPv4_fragment\n"
 
xor edx, edx
and ecx, not 111b ; align 4
 
add dl, [esi+1]
adc dh, [esi+0]
cmp ecx, IPv4_Packet.DataOrOptional + 8 ; must be able to put at least 8 bytes
jl .err2
 
adc dl, [esi+3]
adc dh, [esi+2]
push esi ecx
mov eax, [esi + IPv4_Packet.DestinationAddress]
call ARP_IP_to_MAC
pop ecx esi
cmp eax, -1
jz .err2
 
adc dl, [esi+5]
adc dh, [esi+4]
push ebx
push ax
 
adc dl, [esi+7]
adc dh, [esi+6]
mov ebx, [NET_DRV_LIST]
lea eax, [ebx + ETH_DEVICE.mac]
push eax
 
adc dl, [esi+9]
adc dh, [esi+8]
 
; we skip 11th and 12th byte, they are the checksum bytes and should be 0 for re-calculation
push esi ; ptr to ip header
sub ecx, 20 ; substract header size
push ecx ; max data size
push dword 0 ; offset
 
adc dl, [esi+13]
adc dh, [esi+12]
.new_fragment:
DEBUGF 1,"Ipv4_fragment - new_fragmentn"
 
adc dl, [esi+15]
adc dh, [esi+14]
 
adc dl, [esi+17]
adc dh, [esi+16]
mov eax, [esp + 3*4]
lea ebx, [esp + 4*4]
mov di , ETHER_IPv4
call ETH_output
 
adc dl, [esi+19]
adc dh, [esi+18]
cmp edi, -1
jz .err
 
adc edx, 0
; copy header
mov esi, [esp + 2*4]
mov ecx, 5 ; 5 dwords: TODO: use IHL field of the header!
rep movsd
 
call checksum_2
; copy data
mov esi, [esp + 2*4]
add esi, 20
add esi, [esp] ; offset
 
neg word [esi+10] ; zero will stay zero so we just get the checksum
add word [esi+10], dx ; , else we will get (new checksum - old checksum) in the end, wich should be 0 :)
mov ecx, [esp + 1*4]
DEBUGF 1,"IPv4_fragment - copying data (%u bytes)\n", ecx
rep movsb
 
ret
; now, correct header
mov ecx, [esp + 1*4]
add ecx, 20
xchg cl, ch
mov [edi + IPv4_Packet.TotalLength], cx
 
mov ecx, [esp] ; offset
xchg cl, ch
 
; cmp dword[esp + 4*4], 0 ; last fragment?;<<<<<<
; je .last_fragment
or cx, 1 shl 2 ; more fragments
; .last_fragment:
mov [edi + IPv4_Packet.FlagsAndFragmentOffset], cx
 
mov [edi + IPv4_Packet.HeaderChecksum], 0
 
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<< send the packet
mov ecx, [esp + 1*4]
 
push edx eax
IPv4_checksum edi
 
call [ebx + NET_DEVICE.transmit]
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 
mov ecx, [esp+4]
add [esp], ecx
 
mov ecx, [esp+3*4+6+4] ; ptr to begin of buff
add ecx, [esp+3*4+6+4+4] ; buff size
sub ecx, [esp+2*4] ; ptr to ip header
add ecx, [esp] ; offset
 
DEBUGF 1,"Ipv4_fragment - bytes remaining: %u\n", ecx
 
cmp ecx, [esp+1*4]
jge .new_fragment
 
mov [esp+4], ecx ; set fragment size to remaining packet size
jmp .new_fragment
 
.err:
DEBUGF 1,"Ipv4_fragment - failed\n"
.done:
add esp, 12 + 4 + 6
.err2:
DEBUGF 1,"Ipv4_fragment - dumping\n"
call kernel_free
add esp, 4
 
ret
 
 
 
;---------------------------------------------------------------------------
;
; IPv4_dest_to_dev
;
; IN: Destination IP in eax
; OUT: device id in edi
; IN: eax = Destination IP
; OUT: edi = device id * 4
;
;---------------------------------------------------------------------------
align 4
IPv4_dest_to_dev:
 
DEBUGF 1,"IPv4 destination to device: "
cmp eax, 0xffffffff
je .invalid
 
xor edi, edi
mov ecx, MAX_IP
 
.loop:
mov ebx, [IP_LIST+edi] ; we dont need to worry about non exisiting ip interfaces
and ebx, [SUBNET_LIST+edi] ; they have IP and SUBNET set to all one's, so they will have no match except 255.255.255.255
; (only a moron would insert that ip into this function..)
mov ebx, [IP_LIST+edi]
and ebx, [SUBNET_LIST+edi]
jz .next
 
mov edx, eax
and edx, [SUBNET_LIST+edi]
 
cmp ebx, edx
je .found_it
 
.next:
add edi, 4
loop .loop
 
.invalid:
xor edi, edi ; if none found, use device 0 as default device
 
.found_it:
shr edi, 2
DEBUGF 1,"IPv4_dest_to_dev: %u\n", edi
 
DEBUGF 1,"%u\n",edi
 
ret
 
 
868,8 → 928,4
add eax, GATEWAY_LIST
mov [eax], ecx
xor eax, eax
ret
 
 
 
 
ret
/kernel/branches/net/network/ethernet.inc
56,37 → 56,33
;
; This function resets all ethernet variables
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
ETH_init:
macro ETH_init {
 
mov [ETH_RUNNING], 0
 
ret
}
 
 
;-----------------------------------------------------------------
;
; ETH_Receiver:
; ETH_input
;
; This function is called by ethernet drivers,
; It pushes the received ethernet packets onto the eth_in_queue
;
; IN: [esp] = Pointer to buffer
; [esp-4] = size of buffer
; [esp+4] = size of buffer
; ebx = pointer to eth_device
; OUT: /
;
;-----------------------------------------------------------------
align 4
ETH_receiver:
ETH_input:
mov eax, [esp]
mov ecx, [esp+4]
 
DEBUGF 1,"ETH_Handler - size: %u\n", ecx
DEBUGF 1,"ETH_input - size: %u\n", ecx
cmp ecx, 60 ; check packet length
jl .dump
sub ecx, ETH_FRAME.Data
95,10 → 91,10
mov ax , [eax + ETH_FRAME.Type]
 
cmp ax, ETHER_IPv4
je IPv4_handler
je IPv4_input
 
cmp ax, ETHER_ARP
je ARP_handler
je ARP_input
 
; cmp ax, ETHER_PPP_DISCOVERY
; je PPPOE_discovery
106,7 → 102,7
DEBUGF 2,"Unknown ethernet packet type %x\n", ax
 
.dump:
DEBUGF 2,"ETH_Handler - dumping\n"
DEBUGF 2,"ETH_input - dumping\n"
call kernel_free
add esp, 4
ret
113,12 → 109,12
 
;-----------------------------------------------------------------
;
; ETH_create_packet
; ETH_output
;
; IN: eax = pointer to source mac
; ebx = pointer to destination mac
; ebx = device ptr
; ecx = packet size
; edx = device number
; edx = pointer to destination mac
; di = protocol
;
; OUT: edi = 0 on error, pointer to buffer otherwise
129,62 → 125,60
;
;-----------------------------------------------------------------
align 4
ETH_create_packet:
ETH_output:
 
DEBUGF 1,"Creating Ethernet Packet (size=%u): \n", ecx
DEBUGF 1,"ETH_output: size=%u device:%x\n", ecx, ebx
 
push esi
mov esi, [NET_DRV_LIST] ;;; TODO: FIXME
cmp ecx, [esi + NET_DEVICE.mtu]
pop esi
cmp ecx, [ebx + NET_DEVICE.mtu]
jg .exit
 
push ecx di eax ebx edx
push ecx ; << 1
push di eax edx ; << 2
add ecx, ETH_FRAME.Data
 
add ecx, ETH_FRAME.Data
push ecx
push ecx
call kernel_alloc
push ecx ; << 3
 
push ecx ; << 4
call kernel_alloc ; >> 4
test eax, eax
jz .out_of_ram
mov edi, eax
test edi, edi
jz .pop_exit
 
pop ecx
pop edx
pop ecx ; >> 3
 
pop esi
pop esi ; >> 2
movsd
movsw
pop esi
pop esi ; >> 2
movsd
movsw
pop ax
pop ax ; >> 2
stosw
 
lea eax, [edi - ETH_FRAME.Data] ; Set eax to buffer start
mov edx, ecx ; Set ebx to complete buffer size
pop ecx
mov edx, ecx ; Set edx to complete buffer size
 
xor ebx, ebx ;;;; TODO: Fixme
mov ebx, [NET_DRV_LIST + ebx]
pop ecx ; >> 1
 
cmp edx, 46 + ETH_FRAME.Data ; If data size is less then 46, add padding bytes
jge .continue
mov edx, 46 + ETH_FRAME.Data
.continue:
cmp edx, 60-1 ; minimum ethernet packet size
jle .adjust_size
DEBUGF 1,"ETH_output: done: %x total size: %u\n", eax, edx
ret
 
DEBUGF 1,"done: %x size:%u device:%x\n", eax, edx, ebx
.adjust_size:
mov edx, 60
ret
 
.pop_exit:
DEBUGF 2,"Out of ram space!!\n"
add esp, 18
and edi, 0
.out_of_ram:
DEBUGF 2,"ETH_output: Out of ram space!!\n"
add esp, 3*4+2+4
sub edi, edi
ret
 
.exit:
DEBUGF 2,"Packet too large!\n"
and edi, 0
DEBUGF 2,"ETH_output: Packet too large!\n"
sub edi, edi
;;; dec edi
ret
 
 
/kernel/branches/net/network/icmp.inc
109,21 → 109,16
;
; ICMP_init
;
; This function resets all ICMP variables
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
ICMP_init:
 
macro ICMP_init {
 
xor eax, eax
mov edi, ICMP_PACKETS_TX
mov ecx, 2*MAX_IP
rep stosd
 
ret
}
 
235,7 → 230,7
call IPv4_dest_to_dev
cmp edi,-1
je .dump
inc [ICMP_PACKETS_RX+4*edi]
inc [ICMP_PACKETS_RX+edi]
 
DEBUGF 1,"Found valid ICMP packet for socket %x\n", esi
 
284,10 → 279,10
push esi edi edx
 
add ecx, ICMP_Packet.Data
mov di , IP_PROTO_ICMP
mov di , IP_PROTO_ICMP SHL 8 + 128 ; TTL
shr edx, 16
 
call IPv4_create_packet
call IPv4_output
jz .exit
 
DEBUGF 1,"full icmp packet size: %u\n", edx
/kernel/branches/net/network/socket.inc
5,9 → 5,11
;; ;;
;; SOCKET.INC ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; based on code by mike.dld ;;
;; Written by hidnplayr@kolibrios.org, ;;
;; and Clevermouse. ;;
;; ;;
;; Based on code by mike.dld ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
31,9 → 33,7
.errorcode dd ?
 
.options dd ?
.SO_SND.SB_CC dd ? ;;;;; socket options: number of bytes in socket
.SO_RCV.SB_CC dd ?
.state dd ? ;;;;;;;;;
.state dd ?
 
.end:
end virtual
51,15 → 51,6
.end:
end virtual
 
virtual at SOCKET.end
 
SOCKET_virtual:
 
.ConnectedTo dd ? ; Socket number of other socket this one is connected to
 
.end:
end virtual
 
virtual at IP_SOCKET.end
 
TCP_SOCKET:
114,7 → 105,6
 
;----------------------
; Transmit timing stuff
 
.t_idle dd ?
.t_rtt dd ?
.t_rtseq dd ?
125,7 → 115,6
 
;-----------------
; Out-of-band data
 
.t_oobflags dd ?
.t_iobc dd ?
.t_softerror dd ?
133,7 → 122,6
 
;---------
; RFC 1323
 
.SND_SCALE db ? ; Scale factor
.RCV_SCALE db ?
.request_r_scale db ?
146,7 → 134,6
 
;-------
; Timers
 
.timer_retransmission dw ? ; rexmt
.timer_ack dw ?
.timer_persist dw ?
176,9 → 163,32
.end:
end virtual
 
struc RING_BUFFER {
.start_ptr dd ? ; Pointer to start of buffer
.end_ptr dd ? ; pointer to end of buffer
.read_ptr dd ? ; Read pointer
.write_ptr dd ? ; Write pointer
.size dd ? ; Number of bytes buffered
}
 
virtual at 0
 
RING_BUFFER RING_BUFFER
 
end virtual
 
virtual at TCP_SOCKET.end
 
rcv RING_BUFFER
snd RING_BUFFER
 
STREAM_SOCKET:
.end:
 
end virtual
 
 
struct socket_queue_entry
; .owner dd ?
.data_ptr dd ?
.buf_ptr dd ?
.data_size dd ?
186,7 → 196,6
ends
 
 
MAX_backlog equ 20 ; backlog for stream sockets
SOCKETBUFFSIZE equ 4096 ; in bytes
 
SOCKET_QUEUE_SIZE equ 10 ; maximum number ofincoming packets queued for 1 socket
204,14 → 213,8
;
; SOCKET_init
;
; -
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
socket_init:
macro SOCKET_init {
 
xor eax, eax
mov edi, net_sockets
218,12 → 221,35
mov ecx, 4
rep stosd
 
mov [last_UDP_port], MIN_EPHEMERAL_PORT
mov [last_TCP_port], MIN_EPHEMERAL_PORT
;--- for random port --
 
ret
mov al, 0x0 ; set up 1s timer
out 0x70, al
in al, 0x71
 
;----------------------
 
@@:
pseudo_random eax
cmp ax, MIN_EPHEMERAL_PORT
jl @r
cmp ax, MAX_EPHEMERAL_PORT
jg @r
 
mov [last_UDP_port], ax
 
@@:
pseudo_random eax
cmp ax, MIN_EPHEMERAL_PORT
jl @r
cmp ax, MAX_EPHEMERAL_PORT
jg @r
 
mov [last_TCP_port], ax
 
}
 
 
;-----------------------------------------------------------------
;
; Socket API (function 74)
232,11 → 258,20
align 4
sys_socket:
cmp ebx, 8 ; highest possible number
jg s_error
lea ebx, [.table + 4*ebx]
jg @f
lea ebx, [sock_sysfn_table + 4*ebx]
jmp dword [ebx]
@@:
cmp ebx, 255
jz SOCKET_debug
 
.table:
s_error:
DEBUGF 1,"socket error\n"
mov dword [esp+32], -1
 
ret
 
sock_sysfn_table:
dd SOCKET_open ; 0
dd SOCKET_close ; 1
dd SOCKET_bind ; 2
249,13 → 284,7
; dd SOCKET_set_opt ; 9
 
 
s_error:
DEBUGF 1,"socket error\n"
mov dword [esp+32], -1
 
ret
 
 
;-----------------------------------------------------------------
;
; SOCKET_open
280,10 → 309,44
 
mov [esp+32], edi
 
cmp ecx, AF_INET4
jnz .no_stream
 
push [IP_LIST] ;;;;
pop [eax + IP_SOCKET.LocalIP] ;;;;
 
cmp edx, IP_PROTO_TCP
jnz .no_stream
 
mov esi, eax
stdcall kernel_alloc, SOCKET_MAXDATA
mov [esi + rcv.start_ptr], eax
mov [esi + rcv.write_ptr], eax
mov [esi + rcv.read_ptr], eax
mov [esi + rcv.size], 0
add eax, SOCKET_MAXDATA
mov [esi + rcv.end_ptr], eax
 
stdcall kernel_alloc, SOCKET_MAXDATA
mov [esi + snd.start_ptr], eax
mov [esi + snd.write_ptr], eax
mov [esi + snd.read_ptr], eax
mov [esi + snd.size], 0
add eax, SOCKET_MAXDATA
mov [esi + snd.end_ptr], eax
 
ret
 
.no_stream:
 
push edi
init_queue (eax + SOCKET_QUEUE_LOCATION)
pop edi
 
ret
 
 
 
;-----------------------------------------------------------------
;
; SOCKET_bind
347,9 → 410,6
DEBUGF 1,"using local port: %u\n", bx
mov word [eax + UDP_SOCKET.LocalPort], bx
 
mov ebx, dword [edx + 4]
mov dword [eax + IP_SOCKET.LocalIP], ebx
 
DEBUGF 1,"local ip: %u.%u.%u.%u\n",\
[eax + IP_SOCKET.LocalIP + 0]:1,[eax + IP_SOCKET.LocalIP + 1]:1,\
[eax + IP_SOCKET.LocalIP + 2]:1,[eax + IP_SOCKET.LocalIP + 3]:1
411,21 → 471,11
 
 
.tcp:
; set sequence number
 
mov ebx, [TCP_sequence_num]
add [TCP_sequence_num], 6400
mov [eax + TCP_SOCKET.ISS], ebx
 
mov [eax + TCP_SOCKET.timer_keepalive], 120 ; 120*630ms => 75,6 seconds
 
lea ebx, [eax + SOCKET.lock]
call wait_mutex
 
; fill in remote port and IP
 
;;;;;; mov [eax + TCP_SOCKET.wndsizeTimer], 0 ; Reset the window timer.
 
mov bx , word [edx + 2]
mov [eax + TCP_SOCKET.RemotePort], bx
DEBUGF 1,"remote port: %u\n", bx
435,21 → 485,25
 
; check if local port and IP is ok
 
cmp [eax + IP_SOCKET.LocalIP], 0
jne @f
push [IP_LIST] ;;;;; device zero = default
pop [eax + IP_SOCKET.LocalIP]
@@:
 
cmp [eax + TCP_SOCKET.LocalPort], 0
jne @f
call SOCKET_find_port
@@:
DEBUGF 1,"local port: %u\n", [eax + TCP_SOCKET.LocalPort]:2
 
DEBUGF 1,"remote port: %u\n", [eax + TCP_SOCKET.LocalPort]:2
;;;;;
mov [eax + TCP_SOCKET.timer_persist], 0
mov [eax + TCP_SOCKET.t_state], TCB_SYN_SENT
mov ebx, [TCP_sequence_num]
add [TCP_sequence_num], 6400
mov [eax + TCP_SOCKET.ISS], ebx
mov [eax + TCP_SOCKET.timer_keepalive], 120 ; 120*640ms => 75,6 seconds
 
; mov [eax + TCP_SOCKET.t_state], TCB_SYN_SENT
;;;; mov [ebx + TCP_SOCKET.timer_retransmission],
 
push eax
call TCP_output
pop eax
 
mov [eax + SOCKET.lock], 0
 
583,11 → 637,11
test [eax + TCP_SOCKET.t_state], TCB_SYN_RECEIVED ;;;;;;
jz .free
 
call TCP_output
;;; call TCP_output
 
mov dword [esp+32], 0
;;; mov dword [esp+32], 0
 
ret
;;; ret
 
; state must be LISTEN, SYN_SENT, CLOSED or maybe even invalid
; so, we may destroy the socket
688,6 → 742,12
.af_inet4:
DEBUGF 1,"af_inet4\n"
 
cmp [eax + IP_SOCKET.LocalIP], 0
jne @f
mov ebx, [IP_LIST] ;;;;
mov dword [eax + IP_SOCKET.LocalIP], ebx
@@:
 
cmp [eax + SOCKET.Type], IP_PROTO_TCP
je .tcp
 
790,8 → 850,37
ret
 
 
 
;-----------------------------------------------------------------
;
; SOCKET_debug
;
; Copies socket variables to application buffer
;
; IN: ecx = socket number
; edx = pointer to buffer
;
; OUT: -1 on error
;-----------------------------------------------------------------
align 4
SOCKET_debug:
 
DEBUGF 1,"socket_debug\n"
 
call SOCKET_num_to_ptr
jz s_error
 
mov esi, eax
mov edi, edx
mov ecx, SOCKETBUFFSIZE/4
rep movsd
 
mov dword [esp+32], 0
ret
 
 
;-----------------------------------------------------------------
;
; SOCKET_find_port
;
; Fills in the local port number for TCP and UDP sockets
941,6 → 1030,188
 
;-----------------------------------------------------------------
;
; SOCKET_ring_add
;
; Adds data to a stream socket
;
; IN: eax = ptr to ring struct
; ecx = data size
; esi = ptr to data
;
; OUT: eax = number of bytes stored
;
;-----------------------------------------------------------------
align 4
SOCKET_ring_add:
 
DEBUGF 1,"SOCKET_ring_add: ringbuff=%x ptr=%x size=%u\n", eax, esi, ecx
 
mov edi, [eax + RING_BUFFER.size]
add edi, ecx
cmp edi, SOCKET_MAXDATA
jg .too_large
 
mov [eax + RING_BUFFER.size], edi ; update size
.copy:
push ecx ;<<<< 1
mov edi, [eax + RING_BUFFER.write_ptr] ; set write ptr in edi
add [eax + RING_BUFFER.write_ptr], ecx ; update write pointer
mov edx, [eax + RING_BUFFER.end_ptr]
cmp edx, [eax + RING_BUFFER.write_ptr]
jg .copy_in_2
je .wrap_write_ptr
 
.copy_more:
push ecx
and ecx, 3
rep movsb
pop ecx
shr ecx, 2
rep movsd
pop ecx ; >>>> 1/2
DEBUGF 2,"Copied %u bytes\n", ecx
 
ret
 
.too_large:
mov ecx, SOCKET_MAXDATA ; calculate number of bytes available in buffer
sub ecx, [eax + RING_BUFFER.size]
jz .full
 
mov [eax + RING_BUFFER.size], SOCKET_MAXDATA ; update size, we will fill buffer completely
 
jmp .copy
 
.full:
DEBUGF 2,"Ring buffer is full!\n"
xor ecx, ecx
ret
 
.copy_in_2:
DEBUGF 1,"Copying in 2 passes\n"
 
mov edx, ecx
mov ecx, [eax + RING_BUFFER.end_ptr] ; find number of bytes till end of buffer
sub ecx, edi
sub edx, ecx
push edx ; <<<< 2
 
mov edi, [eax + RING_BUFFER.start_ptr]
call .copy_more
 
.wrap_write_ptr:
sub [eax + RING_BUFFER.write_ptr], SOCKET_MAXDATA ; update write pointer
jmp .copy_more
 
 
 
 
;-----------------------------------------------------------------
;
; SOCKET_ring_read
;
; reads the data, but let the data remain in the buffer
;
; IN: eax = ptr to ring struct
; ecx = buffer size
; edi = ptr to buffer
;
; OUT: eax = number of bytes read
;
;-----------------------------------------------------------------
align 4
SOCKET_ring_read:
 
DEBUGF 1,"SOCKET_ring_read: ringbuff=%x ptr=%x size=%u\n", eax, esi, ecx
 
cmp [eax + RING_BUFFER.size], ecx ; update size
jl .too_large
 
mov esi, [eax + RING_BUFFER.read_ptr] ; update read ptr
.copy:
push ecx ;<<<< 1
mov edx, [eax + RING_BUFFER.read_ptr]
add edx, ecx
cmp edx, [eax + RING_BUFFER.end_ptr]
jg .copy_in_2
 
.copy_more:
push ecx
and ecx, 3
rep movsb
pop ecx
shr ecx, 2
rep movsd
pop ecx ; >>>> 1/2
DEBUGF 2,"Copied %u bytes\n", ecx
 
ret
 
.too_large:
mov ecx, [eax + RING_BUFFER.size]
jmp .copy
 
.full:
DEBUGF 2,"Ring buffer is full!\n"
xor ecx, ecx
ret
 
.copy_in_2:
DEBUGF 1,"Copying in 2 passes\n"
 
mov edx, ecx
mov ecx, [eax + RING_BUFFER.end_ptr] ; find number of bytes till end of buffer
sub ecx, edi
sub edx, ecx
push edx ; <<<< 2
 
mov esi, [eax + RING_BUFFER.start_ptr]
call .copy_more
 
 
;-----------------------------------------------------------------
;
; SOCKET_ring_free
;
; Free's some bytes from the ringbuffer
;
; IN: eax = ptr to ring struct
; ecx = data size
;
; OUT: ecx = number of bytes free-ed
;
;-----------------------------------------------------------------
align 4
SOCKET_ring_free:
 
DEBUGF 1,"Trying to free %u bytes of data from ring %x\n", ecx, eax
 
cmp ecx, [eax + RING_BUFFER.size]
jle .go_for_it
 
cmp ecx, SOCKET_MAXDATA ;;;;
jg .moron_input
 
mov ecx, [eax + RING_BUFFER.size]
 
.go_for_it:
sub [eax + RING_BUFFER.size], ecx
add [eax + RING_BUFFER.read_ptr], ecx
 
mov edx, [eax + RING_BUFFER.end_ptr]
cmp [eax + RING_BUFFER.read_ptr], edx
jl @f
sub [eax + RING_BUFFER.read_ptr], SOCKET_MAXDATA ;;;;;
@@:
ret
 
.moron_input:
xor ecx, ecx
ret
 
 
;-----------------------------------------------------------------
;
; SOCKET_notify_owner
;
; notify's the owner of a socket that something happened
1011,7 → 1282,7
push ecx ebx
 
stdcall kernel_alloc, SOCKETBUFFSIZE
DEBUGF 1, "socket_alloc: %x ", eax
DEBUGF 1, "SOCKET_alloc: ptr=%x\n", eax
or eax, eax
jz .exit
 
1023,10 → 1294,7
rep stosd
pop edi eax
 
init_queue (eax + SOCKET_QUEUE_LOCATION)
 
; find first free socket number and use it
 
mov ebx, net_sockets
xor ecx, ecx
.next_socket_number:
1033,7 → 1301,7
inc ecx
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
test ebx, ebx
jz .last_socket
cmp [ebx + SOCKET.Number], ecx
jne .next_socket
1042,23 → 1310,21
 
.last_socket:
mov [eax + SOCKET.Number], ecx
DEBUGF 1, "SOCKET_alloc: number=%u\n", ecx
mov edi, ecx
 
DEBUGF 1, "(number: %u)\n", ecx
 
; Fill in PID
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
 
mov [eax + SOCKET.PID], ebx
 
; add socket to the list by changing pointers
 
; add socket to the list by re-arranging some pointers
mov ebx, [net_sockets + SOCKET.NextPtr]
 
mov [eax + SOCKET.PrevPtr], net_sockets
mov [eax + SOCKET.NextPtr], ebx
 
or ebx, ebx
test ebx, ebx
jz @f
add ebx, SOCKET.lock ; lock the next socket
call wait_mutex
1068,8 → 1334,6
@@:
 
mov [net_sockets + SOCKET.NextPtr], eax
 
mov edi, ecx
or eax, eax ; used to clear zero flag
.exit:
pop ebx ecx
1101,6 → 1365,16
 
DEBUGF 1, "freeing socket..\n"
 
cmp [eax + SOCKET.Domain], AF_INET4
jnz .no_stream
 
cmp [eax + SOCKET.Type], IP_PROTO_TCP
jnz .no_stream
 
stdcall kernel_free, [eax + rcv.start_ptr]
stdcall kernel_free, [eax + snd.start_ptr]
.no_stream:
 
push eax ; this will be passed to kernel_free
mov ebx, [eax + SOCKET.NextPtr]
mov eax, [eax + SOCKET.PrevPtr]
1126,6 → 1400,39
ret
 
 
; socket nr in ebx
; new socket nr in eax
; preserver edx
 
align 4
SOCKET_fork:
 
;; Exit if backlog queue is full
; mov ax, [ebx + TCP_SOCKET.backlog_cur]
; cmp ax, [ebx + TCP_SOCKET.backlog]
; jae .exit
 
; Allocate new socket
call SOCKET_alloc
;;; jz .fail
 
; Copy structure from current socket to new, (including lock!)
; We start at PID to reserve the socket num, and the 2 pointers at beginning of socket
lea esi, [edx + SOCKET.PID]
lea edi, [eax + SOCKET.PID]
mov ecx, (TCP_SOCKET.end - SOCKET.PID + 3)/4
rep movsd
 
;; Push pointer to new socket to queue
; movzx ecx, [ebx + TCP_SOCKET.backlog_cur]
; inc [ebx + TCP_SOCKET.backlog_cur]
; mov [ebx + TCP_SOCKET.end + ecx*4], eax
 
;;;; mov [eax + IP_SOCKET.RemoteIP], esi ; IP source address
 
ret
 
 
;---------------------------------------------------
;
; SOCKET_num_to_ptr
/kernel/branches/net/network/stack.inc
68,13 → 68,15
; Socket options
SO_ACCEPTCON equ 1
 
SOCKET_MAXDATA equ 4096
SOCKET_MAXDATA equ 4096*32 ; must be 4096*(power of 2) where 'power of 2' is at least 8
 
; Network driver types
NET_TYPE_ETH equ 1
NET_TYPE_SLIP equ 2
 
MAX_backlog equ 20 ; backlog for stream sockets
 
 
virtual at 0
 
NET_DEVICE:
92,6 → 94,8
.packets_tx dd ? ;
.packets_rx dd ? ;
 
; .chksum dd ? ; bitmask stating available checksum routines on hardware
 
.end:
 
end virtual
102,6 → 106,7
add reg, [esp]
rol reg, 5
xor reg, [timer_ticks]
add reg, [CPU_FREQ]
imul reg, 214013
xor reg, 0xdeadbeef
rol reg, 9
122,6 → 127,18
}
 
 
macro packet_to_debug { ; set esi to packet you want to print, ecx to number of bytes
 
local .loop
 
.loop:
lodsb
DEBUGF 1,"%x ", eax:2
loop @r
 
}
 
 
include "queue.inc"
 
include "ethernet.inc"
161,26 → 178,24
stack_init:
 
; Init the network drivers list
 
xor eax, eax
mov edi, NET_RUNNING
mov ecx, MAX_NET_DEVICES + 1
rep stosd
 
; Call other init procedures
ETH_init
; SLIP_init
; PPPOE_init
 
call ETH_init
; call SLIP_init
IPv4_init
ICMP_init
 
call IPv4_init
call ICMP_init
ARP_init
UDP_init
TCP_init
 
call ARP_init
call UDP_init
call TCP_init
SOCKET_init
 
call socket_init
 
mov [net_tmr_count], 0
 
ret
211,14 → 226,14
test [net_10ms], 0x0f ; 160ms
jnz .exit
 
; call TCP_timer_160ms
TCP_timer_160ms
 
test [net_10ms], 0x3f ; 640ms
jnz .exit
 
; call TCP_timer_640ms
call ARP_decrease_entry_ttls
call IPv4_decrease_fragment_ttls
TCP_timer_640ms
ARP_decrease_entry_ttls
IPv4_decrease_fragment_ttls
 
.exit:
ret
275,13 → 290,13
jmp .error
 
.ethernet:
DEBUGF 1,"Trying to add an ethernet driver\n"
DEBUGF 1,"Trying to add an ethernet device\n"
 
inc [ETH_RUNNING] ; Indicate that one more ethernet device is up and running
jmp .add_it
 
.slip:
DEBUGF 1,"Trying to add a slip driver\n"
DEBUGF 1,"Trying to add a slip device\n"
;;;;
jmp .error
 
490,8 → 505,8
 
mov ecx, edx
shr ecx, 16
add edx, ecx
 
add dx, cx
test dx, dx ; it seems that ZF is not set when CF is set :(
not dx
jnz .not_zero
dec dx
/kernel/branches/net/network/tcp.inc
73,6 → 73,9
TCP_max_rxtshift equ 12 ; max retransmissions waiting for ACK
TCP_max_keepcnt equ 8 ; max keepalive probes
 
;
TCP_max_winshift equ 14
TCP_max_win equ 65535
 
struct TCP_segment
.SourcePort dw ?
117,12 → 120,8
;
; This function resets all TCP variables
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
TCP_init:
macro TCP_init {
 
xor eax, eax
mov edi, TCP_segments_tx
129,9 → 128,10
mov ecx, (6*IP_MAX_INTERFACES)
rep stosd
 
mov [TCP_sequence_num], 1
pseudo_random eax
mov [TCP_sequence_num], eax
 
ret
}
 
 
;----------------------
138,9 → 138,11
;
;
;----------------------
align 4
TCP_timer_160ms:
macro TCP_timer_160ms {
 
local .loop
local .exit
 
mov eax, net_sockets
.loop:
mov eax, [eax + SOCKET.NextPtr]
156,7 → 158,7
DEBUGF 1,"TCP ack for socket %x expired, time to piggyback!\n", eax
 
push eax
call TCP_respond
call TCP_respond_socket
pop eax
 
jmp .loop
163,7 → 165,7
 
.exit:
 
ret
}
 
 
;-----------------------------------------------------------------
170,9 → 172,11
;
;
;-----------------------------------------------------------------
align 4
TCP_timer_640ms:
macro TCP_timer_640ms {
 
local .loop
local .exit
 
; Update TCP sequence number
 
add [TCP_sequence_num], 64000
190,6 → 194,7
cmp [eax + SOCKET.Type], IP_PROTO_TCP
jne .loop
 
inc [eax + TCP_SOCKET.t_idle]
dec [eax + TCP_SOCKET.timer_retransmission]
jnz .check_more2
 
221,9 → 226,47
 
jmp .loop
.exit:
ret
}
 
 
 
 
macro TCP_checksum IP1, IP2 {
 
;-------------
; Pseudoheader
 
; protocol type
mov edx, IP_PROTO_TCP
 
; source address
add dl, byte [IP1+1+4]
adc dh, byte [IP1+0+4]
adc dl, byte [IP1+3+4]
adc dh, byte [IP1+2+4]
 
; destination address
adc dl, byte [IP2+1+8]
adc dh, byte [IP2+0+8]
adc dl, byte [IP2+3+8]
adc dh, byte [IP2+2+8]
 
; size
adc dl, cl
adc dh, ch
 
;---------------------
; Real header and data
 
push esi
call checksum_1
call checksum_2
pop esi
 
} ; returns in dx only
 
 
 
;-----------------------------------------------------------------
;
; TCP_input:
243,8 → 286,7
align 4
TCP_input:
 
DEBUGF 1,"TCP_input\n"
 
DEBUGF 1,"TCP_input size=%u\n", ecx
; Offset must be greater than or equal to the size of the standard TCP header (20) and less than or equal to the TCP length.
 
movzx eax, [edx + TCP_segment.DataOffset]
251,39 → 293,39
and eax, 0xf0
shr al , 2
 
DEBUGF 1,"data offset: %u\n", eax
DEBUGF 1,"headersize=%u\n", eax
 
cmp eax, 20
jl .drop
 
cmp eax, ecx
jg .drop
 
;-------------------------------
; Now, re-calculate the checksum
 
push eax edx ebx
 
push edi
push esi
push eax ecx edx
pushw [edx + TCP_segment.Checksum]
mov [edx + TCP_segment.Checksum], 0
push esi edi
mov esi, edx
call TCP_checksum ; this destroys edx, ecx and esi (but not edi! :)
 
pop ebx edx eax
 
cmp [edx + TCP_segment.Checksum], 0
TCP_checksum
pop esi edi ; yes, swap them (we dont need dest addr)
pop cx ; previous checksum
cmp cx, dx
pop edx ecx esi
jnz .drop
 
DEBUGF 1,"Checksum is correct\n"
 
sub ecx, esi ; update packet size
jl .drop
 
;-----------------------------------------------------------------------------------------
; Check if this packet has a timestamp option (We do it here so we can process it quickly)
 
cmp eax, 20 + 12 ; Timestamp option is 12 bytes
cmp esi, 20 + 12 ; Timestamp option is 12 bytes
jl .no_timestamp
je .is_ok
 
cmp byte [edx + TCP_segment.Data + 12], 0 ; end of option list
cmp byte [edx + TCP_segment.Data + 12], TCP_OPT_EOL ; end of option list
jne .no_timestamp
 
.is_ok:
295,11 → 337,9
 
DEBUGF 1,"timestamp ok\n"
 
; TODO: Parse the options
; TODO: Parse the option
; TODO: Set a Bit in the TCP to tell all options are parsed
 
ret
 
.no_timestamp:
 
;-------------------------------------------
310,6 → 350,8
 
ntohlw [edx + TCP_segment.Window]
ntohlw [edx + TCP_segment.UrgentPointer]
ntohlw [edx + TCP_segment.SourcePort]
ntohlw [edx + TCP_segment.DestinationPort]
 
;------------------------------------------------------------
; Next thing to do is find the TCB (thus, the socket pointer)
333,7 → 375,7
jne .socket_loop
 
mov eax, [ebx + IP_SOCKET.RemoteIP]
cmp eax, esi
cmp eax, edi ; sender IP
je @f
test eax, eax
jnz .socket_loop
352,72 → 394,126
;----------------------------
; Check if socket isnt closed
 
cmp [TCP_SOCKET.t_state], TCB_CLOSED
cmp [ebx + TCP_SOCKET.t_state], TCB_CLOSED
je .drop
 
;----------------
; Lock the socket
 
add ebx, SOCKET.lock ; TODO: figure out if we should lock now already
call wait_mutex
sub ebx, SOCKET.lock
;; add ebx, SOCKET.lock ; TODO: figure out if we should lock now already
;; call wait_mutex
;; sub ebx, SOCKET.lock
 
;---------------------------------------
; unscale the window into a 32 bit value ;;;;;;
DEBUGF 1,"Socket locked\n"
 
;----------------------------------------------------------------------------------------
; unscale the window into a 32 bit value (notice that SND_SCALE must be initialised to 0)
 
movzx eax, [edx + TCP_segment.Window]
xchg al, ah
 
test [edx + TCP_segment.Flags], TH_SYN
jnz .no_syn
 
push cx
mov cl , [ebx + TCP_SOCKET.SND_SCALE]
shl eax, cl
pop cx
 
.no_syn:
;;;; do something with eax
 
;-----------------------------------
; Is this socket a listening socket?
 
; If so, create a new socket
; test [ebx + SOCKET.options], SO_ACCEPTCON
; jnz .listening_socket ;;;;; TODO
 
test [ebx + SOCKET.options], SO_ACCEPTCON
jz .no_accept_conn
;-------------------------------------
; Reset idle timer and keepalive timer
 
mov [ebx + TCP_SOCKET.t_idle], 0
mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval
 
; TODO: create a new socket
;--------------------
; Process TCP options
 
cmp esi, 20 ; esi is headersize
je .no_options
 
.no_accept_conn:
DEBUGF 1,"Segment has options\n"
 
;----------------------------
; Compute window scale factor
test [ebx + TCP_SOCKET.t_state], TCB_LISTEN ; no options when in listen state
jz .no_options
 
lea edi, [edx + TCP_segment.Data]
lea eax, [edx + esi]
 
; TODO
.opt_loop:
cmp edi, eax
jge .no_options
 
cmp byte [edi], TCP_OPT_EOL ; end of option list?
jz .no_options
 
;-------------------------------------
; Reset idle timer and keepalive timer
cmp byte [edi], TCP_OPT_NOP ; nop ?
jz .opt_nop
 
;;;; TODO: idle timer?
cmp byte [edi], TCP_OPT_MAXSEG
je .opt_maxseg
 
mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval
cmp byte [edi], TCP_OPT_WINDOW
je .opt_window
 
;-----------------------------------------
; Process TCP options if not in LISTEN state
cmp byte [edi], TCP_OPT_TIMESTAMP
je .opt_timestamp
 
test [ebx + TCP_SOCKET.t_state], TCB_LISTEN
jz .dont_do_options
jmp .no_options ; If we reach here, some unknown options were received, skip them all!
 
call TCP_do_options
.opt_nop:
inc edi
jmp .opt_loop
 
.dont_do_options:
.opt_maxseg:
cmp byte [edi+1], 4
jne .no_options ; error occured, ignore all options!
 
test [edx + TCP_segment.Flags], TH_SYN
jz @f
 
DEBUGF 1,"Got maxseg option"
 
;;;;;
@@:
add edi, 4
jmp .opt_loop
 
 
.opt_window:
cmp byte [edi+1], 3
jne .no_options
 
test [edx + TCP_segment.Flags], TH_SYN
jz @f
 
DEBUGF 1,"Got window option"
 
;;;;;
@@:
add edi, 3
jmp .opt_loop
 
 
.opt_timestamp:
cmp byte [edi+1], 10
jne .no_options
 
DEBUGF 1,"Got timestamp option"
 
;;;;;
 
add edi, 10
jmp .opt_loop
 
.no_options:
 
;-----------------------------------------------------------------------
; Time to do some header prediction (Original Principle by Van Jacobson)
 
 
; There are two common cases for an uni-directional data transfer.
;
; General rule: the packets has no control flags, is in-sequence,
430,13 → 526,13
; - If the length is not 0 and the ACK didn't move, we're the receiver side of the transfer.
; If the packets are in order (data queue is empty), add the data to the socket buffer and request a delayed ACK
 
cmp [TCP_SOCKET.t_state], TCB_ESTABLISHED
cmp [ebx + TCP_SOCKET.t_state], TCB_ESTABLISHED
jnz .not_uni_xfer
 
test [TCP_segment.Flags], TH_SYN + TH_FIN + TH_RST + TH_URG
test [edx + TCP_segment.Flags], TH_SYN + TH_FIN + TH_RST + TH_URG
jnz .not_uni_xfer
 
test [TCP_segment.Flags], TH_ACK
test [edx + TCP_segment.Flags], TH_ACK
jz .not_uni_xfer
 
mov eax, [edx + TCP_segment.SequenceNumber]
443,7 → 539,7
cmp eax, [ebx + TCP_SOCKET.RCV_NXT]
jne .not_uni_xfer
 
movzx eax, [edx + TCP_segment.Window] ;;;;;
movzx eax, [edx + TCP_segment.Window] ;;;;; (should use pre-calculated value isntead: todo: figure out where to store it)
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jne .not_uni_xfer
 
451,47 → 547,30
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jne .not_uni_xfer
 
;-------------------------------------------------------------------------------
; If last ACK falls within this segment's sequence number, record the timestamp.
 
; TODO: check if it has a timestamp
 
 
 
 
;---------------------------------------
; check if we are sender in the uni-xfer
 
; If the following 4 conditions are all true, this segment is a pure ACK.
;
; - The segment contains no data (ti_len is 0).
 
movzx eax, [edx + TCP_segment.DataOffset]
and eax, 11110000b
shr eax, 2
sub ecx, eax
; - The segment contains no data.
test ecx, ecx
jnz .not_sender
 
; - The acknowledgment field in the segment (ti_ack) is greater than the largest unacknowledged sequence number (snd_una).
; Since this test is "greater than" and not "greater than or equal to," it is true only if some positive amount of data is acknowledged by the ACK.
 
mov eax, [edx + TCP_segment.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
jle .not_uni_xfer
 
; - The acknowledgment field in the segment (ti_ack) is less than or equal to the maximum sequence number sent (snd_max).
 
; mov eax, [edx + TCP_segment.Ack]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jg .not_uni_xfer
 
; - The congestion window (snd_cwnd) is greater than or equal to the current send window (snd_wnd).
; - The congestion window is greater than or equal to the current send window.
; This test is true only if the window is fully open, that is, the connection is not in the middle of slow start or congestion avoidance.
 
mov eax, [ebx + TCP_SOCKET.SND_CWND]
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jl .not_uni_xfer
 
; - The acknowledgment field in the segment is less than or equal to the maximum sequence number sent.
mov ecx, [edx + TCP_segment.AckNumber]
cmp ecx, [ebx + TCP_SOCKET.SND_MAX]
jg .not_uni_xfer
 
; - The acknowledgment field in the segment is greater than the largest unacknowledged sequence number.
sub ecx, [ebx + TCP_SOCKET.SND_UNA]
jle .not_uni_xfer
 
DEBUGF 1,"Header prediction: we are sender\n"
 
;---------------------------------
500,7 → 579,11
; Update RTT estimators
 
; Delete acknowledged bytes from send buffer
; notice how ecx already holds number of bytes ack-ed
 
lea eax, [ebx + snd]
call SOCKET_ring_free
 
; Stop retransmit timer
mov [ebx + TCP_SOCKET.timer_ack], 0
 
513,27 → 596,19
 
jmp .drop
 
 
 
 
;-------------------------------------------------
; maybe we are the receiver in the uni-xfer then..
 
.not_sender:
; The amount of data in the segment (ti_len) is greater than 0 (data count is in ecx)
; - The amount of data in the segment is greater than 0 (data count is in ecx)
 
 
; The acknowledgment field (ti_ack) equals the largest unacknowledged sequence number. This means no data is acknowledged by this segment.
; - The acknowledgment field equals the largest unacknowledged sequence number. This means no data is acknowledged by this segment.
mov eax, [edx + TCP_segment.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
jne .not_uni_xfer
 
; The reassembly list of out-of-order segments for the connection is empty (seg_next equals tp).
;;;;
jnz .not_uni_xfer
; - The reassembly list of out-of-order segments for the connection is empty (seg_next equals tp). ;;;;;;;
 
; There is room in the receive buffer for the data in the segment.
;;;;
jnz .not_uni_xfer
 
;-------------------------------------
541,63 → 616,41
 
DEBUGF 1,"header prediction: we are receiver\nreceiving %u bytes of data\n", ecx
 
; The next expected receive sequence number (rcv_nxt) is incremented by the number of bytes of data.
add esi, edx
lea eax, [ebx + rcv]
call SOCKET_ring_add ; Add the data to the socket buffer
 
add [ebx + TCP_SOCKET.RCV_NXT], ecx
add [ebx + TCP_SOCKET.RCV_NXT], ecx ; Update sequence number with number of bytes we have copied
or [ebx + TCP_SOCKET.t_flags], TF_DELACK ; Set delayed ack flag
 
; Add the data to the socket buffer
mov eax, ebx
;;; mov...
call SOCKET_input
 
; The delayed-ACK flag is set and the input processing is complete.
 
jmp .drop
 
 
 
 
 
;----------------------------------------------------
; Header prediction failed, doing it the slow way..
; Header prediction failed, doing it the slow way.. ;;;;; current implementation of header prediction destroys some regs (ecx) !!
 
.not_uni_xfer:
 
DEBUGF 1,"Header prediction failed\n"
 
;------------------------
; calculate header length ;;;;; we already calculated this before!
movzx eax, [edx + TCP_segment.DataOffset]
and eax, 0xf0
shr eax, 2
 
; Update edx to point to data..
add edx, eax
; ..and ecx to give data size
sub ecx, eax
 
;------------------------------
; Calculate receive window size
 
;;;;
 
 
;-------------------------
; TCP slow input procedure
 
DEBUGF 1,"TCP slow input procedure\n"
 
cmp [eax + TCP_SOCKET.t_state], TCB_LISTEN
cmp [ebx + TCP_SOCKET.t_state], TCB_LISTEN
je .LISTEN
 
cmp [eax + TCP_SOCKET.t_state], TCB_SYN_SENT
cmp [ebx + TCP_SOCKET.t_state], TCB_SYN_SENT
je .SYN_SENT
 
 
;--------------------------------------------
; Protection Against Wrapped Sequence Numbers
 
 
; First, check timestamp if present
 
;;;; TODO
608,6 → 661,9
 
jmp .trim_then_step6
 
;-------------
; Passive Open
 
align 4
.LISTEN:
 
622,47 → 678,37
test [edx + TCP_segment.Flags], TH_SYN
jz .drop
 
; TODO: find sender ip address somewhere!
; TODO: check if it's a broadcast or multicast, and drop if so
 
;;; 28.6
call SOCKET_fork
jz .drop ; if we could not open a new connection, drop segment (;;;; should we send RST too?)
 
; create a new socket and fill in the nescessary variables
;-----------------------
; Fill in some variables
 
;; Exit if backlog queue is full
; mov ax, [ebx + TCP_SOCKET.backlog_cur]
; cmp ax, [ebx + TCP_SOCKET.backlog]
; jae .exit
add [TCP_sequence_num], 64000
 
; Allocate new socket
call SOCKET_alloc
;;; jz .fail
push [edx + TCP_segment.SourcePort]
pop [eax + TCP_SOCKET.RemotePort]
 
; Copy structure from current socket to new, (including lock!)
; We start at PID to reserve the socket num, and the 2 pointers at beginning of socket
lea esi, [edx + SOCKET.PID]
lea edi, [eax + SOCKET.PID]
mov ecx, (TCP_SOCKET.end - SOCKET.PID + 3)/4
rep movsd
push [edx + TCP_segment.SequenceNumber]
pop [eax + TCP_SOCKET.IRS]
 
;; Push pointer to new socket to queue
; movzx ecx, [ebx + TCP_SOCKET.backlog_cur]
; inc [ebx + TCP_SOCKET.backlog_cur]
; mov [ebx + TCP_SOCKET.end + ecx*4], eax
push [eax + TCP_SOCKET.ISS]
pop [eax + TCP_SOCKET.SND_NXT]
 
mov [eax + IP_SOCKET.RemoteIP], esi ; IP source address
mov [eax + TCP_SOCKET.t_state], TCB_SYN_RECEIVED
mov [eax + TCP_SOCKET.t_flags], TF_ACKNOW
mov [eax + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval
 
mov cx, [edx + TCP_segment.SourcePort]
mov [eax + TCP_SOCKET.RemotePort], cx
mov ebx, eax
 
mov ecx, [edx + TCP_segment.SequenceNumber]
mov [eax + TCP_SOCKET.IRS], ecx
 
mov ecx, [eax + TCP_SOCKET.ISS]
mov [eax + TCP_SOCKET.SND_NXT], ecx
 
jmp .trim_then_step6
 
 
;------------
; Active Open
 
align 4
.SYN_SENT:
676,12 → 722,13
cmp eax, [ebx + TCP_SOCKET.ISS]
jle .drop_with_reset
 
mov eax, [edx + TCP_segment.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jg .drop_with_reset
DEBUGF 1,"snd_max = %x\n", [ebx + TCP_SOCKET.SND_MAX] ;;; TODO: set this, but where?
 
; mov eax, [edx + TCP_segment.AckNumber]
;; cmp eax, [ebx + TCP_SOCKET.SND_MAX]
;; jg .drop_with_reset
@@:
 
 
test [edx + TCP_segment.Flags], TH_RST
jz @f
 
696,24 → 743,41
test [edx + TCP_segment.Flags], TH_SYN
jz .drop
 
; now, process received SYN in response to an active open
; at this point, segment seems to be valid
 
test [edx + TCP_segment.Flags], TH_ACK
jz @f
jz .no_syn_ack
 
; now, process received SYN in response to an active open
 
mov eax, [edx + TCP_segment.AckNumber]
mov [ebx + TCP_SOCKET.SND_UNA], eax
 
mov eax, [ebx + TCP_SOCKET.SND_UNA]
cmp eax, [ebx + TCP_SOCKET.SND_NXT]
jle @f
mov [ebx + TCP_SOCKET.SND_NXT], eax
@@:
 
mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval
mov [ebx + TCP_SOCKET.timer_retransmission], 0
.no_syn_ack:
 
mov eax, [edx + TCP_segment.SequenceNumber]
mov [ebx + TCP_SOCKET.IRS], eax
mov [ebx + TCP_SOCKET.timer_retransmission], 0 ; disable retransmission
 
push [edx + TCP_segment.SequenceNumber]
pop [ebx + TCP_SOCKET.IRS]
 
;;; TODO: tcp_rcvseqinit
 
mov [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
 
mov eax, [ebx + TCP_SOCKET.SND_UNA]
cmp eax, [ebx + TCP_SOCKET.ISS]
jle .simultaneous_open
 
test [edx + TCP_segment.Flags], TH_ACK
jz .simultaneous_open
 
DEBUGF 1,"TCP: active open\n"
 
; TODO: update stats
; TODO: set socket state to connected
 
mov [ebx + TCP_SOCKET.t_state], TCB_ESTABLISHED
721,9 → 785,11
; TODO: check if we should scale the connection (567-572)
; TODO: update RTT estimators
 
jmp .trimthenstep6
 
@@:
.simultaneous_open:
 
DEBUGF 1,"TCP: simultaneous open\n"
; We have received a syn but no ACK, so we are having a simultaneous open..
mov [ebx + TCP_SOCKET.t_state], TCB_SYN_RECEIVED
 
744,16 → 810,15
; TODO...
@@:
;;;;;
;;; jmp .step6
jmp .step6
 
 
 
 
 
align 4
.trim_then_step6:
 
DEBUGF 1,"Trim, then step 6\n"
DEBUGF 1,"Trimming window\n"
 
;----------------------------
; trim any data not in window
783,9 → 848,10
 
.no_drop:
 
DEBUGF 1,"Going to drop %u bytes of data", eax
 
; eax holds number of bytes to drop
 
 
;----------------------------------
; Check for entire duplicate packet
 
794,11 → 860,6
 
;;; TODO: figure 28.30
 
;; inc [TCP_segments_rx]
 
;; add dword [TCP_bytes_rx], ecx
;; adc dword [TCP_bytes_rx+4], 0
 
;------------------------
; Check for duplicate FIN
 
811,8 → 872,7
 
mov eax, ecx
and [edx + TCP_segment.Flags], not TH_FIN
;;; TODO: set ACKNOW flag
 
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
jmp .no_duplicate
@@:
 
819,7 → 879,6
; Handle the case when a bound socket connects to itself
; Allow packets with a SYN and an ACKto continue with the processing
 
 
;-------------------------------------
; Generate duplicate ACK if nescessary
 
833,12 → 892,14
 
.duplicate:
 
DEBUGF 1,"Duplicate received"
 
;----------------------------------------
; Update statistics for duplicate packets
 
;;; TODO
 
;;; DROP the packet ??
jmp .drop ;;; DROP the packet ??
 
.no_duplicate:
 
898,7 → 959,6
 
.no_excess_data:
 
 
;-----------------
; Record timestamp
 
910,6 → 970,8
test [edx + TCP_segment.Flags], TH_RST
jz .rst_skip
 
DEBUGF 1,"Got an RST flag"
 
mov eax, [ebx + TCP_SOCKET.t_state]
shl eax, 2
jmp dword [eax + .rst_sw_list]
929,6 → 991,8
 
.econnrefused:
 
DEBUGF 1,"Connection refused"
 
;;; TODO: debug info
 
jmp .close
935,13 → 999,19
 
.econnreset:
 
DEBUGF 1,"Connection reset"
 
;;; TODO: debug info
.close:
 
DEBUGF 1,"Closing connection"
 
;;; update stats
 
.rst_close:
 
DEBUGF 1,"Closing with reset"
 
;;; Close the socket
jmp .drop
 
1006,12 → 1076,15
;------------------------------------------
; Remove acknowledged data from send buffer
 
;;;; 943 - 956
lea eax, [ebx + snd]
mov ecx, ecx ;;;; 943 - 956
call SOCKET_ring_free
 
;---------------------------------------
; Wake up process waiting on send buffer
 
;;;;;
mov eax, ebx
call SOCKET_notify_owner
 
mov eax, [ebx + TCP_SOCKET.t_state]
shl eax, 2
1103,7 → 1176,6
 
.no_window_update:
 
 
;-----------------
; process URG flag
 
1121,7 → 1193,7
;;; 1040-1050
 
movzx eax, [edx + TCP_segment.UrgentPointer]
add eax, [ebx + SOCKET.SO_RCV.SB_CC]
add eax, [ebx + rcv.size]
cmp eax, SOCKET_MAXDATA
jle .not_urgent
 
1134,19 → 1206,19
;--------------------------------------
; processing of received urgent pointer
 
;;; 1051-1093
;;; TODO (1051-1093)
 
align 4
;--------------------------------
; process the data in the segment
 
.do_data:
 
DEBUGF 1,"Do data:\n"
DEBUGF 1,"TCP: do data:\n"
 
; process the data in the segment
 
test [edx + TCP_segment.Flags], TH_FIN
jz .process_fin
jnz .process_fin
 
test [ebx + TCP_SOCKET.t_state], TCB_FIN_WAIT_1 ;;;;;
test [ebx + TCP_SOCKET.t_state], TCB_FIN_WAIT_1
jge .dont_do_data
 
DEBUGF 1,"Processing data in segment\n"
1158,7 → 1230,6
 
.dont_do_data:
 
 
;---------------
; FIN processing
 
1209,16 → 1280,26
;jnz .outputnow
 
test [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
jz .ret
jnz .ack_now
 
.outputnow:
mov [ebx + SOCKET.lock], 0
call kernel_free
add esp, 4
ret
 
.ack_now:
 
DEBUGF 1,"ACK now!\n"
 
push ebx
mov eax, ebx
call TCP_output
pop ebx
 
.ret:
mov [ebx + SOCKET.lock], 0
 
call kernel_free
ret 4
add esp, 4
ret
 
;------------------------------------------
; Generate an ACK, droping incoming segment
1233,12 → 1314,15
 
and [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
 
push ebx
mov eax, ebx
call TCP_output
pop ebx
 
mov [ebx + SOCKET.lock], 0
 
call kernel_free
ret 4
add esp, 4
ret
 
 
;-------------------------------------------
1261,15 → 1345,15
jnz .respond_syn
 
mov [ebx + SOCKET.lock], 0
 
call kernel_free
ret 4
add esp, 4
ret
 
.respond_ack:
 
;;;;
 
call TCP_respond
call TCP_respond_segment
 
jmp .destroy_new_socket
 
1278,7 → 1362,7
 
;;;;
 
call TCP_respond
call TCP_respond_segment
 
jmp .destroy_new_socket
 
1297,128 → 1381,16
;;;; kill the newly created socket
 
mov [ebx + SOCKET.lock], 0
 
call kernel_free
ret 4
add esp, 4
ret
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
 
;---------------------
;
; TCP_do_options
;
;-------------------
 
align 4
TCP_do_options:
 
DEBUGF 1,"TCP_do_options\n"
 
push eax
sub eax, 20
jz .no_options
 
lea esi, [edx + TCP_segment.Data]
 
 
;-------------------------------------------
; Begin the loop by checking for EOL and NOP
 
.loop:
 
cmp byte [esi], TCP_OPT_EOL ; end of option list?
jz .no_options
 
cmp byte [esi], TCP_OPT_NOP ; nop ?
;;; cmove edi, 1 ; if so, set option size to 1
jz .continue ; and continue scanning
 
;------------------
; We have an option
 
movzx edi, byte [esi + 1] ; get the length of this option in edi
 
 
;--------------------------------------
; Check for Maximum segment size option
 
cmp byte [esi], TCP_OPT_MAXSEG
jne .no_maxseg
 
cmp edi, 4 ; option length
jne .continue
 
test [edx + TCP_segment.Flags], TH_SYN
jz .continue
 
; Now parse the option...
 
jmp .continue
 
.no_maxseg:
 
;------------------------
; Check for Window option
 
cmp byte [esi], TCP_OPT_WINDOW
jne .no_window
 
cmp edi, 3 ; option length
jne .continue
 
test [edx + TCP_segment.Flags], TH_SYN
jz .continue
 
; ...
 
jmp .continue
 
.no_window:
 
;---------------------------
; Check for Timestamp option
 
cmp byte [esi], TCP_OPT_TIMESTAMP
jne .no_timestamp
 
cmp edi, 10 ; option length
jne .continue
 
; ...
 
 
jmp .continue
 
.no_timestamp:
 
;----------------------------------
; Future options may be placed here
 
 
 
 
;------------------------------
; Continue scanning for options
 
.continue:
add esi, edi
sub eax, edi
jg .loop
 
.no_options:
 
pop eax
 
ret
 
 
 
 
;---------------------------
;
; TCP_pull_out_of_band
;
1441,14 → 1413,6
 
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
 
 
 
;-----------------------------------------------------------------
;
; TCP_output
1494,7 → 1458,7
mov ecx, [eax + TCP_SOCKET.SND_CWND] ;
@@: ;
 
call TCP_outflags
call TCP_outflags ; in dl
 
; If in persist timeout with window of 0, send 1 byte.
; Otherwise, if window is small but nonzero, and timer expired,
1506,7 → 1470,7
test ecx, ecx
jnz .no_zero_window
 
cmp ebx, [eax + SOCKET.SO_SND.SB_CC]
cmp ebx, [eax + snd.size]
jge @f
 
and dl, not (TH_FIN) ; clear the FIN flag ??? how can it be set before?
1517,7 → 1481,7
 
.no_zero_window:
 
;;; mov [eax + TCP_SOCKET.t_timer....TCPT_PERSIST], 0
mov [eax + TCP_SOCKET.timer_persist], 0 ;;;;
mov [eax + TCP_SOCKET.t_rxtshift], 0
 
.no_persist_timeout:
1524,7 → 1488,7
 
;;;106
 
mov esi, [eax + SOCKET.SO_SND.SB_CC]
mov esi, [eax + snd.size]
cmp esi, ecx
jl @f
mov esi, ecx
1546,7 → 1510,7
test ecx, ecx
jnz @f
 
;;; mov [eax + TCP_SOCKET.t_timer..TCPT_REXMT], 0
mov [eax + TCP_SOCKET.timer_retransmission], 0 ; cancel retransmit
 
push [eax + TCP_SOCKET.SND_UNA]
pop [eax + TCP_SOCKET.SND_NXT]
1569,7 → 1533,7
mov edi, [eax + TCP_SOCKET.SND_NXT]
add edi, esi ; len
sub edi, [eax + TCP_SOCKET.SND_UNA]
add edi, [eax + SOCKET.SO_SND.SB_CC]
add edi, [eax + snd.size]
cmp edi, 0
jle @f
 
1578,17 → 1542,16
@@:
 
 
;;;; 130 TODO: set window (ecx) to space in send buffer
; set ecx to space available in receive buffer
; From now on, ecx will be the window we advertise to the other end
 
mov ecx, SOCKET_MAXDATA
sub ecx, [eax + rcv.size]
 
;------------------------------
; Sender silly window avoidance
 
test esi, esi
jz .zero_length
 
 
cmp esi, [eax + TCP_SOCKET.t_maxseg]
cmp ecx, [eax + TCP_SOCKET.t_maxseg]
je .send
 
;;; TODO: 144-145
1596,16 → 1559,20
test [eax + TCP_SOCKET.t_force], -1
jnz .send
 
;;; TODO: 149..152
mov ebx, [eax + TCP_SOCKET.max_sndwnd]
shr ebx, 1
cmp ecx, ebx
jge .send
 
.zero_length:
mov ebx, [eax + TCP_SOCKET.SND_NXT]
cmp ebx, [eax + TCP_SOCKET.SND_MAX]
jl .send
 
 
;----------------------------------------
; Check if a window update should be sent
 
cmp ecx, 0 ; window
jle .no_window
test ecx, ecx ; window
jz .no_window
 
;;; TODO 154-172
 
1641,8 → 1608,6
 
DEBUGF 1,"Entering persist state\n"
 
 
 
;--------------------------------------
; No reason to send a segment, just ret
 
1651,14 → 1616,11
ret
 
 
 
 
 
;-----------------------------------------------
;
; Send a segment
;
; ebx = socket pointer
; eax = socket pointer
; dl = flags
;
;-----------------------------------------------
1667,8 → 1629,11
 
DEBUGF 1,"Preparing to send a segment\n"
 
xor edi, edi ; edi will contain the number of header option bytes
mov edi, TCP_segment.Data ; edi will contain headersize
 
sub esp, 8 ; create some space on stack
push eax ; save this too..
 
;------------------------------------
; Send options with first SYN segment
 
1675,35 → 1640,32
test dl, TH_SYN
jz .no_options
 
mov eax, [ebx + TCP_SOCKET.ISS]
mov [ebx + TCP_SOCKET.SND_NXT], eax
push [eax + TCP_SOCKET.ISS]
pop [eax + TCP_SOCKET.SND_NXT]
 
test [ebx + TCP_SOCKET.t_flags], TF_NOOPT
test [eax + TCP_SOCKET.t_flags], TF_NOOPT
jnz .no_options
 
mov eax, TCP_OPT_MAXSEG shl 24 + 4 shl 16
mov ax, 1280 ;;;;;;
bswap eax
push eax
mov ecx, 1460
or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16
bswap ecx
push ecx
add di, 4
 
mov di, 4
 
test [ebx + TCP_SOCKET.t_flags], TF_REQ_SCALE
test [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE
jz .no_syn
 
test dl, TH_ACK
jnz .scale_opt
 
test [ebx + TCP_SOCKET.t_flags], TF_RCVD_SCALE
test [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE
jz .no_syn
 
.scale_opt:
 
mov eax, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP
mov ah, byte [ebx + TCP_SOCKET.request_r_scale]
bswap eax
push eax
 
movzx ecx, byte [eax + TCP_SOCKET.request_r_scale]
or ecx, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP shl 8
bswap ecx
pushd ecx
add di, 4
 
.no_syn:
1711,7 → 1673,7
;------------------------------------
; Make the timestamp option if needed
 
test [ebx + TCP_SOCKET.t_flags], TF_REQ_TSTMP
test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP
jz .no_timestamp
 
test dl, TH_RST
1720,145 → 1682,129
test dl, TH_ACK
jz .timestamp
 
test [ebx + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
test [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
jz .no_timestamp
 
.timestamp:
 
DEBUGF 1,"Creating a timestamp\n"
 
push dword (TCP_OPT_TIMESTAMP shl 8 + 10 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24)
mov esi, [timer_ticks]
bswap esi
push esi
pushw 0
mov eax, [timer_ticks]
bswap eax
push eax
 
pushd TCP_OPT_TIMESTAMP + 10 shl 8 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24
add di, 10
 
.no_timestamp:
 
;; TODO: check if we dont exceed the max segment size
 
.no_options:
add edi, TCP_segment.Data
; eax = socket ptr
; edx = flags
; ecx = data size
; edi = header size
; esi = snd ring buff ptr
 
;-----------------------------------
; Check if we have some data to send
 
;;; mov ecx, [huppeldepup]
 
test ecx, ecx
jz .no_data
 
;;; 278-316
 
jmp .header
 
.no_data:
 
;;; 317-338
 
 
;----------
 
push di dx ebx
 
xor ecx, ecx ;;;;;
add ecx, edi ; total TCP segment size
 
mov eax, [ebx + IP_SOCKET.RemoteIP]
mov ebx, [ebx + IP_SOCKET.LocalIP]
mov di , IP_PROTO_TCP
call IPv4_create_packet
; Start by pushing all TCP header values in reverse order on stack
; (essentially, creating the tcp header!)
 
;;;; jz .fail
pushw 0 ; .UrgentPointer dw ?
pushw 0 ; .Checksum dw ?
pushw 0x00a0 ; .Window dw ? ;;;;;;;
shl edi, 2 ; .DataOffset db ? only 4 left-most bits
shl dx, 8
or dx, di ; .Flags db ?
pushw dx
shr edi, 2 ; .DataOffset db ? ;;;;
 
push edx eax
call [ebx + NET_DEVICE.transmit]
ret
push [eax + TCP_SOCKET.RCV_NXT] ; .AckNumber dd ?
ntohld [esp]
 
;----------------
push [eax + TCP_SOCKET.SND_NXT] ; .SequenceNumber dd ?
ntohld [esp]
 
push [eax + TCP_SOCKET.RemotePort] ; .DestinationPort dw ?
ntohlw [esp]
 
;-------------------------------
; Now, create the 20-byte header
push [eax + TCP_SOCKET.LocalPort] ; .SourcePort dw ?
ntohlw [esp]
 
.header:
push edi ; header size
 
;-----------------------
; Fill in the TCP header
pop esi
; Create the IP packet
mov ebx, [eax + IP_SOCKET.LocalIP] ; source ip
mov eax, [eax + IP_SOCKET.RemoteIP] ; dest ip
; mov ecx, ; data length
; mov dx, ; fragment id
mov di, IP_PROTO_TCP shl 8 + 128
call IPv4_output
jz .fail
 
push [esi + TCP_SOCKET.SND_NXT]
rol word [esp], 8
rol dword [esp], 16
pop [edi + TCP_segment.SequenceNumber]
;-----------------------------------------
; Move TCP header from stack to TCP packet
 
push [esi + TCP_SOCKET.RCV_NXT]
rol word [esp], 8
rol dword [esp], 16
pop [edi + TCP_segment.AckNumber]
; pop ecx ; header size
; mov esi, esp
; add esp, ecx
; shr ecx, 2
; rep movsd
 
push [esi + TCP_SOCKET.LocalPort]
rol word [esp], 8
pop [edi + TCP_segment.SourcePort]
mov ecx, [esp]
lea esi, [esp+4]
shr ecx, 2
rep movsd
 
push [esi + TCP_SOCKET.RemotePort]
rol word [esp], 8
pop [edi + TCP_segment.DestinationPort]
pop ecx
add esp, ecx
 
mov [esp + 3*4+4], edx ; packet size
mov [esp + 3*4], eax ; packet ptr
 
mov [edi + TCP_segment.Window], 0x0005
; 1280 bytes
mov [edi + TCP_segment.UrgentPointer], 0
mov edx, edi
sub edx, ecx
 
mov [edi + TCP_segment.DataOffset], 0x50
 
mov [edi + TCP_segment.Flags], cl
 
mov [edi + TCP_segment.Checksum], 0
 
;-----
 
 
;--------------
; Copy the data
 
pop esi
push edi
add edi, TCP_segment.Data ;;
sub ecx, TCP_segment.Data ;;;
; eax = ptr to ring struct
; ecx = buffer size
; edi = ptr to buffer
 
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
test ecx, ecx
jz .nd
rep movsd
.nd:
pop edi
mov eax, [esp] ; socket ptr
push ecx edx
add eax, snd
call SOCKET_ring_read
pop esi ecx
pop eax
 
;--------------------
; Create the checksum
;-------------------------------------------------------------
; Create the checksum (we have already pushed IPs onto stack)
 
push [ebx + IP_SOCKET.LocalIP]
push [ebx + IP_SOCKET.RemoteIP]
call TCP_checksum
DEBUGF 1,"checksum: ptr=%x size=%u\n", esi, ecx
DEBUGF 1,"ip=%x\n", [esp]:8
DEBUGF 1,"ip=%x\n", [esp+4]:8
 
TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP)
mov [esi+TCP_segment.Checksum], dx
 
;----------------
; Send the packet
 
;;;;;
 
 
DEBUGF 1,"Sending TCP Packet to device %x\n", ebx
call [ebx + NET_DEVICE.transmit]
ret
 
 
.fail:
pop ecx
add esp, ecx
add esp, 4+4+8+4
DEBUGF 1,"TCP_output: failed\n"
ret
 
 
 
;-------------------------
;
; TCP_outflags
1893,6 → 1839,8
db TH_ACK ; TCB_TIMED_WAIT
 
 
 
 
;-------------------------
;
; TCP_drop
1924,62 → 1872,68
 
;---------------------------------------
;
; TCP_ack
;
; The easy way to send an ACK/RST/keepalive segment
;
; IN: eax = socket ptr
; -or-
; edx = packet ptr (eax must be 0)
; TCP_respond_socket:
;
; IN: ebx = socket ptr
; cl = flags
;
; OUT: /
;
;---------------------------------------
;--------------------------------------
align 4
TCP_respond:
TCP_respond_socket:
 
DEBUGF 1,"TCP_respond\n"
DEBUGF 1,"TCP_respond_socket\n"
 
;---------------------
; Create the IP packet
 
push cx eax edx
mov ebx, [eax + IP_SOCKET.LocalIP]
mov eax, [eax + IP_SOCKET.RemoteIP]
push cx ebx
mov eax, [ebx + IP_SOCKET.RemoteIP]
mov ebx, [ebx + IP_SOCKET.LocalIP]
mov ecx, TCP_segment.Data
mov di , IP_PROTO_TCP
call IPv4_create_packet
mov di , IP_PROTO_TCP shl 8 + 128
call IPv4_output
test edi, edi
jz .error
pop esi cx
push edx eax
 
;---------------------------
; Now fill in the TCP header
;-----------------------------------------------
; Fill in the TCP header by using the socket ptr
 
pop ecx
pop esi
mov ax, [esi + TCP_SOCKET.LocalPort]
rol ax, 8
stosw
mov ax, [esi + TCP_SOCKET.RemotePort]
rol ax, 8
stosw
mov eax, [esi + TCP_SOCKET.SND_NXT]
bswap eax
stosd
mov eax, [esi + TCP_SOCKET.RCV_NXT]
bswap eax
stosd
mov al, 0x50 ; Dataoffset: 20 bytes
stosb
mov al, cl
stosb
mov ax, [esi + TCP_SOCKET.RCV_WND]
rol ax, 8
stosw ; window
xor eax, eax
stosd ; checksum + urgentpointer
 
test esi, esi
; jz
 
 
push edx eax
 
push dword .checksum
je .use_segment
jmp .use_socket
 
;---------------------
; Fill in the checksum
 
.checksum:
sub edi, TCP_segment.Data
mov ecx, TCP_segment.Data
xchg esi, edi
TCP_checksum (edi + IP_SOCKET.LocalIP), (esi + IP_SOCKET.RemoteIP)
mov [esi+TCP_segment.Checksum], dx
 
push [esi + IP_SOCKET.LocalIP]
push [esi + IP_SOCKET.RemoteIP]
lea esi, [edi - 20]
xor ecx, ecx
call TCP_checksum
 
;--------------------
; And send the segment
 
1987,16 → 1941,42
ret
 
.error:
DEBUGF 1,"TCP_ack failed\n"
add esp, 4
DEBUGF 1,"TCP_respond failed\n"
add esp, 2+4
 
ret
 
 
 
;-------------------------
; TCP_respond.segment:
;
; IN: edx = segment ptr (a previously received segment)
; cl = flags
 
align 4
TCP_respond_segment:
 
DEBUGF 1,"TCP_respond_segment\n"
 
;---------------------
; Create the IP packet
 
push cx edx
mov ebx, [edx - 20 + IPv4_Packet.SourceAddress] ;;;; and what if ip packet had options?!
mov eax, [edx - 20 + IPv4_Packet.DestinationAddress] ;;;
mov ecx, TCP_segment.Data
mov di , IP_PROTO_TCP shl 8 + 128
call IPv4_output
test edi, edi
jz .error
 
pop esi cx
push edx eax
 
;---------------------------------------------------
; Fill in the TCP header by using a received segment
 
.use_segment:
 
mov ax, [esi + TCP_segment.DestinationPort]
rol ax, 8
stosw
2018,98 → 1998,30
xor eax, eax
stosd ; checksum + urgentpointer
 
ret
;---------------------
; Fill in the checksum
 
.checksum:
lea esi, [edi - TCP_segment.Data]
mov ecx, TCP_segment.Data
TCP_checksum (esi - 20 + IPv4_Packet.DestinationAddress), (esi - 20 + IPv4_Packet.DestinationAddress)
mov [esi+TCP_segment.Checksum], dx
 
;-----------------------------------------------
; Fill in the TCP header by using the socket ptr
;--------------------
; And send the segment
 
.use_socket:
call [ebx + NET_DEVICE.transmit]
ret
 
mov ax, [esi + TCP_SOCKET.LocalPort]
rol ax, 8
stosw
mov ax, [esi + TCP_SOCKET.RemotePort]
rol ax, 8
stosw
mov eax, [esi + TCP_SOCKET.SND_NXT]
bswap eax
stosd
mov eax, [esi + TCP_SOCKET.RCV_NXT]
bswap eax
stosd
mov al, 0x50 ; Dataoffset: 20 bytes
stosb
mov al, cl
stosb
mov ax, [esi + TCP_SOCKET.RCV_WND]
rol ax, 8
stosw ; window
xor eax, eax
stosd ; checksum + urgentpointer
.error:
DEBUGF 1,"TCP_respond failed\n"
add esp, 2+4
 
ret
 
 
 
;-----------------------------------------------------------------
;
; TCP_checksum
;
; This is the fast procedure to create or check a UDP header
; - To create a new checksum, the checksum field must be set to 0 before computation
; - To check an existing checksum, leave the checksum as is,
; and it will be 0 after this procedure, if it was correct
;
; IN: push source ip
; push dest ip
;
; esi = packet ptr
;
; OUT: checksum is filled in in packet! (but also in dx)
;
;-----------------------------------------------------------------
align 4
TCP_checksum:
 
;-------------
; Pseudoheader
 
; protocol type
mov edx, IP_PROTO_TCP ; NO shl 8 here ! (it took me ages to figure this one out)
 
; source address
add dl, [esp+1+4]
adc dh, [esp+0+4]
adc dl, [esp+3+4]
adc dh, [esp+2+4]
 
; destination address
adc dl, [esp+1+8]
adc dh, [esp+0+8]
adc dl, [esp+3+8]
adc dh, [esp+2+8]
 
; size
adc dl, cl
adc dh, ch
 
;---------------------
; Real header and data
 
push esi
call checksum_1
call checksum_2
pop esi
 
neg [esi+UDP_Packet.Checksum] ; zero will stay zero so we just get the checksum
add [esi+UDP_Packet.Checksum], dx ; , else we will get (new checksum - old checksum) in the end, wich should be 0 :)
 
ret 8 ; Remove the IPs from stack
 
 
 
 
;---------------------------------------------------------------------------
;
; TCP_API
/kernel/branches/net/network/udp.inc
40,21 → 40,64
;
; This function resets all UDP variables
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
UDP_init:
macro UDP_init {
 
xor eax, eax
mov edi, UDP_PACKETS_TX
mov ecx, 2*MAX_IP
rep stosd
}
 
ret
 
 
 
macro UDP_checksum IP1, IP2 { ; esi = ptr to udp packet, ecx = packet size, destroys: ecx, edx
 
; Pseudoheader
mov edx, IP_PROTO_UDP
 
add dl, [IP1+1+4]
adc dh, [IP1+0+4]
adc dl, [IP1+3+4]
adc dh, [IP1+2+4]
 
adc dl, [IP2+1+8]
adc dh, [IP2+0+8]
adc dl, [IP2+3+8]
adc dh, [IP2+2+8]
 
adc dl, cl ; byte[esi+UDP_Packet.Length+1]
adc dh, ch ; byte[esi+UDP_Packet.Length+0]
 
; Done with pseudoheader, now do real header
adc dl, byte[esi+UDP_Packet.SourcePort+1]
adc dh, byte[esi+UDP_Packet.SourcePort+0]
 
adc dl, byte[esi+UDP_Packet.DestinationPort+1]
adc dh, byte[esi+UDP_Packet.DestinationPort+0]
 
adc dl, byte[esi+UDP_Packet.Length+1]
adc dh, byte[esi+UDP_Packet.Length+0]
 
adc edx, 0
 
; Done with header, now do data
push esi
movzx ecx, [esi+UDP_Packet.Length]
rol cx , 8
sub cx , UDP_Packet.Data
add esi, UDP_Packet.Data
 
call checksum_1
call checksum_2
pop esi
 
add [esi+UDP_Packet.Checksum], dx ; this final instruction will set or clear ZF :)
 
}
 
 
;-----------------------------------------------------------------
;
; UDP_input:
62,11 → 105,11
; Called by IPv4_input,
; this procedure will inject the udp data diagrams in the application sockets.
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; pointer to device struct in ebx
; UDP Packet size in ecx
; pointer to UDP Packet in edx
; IN: [esp] = Pointer to buffer
; [esp+4] = size of buffer
; ebx = ptr to device struct
; ecx = UDP Packet size
; edx = ptr to UDP header
;
; esi = ipv4 source address
; edi = ipv4 dest address
80,18 → 123,18
DEBUGF 1,"UDP_input, size:%u\n", ecx
 
; First validate, checksum:
cmp [edx + UDP_Packet.Checksum], 0
jz .no_checksum
neg [esi+UDP_Packet.Checksum] ; substract chechksum from 0
jz .no_checksum ; if checksum is zero, it is considered valid and we continue processing
; otherwise, we will re-calculate the checksum and add it to this value, thus creating 0 when it is correct
 
xchg edi, esi ; save ipv4 source address to edi so we can use it later
 
push edx
push esi edi
push edi
push esi
mov esi, edx
call UDP_checksum ; this destroys edx, ecx and esi (but not edi...)
UDP_checksum (esp), (esp+4)
pop edi
pop esi ; we dont need it, but it is smaller then add esp, 4
pop edx
 
cmp [edx + UDP_Packet.Checksum], 0
jnz .checksum_mismatch
 
.no_checksum:
168,12 → 211,6
 
DEBUGF 2,"UDP_Handler - checksum mismatch\n"
 
; mov esi, edx
; @@: ;
; lodsb ;
; DEBUGF 2,"%x ", eax:2 ;
; loop @r ;
 
.dump:
call kernel_free
add esp, 4 ; pop (balance stack)
207,26 → 244,23
DEBUGF 1,"local port: %u\n", dx
rol dx, 8
 
 
mov ebx, [eax + IP_SOCKET.LocalIP]
mov eax, [eax + IP_SOCKET.RemoteIP]
 
mov di , IP_PROTO_UDP
mov di, IP_PROTO_UDP shl 8 + 128
sub esp, 8 ; Data ptr and data size will be placed here
add ecx, UDP_Packet.Data
 
;;; TODO: fragment id
 
push edx esi
call IPv4_create_packet
call IPv4_output
jz .fail
 
mov [esp + 8], eax ; pointer to buffer start
mov [esp + 8 + 4], edx ; buffer size
 
rol cx, 8
mov [edi + UDP_Packet.Length], cx
ror cx, 8
rol [edi + UDP_Packet.Length], 8
 
pop esi
push edi ecx
240,13 → 274,11
pop ecx edi
 
pop dword [edi + UDP_Packet.SourcePort]
mov [edi + UDP_Packet.Checksum], 0 ; set it to zero, to calculate checksum
 
; Checksum
mov esi, edi
pushd [edi-4] ; destination address ; TODO: fix this, IPv4 packet could have options..
pushd [edi-8] ; source address
call UDP_checksum
mov [edi + UDP_Packet.Checksum], 0
UDP_checksum (edi-4), (edi-8) ; TODO: fix this, IPv4 packet could have options..
 
inc [UDP_PACKETS_TX]
 
254,81 → 286,15
 
call [ebx + NET_DEVICE.transmit]
ret
 
.fail:
add esp, 8+8
DEBUGF 1,"UDP_output: failed\n"
add esp, 4+4+8
xor eax, eax
ret
 
 
 
 
;-----------------------------------------------------------------
;
; UDP_checksum
;
; This is the fast procedure to create or check a UDP header
; - To create a new checksum, the checksum field must be set to 0 before computation
; - To check an existing checksum, leave the checksum as is,
; and it will be 0 after this procedure, if it was correct
;
; IN: push source ip
; push dest ip
; esi = packet ptr
;
; OUT: checksum is filled in in packet! (but also in dx)
;
;-----------------------------------------------------------------
 
align 4
UDP_checksum:
 
; Pseudoheader
mov edx, IP_PROTO_UDP ; NO shl 8 here ! (it took me ages to figure this one out)
 
add dl, [esp+1+4]
adc dh, [esp+0+4]
adc dl, [esp+3+4]
adc dh, [esp+2+4]
 
adc dl, [esp+1+8]
adc dh, [esp+0+8]
adc dl, [esp+3+8]
adc dh, [esp+2+8]
 
 
adc dl, cl ; byte[esi+UDP_Packet.Length+1]
adc dh, ch ; byte[esi+UDP_Packet.Length+0]
 
; Done with pseudoheader, now do real header
adc dl, byte[esi+UDP_Packet.SourcePort+1]
adc dh, byte[esi+UDP_Packet.SourcePort+0]
 
adc dl, byte[esi+UDP_Packet.DestinationPort+1]
adc dh, byte[esi+UDP_Packet.DestinationPort+0]
 
adc dl, byte[esi+UDP_Packet.Length+1]
adc dh, byte[esi+UDP_Packet.Length+0]
 
adc edx, 0
 
; Done with header, now do data
push esi
movzx ecx, [esi+UDP_Packet.Length]
rol cx , 8
sub cx , UDP_Packet.Data
add esi, UDP_Packet.Data
 
call checksum_1
call checksum_2
pop esi
 
neg [esi+UDP_Packet.Checksum] ; zero will stay zero so we just get the checksum
add [esi+UDP_Packet.Checksum], dx ; , else we will get (new checksum - old checksum) in the end, wich should be 0 :)
 
ret 8
 
 
 
 
;---------------------------------------------------------------------------
;
; UDP_API