Subversion Repositories Kolibri OS

Compare Revisions

No changes between revisions

Regard whitespace Rev 9017 → Rev 9019

/kernel/branches/kolibri-ahci/network/ethernet.inc
0,0 → 1,346
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; ETHERNET.INC ;;
;; ;;
;; Ethernet network layer for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
ETH_FRAME_MINIMUM = 60
ETH_QUEUE_SIZE = 255
 
struct ETH_header
 
DstMAC dp ? ; destination MAC-address
SrcMAC dp ? ; source MAC-address
Type dw ? ; type of the upper-layer protocol
 
ends
 
struct ETH_DEVICE NET_DEVICE
 
mac dp ?
 
ends
 
iglobal
align 4
 
ETH_BROADCAST dp 0xffffffffffff
 
ETH_frame_queued dd 0 ; Number of queued frames
 
ETH_frame_head dd ETH_frame_head ; Pointer to next frame in the linked list
ETH_frame_tail dd ETH_frame_head ; Pointer to last frame in the linked list
 
endg
 
uglobal
align 4
ETH_input_event dd ?
endg
 
macro eth_init {
 
movi ebx, 1
mov ecx, eth_process_input
call new_sys_threads
test eax, eax
jns @f
DEBUGF DEBUG_NETWORK_ERROR,'K : cannot create kernel thread for ethernet, error %d\n', eax
@@:
 
}
 
;-----------------------------------------------------------------;
; ;
; eth_input: This function is called by ethernet drivers. ;
; Push the received ethernet packet onto the ethernet input queue.;
; ;
; IN: [esp] = Pointer to buffer ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
eth_input:
 
pop eax
 
if defined NETWORK_SANITY_CHECKS
cmp eax, [net_buffs_low]
jb .assert_mbuff
cmp eax, [net_buffs_high]
ja .assert_mbuff
test eax, 0x7ff
jnz .assert_mbuff
end if
 
spin_lock_irqsave
 
cmp [ETH_frame_queued], ETH_QUEUE_SIZE
jae .full
inc [ETH_frame_queued]
 
; Add frame to the end of the linked list
mov [eax + NET_BUFF.NextPtr], ETH_frame_head
 
mov ebx, [ETH_frame_tail]
mov [eax + NET_BUFF.PrevPtr], ebx
 
mov [ETH_frame_tail], eax
mov [ebx + NET_BUFF.NextPtr], eax
 
spin_unlock_irqrestore
 
; Mark it as being an Ethernet Frame
mov [eax + NET_BUFF.type], NET_BUFF_ETH
 
; Now queue an event to process it
xor edx, edx
mov eax, [ETH_input_event]
mov ebx, [eax + EVENT.id]
xor esi, esi
call raise_event
 
ret
 
.full:
mov ebx, [eax + NET_BUFF.device]
inc [ebx + NET_DEVICE.packets_rx_ovr]
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH incoming queue is full, discarding packet!\n"
spin_unlock_irqrestore
stdcall net_buff_free, eax
ret
 
if defined NETWORK_SANITY_CHECKS
.assert_mbuff:
DEBUGF DEBUG_NETWORK_ERROR, "eth_input: invalid buffer 0x%x\n", eax
DEBUGF DEBUG_NETWORK_ERROR, "eth_input: caller=0x%x\n", [esp+4]
xor eax, eax
ret
end if
 
 
 
;-----------------------------------------------------------------;
; ;
; eth_process_input: Process packets from ethernet input queue. ;
; ;
; IN: / ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
eth_process_input:
 
xor esi, esi
mov ecx, MANUAL_DESTROY
call create_event
mov [ETH_input_event], eax
pushf
.wait:
popf
mov eax, [ETH_input_event]
mov ebx, [eax + EVENT.id]
call wait_event
 
.loop:
pushf
cli
cmp [ETH_frame_queued], 0
je .wait
 
dec [ETH_frame_queued]
 
mov esi, [ETH_frame_head]
mov ebx, [esi + NET_BUFF.NextPtr]
 
mov [ETH_frame_head], ebx
mov [ebx + NET_BUFF.PrevPtr], ETH_frame_head
 
popf
 
mov eax, [esi + NET_BUFF.offset]
add eax, esi
mov ecx, [esi + NET_BUFF.length]
mov ebx, [esi + NET_BUFF.device]
 
pushd .loop ; return address for protocol handler
push esi ; keep pointer to NET_BUFF on stack
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_input: size=%u\n", ecx
sub ecx, sizeof.ETH_header
jb .err
 
; Set registers for protocol handlers
lea edx, [eax + sizeof.ETH_header]
mov ax, [eax + ETH_header.Type]
 
; Place protocol handlers here
cmp ax, ETHER_PROTO_IPv4
je ipv4_input
 
cmp ax, ETHER_PROTO_ARP
je arp_input
 
; cmp ax, ETHER_PROTO_IPv6
; je ipv6_input
 
; cmp ax, ETHER_PROTO_PPP_DISCOVERY
; je pppoe_discovery_input
 
; cmp ax, ETHER_PROTO_PPP_SESSION
; je pppoe_session_input
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_input: Unknown packet type=%x\n", ax
 
.drop:
mov eax, [esp]
mov eax, [eax + NET_BUFF.device]
inc [eax + NET_DEVICE.packets_rx_drop]
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_input: dropping\n"
call net_buff_free
ret
 
.err:
mov eax, [esp]
mov eax, [eax + NET_BUFF.device]
inc [eax + NET_DEVICE.packets_rx_err]
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_input: invalid frame received\n"
call net_buff_free
ret
 
 
 
 
;-----------------------------------------------------------------;
; ;
; eth_output ;
; ;
; IN: ax = protocol ;
; ebx = device ptr ;
; ecx = payload size ;
; edx = pointer to destination mac ;
; ;
; OUT: eax = start of net frame / 0 on error ;
; ebx = device ptr ;
; ecx = payload size ;
; edi = start of payload ;
; ;
;-----------------------------------------------------------------;
align 4
eth_output:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_output: size=%u device=%x\n", ecx, ebx
 
cmp ecx, [ebx + ETH_DEVICE.mtu]
ja .too_large
 
push ecx
push ax edx
 
add ecx, sizeof.ETH_header + NET_BUFF.data
stdcall net_buff_alloc, ecx
test eax, eax
jz .out_of_ram
 
mov [eax + NET_BUFF.type], NET_BUFF_ETH
mov [eax + NET_BUFF.device], ebx
mov [eax + NET_BUFF.offset], NET_BUFF.data
lea edi, [eax + NET_BUFF.data]
 
pop esi
movsd
movsw
lea esi, [ebx + ETH_DEVICE.mac]
movsd
movsw
pop ax
stosw
 
lea eax, [edi - sizeof.ETH_header - NET_BUFF.data] ; Set eax to buffer start
pop ecx
 
lea edx, [ecx + sizeof.ETH_header] ; Set edx to complete buffer size
cmp edx, ETH_FRAME_MINIMUM
jbe .adjust_size
.done:
mov [eax + NET_BUFF.length], edx
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_output: ptr=%x size=%u\n", eax, edx
ret
 
.adjust_size:
mov edx, ETH_FRAME_MINIMUM
test edx, edx ; clear zero flag
jmp .done
 
.out_of_ram:
inc [ebx + NET_DEVICE.packets_tx_drop]
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_output: Out of ram!\n"
add esp, 4+2
pop ecx
xor eax, eax
ret
 
.too_large:
inc [eax + NET_DEVICE.packets_tx_err]
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_output: Packet too large!\n"
xor eax, eax
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; eth_api: Part of system function 76. ;
; ;
; IN: bl = subfunction number ;
; bh = device number ;
; ecx, edx, .. depends on subfunction ;
; ;
; OUT: depends on subfunction ;
; ;
;-----------------------------------------------------------------;
align 4
eth_api:
 
cmp bh, NET_DEVICES_MAX
ja .error
movzx eax, bh
mov eax, dword [net_device_list + 4*eax]
cmp [eax + NET_DEVICE.device_type], NET_DEVICE_ETH
jne .error
 
and ebx, 0xff
cmp ebx, .number
ja .error
jmp dword [.table + 4*ebx]
 
.table:
dd .read_mac ; 0
.number = ($ - .table) / 4 - 1
 
.error:
or eax, -1
ret
 
 
.read_mac:
movzx ebx, word [eax + ETH_DEVICE.mac]
mov eax, dword [eax + ETH_DEVICE.mac + 2]
mov [esp+20+4], ebx ; FIXME
ret
 
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/stack.inc
0,0 → 1,984
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; STACK.INC ;;
;; ;;
;; TCP/IP stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Some parts of code are based on the work of: ;;
;; Mike Hibbett (menuetos network stack) ;;
;; Eugen Brasoveanu (solar os network stack and drivers) ;;
;; mike.dld (kolibrios socket code) ;;
;; ;;
;; TCP part is based on 4.4BSD ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
uglobal
net_10ms dd ?
net_tmr_count dw ?
endg
 
DEBUG_NETWORK_ERROR = 1
DEBUG_NETWORK_VERBOSE = 0
NETWORK_SANITY_CHECKS = 1
 
NET_DEVICES_MAX = 16
NET_BUFFERS = 512
NET_BUFFER_SIZE = 2048
ARP_BLOCK = 1 ; true or false
 
EPHEMERAL_PORT_MIN = 49152
EPHEMERAL_PORT_MAX = 61000
MIN_EPHEMERAL_PORT_N = 0x00C0 ; same in Network byte order (FIXME)
MAX_EPHEMERAL_PORT_N = 0x48EE ; same in Network byte order (FIXME)
 
; Ethernet protocol numbers
ETHER_PROTO_ARP = 0x0608
ETHER_PROTO_IPv4 = 0x0008
ETHER_PROTO_IPv6 = 0xDD86
ETHER_PROTO_PPP_DISCOVERY = 0x6388
ETHER_PROTO_PPP_SESSION = 0x6488
 
; Internet protocol numbers
IP_PROTO_IP = 0
IP_PROTO_ICMP = 1
IP_PROTO_TCP = 6
IP_PROTO_UDP = 17
IP_PROTO_RAW = 255
 
; IP options
IP_TOS = 1
IP_TTL = 2
IP_HDRINCL = 3
 
; PPP protocol numbers
PPP_PROTO_IPv4 = 0x2100
PPP_PROTO_IPV6 = 0x5780
PPP_PROTO_ETHERNET = 666 ; FIXME
 
;Protocol family
AF_UNSPEC = 0
AF_LOCAL = 1
AF_INET4 = 2
AF_INET6 = 10
AF_PPP = 777 ; FIXME
 
; Socket types
SOCK_STREAM = 1
SOCK_DGRAM = 2
SOCK_RAW = 3
 
; Socket level
SOL_SOCKET = 0xffff
 
; Socket options
SO_ACCEPTCON = 1 shl 0
SO_BROADCAST = 1 shl 1
SO_DEBUG = 1 shl 2
SO_DONTROUTE = 1 shl 3
SO_KEEPALIVE = 1 shl 4
SO_OOBINLINE = 1 shl 5
SO_REUSEADDR = 1 shl 6
SO_REUSEPORT = 1 shl 7
SO_USELOOPBACK = 1 shl 8
SO_BINDTODEVICE = 1 shl 9
SO_LINGER = 1 shl 10
 
SO_NONBLOCK = 1 shl 31
 
; Socket flags for user calls
MSG_PEEK = 0x02
MSG_DONTWAIT = 0x40
 
; Socket States
SS_NOFDREF = 0x0001 ; no file table ref any more
SS_ISCONNECTED = 0x0002 ; socket connected to a peer
SS_ISCONNECTING = 0x0004 ; in process of connecting to peer
SS_ISDISCONNECTING = 0x0008 ; in process of disconnecting
SS_CANTSENDMORE = 0x0010 ; can't send more data to peer
SS_CANTRCVMORE = 0x0020 ; can't receive more data from peer
SS_RCVATMARK = 0x0040 ; at mark on input
SS_ISABORTING = 0x0080 ; aborting fd references - close()
SS_RESTARTSYS = 0x0100 ; restart blocked system calls
SS_ISDISCONNECTED = 0x0800 ; socket disconnected from peer
 
SS_ASYNC = 0x1000 ; async i/o notify
SS_ISCONFIRMING = 0x2000 ; deciding to accept connection req
SS_MORETOCOME = 0x4000
 
SS_BLOCKED = 0x8000
 
 
SOCKET_BUFFER_SIZE = 4096*8 ; must be 4096*(power of 2) where 'power of 2' is at least 8
MAX_backlog = 20 ; maximum backlog for stream sockets
 
; Error Codes
ENOBUFS = 1
EINPROGRESS = 2
EOPNOTSUPP = 4
EWOULDBLOCK = 6
ENOTCONN = 9
EALREADY = 10
EINVAL = 11
EMSGSIZE = 12
ENOMEM = 18
EADDRINUSE = 20
EADDRNOTAVAIL = 21
ECONNRESET = 52
ECONNABORTED = 53
EISCONN = 56
ETIMEDOUT = 60
ECONNREFUSED = 61
 
; Api protocol numbers
API_ETH = 0
API_IPv4 = 1
API_ICMP = 2
API_UDP = 3
API_TCP = 4
API_ARP = 5
API_PPPOE = 6
API_IPv6 = 7
 
; Network device types
NET_DEVICE_LOOPBACK = 0
NET_DEVICE_ETH = 1
NET_DEVICE_SLIP = 2
 
; Network link types (link protocols)
NET_LINK_LOOPBACK = 0
NET_LINK_MAC = 1 ; Media access control (ethernet, isdn, ...)
NET_LINK_PPP = 2 ; Point to Point Protocol (PPPoE, ...)
NET_LINK_IEEE802.11 = 3 ; IEEE 802.11 (WiFi)
 
; Hardware acceleration bits
NET_HWACC_TCP_IPv4_IN = 1 shl 0
NET_HWACC_TCP_IPv4_OUT = 1 shl 1
 
; Network frame types
NET_BUFF_LOOPBACK = 0
NET_BUFF_ETH = 1
 
struct NET_DEVICE
 
device_type dd ? ; Type field
mtu dd ? ; Maximal Transmission Unit
name dd ? ; Ptr to 0 terminated string
 
unload dd ? ; Ptrs to driver functions
reset dd ? ;
transmit dd ? ;
 
link_state dd ? ; link state (0 = no link)
hwacc dd ? ; bitmask stating enabled HW accelerations (offload engines)
 
bytes_tx dq ? ; Statistics, updated by the driver
bytes_rx dq ? ;
 
packets_tx dd ? ;
packets_tx_err dd ? ; CRC errors, too long or too short frames
packets_tx_drop dd ? ;
packets_tx_ovr dd ? ; FIFO overrun
 
packets_rx dd ? ;
packets_rx_err dd ? ; CRC errors, too long or too short frames
packets_rx_drop dd ? ;
packets_rx_ovr dd ? ; FIFO overrun
 
ends
 
struct NET_BUFF
 
NextPtr dd ? ; pointer to next frame in list
PrevPtr dd ? ; pointer to previous frame in list
device dd ? ; ptr to NET_DEVICE structure
type dd ? ; encapsulation type: e.g. Ethernet
length dd ? ; size of encapsulated data
offset dd ? ; offset to actual data (24 bytes for default frame)
data rb 0
 
ends
 
 
; Exactly as it says..
macro pseudo_random reg {
add reg, [esp]
rol reg, 5
xor reg, [timer_ticks]
; add reg, [CPU_FREQ]
imul reg, 214013
xor reg, 0xdeadbeef
rol reg, 9
}
 
; Network to Hardware byte order (dword)
macro ntohd reg {
 
rol word reg, 8
rol dword reg, 16
rol word reg , 8
 
}
 
; Network to Hardware byte order (word)
macro ntohw reg {
 
rol word reg, 8
 
}
 
 
include "queue.inc"
 
include "loopback.inc"
include "ethernet.inc"
 
include "PPPoE.inc"
 
include "ARP.inc"
include "IPv4.inc"
include "IPv6.inc"
 
include "icmp.inc"
include "udp.inc"
include "tcp.inc"
 
include "socket.inc"
 
 
 
uglobal
align 4
 
net_device_count dd ?
net_device_list rd NET_DEVICES_MAX
 
net_buffs_free rd NET_BUFFERS ; list of pointers to actual net buffs
.current dd ? ; pointer to current element in net_buffs_free list
 
if defined NETWORK_SANITY_CHECKS
net_buffs_low dd ? ; actual net buff mem region start
net_buffs_high dd ? ; actual net buff mem region stop
end if
 
endg
 
 
;-----------------------------------------------------------------;
; ;
; stack_init: Initialize all network variables ;
; ;
; IN: / ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
stack_init:
 
; allocate network buffers
stdcall kernel_alloc, NET_BUFFER_SIZE*NET_BUFFERS
test eax, eax
jz .fail
 
if defined NETWORK_SANITY_CHECKS
mov [net_buffs_low], eax
end if
 
mov edi, net_buffs_free
mov ecx, NET_BUFFERS
cld
.loop:
stosd
add eax, NET_BUFFER_SIZE
dec ecx
jnz .loop
 
if defined NETWORK_SANITY_CHECKS
sub eax, NET_BUFFER_SIZE
mov [net_buffs_high], eax
end if
 
mov eax, net_buffs_free
stosd
 
; Init the network drivers list
xor eax, eax
mov edi, net_device_count
mov ecx, (NET_DEVICES_MAX + 1)
rep stosd
 
eth_init
 
pppoe_init
 
ipv4_init
; ipv6_init
icmp_init
 
arp_init
udp_init
tcp_init
 
socket_init
 
loop_init
 
mov [net_tmr_count], 0
ret
 
.fail:
DEBUGF DEBUG_NETWORK_ERROR, "Stack init failed!\n"
ret
 
 
 
; Wakeup every tick.
proc stack_handler_has_work?
 
mov eax, [timer_ticks]
cmp eax, [net_10ms]
 
ret
endp
 
 
;-----------------------------------------------------------------;
; ;
; stack_handler: Network handlers called from os_loop. ;
; ;
; IN: / ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
stack_handler:
 
; Test for 10ms tick
mov eax, [timer_ticks]
cmp eax, [net_10ms]
je .exit
mov [net_10ms], eax
 
cmp [net_device_count], 0
je .exit
 
test [net_10ms], 0x0f ; 160ms
jnz .exit
 
tcp_timer_160ms
 
test [net_10ms], 0x3f ; 640ms
jnz .exit
 
arp_decrease_entry_ttls
ipv4_decrease_fragment_ttls
 
xor edx, edx
mov eax, [TCP_timer1_event]
mov ebx, [eax + EVENT.id]
xor esi, esi
call raise_event
 
.exit:
ret
 
 
align 4
proc net_buff_alloc stdcall, buffersize
 
cmp [buffersize], NET_BUFFER_SIZE
ja .too_large
 
spin_lock_irqsave
 
mov eax, [net_buffs_free.current]
cmp eax, net_buffs_free+NET_BUFFERS*4
jae .out_of_mem
mov eax, [eax]
add [net_buffs_free.current], 4
 
spin_unlock_irqrestore
 
if defined NETWORK_SANITY_CHECKS
cmp eax, [net_buffs_low]
cmp eax, [net_buffs_low]
jb .assert_mbuff
cmp eax, [net_buffs_high]
ja .assert_mbuff
test eax, 0x7ff
jnz .assert_mbuff
end if
 
DEBUGF DEBUG_NETWORK_VERBOSE, "net_buff_alloc: 0x%x\n", eax
ret
 
.out_of_mem:
spin_unlock_irqrestore
 
xor eax, eax
DEBUGF DEBUG_NETWORK_ERROR, "net_buff_alloc: out of mem!\n"
ret
 
.too_large:
xor eax, eax
DEBUGF DEBUG_NETWORK_ERROR, "net_buff_alloc: too large!\n"
ret
 
if defined NETWORK_SANITY_CHECKS
.assert_mbuff:
DEBUGF DEBUG_NETWORK_ERROR, "net_buff_alloc: invalid buffer 0x%x\n", eax
DEBUGF DEBUG_NETWORK_ERROR, "net_buff_alloc: caller=0x%x\n", [esp+4]
xor eax, eax
ret
end if
 
endp
 
 
align 4
proc net_buff_free stdcall, buffer
 
DEBUGF DEBUG_NETWORK_VERBOSE, "net_buff_free: 0x%x\n", [buffer]
 
if defined NETWORK_SANITY_CHECKS
mov eax, [buffer]
cmp eax, [net_buffs_low]
jb .assert_mbuff
cmp eax, [net_buffs_high]
ja .assert_mbuff
test eax, 0x7ff
jnz .assert_mbuff
end if
 
spin_lock_irqsave
 
sub [net_buffs_free.current], 4 ; move pointer backwards
mov eax, [net_buffs_free.current] ; place free'd buffer pointer on the list
push [buffer]
pop dword[eax]
 
spin_unlock_irqrestore
 
ret
 
if defined NETWORK_SANITY_CHECKS
.assert_mbuff:
DEBUGF DEBUG_NETWORK_ERROR, "net_buff_free: invalid buffer 0x%x\n", eax
DEBUGF DEBUG_NETWORK_ERROR, "net_buff_free: caller=0x%x\n", [esp+4]
xor eax, eax
ret
end if
 
endp
 
 
align 4
net_link_changed:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "net_link_changed device=0x%x status=0x%x\n", ebx, [ebx + NET_DEVICE.link_state]
 
align 4
net_send_event:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "net_send_event\n"
 
; Send event to all applications
push edi ecx
mov edi, SLOT_BASE
mov ecx, [thread_count]
.loop:
add edi, sizeof.APPDATA
or [edi + APPDATA.occurred_events], EVENT_NETWORK2
loop .loop
pop ecx edi
 
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; net_add_device: Called by network driver to register interface. ;
; ;
; IN: ebx = ptr to device structure ;
; ;
; OUT: eax = device num on success ;
; eax = -1 on error ;
; ;
;-----------------------------------------------------------------;
align 4
net_add_device:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "net_add_device: %x\n", ebx ;;; TODO: use mutex to lock net device list
 
cmp [net_device_count], NET_DEVICES_MAX
jae .error
 
;----------------------------------
; Check if device is already listed
mov eax, ebx
mov ecx, NET_DEVICES_MAX ; We need to check whole list because a device may be removed without re-organizing list
mov edi, net_device_list
 
repne scasd ; See if device is already in the list
jz .error
 
;----------------------------
; Find empty slot in the list
xor eax, eax
mov ecx, NET_DEVICES_MAX
mov edi, net_device_list
 
repne scasd
jnz .error
 
sub edi, 4
 
;-----------------------------
; Add device to the found slot
mov [edi], ebx ; add device to list
 
mov eax, edi ; Calculate device number in eax
sub eax, net_device_list
shr eax, 2
 
inc [net_device_count] ; Indicate that one more network device is up and running
 
call net_send_event
 
DEBUGF DEBUG_NETWORK_VERBOSE, "Device number: %u\n", eax
ret
 
.error:
or eax, -1
DEBUGF DEBUG_NETWORK_ERROR, "Adding network device failed\n"
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; net_remove_device: Called by network driver to unregister dev. ;
; ;
; IN: ebx = ptr to device ;
; ;
; OUT: eax: -1 on error ;
; ;
;-----------------------------------------------------------------;
align 4
net_remove_device:
 
cmp [net_device_count], 0
je .error
 
;----------------------------
; Find the driver in the list
 
mov eax, ebx
mov ecx, NET_DEVICES_MAX
mov edi, net_device_list
 
repne scasd
jnz .error
 
;------------------------
; Remove it from the list
 
xor eax, eax
mov dword [edi-4], eax
dec [net_device_count]
 
call net_send_event
 
xor eax, eax
ret
 
.error:
or eax, -1
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; net_ptr_to_num ;
; ;
; IN: ebx = ptr to device struct ;
; ;
; OUT: edi = device number ;
; edi = -1 on error ;
; ;
;-----------------------------------------------------------------;
align 4
net_ptr_to_num:
 
call net_ptr_to_num4
ror edi, 2 ; If -1, stay -1
; valid device numbers have last two bits 0, so do just shr
 
ret
 
align 4
net_ptr_to_num4: ; Todo, place number in device structure so we only need to verify?
 
test ebx, ebx
jz .fail
 
push ecx
mov ecx, NET_DEVICES_MAX
mov edi, net_device_list
.loop:
cmp ebx, [edi]
je .found
add edi, 4
dec ecx
jnz .loop
 
pop ecx
.fail:
or edi, -1
ret
 
.found:
sub edi, net_device_list
pop ecx
ret
 
;-----------------------------------------------------------------;
; ;
; checksum_1: Calculate semi-checksum for network packets. ;
; ;
; IN: edx = start offset for semi-checksum ;
; esi = pointer to data ;
; ecx = data size ;
; ;
; OUT: edx = semi-checksum ;
; ;
;-----------------------------------------------------------------;
align 4
checksum_1:
 
shr ecx, 1
pushf
jz .no_2
 
shr ecx, 1
pushf
jz .no_4
 
shr ecx, 1
pushf
jz .no_8
 
.loop:
add dl, [esi+1]
adc dh, [esi+0]
 
adc dl, [esi+3]
adc dh, [esi+2]
 
adc dl, [esi+5]
adc dh, [esi+4]
 
adc dl, [esi+7]
adc dh, [esi+6]
 
adc edx, 0
add esi, 8
 
dec ecx
jnz .loop
 
adc edx, 0
 
.no_8:
popf
jnc .no_4
 
add dl, [esi+1]
adc dh, [esi+0]
 
adc dl, [esi+3]
adc dh, [esi+2]
 
adc edx, 0
add esi, 4
 
.no_4:
popf
jnc .no_2
 
add dl, [esi+1]
adc dh, [esi+0]
 
adc edx, 0
inc esi
inc esi
 
.no_2:
popf
jnc .end
 
add dh, [esi+0]
adc edx, 0
.end:
ret
 
;-----------------------------------------------------------------;
; ;
; checksum_2: Calculate the final ip/tcp/udp checksum. ;
; ;
; IN: edx = semi-checksum ;
; ;
; OUT: dx = checksum (in INET byte order) ;
; ;
;-----------------------------------------------------------------;
align 4
checksum_2:
 
mov ecx, edx
shr ecx, 16
and edx, 0xffff
add edx, ecx
 
mov ecx, edx
shr ecx, 16
add dx, cx
test dx, dx ; it seems that ZF is not set when CF is set :(
not dx
jnz .not_zero
dec dx
.not_zero:
xchg dl, dh
 
DEBUGF DEBUG_NETWORK_VERBOSE, "Checksum: %x\n", dx
 
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; System function 74: Low level access to network devices. ;
; ;
;-----------------------------------------------------------------;
align 4
sys_network:
 
cmp bl, 255
jne @f
 
mov eax, [net_device_count]
mov [esp+32], eax
ret
 
@@:
cmp bh, NET_DEVICES_MAX ; Check if device number exists
jae .doesnt_exist
 
mov esi, ebx
and esi, 0x0000ff00
shr esi, 6
 
cmp dword[esi + net_device_list], 0 ; check if device is running
je .doesnt_exist
 
mov eax, [esi + net_device_list]
 
and ebx, 0x000000ff
cmp ebx, .number
ja .doesnt_exist
jmp dword [.table + 4*ebx]
 
.table:
dd .get_type ; 0
dd .get_dev_name ; 1
dd .reset ; 2
dd .stop ; 3
dd .get_ptr ; 4
dd .get_drv_name ; 5
 
dd .packets_tx ; 6
dd .packets_rx ; 7
dd .bytes_tx ; 8
dd .bytes_rx ; 9
dd .state ; 10
dd .packets_tx_err ; 11
dd .packets_tx_drop ; 12
dd .packets_tx_ovr ; 13
dd .packets_rx_err ; 14
dd .packets_rx_drop ; 15
dd .packets_rx_ovr ; 16
 
.number = ($ - .table) / 4 - 1
 
.get_type:
mov eax, [eax + NET_DEVICE.device_type]
mov [esp+32], eax
ret
 
.get_dev_name:
mov ebx, eax
stdcall is_region_userspace, ecx, 64
jz .bad_buffer
mov esi, [ebx + NET_DEVICE.name]
mov edi, ecx
 
mov ecx, 64/4 ; max length
rep movsd
 
xor eax, eax
mov [esp+32], eax
ret
 
.reset:
call [eax + NET_DEVICE.reset]
mov [esp+32], eax
ret
 
.stop:
call [eax + NET_DEVICE.unload]
mov [esp+32], eax
ret
 
 
.get_ptr:
mov [esp+32], eax
ret
 
 
.get_drv_name:
xor eax, eax
mov [esp+32], eax
ret
 
.packets_tx:
mov eax, [eax + NET_DEVICE.packets_tx]
mov [esp+32], eax
ret
 
.packets_rx:
mov eax, [eax + NET_DEVICE.packets_rx]
mov [esp+32], eax
ret
 
.bytes_tx:
mov ebx, dword[eax + NET_DEVICE.bytes_tx + 4]
mov [esp+20], ebx
mov eax, dword[eax + NET_DEVICE.bytes_tx]
mov [esp+32], eax
ret
 
.bytes_rx:
mov ebx, dword[eax + NET_DEVICE.bytes_rx + 4]
mov [esp+20], ebx
mov eax, dword[eax + NET_DEVICE.bytes_rx]
mov [esp+32], eax
ret
 
.packets_tx_err:
mov eax, [eax + NET_DEVICE.packets_tx_err]
mov [esp+32], eax
ret
 
.packets_tx_drop:
mov eax, [eax + NET_DEVICE.packets_tx_drop]
mov [esp+32], eax
ret
 
.packets_tx_ovr:
mov eax, [eax + NET_DEVICE.packets_tx_ovr]
mov [esp+32], eax
ret
 
.packets_rx_err:
mov eax, [eax + NET_DEVICE.packets_rx_err]
mov [esp+32], eax
ret
 
.packets_rx_drop:
mov eax, [eax + NET_DEVICE.packets_rx_drop]
mov [esp+32], eax
ret
 
.packets_rx_ovr:
mov eax, [eax + NET_DEVICE.packets_rx_ovr]
mov [esp+32], eax
ret
 
.state:
mov eax, [eax + NET_DEVICE.link_state]
mov [esp+32], eax
ret
 
 
.doesnt_exist:
.bad_buffer: ; Sanity check failed, exit
mov dword[esp+32], -1
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; System function 76: Low level access to protocol handlers. ;
; ;
;-----------------------------------------------------------------;
align 4
sys_protocols:
cmp bh, NET_DEVICES_MAX ; Check if device number exists
jae .doesnt_exist
 
mov eax, ebx
and eax, 0x0000ff00
shr eax, 6 ; now we have the device num * 4 in eax
cmp [eax + net_device_list], 0 ; check if device is running
je .doesnt_exist
 
push .return ; return address (we will be using jumps instead of calls)
 
mov eax, ebx ; set ax to protocol number
shr eax, 16 ;
 
cmp ax, API_ETH
je eth_api
 
cmp ax, API_IPv4
je ipv4_api
 
cmp ax, API_ICMP
je icmp_api
 
cmp ax, API_UDP
je udp_api
 
cmp ax, API_TCP
je tcp_api
 
cmp ax, API_ARP
je arp_api
 
cmp ax, API_PPPOE
je pppoe_api
 
cmp ax, API_IPv6
je ipv6_api
 
add esp, 4 ; if we reached here, no function was called, so we need to balance stack
 
.doesnt_exist:
mov eax, -1
 
.return:
mov [esp+28+4], eax ; return eax value to the program
ret
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/socket.inc
0,0 → 1,2525
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org, ;;
;; and Clevermouse. ;;
;; ;;
;; Based on code by mike.dld ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
struct SOCKET
 
NextPtr dd ? ; pointer to next socket in list
PrevPtr dd ? ; pointer to previous socket in list
Number dd ? ; socket number
 
mutex MUTEX
 
PID dd ? ; process ID
TID dd ? ; thread ID
Domain dd ? ; INET4/INET6/LOCAL/..
Type dd ? ; RAW/STREAM/DGRAM
Protocol dd ? ; UDP/TCP/ARP/ICMP
errorcode dd ?
device dd ? ; device pointer, paired socket pointer if it's a local socket
 
options dd ?
state dd ?
backlog dw ? ; number of incoming connections that can be queued
 
snd_proc dd ?
rcv_proc dd ?
connect_proc dd ?
 
ends
 
struct IP_SOCKET SOCKET
 
LocalIP rd 4 ; network byte order
RemoteIP rd 4 ; network byte order
ttl db ?
rb 3 ; align
 
ends
 
struct TCP_SOCKET IP_SOCKET
 
LocalPort dw ? ; network byte order
RemotePort dw ? ; network byte order
 
t_state dd ? ; TCB state
t_rxtshift db ?
rb 3 ; align
t_rxtcur dd ?
t_dupacks dd ?
t_maxseg dd ?
t_flags dd ?
 
;---------------
; RFC783 page 21
 
; send sequence
SND_UNA dd ? ; sequence number of unack'ed sent Packets
SND_NXT dd ? ; next send sequence number to use
SND_UP dd ? ; urgent pointer
SND_WL1 dd ? ; the sequence number of the last segment used to update the send window
SND_WL2 dd ? ; the acknowledgment number of the last segment used to update the send window
ISS dd ? ; initial send sequence number
SND_WND dd ? ; send window
 
; receive sequence
RCV_WND dd ? ; receive window
RCV_NXT dd ? ; next receive sequence number to use
RCV_UP dd ? ; urgent pointer
IRS dd ? ; initial receive sequence number
 
;---------------------
; Additional variables
 
; receive variables
RCV_ADV dd ?
 
; retransmit variables
SND_MAX dd ?
 
; congestion control
SND_CWND dd ? ; congestion window
SND_SSTHRESH dd ? ; slow start threshold
 
;----------------------
; Transmit timing stuff
t_idle dd ?
t_rtt dd ? ; round trip time
t_rtseq dd ?
t_srtt dd ? ; smoothed round trip time
t_rttvar dd ?
t_rttmin dd ?
max_sndwnd dd ?
 
;-----------------
; Out-of-band data
t_oobflags dd ?
t_iobc dd ?
t_softerror dd ?
 
 
;---------
; RFC 1323 ; the order of next 4 elements may not change
 
SND_SCALE db ?
RCV_SCALE db ?
requested_s_scale db ?
request_r_scale db ?
 
ts_recent dd ? ; a copy of the most-recent valid timestamp from the other end
ts_recent_age dd ?
last_ack_sent dd ?
 
 
;-------
; Timers
timer_flags dd ?
timer_retransmission dd ? ; rexmt
timer_persist dd ?
timer_keepalive dd ? ; keepalive/syn timeout
timer_timed_wait dd ? ; also used as 2msl timer
timer_connect dd ?
 
; extra
 
ts_ecr dd ? ; timestamp echo reply
ts_val dd ?
 
seg_next dd ? ; re-assembly queue
 
ends
 
struct UDP_SOCKET IP_SOCKET
 
LocalPort dw ? ; in network byte order
RemotePort dw ? ; in network byte order
 
ends
 
struct RING_BUFFER
 
mutex MUTEX
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
 
ends
 
struct STREAM_SOCKET TCP_SOCKET
 
rcv RING_BUFFER
snd RING_BUFFER
 
ends
 
struct socket_queue_entry
 
data_ptr dd ?
data_size dd ?
buf_ptr dd ?
 
ends
 
struct socket_options
 
level dd ?
optname dd ?
optlen dd ?
optval dd ?
 
ends
 
SOCKET_STRUCT_SIZE = 4096 ; in bytes
 
SOCKET_QUEUE_SIZE = 10 ; maximum number of incoming packets queued for 1 socket
; the incoming packet queue for sockets is placed in the socket struct itself, at this location from start
SOCKET_QUEUE_LOCATION = (SOCKET_STRUCT_SIZE - SOCKET_QUEUE_SIZE*sizeof.socket_queue_entry - sizeof.queue)
 
uglobal
align 4
 
net_sockets rd 4
last_socket_num dd ?
last_UDP_port dw ? ; last used ephemeral port
last_TCP_port dw ? ;
socket_mutex MUTEX
 
endg
 
 
;-----------------------------------------------------------------;
; ;
; socket_init ;
; ;
;-----------------------------------------------------------------;
macro socket_init {
 
xor eax, eax
mov edi, net_sockets
mov ecx, 5
rep stosd
 
@@:
pseudo_random eax
cmp ax, EPHEMERAL_PORT_MIN
jb @r
cmp ax, EPHEMERAL_PORT_MAX
ja @r
xchg al, ah
mov [last_UDP_port], ax
 
@@:
pseudo_random eax
cmp ax, EPHEMERAL_PORT_MIN
jb @r
cmp ax, EPHEMERAL_PORT_MAX
ja @r
xchg al, ah
mov [last_TCP_port], ax
 
mov ecx, socket_mutex
call mutex_init
 
}
 
;-----------------------------------------------------------------;
; ;
; Sockets API (system function 75) ;
; ;
;-----------------------------------------------------------------;
align 4
sys_socket:
 
mov dword[esp+20], 0 ; Set error code to 0
 
cmp ebx, 255
jz socket_debug
 
cmp ebx, .number
ja .error
jmp dword [.table + 4*ebx]
 
.table:
dd socket_open ; 0
dd socket_close ; 1
dd socket_bind ; 2
dd socket_listen ; 3
dd socket_connect ; 4
dd socket_accept ; 5
dd socket_send ; 6
dd socket_receive ; 7
dd socket_set_opt ; 8
dd socket_get_opt ; 9
dd socket_pair ; 10
.number = ($ - .table) / 4 - 1
 
.error:
mov dword[esp+32], -1
mov dword[esp+20], EINVAL
 
ret
 
;-----------------------------------------------------------------;
; ;
; socket_open: Create a new socket. ;
; ;
; IN: ecx = domain ;
; edx = type ;
; esi = protocol ;
; ;
; OUT: eax = socket number ;
; eax = -1 on error ;
; ebx = errorcode on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_open:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_open: domain=%u type=%u protocol=%x\n", ecx, edx, esi
 
push ecx edx esi
call socket_alloc
pop esi edx ecx
test eax, eax
jz .nobuffs
 
mov [esp+32], edi ; return socketnumber
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_open: socknum=%u\n", edi
 
test edx, SO_NONBLOCK
jz @f
or [eax + SOCKET.options], SO_NONBLOCK
and edx, not SO_NONBLOCK
@@:
 
mov [eax + SOCKET.Domain], ecx
mov [eax + SOCKET.Type], edx
mov [eax + SOCKET.Protocol], esi
mov [eax + SOCKET.connect_proc], connect_notsupp
 
cmp ecx, AF_INET4
jne .no_inet4
 
mov [eax + IP_SOCKET.ttl], 128
 
cmp edx, SOCK_DGRAM
je .udp
 
cmp edx, SOCK_STREAM
je .tcp
 
cmp edx, SOCK_RAW
je .raw
 
.no_inet4:
cmp ecx, AF_PPP
jne .no_ppp
 
cmp esi, PPP_PROTO_ETHERNET
je .pppoe
 
.no_ppp:
.unsupported:
push eax
call socket_free
pop eax
mov dword[esp+20], EOPNOTSUPP
mov dword[esp+32], -1
ret
 
.nobuffs:
mov dword[esp+20], ENOBUFS
mov dword[esp+32], -1
ret
 
.raw:
test esi, esi ; IP_PROTO_IP
jz .raw_ip
 
cmp esi, IP_PROTO_ICMP
je .raw_icmp
 
jmp .unsupported
 
align 4
.udp:
push eax
init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up data receiving queue
pop eax
 
mov [eax + SOCKET.Protocol], IP_PROTO_UDP
mov [eax + SOCKET.snd_proc], socket_send_udp
mov [eax + SOCKET.rcv_proc], socket_receive_dgram
mov [eax + SOCKET.connect_proc], udp_connect
ret
 
align 4
.tcp:
mov [eax + SOCKET.Protocol], IP_PROTO_TCP
mov [eax + SOCKET.snd_proc], socket_send_tcp
mov [eax + SOCKET.rcv_proc], socket_receive_tcp
mov [eax + SOCKET.connect_proc], tcp_connect
 
tcp_init_socket eax
ret
 
 
align 4
.raw_ip:
push eax
init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up data receiving queue
pop eax
 
mov [eax + SOCKET.snd_proc], socket_send_ip
mov [eax + SOCKET.rcv_proc], socket_receive_dgram
mov [eax + SOCKET.connect_proc], ipv4_connect
ret
 
 
align 4
.raw_icmp:
push eax
init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up data receiving queue
pop eax
 
mov [eax + SOCKET.snd_proc], socket_send_icmp
mov [eax + SOCKET.rcv_proc], socket_receive_dgram
mov [eax + SOCKET.connect_proc], ipv4_connect
ret
 
align 4
.pppoe:
push eax
init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up data receiving queue
pop eax
 
mov [eax + SOCKET.snd_proc], socket_send_pppoe
mov [eax + SOCKET.rcv_proc], socket_receive_dgram
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_bind: Bind to a local port. ;
; ;
; IN: ecx = socket number ;
; edx = pointer to sockaddr struct ;
; esi = length of sockaddr struct ;
; ;
; OUT: eax = 0 on success ;
; eax = -1 on error ;
; ebx = errorcode on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_bind:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_bind: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi
 
call socket_num_to_ptr
test eax, eax
jz .invalid
 
cmp esi, 2
jb .invalid
 
cmp [eax + UDP_SOCKET.LocalPort], 0 ; Socket can only be bound once
jnz .invalid
 
cmp word[edx], AF_INET4
je .af_inet4
 
cmp word[edx], AF_LOCAL
je .af_local
 
.notsupp:
mov dword[esp+20], EOPNOTSUPP
mov dword[esp+32], -1
ret
 
.invalid:
mov dword[esp+20], EINVAL
mov dword[esp+32], -1
ret
 
.af_local:
; TODO: write code here
mov dword[esp+32], 0
ret
 
.af_inet4:
cmp esi, 6
jb .invalid
 
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP
je .udp
 
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
je .tcp
 
jmp .notsupp
 
.tcp:
.udp:
pushd [edx + 4] ; First, fill in the IP
popd [eax + IP_SOCKET.LocalIP]
 
mov bx, [edx + 2] ; Did caller specify a local port?
test bx, bx
jnz .just_check
call socket_find_port ; Nope, find an ephemeral one
jmp .done
 
.just_check:
call socket_check_port ; Yes, check if it's still available
jz .addrinuse ; ZF is set by socket_check_port on error
 
.done:
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_bind: 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
 
mov dword[esp+32], 0
ret
 
.addrinuse:
mov dword[esp+32], -1
mov dword[esp+20], EADDRINUSE
ret
 
 
 
 
;-----------------------------------------------------------------;
; ;
; socket_connect: Connect to the remote host. ;
; ;
; IN: ecx = socket number ;
; edx = pointer to sockaddr struct ;
; esi = length of sockaddr struct ;
; ;
; OUT: eax = 0 on success ;
; eax = -1 on error ;
; ebx = errorcode on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_connect:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_connect: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi
 
call socket_num_to_ptr
test eax, eax
jz .invalid
 
cmp esi, 8
jb .invalid
 
cmp [eax + SOCKET.state], SS_ISCONNECTING
je .already
 
test [eax + SOCKET.options], SO_ACCEPTCON
jnz .notsupp
 
call [eax + SOCKET.connect_proc]
 
mov dword[esp+20], ebx
mov dword[esp+32], eax
ret
 
 
.notsupp:
mov dword[esp+20], EOPNOTSUPP
mov dword[esp+32], -1
ret
 
.invalid:
mov dword[esp+20], EINVAL
mov dword[esp+32], -1
ret
 
.already:
mov dword[esp+20], EALREADY
mov dword[esp+32], -1
ret
 
 
connect_notsupp:
xor eax, eax
dec eax
mov ebx, EOPNOTSUPP
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_listen: Listen for incoming connections. ;
; ;
; IN: ecx = socket number ;
; edx = backlog in edx ;
; ;
; OUT: eax = 0 on success ;
; eax = -1 on error ;
; ebx = errorcode on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_listen:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_listen: socknum=%u backlog=%u\n", ecx, edx
 
call socket_num_to_ptr
test eax, eax
jz .invalid
 
cmp [eax + SOCKET.Domain], AF_INET4
jne .notsupp
 
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
jne .invalid
 
cmp [eax + TCP_SOCKET.LocalPort], 0
je .already
 
cmp [eax + IP_SOCKET.LocalIP], 0
jne @f
push [IPv4_address + 4] ;;; fixme!!!!
pop [eax + IP_SOCKET.LocalIP]
@@:
 
cmp edx, MAX_backlog
jbe @f
mov edx, MAX_backlog
@@:
 
mov [eax + SOCKET.backlog], dx
or [eax + SOCKET.options], SO_ACCEPTCON
mov [eax + TCP_SOCKET.t_state], TCPS_LISTEN
mov [eax + TCP_SOCKET.timer_keepalive], 0 ; disable keepalive timer
 
push eax
init_queue (eax + SOCKET_QUEUE_LOCATION) ; Set up sockets queue
pop eax
 
mov dword[esp+32], 0
ret
 
.notsupp:
mov dword[esp+20], EOPNOTSUPP
mov dword[esp+32], -1
ret
 
.invalid:
mov dword[esp+20], EINVAL
mov dword[esp+32], -1
ret
 
.already:
mov dword[esp+20], EALREADY
mov dword[esp+32], -1
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_accept: Accept an incoming connection. ;
; ;
; IN: ecx = socket number (of listening socket) ;
; edx = ptr to sockaddr struct ;
; esi = length of sockaddr struct ;
; ;
; OUT: eax = newly created socket num ;
; eax = -1 on error ;
; ebx = errorcode on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_accept:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_accept: socknum=%u sockaddr=%x length=%u\n", ecx, edx, esi
 
call socket_num_to_ptr
test eax, eax
jz .invalid
 
test [eax + SOCKET.options], SO_ACCEPTCON
jz .invalid
 
cmp [eax + SOCKET.Domain], AF_INET4
jne .notsupp
 
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
jne .invalid
 
.loop:
get_from_queue (eax + SOCKET_QUEUE_LOCATION), MAX_backlog, 4, .block
 
; Ok, we got a socket ptr
mov eax, [esi]
 
; Verify that it is (still) a valid socket
call socket_check
jz .invalid
 
; Change sockets thread owner ID to that of the current thread
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
mov [eax + SOCKET.TID], ebx
 
; Return socket number to caller
mov eax, [eax + SOCKET.Number]
mov [esp+32], eax
ret
 
.block:
test [eax + SOCKET.options], SO_NONBLOCK
jnz .wouldblock
 
call socket_block
jmp .loop
 
.wouldblock:
mov dword[esp+20], EWOULDBLOCK
mov dword[esp+32], -1
ret
 
.invalid:
mov dword[esp+20], EINVAL
mov dword[esp+32], -1
ret
 
.notsupp:
mov dword[esp+20], EOPNOTSUPP
mov dword[esp+32], -1
ret
 
;-----------------------------------------------------------------;
; ;
; socket_close: Close the socket (and connection). ;
; ;
; IN: ecx = socket number ;
; ;
; OUT: eax = 0 on success ;
; eax = -1 on error ;
; ebx = errorcode on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_close:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_close: socknum=%u\n", ecx
 
call socket_num_to_ptr
test eax, eax
jz .invalid
 
mov dword[esp+32], 0 ; The socket exists, so we will succeed in closing it.
 
or [eax + SOCKET.options], SO_NONBLOCK ; Mark the socket as non blocking, we dont want it to block any longer!
 
test [eax + SOCKET.state], SS_BLOCKED ; Is the socket still in blocked state?
jz @f
call socket_notify ; Unblock it.
@@:
 
cmp [eax + SOCKET.Domain], AF_INET4
jne .free
 
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
je .tcp
 
.free:
call socket_free
ret
 
.tcp:
test [eax + SOCKET.state], SS_ISCONNECTED
jz @f
test [eax + SOCKET.state], SS_ISDISCONNECTING
jnz @f
call tcp_disconnect
@@:
; TODO:
; ...
; call socket_free
ret
 
 
.invalid:
mov dword[esp+20], EINVAL
mov dword[esp+32], -1
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_receive: Receive some data from the remote end. ;
; ;
; IN: ecx = socket number ;
; edx = addr to application buffer ;
; edx = length of application buffer ;
; edi = flags ;
; ;
; OUT: eax = number of bytes copied ;
; eax = -1 on error ;
; eax = 0 when socket has been closed by the remote end ;
; ebx = errorcode on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_receive:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_receive: socknum=%u bufaddr=%x buflength=%u flags=%x\n", ecx, edx, esi, edi
 
call socket_num_to_ptr
test eax, eax
jz .invalid
 
.loop:
push edi
call [eax + SOCKET.rcv_proc]
pop edi
 
test [eax + SOCKET.state], SS_CANTRCVMORE
jnz .last_data
 
cmp ebx, EWOULDBLOCK
jne .return
 
test edi, MSG_DONTWAIT
jnz .return_err
 
test [eax + SOCKET.options], SO_NONBLOCK
jnz .return_err
 
call socket_block
jmp .loop
 
 
.invalid:
push EINVAL
pop ebx
.return_err:
mov ecx, -1
.return:
mov [esp+20], ebx
mov [esp+32], ecx
ret
 
.last_data:
test ecx, ecx
jz .return
call socket_notify ; Call me again!
jmp .return
 
 
 
 
align 4
socket_receive_dgram:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_receive: DGRAM\n"
 
test edi, MSG_PEEK
jnz .peek
 
mov ebx, esi ; buffer length
 
get_from_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, sizeof.socket_queue_entry, .wouldblock ; sets esi only on success.
mov ecx, [esi + socket_queue_entry.data_size]
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_receive: %u bytes data\n", ecx
 
cmp ecx, ebx ; If data segment does not fit in applications buffer, abort
ja .too_small
 
push eax ecx
push [esi + socket_queue_entry.buf_ptr] ; save the buffer addr so we can clear it later
mov esi, [esi + socket_queue_entry.data_ptr]
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_receive: Source buffer=%x real addr=%x\n", [esp], esi
 
; copy the data from kernel buffer to application buffer
mov edi, edx ; bufferaddr
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
test ecx, ecx
jz .nd
rep movsd
.nd:
 
call net_buff_free
pop ecx eax ; return number of bytes copied to application
cmp [eax + SOCKET_QUEUE_LOCATION + queue.size], 0
je @f
call socket_notify ; Queue another network event
@@:
xor ebx, ebx ; errorcode = 0 (no error)
ret
 
.too_small:
mov ecx, -1
push EMSGSIZE
pop ebx
ret
 
.wouldblock:
push EWOULDBLOCK
pop ebx
ret
 
.peek:
xor ebx, ebx
xor ecx, ecx
cmp [eax + SOCKET_QUEUE_LOCATION + queue.size], 0
je @f
mov esi, [eax + SOCKET_QUEUE_LOCATION + queue.r_ptr]
mov ecx, [esi + socket_queue_entry.data_size]
@@:
ret
 
align 4
socket_receive_tcp:
 
call socket_receive_stream
 
test ecx, ecx
jz @f
push eax ebx ecx
call tcp_output
pop ecx ebx eax
@@:
 
ret
 
 
align 4
socket_receive_local:
 
; does this socket have a PID yet?
cmp [eax + SOCKET.PID], 0
jne @f
 
; Change PID to that of current process
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
mov [eax + SOCKET.PID], ebx
mov [eax + SOCKET.TID], ebx ; currently TID = PID in kolibrios :(
@@:
 
mov [eax + SOCKET.rcv_proc], socket_receive_stream
 
; ... continue to SOCKET_receive_stream
 
align 4
socket_receive_stream:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_receive: STREAM\n"
 
cmp [eax + STREAM_SOCKET.rcv.size], 0
je .wouldblock
 
test edi, MSG_PEEK
jnz .peek
 
mov ecx, esi
mov edi, edx
xor edx, edx
 
push eax
add eax, STREAM_SOCKET.rcv
call socket_ring_read ; copy data from kernel buffer to application buffer
call socket_ring_free ; free read memory
pop eax
 
cmp [eax + STREAM_SOCKET.rcv.size], 0
jne .more_data
xor ebx, ebx ; errorcode = 0 (no error)
ret
 
.more_data:
call socket_notify ; Queue another network event
xor ebx, ebx ; errorcode = 0 (no error)
ret
 
.wouldblock:
push EWOULDBLOCK
pop ebx
xor ecx, ecx
ret
 
.peek:
mov ecx, [eax + STREAM_SOCKET.rcv.size]
xor ebx, ebx
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_send: Send some data to the remote end. ;
; ;
; IN: ecx = socket number ;
; edx = pointer to data ;
; esi = data length ;
; edi = flags ;
; ;
; OUT: eax = number of bytes sent ;
; eax = -1 on error ;
; ebx = errorcode on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_send:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: socknum=%u data ptr=%x length=%u flags=%x\n", ecx, edx, esi, edi
 
call socket_num_to_ptr
test eax, eax
jz .invalid
 
mov ecx, esi
mov esi, edx
 
jmp [eax + SOCKET.snd_proc]
 
.invalid:
mov dword[esp+20], EINVAL
mov dword[esp+32], -1
ret
 
 
align 4
socket_send_udp:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: UDP\n"
 
mov [esp+32], ecx
call udp_output
cmp eax, -1
je .error
ret
 
.error:
mov dword[esp+32], -1
mov dword[esp+20], EMSGSIZE ; FIXME: UDP_output should return error codes!
ret
 
 
align 4
socket_send_tcp:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: TCP\n"
 
push eax
add eax, STREAM_SOCKET.snd
call socket_ring_write
pop eax
 
mov [esp+32], ecx
mov [eax + SOCKET.errorcode], 0
push eax
call tcp_output ; FIXME: this doesnt look pretty, does it?
pop eax
mov eax, [eax + SOCKET.errorcode]
mov [esp+20], eax
ret
 
 
align 4
socket_send_ip:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: IPv4\n"
 
mov [esp+32], ecx
call ipv4_output_raw
cmp eax, -1
je .error
ret
 
.error:
mov dword[esp+32], eax
mov dword[esp+20], ebx
ret
 
 
align 4
socket_send_icmp:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: ICMP\n"
 
mov [esp+32], ecx
call icmp_output_raw
cmp eax, -1
je .error
ret
 
.error:
mov dword[esp+32], eax
mov dword[esp+20], ebx
ret
 
 
align 4
socket_send_pppoe:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: PPPoE\n"
 
mov [esp+32], ecx
mov ebx, [eax + SOCKET.device]
 
call pppoe_discovery_output ; FIXME: errorcodes
cmp eax, -1
je .error
ret
 
.error:
mov dword[esp+32], -1
mov dword[esp+20], EMSGSIZE
ret
 
 
 
align 4
socket_send_local:
 
; does this socket have a PID yet?
cmp [eax + SOCKET.PID], 0
jne @f
 
; Change PID to that of current process
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
mov [eax + SOCKET.PID], ebx
mov [eax + SOCKET.TID], ebx ; currently TID = PID in kolibrios :(
@@:
mov [eax + SOCKET.snd_proc], socket_send_local_initialized
 
align 4
socket_send_local_initialized:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_send: LOCAL\n"
 
; get the other side's socket and check if it still exists
mov eax, [eax + SOCKET.device]
call socket_check
jz .invalid
 
; allright, shove in the data!
push eax
add eax, STREAM_SOCKET.rcv
call socket_ring_write
pop eax
 
; return the number of written bytes (or errorcode) to application
mov [esp+32], ecx
 
; and notify the other end
call socket_notify
 
ret
 
.invalid:
mov dword[esp+32], -1
mov dword[esp+20], EINVAL
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_get_opt: Read a socket option ;
; ;
; IN: ecx = socket number ;
; edx = pointer to socket options struct ;
; ;
; OUT: eax = 0 on success ;
; eax = -1 on error ;
; ebx = errorcode on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_get_opt:
 
; FIXME:
; At moment, uses only pseudo-optname -2 for get last_ack_number for TCP.
; TODO: find best way to notify that send()'ed data were acknowledged
; Also pseudo-optname -3 is valid and returns socket state, one of TCPS_*.
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_get_opt\n"
 
call socket_num_to_ptr
test eax, eax
jz .invalid
 
cmp dword [edx], IP_PROTO_TCP
jne .invalid
cmp dword [edx+4], -2
je @f
cmp dword [edx+4], -3
jne .invalid
@@:
; mov eax, [edx+12]
; test eax, eax
; jz .fail
; cmp dword [eax], 4
; mov dword [eax], 4
; jb .fail
; stdcall net_socket_num_to_addr, ecx
; test eax, eax
; jz .fail
; ; todo: check that eax is really TCP socket
; mov ecx, [eax + TCP_SOCKET.last_ack_number]
; cmp dword [edx+4], -2
; jz @f
; mov ecx, [eax + TCP_SOCKET.state]
@@:
mov eax, [edx+8]
test eax, eax
jz @f
mov [eax], ecx
@@:
mov dword [esp+32], 0
ret
 
.invalid:
mov dword[esp+32], -1
mov dword[esp+20], EINVAL
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_set_options: Set a socket option. ;
; ;
; IN: ecx = socket number ;
; edx = pointer to socket options struct ;
; ;
; OUT: eax = 0 on success ;
; eax = -1 on error ;
; ebx = errorcode on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_set_opt:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_set_opt\n"
 
call socket_num_to_ptr
test eax, eax
jz .invalid
 
cmp [edx + socket_options.level], IP_PROTO_IP
je .ip
cmp [edx + socket_options.level], SOL_SOCKET
jne .invalid
 
.socket:
cmp [edx + socket_options.optname], SO_BINDTODEVICE
jne .invalid
 
.bind:
cmp [edx + socket_options.optlen], 0
je .unbind
 
movzx edx, byte[edx + socket_options.optval]
cmp edx, NET_DEVICES_MAX
ja .invalid
 
mov edx, [net_device_list + 4*edx]
test edx, edx
jz .already
mov [eax + SOCKET.device], edx
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_set_opt: Bound socket %x to device %x\n", eax, edx
 
mov dword[esp+32], 0 ; success!
ret
 
.unbind:
mov [eax + SOCKET.device], 0
 
mov dword[esp+32], 0 ; success!
ret
 
.ip:
cmp [edx + socket_options.optname], IP_TTL
jne .invalid
 
.ttl:
mov bl, byte[edx + socket_options.optval]
mov [eax + IP_SOCKET.ttl], bl
 
mov dword[esp+32], 0 ; success!
ret
 
.already:
mov dword[esp+20], EALREADY
mov dword[esp+32], -1
ret
 
.invalid:
mov dword[esp+20], EINVAL
mov dword[esp+32], -1
ret
 
 
 
 
;-----------------------------------------------------------------;
; ;
; socket_pair: Allocate a pair of linked local sockets. ;
; ;
; IN: / ;
; ;
; OUT: eax = socket1 num on success ;
; eax = -1 on error ;
; ebx = socket2 num on success ;
; ebx = errorcode on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_pair:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_pair\n"
 
call socket_alloc
test eax, eax
jz .nomem1
mov [esp+32], edi ; application's eax
 
mov [eax + SOCKET.Domain], AF_LOCAL
mov [eax + SOCKET.Type], SOCK_STREAM
mov [eax + SOCKET.Protocol], 0 ;;; CHECKME
mov [eax + SOCKET.snd_proc], socket_send_local
mov [eax + SOCKET.rcv_proc], socket_receive_local
mov [eax + SOCKET.PID], 0
mov ebx, eax
 
call socket_alloc
test eax, eax
jz .nomem2
mov [esp+20], edi ; application's ebx
 
mov [eax + SOCKET.Domain], AF_LOCAL
mov [eax + SOCKET.Type], SOCK_STREAM
mov [eax + SOCKET.Protocol], 0 ;;; CHECKME
mov [eax + SOCKET.snd_proc], socket_send_local
mov [eax + SOCKET.rcv_proc], socket_receive_local
mov [eax + SOCKET.PID], 0
 
; Link the two sockets to eachother
mov [eax + SOCKET.device], ebx
mov [ebx + SOCKET.device], eax
 
lea eax, [eax + STREAM_SOCKET.rcv]
call socket_ring_create
test eax, eax
jz .nomem2
 
lea eax, [ebx + STREAM_SOCKET.rcv]
call socket_ring_create
test eax, eax
jz .nomem2
 
ret
 
.nomem2:
mov eax, [esp+20]
call socket_free
 
.nomem1:
mov eax, [esp+32]
call socket_free
 
mov dword[esp+32], -1
mov dword[esp+20], ENOMEM
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; socket_debug: Copy socket variables to application buffer. ;
; ;
; IN: ecx = socket number ;
; edx = pointer to application buffer ;
; ;
; OUT: eax = 0 on success ;
; eax = -1 on error ;
; ebx = errorcode on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_debug:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_debug\n"
 
mov edi, edx
 
test ecx, ecx
jz .returnall
 
call socket_num_to_ptr
test eax, eax
jz .invalid
 
mov esi, eax
mov ecx, SOCKET_STRUCT_SIZE/4
rep movsd
 
mov dword[esp+32], 0
ret
 
.returnall:
mov ebx, net_sockets
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
test ebx, ebx
jz .done
mov eax, [ebx + SOCKET.Number]
stosd
jmp .next_socket
.done:
xor eax, eax
stosd
mov dword[esp+32], eax
ret
 
.invalid:
mov dword[esp+32], -1
mov dword[esp+20], EINVAL
ret
 
 
;-----------------------------------------------------------------;
; ____ ____ ;
; \ / End of sockets API \ / ;
; \/ \/ ;
; () Internally used functions follow () ;
; ;
;-----------------------------------------------------------------;
 
 
;-----------------------------------------------------------------;
; ;
; socket_find_port: ;
; Fill in the local port number for TCP and UDP sockets ;
; This procedure always works because the number of sockets is ;
; limited to a smaller number then the number of possible ports ;
; ;
; IN: eax = socket pointer ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
socket_find_port:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_find_port\n"
 
push ebx esi ecx
 
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP
je .udp
 
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
je .tcp
 
pop ecx esi ebx
ret
 
.udp:
mov bx, [last_UDP_port]
call .findit
mov [last_UDP_port], bx
 
pop ecx esi ebx
ret
 
.tcp:
mov bx, [last_TCP_port]
call .findit
mov [last_TCP_port], bx
 
pop ecx esi ebx
ret
 
 
.restart:
mov bx, MIN_EPHEMERAL_PORT_N
.findit:
cmp bx, MAX_EPHEMERAL_PORT_N
je .restart
 
add bh, 1
adc bl, 0
 
call socket_check_port
jz .findit
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; socket_check_port: (to be used with AF_INET only!) ;
; Checks if a local port number is unused ;
; If the proposed port number is unused, it is filled in in the ;
; socket structure. ;
; ;
; IN: eax = socket ptr ;
; bx = proposed socket number (network byte order) ;
; ;
; OUT: ZF = set on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_check_port:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_check_port: "
 
pusha
mov ecx, socket_mutex
call mutex_lock
popa
 
mov ecx, [eax + SOCKET.Protocol]
mov edx, [eax + IP_SOCKET.LocalIP]
mov esi, net_sockets
 
.next_socket:
mov esi, [esi + SOCKET.NextPtr]
or esi, esi
jz .port_ok
 
cmp [esi + SOCKET.Protocol], ecx
jne .next_socket
 
cmp [esi + IP_SOCKET.LocalIP], edx
jne .next_socket
 
cmp [esi + UDP_SOCKET.LocalPort], bx
jne .next_socket
 
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
DEBUGF DEBUG_NETWORK_VERBOSE, "local port %x already in use\n", bx ; FIXME: find a way to print big endian values with debugf
ret
 
.port_ok:
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
DEBUGF DEBUG_NETWORK_VERBOSE, "local port %x is free\n", bx ; FIXME: find a way to print big endian values with debugf
mov [eax + UDP_SOCKET.LocalPort], bx
or bx, bx ; clear the zero-flag
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; socket_input: Update a (stateless) socket with received data. ;
; ;
; Note: The socket's mutex should already be set ! ;
; ;
; IN: eax = socket ptr ;
; ecx = data size ;
; esi = ptr to data ;
; [esp] = ptr to buf ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
socket_input:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_input: socket=%x, data=%x size=%u\n", eax, esi, ecx
 
push ecx
push esi
mov esi, esp
 
add_to_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, sizeof.socket_queue_entry, .full
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_input: success\n"
add esp, sizeof.socket_queue_entry
 
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
popa
 
jmp socket_notify
 
.full:
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_input: socket %x is full!\n", eax
 
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
popa
 
add esp, 8
call net_buff_free
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_ring_create: Create a ringbuffer for sockets. ;
; ;
; IN: eax = ptr to ring struct ;
; ;
; OUT: eax = 0 on error ;
; eax = start ptr ;
; ;
;-----------------------------------------------------------------;
align 4
socket_ring_create:
 
push esi
mov esi, eax
 
push edx
stdcall create_ring_buffer, SOCKET_BUFFER_SIZE, PG_SWR
pop edx
test eax, eax
jz .fail
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_ring_create: %x\n", eax
 
pusha
lea ecx, [esi + RING_BUFFER.mutex]
call mutex_init
popa
 
mov [esi + RING_BUFFER.start_ptr], eax
mov [esi + RING_BUFFER.write_ptr], eax
mov [esi + RING_BUFFER.read_ptr], eax
mov [esi + RING_BUFFER.size], 0
add eax, SOCKET_BUFFER_SIZE
mov [esi + RING_BUFFER.end_ptr], eax
mov eax, esi
 
pop esi
ret
 
.fail:
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_ring_create: Out of memory!\n"
pop esi
ret
 
;-----------------------------------------------------------------;
; ;
; socket_ring_write: Write data to ring buffer. ;
; ;
; IN: eax = ptr to ring struct ;
; ecx = data size ;
; esi = ptr to data ;
; ;
; OUT: ecx = number of bytes stored ;
; ;
;-----------------------------------------------------------------;
align 4
socket_ring_write:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_ring_write: ringbuff=%x ptr=%x size=%u\n", eax, esi, ecx
 
; lock mutex
pusha
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_lock ; TODO: check what registers this function actually destroys
popa
 
; calculate available size
mov edi, SOCKET_BUFFER_SIZE
sub edi, [eax + RING_BUFFER.size] ; available buffer size in edi
cmp ecx, edi
jbe .copy
mov ecx, edi
.copy:
mov edi, [eax + RING_BUFFER.write_ptr]
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_ring_write: %u bytes from %x to %x\n", ecx, esi, edi
 
; update write ptr
push edi
add edi, ecx
cmp edi, [eax + RING_BUFFER.end_ptr]
jb @f
sub edi, SOCKET_BUFFER_SIZE ; WRAP
@@:
mov [eax + RING_BUFFER.write_ptr], edi
pop edi
 
; update size
add [eax + RING_BUFFER.size], ecx
 
; copy the data
push ecx
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
test ecx, ecx
jz .nd
rep movsd
.nd:
pop ecx
 
; unlock mutex
pusha
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_unlock ; TODO: check what registers this function actually destroys
popa
 
ret
 
;-----------------------------------------------------------------;
; ;
; socket_ring_read: Read from ring buffer ;
; ;
; IN: eax = ring struct ptr ;
; ecx = bytes to read ;
; edx = offset ;
; edi = ptr to buffer start ;
; ;
; OUT: eax = unchanged ;
; ecx = number of bytes read (0 on error) ;
; edx = destroyed ;
; esi = destroyed ;
; edi = ptr to buffer end ;
; ;
;-----------------------------------------------------------------;
align 4
socket_ring_read:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_ring_read: ringbuff=%x ptr=%x size=%u offset=%x\n", eax, edi, ecx, edx
 
pusha
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_lock ; TODO: check what registers this function actually destroys
popa
 
mov esi, [eax + RING_BUFFER.read_ptr]
add esi, edx ; esi = start_ptr + offset
 
neg edx
add edx, [eax + RING_BUFFER.size] ; edx = snd.size - offset
jle .no_data_at_all
 
pusha
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_unlock ; TODO: check what registers this function actually destroys
popa
 
cmp ecx, edx
ja .less_data
 
.copy:
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_ring_read: %u bytes from %x to %x\n", ecx, esi, edi
push ecx
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
test ecx, ecx
jz .nd
rep movsd
.nd:
pop ecx
ret
 
.no_data_at_all:
pusha
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_unlock ; TODO: check what registers this function actually destroys
popa
 
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_ring_read: no data at all!\n"
xor ecx, ecx
ret
 
.less_data:
mov ecx, edx
jmp .copy
 
 
;-----------------------------------------------------------------;
; ;
; socket_ring_free: Free data from a ringbuffer. ;
; ;
; IN: eax = ptr to ring struct ;
; ecx = data size ;
; ;
; OUT: ecx = number of freed bytes ;
; ;
;-----------------------------------------------------------------;
align 4
socket_ring_free:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_ring_free: %u bytes from ring %x\n", ecx, eax
 
push eax ecx
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_lock ; TODO: check what registers this function actually destroys
pop ecx eax
 
sub [eax + RING_BUFFER.size], ecx
jb .error
add [eax + RING_BUFFER.read_ptr], ecx
 
mov edx, [eax + RING_BUFFER.end_ptr]
cmp [eax + RING_BUFFER.read_ptr], edx
jb @f
sub [eax + RING_BUFFER.read_ptr], SOCKET_BUFFER_SIZE
@@:
 
push eax ecx
lea ecx, [eax + RING_BUFFER.mutex] ; TODO: check what registers this function actually destroys
call mutex_unlock
pop ecx eax
 
ret
 
.error: ; we could free all available bytes, but that would be stupid, i guess..
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_ring_free: buffer=%x error!\n", eax
add [eax + RING_BUFFER.size], ecx
 
push eax
lea ecx, [eax + RING_BUFFER.mutex]
call mutex_unlock ; TODO: check what registers this function actually destroys
pop eax
 
xor ecx, ecx
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_block: Suspend the thread attached to a socket. ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: eax = unchanged ;
; ;
;-----------------------------------------------------------------;
align 4
socket_block:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_block: %x\n", eax
 
push eax
 
pushf
cli
; Set the 'socket is blocked' flag
or [eax + SOCKET.state], SS_BLOCKED
 
; Suspend the thread
push edx
mov edx, [TASK_BASE]
mov [edx + TASKDATA.state], TSTATE_RUN_SUSPENDED
 
; Remember the thread ID so we can wake it up again
mov edx, [edx + TASKDATA.pid]
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_block: suspending thread: %u\n", edx
mov [eax + SOCKET.TID], edx
pop edx
popf
 
call change_task
pop eax
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_block: continuing\n"
 
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_notify: Wake up socket owner thread. ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: eax = unchanged ;
; ;
;-----------------------------------------------------------------;
align 4
socket_notify:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_notify: %x\n", eax
 
call socket_check
jz .error
 
; Find the associated thread's TASK_DATA
push ebx ecx esi
mov ebx, [eax + SOCKET.TID]
test ebx, ebx
jz .error2
xor ecx, ecx
inc ecx
mov esi, TASK_DATA
.next:
cmp [esi + TASKDATA.pid], ebx
je .found
inc ecx
add esi, sizeof.TASKDATA
cmp ecx, [thread_count]
jbe .next
 
.error2:
; PID not found, TODO: close socket!
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_notify: error finding thread 0x%x !\n", ebx
pop esi ecx ebx
ret
 
.error:
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_notify: invalid socket ptr: 0x%x !\n", eax
ret
 
.found:
test [eax + SOCKET.state], SS_BLOCKED
jnz .un_block
 
; Socket and thread exists and socket is of non blocking type.
; We'll try to flag an event to the thread.
shl ecx, 8
or [SLOT_BASE + ecx + APPDATA.occurred_events], EVENT_NETWORK
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_notify: poking thread %u!\n", ebx
pop esi ecx ebx
ret
 
 
.un_block:
; Socket and thread exists and socket is of blocking type
; We'll try to unblock it.
and [eax + SOCKET.state], not SS_BLOCKED ; Clear the 'socket is blocked' flag
mov [esi + TASKDATA.state], TSTATE_RUNNING ; Run the thread
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_notify: Unblocked socket!\n"
pop esi ecx ebx
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_alloc: Allocate memory for socket and put new socket ;
; into the list. Newly created socket is initialized with calling ;
; PID and given a socket number. ;
; ;
; IN: / ;
; ;
; OUT: eax = socket ptr on success ;
; eax = 0 on error ;
; edi = socket number on success ;
; ;
;-----------------------------------------------------------------;
align 4
socket_alloc:
 
push ebx
 
stdcall kernel_alloc, SOCKET_STRUCT_SIZE
or eax, eax
jz .nomem
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_alloc: ptr=%x\n", eax
 
; zero-initialize allocated memory
push eax
mov edi, eax
mov ecx, SOCKET_STRUCT_SIZE / 4
xor eax, eax
rep stosd
pop eax
 
; set send-and receive procedures to return -1
mov [eax + SOCKET.snd_proc], .not_yet
mov [eax + SOCKET.rcv_proc], .not_yet
 
pusha
mov ecx, socket_mutex
call mutex_lock
popa
 
; find first free socket number and use it
mov edi, [last_socket_num]
.next_socket_number:
inc edi
jz .next_socket_number ; avoid socket nr 0
cmp edi, -1
je .next_socket_number ; avoid socket nr -1
mov ebx, net_sockets
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
test ebx, ebx
jz .last_socket
 
cmp [ebx + SOCKET.Number], edi
jne .next_socket
jmp .next_socket_number
 
.last_socket:
mov [last_socket_num], edi
mov [eax + SOCKET.Number], edi
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_alloc: number=%u\n", edi
 
; Fill in PID
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
mov [eax + SOCKET.PID], ebx
mov [eax + SOCKET.TID], ebx ; currently TID = PID in kolibrios :(
 
; init mutex
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_init
popa
 
; 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
 
test ebx, ebx
jz @f
 
pusha
lea ecx, [ebx + SOCKET.mutex]
call mutex_lock
popa
 
mov [ebx + SOCKET.PrevPtr], eax
 
pusha
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
popa
@@:
 
mov [net_sockets + SOCKET.NextPtr], eax
 
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
pop ebx
 
ret
 
.nomem:
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_alloc: Out of memory!\n"
pop ebx
ret
 
.not_yet:
mov dword[esp+20], ENOTCONN
mov dword[esp+32], -1
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_free: Free socket data memory and remove socket from ;
; the list. Caller should lock and unlock socket_mutex. ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
socket_free:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_free: %x\n", eax
 
call socket_check
jz .error
 
push ebx
 
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
 
cmp [eax + SOCKET.Type], SOCK_STREAM
jne .no_stream
 
mov ebx, eax
cmp [eax + STREAM_SOCKET.rcv.start_ptr], 0
je @f
stdcall free_kernel_space, [eax + STREAM_SOCKET.rcv.start_ptr]
@@:
cmp [ebx + STREAM_SOCKET.snd.start_ptr], 0
je @f
stdcall free_kernel_space, [ebx + STREAM_SOCKET.snd.start_ptr]
@@:
mov eax, ebx
.no_stream:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_free: freeing socket %x\n", eax
push eax ; this will be passed to kernel_free
mov ebx, [eax + SOCKET.NextPtr]
mov eax, [eax + SOCKET.PrevPtr]
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_free: linking socket %x to socket %x\n", eax, ebx
 
test eax, eax
jz @f
mov [eax + SOCKET.NextPtr], ebx
@@:
 
test ebx, ebx
jz @f
mov [ebx + SOCKET.PrevPtr], eax
@@:
 
call kernel_free
pop ebx
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_free: success!\n"
 
.error:
ret
 
.error1:
pop ebx
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_free: error!\n"
DEBUGF DEBUG_NETWORK_ERROR, "socket ptr=0x%x caller=0x%x\n", eax, [esp]
ret
 
;-----------------------------------------------------------------;
; ;
; socket_fork: Create a child socket. ;
; ;
; IN: ebx = socket number ;
; ;
; OUT: eax = child socket number on success ;
; eax = 0 on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_fork:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_fork: %x\n", ebx
 
; Exit if backlog queue is full
mov eax, [ebx + SOCKET_QUEUE_LOCATION + queue.size]
cmp ax, [ebx + SOCKET.backlog]
jae .fail
 
; Allocate new socket
push ebx
call socket_alloc
pop ebx
test eax, eax
jz .fail
 
push eax
mov esi, esp
add_to_queue (ebx + SOCKET_QUEUE_LOCATION), MAX_backlog, 4, .fail2
pop eax
 
; Copy structure from current socket to new
; We start at PID to preserve the socket num, 2 pointers and mutex
; TID will be filled in later
lea esi, [ebx + SOCKET.PID]
lea edi, [eax + SOCKET.PID]
mov ecx, (SOCKET_QUEUE_LOCATION - SOCKET.PID + 3)/4
rep movsd
 
and [eax + SOCKET.options], not SO_ACCEPTCON
 
; Notify owner of parent socket
push eax
mov eax, ebx
call socket_notify
pop eax
 
ret
 
.fail2:
add esp, 4+4+4
.fail:
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_fork: failed\n"
xor eax, eax
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_num_to_ptr: Get socket structure address by its number. ;
; ;
; IN: ecx = socket number ;
; ;
; OUT: eax = socket ptr ;
; eax = 0 on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_num_to_ptr:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_num_to_ptr: num=%u ", ecx
 
pusha
mov ecx, socket_mutex
call mutex_lock
popa
 
mov eax, net_sockets
.next_socket:
mov eax, [eax + SOCKET.NextPtr]
test eax, eax
jz .error
cmp [eax + SOCKET.Number], ecx
jne .next_socket
 
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ptr=%x\n", eax
ret
 
.error:
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_num_to_ptr: socket %u not found!\n", eax
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_num_to_ptr: caller = 0x%x\n", [esp]
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_ptr_to_num: Get socket number by its address. ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: eax = socket number ;
; eax = 0 on error ;
; ZF = set on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_ptr_to_num:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_ptr_to_num: ptr=%x ", eax
 
call socket_check
jz .error
 
mov eax, [eax + SOCKET.Number]
 
DEBUGF DEBUG_NETWORK_VERBOSE, "num=%u\n", eax
ret
 
.error:
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_ptr_to_num: not found\n", eax
ret
 
 
;-----------------------------------------------------------------;
; ;
; socket_check: Checks if the given ptr is really a socket ptr. ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: eax = 0 on error ;
; ZF = set on error ;
; ;
;-----------------------------------------------------------------;
align 4
socket_check:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_check: %x\n", eax
 
test eax, eax
jz .error
push ebx
mov ebx, net_sockets
 
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .done
cmp ebx, eax
jnz .next_socket
 
.done:
mov eax, ebx
test eax, eax
pop ebx
ret
 
.error:
DEBUGF DEBUG_NETWORK_ERROR, "SOCKET_check: called with argument 0\n"
DEBUGF DEBUG_NETWORK_ERROR, "stack: 0x%x, 0x%x, 0x%x\n", [esp], [esp+4], [esp+8]
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; socket_check_owner: Check if the caller app owns the socket. ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: ZF = true/false ;
; ;
;-----------------------------------------------------------------;
align 4
socket_check_owner:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_check_owner: %x\n", eax
 
push ebx
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
cmp [eax + SOCKET.PID], ebx
pop ebx
 
ret
 
 
 
 
;-----------------------------------------------------------------;
; ;
; socket_process_end: Kernel calls this function when a certain ;
; process ends. This function will check if the process had any ;
; open sockets and update them accordingly (clean up). ;
; ;
; IN: edx = pid ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
socket_process_end:
 
ret ; FIXME
 
cmp [net_sockets + SOCKET.NextPtr], 0 ; Are there any active sockets at all?
je .quickret ; nope, exit immediately
 
; TODO: run the following code in another thread, to avoid deadlock
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_process_end: %x\n", edx
 
pusha
mov ecx, socket_mutex
call mutex_lock
popa
 
push ebx
mov ebx, net_sockets
 
.next_socket:
mov ebx, [ebx + SOCKET.NextPtr]
.next_socket_test:
test ebx, ebx
jz .done
 
cmp [ebx + SOCKET.PID], edx
jne .next_socket
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_process_end: killing socket %x\n", ebx
 
mov [ebx + SOCKET.PID], 0
mov eax, ebx
mov ebx, [ebx + SOCKET.NextPtr]
 
pusha
cmp [eax + SOCKET.Domain], AF_INET4
jne .free
 
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
jne .free
 
call tcp_disconnect
jmp .closed
 
.free:
call socket_free
 
.closed:
popa
jmp .next_socket_test
 
.done:
pop ebx
 
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
.quickret:
ret
 
 
 
 
;-----------------------------------------------------------------;
; ;
; socket_is_connecting: Update socket state. ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
socket_is_connecting:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_is_connecting: %x\n", eax
 
and [eax + SOCKET.state], not (SS_ISCONNECTED + SS_ISDISCONNECTING + SS_ISCONFIRMING)
or [eax + SOCKET.state], SS_ISCONNECTING
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; socket_is_connected: Update socket state. ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
socket_is_connected:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_is_connected: %x\n", eax
 
and [eax + SOCKET.state], not (SS_ISCONNECTING + SS_ISDISCONNECTING + SS_ISCONFIRMING)
or [eax + SOCKET.state], SS_ISCONNECTED
jmp socket_notify
 
 
 
 
;-----------------------------------------------------------------;
; ;
; socket_is_disconnecting: Update socket state. ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
socket_is_disconnecting:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_is_disconnecting: %x\n", eax
 
and [eax + SOCKET.state], not (SS_ISCONNECTING)
or [eax + SOCKET.state], SS_ISDISCONNECTING + SS_CANTRCVMORE + SS_CANTSENDMORE
jmp socket_notify
 
 
 
;-----------------------------------------------------------------;
; ;
; socket_is_disconnected: Update socket state. ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
socket_is_disconnected:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_is_disconnected: %x\n", eax
 
and [eax + SOCKET.state], not (SS_ISCONNECTING + SS_ISCONNECTED + SS_ISDISCONNECTING)
or [eax + SOCKET.state], SS_CANTRCVMORE + SS_CANTSENDMORE
jmp socket_notify
 
 
 
;-----------------------------------------------------------------;
; ;
; socket_cant_recv_more: Update socket state. ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
socket_cant_recv_more:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_cant_recv_more: %x\n", eax
 
or [eax + SOCKET.state], SS_CANTRCVMORE
jmp socket_notify
 
 
 
;-----------------------------------------------------------------;
; ;
; socket_cant_send_more: Update socket state. ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
socket_cant_send_more:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "SOCKET_cant_send_more: %x\n", eax
 
or [eax + SOCKET.state], SS_CANTSENDMORE
mov [eax + SOCKET.snd_proc], .notconn
jmp socket_notify
 
.notconn:
mov dword[esp+20], ENOTCONN
mov dword[esp+32], -1
ret
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/loopback.inc
0,0 → 1,176
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2019. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; loopback.inc ;;
;; ;;
;; LoopBack device for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
iglobal
align 4
 
LOOPBACK_DEVICE:
 
.device_type dd NET_DEVICE_LOOPBACK
.mtu dd 4096
.name dd .namestr
 
.unload dd loop_dummy
.reset dd loop_dummy
.transmit dd loop_input
 
.bytes_tx dq 0
.bytes_rx dq 0
.packets_tx dd 0
.packets_rx dd 0
 
.link_state dd -1
.hwacc dd NET_HWACC_TCP_IPv4_IN + NET_HWACC_TCP_IPv4_OUT
 
.namestr db 'loopback', 0
 
endg
 
 
macro loop_init {
local .fail
 
mov ebx, LOOPBACK_DEVICE
call net_add_device
 
cmp eax, -1
je .fail
 
mov [IPv4_address], 127 + 1 shl 24
mov [IPv4_subnet], 255
mov [IPv4_broadcast], 0xffffff00 + 127
 
.fail:
}
 
;-----------------------------------------------------------------;
; ;
; loop_dummy ;
; ;
; IN: / ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
loop_dummy:
ret
 
;-----------------------------------------------------------------;
; ;
; loop_input ;
; ;
; IN: [esp+4] = Pointer to buffer ;
; ;
; OUT: eax = 0 on success, errorcode otherwise ;
; ;
;-----------------------------------------------------------------;
align 4
loop_input:
 
mov eax, [esp+4]
 
; Update stats
inc [LOOPBACK_DEVICE.packets_tx]
inc [LOOPBACK_DEVICE.packets_rx]
 
mov ecx, [eax + NET_BUFF.length]
add dword[LOOPBACK_DEVICE.bytes_rx], ecx
adc dword[LOOPBACK_DEVICE.bytes_rx + 4], 0
add dword[LOOPBACK_DEVICE.bytes_tx], ecx
adc dword[LOOPBACK_DEVICE.bytes_tx + 4], 0
 
DEBUGF DEBUG_NETWORK_VERBOSE, "LOOP_input: ptr=%x size=%u\n", eax, ecx
 
; Reverse buffptr and returnaddr on stack
pop edx edi
push edx .done edi
 
; Set registers for protocol handlers
lea edx, [eax + NET_BUFF.data]
mov ebx, [eax + NET_BUFF.device]
mov eax, [eax + NET_BUFF.type]
 
; Place protocol handlers here
cmp eax, AF_INET4
je ipv4_input
 
DEBUGF DEBUG_NETWORK_VERBOSE, "LOOP_input: Unknown packet type=%x\n", eax
 
.dump:
DEBUGF DEBUG_NETWORK_VERBOSE, "LOOP_input: dumping\n"
call net_buff_free
 
or eax, -1
ret
 
.done:
xor eax, eax
ret
 
 
;-----------------------------------------------------------------;
; ;
; loop_output ;
; ;
; IN: ecx = packet size ;
; edi = address family ;
; ;
; OUT: eax = start of net frame / 0 on error ;
; ebx = to device structure ;
; ecx = unchanged (packet size of embedded data) ;
; edi = start of payload ;
; ;
;-----------------------------------------------------------------;
align 4
loop_output:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "LOOP_output\n"
 
cmp ecx, [LOOPBACK_DEVICE.mtu]
ja .too_large
 
push ecx edi
add ecx, NET_BUFF.data
stdcall net_buff_alloc, ecx
test eax, eax
jz .out_of_ram
 
pop edi
mov [eax + NET_BUFF.type], edi
mov ebx, LOOPBACK_DEVICE
mov [eax + NET_BUFF.device], ebx
pop ecx
mov [eax + NET_BUFF.length], ecx
lea edi, [eax + NET_BUFF.data]
 
DEBUGF DEBUG_NETWORK_VERBOSE, "LOOP_output: ptr=%x size=%u\n", eax, ecx
ret
 
.too_large:
DEBUGF DEBUG_NETWORK_ERROR, "LOOP_output: packet is too large\n"
xor eax, eax
ret
 
.out_of_ram:
DEBUGF DEBUG_NETWORK_ERROR, "LOOP_output: out of memory\n"
add esp, 4+4
xor eax, eax
ret
 
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/tcp_output.inc
0,0 → 1,754
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2020. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Based on the code of 4.4BSD ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
TCP_BIT_SENDALOT = 1 shl 0
 
;-----------------------------------------------------------------;
; ;
; tcp_output ;
; ;
; IN: eax = socket pointer ;
; ;
; OUT: eax = 0 on success/errorcode ;
; ;
;-----------------------------------------------------------------;
align 4
proc tcp_output
 
locals
temp_bits db ?
rcv_window dd ?
endl
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: socket=%x state=%u\n", eax, [eax + TCP_SOCKET.t_state]
 
push eax
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
pop eax
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: socket locked\n"
 
; We'll detect the length of the data to be transmitted, and flags to be used
; If there is some data, or any critical controls to send (SYN / RST), then transmit
; Otherwise, investigate further
 
mov ebx, [eax + TCP_SOCKET.SND_MAX]
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
jbe .not_idle
 
mov ebx, [eax + TCP_SOCKET.t_idle]
cmp ebx, [eax + TCP_SOCKET.t_rxtcur]
jbe .not_idle
 
; We have been idle for a while and no ACKS are expected to clock out any data we send..
; Slow start to get ack "clock" running again.
 
mov ebx, [eax + TCP_SOCKET.t_maxseg]
mov [eax + TCP_SOCKET.SND_CWND], ebx
 
.not_idle:
.again:
mov [temp_bits], 0
 
; Calculate offset
 
mov ebx, [eax + TCP_SOCKET.SND_NXT]
sub ebx, [eax + TCP_SOCKET.SND_UNA]
 
; Determine window
 
mov ecx, [eax + TCP_SOCKET.SND_WND]
cmp ecx, [eax + TCP_SOCKET.SND_CWND]
jb @f
mov ecx, [eax + TCP_SOCKET.SND_CWND]
@@:
 
; get flags in dl
 
call tcp_outflags
 
;------------------------
; data being forced out ?
 
; If in persist timeout with window of 0, send 1 byte.
; Otherwise, if window is small but nonzero, and timer expired,
; we will send what we can and go to transmit state
 
test [eax + TCP_SOCKET.t_flags], TF_FORCE
jz .no_force
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: forcing data out\n"
 
test ecx, ecx
jnz .no_zero_window
 
cmp ebx, [eax + STREAM_SOCKET.snd.size]
jae @f
and dl, not (TH_FIN)
@@:
 
inc ecx
jmp .no_force
 
.no_zero_window:
and [eax + TCP_SOCKET.timer_flags], not timer_flag_persist
mov [eax + TCP_SOCKET.t_rxtshift], 0
 
.no_force:
 
;--------------------------------
; Calculate how much data to send
 
mov esi, [eax + STREAM_SOCKET.snd.size]
cmp esi, ecx
jb @f
mov esi, ecx
@@:
sub esi, ebx
 
;------------------------
; check for window shrink
 
; If FIN has been sent, but not ACKed, but we havent been called to retransmit, esi will be -1
; Otherwise, window shrank after we sent into it.
 
jge .not_persist
 
; enter persist state
 
xor esi, esi
 
; If window shrank to 0
 
test ecx, ecx
jnz @f
 
; cancel pending retransmit
 
and [eax + TCP_SOCKET.timer_flags], not timer_flag_retransmission
 
; pull SND_NXT back to (closed) window, We will enter persist state below.
 
push [eax + TCP_SOCKET.SND_UNA]
pop [eax + TCP_SOCKET.SND_NXT]
@@:
 
; If window didn't close completely, just wait for an ACK
 
.not_persist:
 
;---------------------------
; Send one segment at a time
 
cmp esi, [eax + TCP_SOCKET.t_maxseg]
jbe @f
mov esi, [eax + TCP_SOCKET.t_maxseg]
or [temp_bits], TCP_BIT_SENDALOT
@@:
 
;--------------------------------------------
; Turn of FIN flag if send buffer not emptied
 
mov edi, [eax + TCP_SOCKET.SND_NXT]
add edi, esi
sub edi, [eax + TCP_SOCKET.SND_UNA]
cmp edi, [eax + STREAM_SOCKET.snd.size]
jae @f
and dl, not (TH_FIN)
@@:
 
;-------------------------------
; calculate window advertisement
 
xor ecx, ecx
test [eax + SOCKET.state], SS_CANTRCVMORE
jnz @f
mov ecx, SOCKET_BUFFER_SIZE
sub ecx, [eax + STREAM_SOCKET.rcv.size]
@@:
 
;------------------------------
; Sender silly window avoidance
 
test esi, esi
jz .len_zero
 
cmp esi, [eax + TCP_SOCKET.t_maxseg]
je .send
 
add ebx, esi ; offset + length
cmp ebx, [eax + STREAM_SOCKET.snd.size]
jb @f
 
test [eax + TCP_SOCKET.t_flags], TF_NODELAY
jnz .send
 
mov ebx, [eax + TCP_SOCKET.SND_MAX]
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
je .send
@@:
 
test [eax + TCP_SOCKET.t_flags], TF_FORCE
jnz .send
 
mov ebx, [eax + TCP_SOCKET.max_sndwnd]
shr ebx, 1
cmp esi, ebx
jae .send
 
mov ebx, [eax + TCP_SOCKET.SND_NXT]
cmp ebx, [eax + TCP_SOCKET.SND_MAX]
jb .send
 
.len_zero:
 
;----------------------------------------
; Check if a window update should be sent
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: window=%d\n", ecx
 
; Compare available window to amount of window known to peer (as advertised window less next expected input)
; If the difference is at least two max size segments, or at least 50% of the maximum possible window,
; Then we want to send a window update to the peer.
 
test ecx, ecx
jz .no_window
 
push ecx
mov cl, [eax + TCP_SOCKET.RCV_SCALE]
mov ebx, TCP_max_win
shl ebx, cl
pop ecx
sub ebx, [eax + TCP_SOCKET.RCV_ADV]
add ebx, [eax + TCP_SOCKET.RCV_NXT]
 
cmp ebx, ecx
jl @f
mov ebx, ecx
@@:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: we can increase window by %d bytes\n", ebx
 
mov edi, [eax + TCP_SOCKET.t_maxseg]
shl edi, 1
cmp ebx, edi
jae .send
 
cmp ebx, SOCKET_BUFFER_SIZE/2
jae .send
 
.no_window:
 
;--------------------------
; Should a segment be sent?
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: Should a segment be sent?\n"
 
test [eax + TCP_SOCKET.t_flags], TF_ACKNOW ; we need to ACK
jnz .send
 
test dl, TH_SYN + TH_RST ; we need to send a SYN or RST
jnz .send
 
mov ebx, [eax + TCP_SOCKET.SND_UP] ; when urgent pointer is beyond start of send bufer
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
ja .send
 
; Do we need to send a FIN according to our state?
 
test dl, TH_FIN
jz .enter_persist ; no reason to send, enter persist state
 
; Do so if we didnt do it already
 
test [eax + TCP_SOCKET.t_flags], TF_SENTFIN
jz .send
 
; Or when we need to retransmit the FIN
 
mov ebx, [eax + TCP_SOCKET.SND_NXT]
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
je .send
 
;--------------------
; Enter persist state
 
.enter_persist:
 
cmp [eax + STREAM_SOCKET.snd.size], 0 ; Data ready to send?
je @f
test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission or timer_flag_persist
jnz @f
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: Entering persist state\n"
 
mov [eax + TCP_SOCKET.t_rxtshift], 0
call tcp_set_persist
@@:
 
;----------------------------
; No reason to send a segment
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: No reason to send a segment\n"
 
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
popa
 
xor eax, eax
ret
 
 
;-----------------------------------------------
;
; Send a segment
;
; eax = socket pointer
; esi = data len
; dl = flags
;
;-----------------------------------------------
.send:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: socket=%x length=%u flags=%x\n", eax, esi, dl
 
push eax ; save socket ptr
push esi ; and data length too
mov edi, sizeof.TCP_header ; edi will contain headersize
 
;------------------------------------
; Send options with first SYN segment
 
test dl, TH_SYN
jz .options_done
 
push [eax + TCP_SOCKET.ISS]
pop [eax + TCP_SOCKET.SND_NXT]
 
test [eax + TCP_SOCKET.t_flags], TF_NOOPT
jnz .options_done
 
mov ecx, 1460 ;;;; FIXME: use routing blablabla to determine MSS
or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16
bswap ecx
push ecx
add di, 4
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added maxseg option\n"
 
test [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE
jz .no_scale
 
test dl, TH_ACK
jz .scale_opt
 
test [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE
jz .no_scale
 
.scale_opt:
mov cl, [eax + TCP_SOCKET.request_r_scale]
mov ch, TCP_OPT_NOP
pushw cx
pushw TCP_OPT_WINDOW + 3 shl 8
add di, 4
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added scale option\n"
 
.no_scale:
.no_syn:
 
;------------------------------------
; Make the timestamp option if needed
 
test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP
jz .no_timestamp
 
test dl, TH_RST
jnz .no_timestamp
 
test dl, TH_ACK
jz .timestamp
 
test [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
jz .no_timestamp
 
.timestamp:
pushd 0
pushd [timer_ticks]
pushd TCP_OPT_NOP + TCP_OPT_NOP shl 8 + TCP_OPT_TIMESTAMP shl 16 + 10 shl 24
add di, 12
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: added timestamp\n"
 
.no_timestamp:
 
; <Add additional options here>
 
.options_done:
 
; eax = socket ptr
; edx = flags
; edi = header size
; esi = data len
 
;---------------------------------------------
; check if we dont exceed the max segment size
 
add esi, edi ; total TCP segment size
cmp esi, [eax + TCP_SOCKET.t_maxseg]
jbe .no_overflow
 
mov esi, [eax + TCP_SOCKET.t_maxseg]
or [temp_bits], TCP_BIT_SENDALOT
.no_overflow:
 
; Update stats
 
test esi, esi
jz .zero_data
 
test [eax + TCP_SOCKET.t_flags], TF_FORCE
jz @f
cmp esi, 1
jne @f
inc [TCPS_sndprobe]
jmp .eos
@@:
 
mov ebx, [eax + TCP_SOCKET.SND_NXT]
cmp ebx, [eax + TCP_SOCKET.SND_MAX]
jae @f
inc [TCPS_sndrexmitpack]
add [TCPS_sndrexmitbyte], esi
jmp .eos
@@:
inc [TCPS_sndpack]
add [TCPS_sndbyte], esi
jmp .eos
 
.zero_data:
test [eax + TCP_SOCKET.t_flags], TF_ACKNOW
jz @f
inc [TCPS_sndacks]
jmp .eos
@@:
test dl, TH_SYN + TH_FIN + TH_RST
jz @f
inc [TCPS_sndctrl]
jmp .eos
@@:
mov ebx, [eax + TCP_SOCKET.SND_UP]
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
jb @f
inc [TCPS_sndurg]
jmp .eos
@@:
inc [TCPS_sndwinup]
 
.eos:
 
;---------------------------------------------------
; Dont increase sequence number when resending a FIN
 
test dl, TH_FIN
jz .no_fin_retransmit
 
test [eax + TCP_SOCKET.t_flags], TF_SENTFIN
jz .no_fin_retransmit
 
mov ebx, [eax + TCP_SOCKET.SND_NXT]
cmp ebx, [eax + TCP_SOCKET.SND_MAX]
jne .no_fin_retransmit
 
dec [eax + TCP_SOCKET.SND_NXT]
 
.no_fin_retransmit:
 
;----------------------------------------------------
; Calculate the receive window.
; Dont shrink window, but avoid silly window syndrome
 
xor ebx, ebx
test [eax + SOCKET.state], SS_CANTRCVMORE
jnz @f
mov ebx, SOCKET_BUFFER_SIZE
sub ebx, [eax + STREAM_SOCKET.rcv.size]
 
cmp ebx, SOCKET_BUFFER_SIZE/4
jge @f
cmp ebx, [eax + TCP_SOCKET.t_maxseg]
jge @f
xor ebx, ebx
@@:
 
 
mov cl, [eax + TCP_SOCKET.RCV_SCALE]
push eax
mov eax, TCP_max_win
shl eax, cl
cmp ebx, eax
jle @f
mov ebx, eax
@@:
pop eax
 
 
mov ecx, [eax + TCP_SOCKET.RCV_ADV]
sub ecx, [eax + TCP_SOCKET.RCV_NXT]
cmp ebx, ecx
jg @f
mov ebx, ecx
@@:
 
;; TODO URGENT POINTER
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: window=%u\n", ebx
mov [rcv_window], ebx
 
mov cl, [eax + TCP_SOCKET.RCV_SCALE]
shr ebx, cl
xchg bl, bh
 
;-----------------------------------------------------------------
; Start by pushing all TCP header values in reverse order on stack
; (essentially, creating the tcp header on the stack!)
 
pushw 0 ; UrgentPointer
pushw 0 ; Checksum
pushw bx ; Window
shl edi, 2 ; DataOffset
shl dx, 8
or dx, di ; Flags
pushw dx
shr edi, 2 ; DataOffset
 
push [eax + TCP_SOCKET.RCV_NXT] ; AckNumber
ntohd [esp]
 
push [eax + TCP_SOCKET.SND_NXT] ; SequenceNumber
ntohd [esp]
 
push [eax + TCP_SOCKET.RemotePort] ; DestinationPort
push [eax + TCP_SOCKET.LocalPort] ; SourcePort
 
push edi ; header size
 
;---------------------
; Create the IP packet
 
mov ecx, esi
mov ebx, [eax + IP_SOCKET.device]
mov edx, [eax + IP_SOCKET.LocalIP] ; source ip
mov edi, [eax + IP_SOCKET.RemoteIP] ; dest ip
mov al, [eax + IP_SOCKET.ttl]
mov ah, IP_PROTO_TCP
call ipv4_output
jz .ip_error
 
;------------------------------------------
; Move TCP header from stack to TCP segment
 
push ecx
mov ecx, [esp + 4]
lea esi, [esp + 8]
shr ecx, 2 ; count is in bytes, we will work with dwords
rep movsd
pop ecx ; full TCP packet size
 
pop esi ; headersize
add esp, esi ; remove it from stack
 
push eax ; packet ptr for send proc
 
mov edx, edi ; begin of data
sub edx, esi ; begin of packet (edi = begin of data)
push ecx
sub ecx, esi ; data size
 
;--------------
; Copy the data
 
; eax = ptr to ring struct
; ecx = buffer size
; edi = ptr to buffer
 
mov eax, [esp + 12] ; get socket ptr
 
push edx
push [eax + TCP_SOCKET.SND_NXT] ; we'll need this for timing the transmission
test ecx, ecx
jz .nodata
mov edx, [eax + TCP_SOCKET.SND_NXT]
add [eax + TCP_SOCKET.SND_NXT], ecx ; update sequence number
sub edx, [eax + TCP_SOCKET.SND_UNA] ; offset
add eax, STREAM_SOCKET.snd
call socket_ring_read
.nodata:
pop edi
pop esi ; begin of data
pop ecx ; full packet size
mov eax, [esp + 8] ; socket ptr
 
;----------------------------
; initialize retransmit timer
 
;TODO: check t_force and persist
 
test [esi + TCP_header.Flags], TH_SYN + TH_FIN ; syn and fin take a sequence number
jz @f
inc [eax + TCP_SOCKET.SND_NXT]
test [esi + TCP_header.Flags], TH_FIN
jz @f
or [eax + TCP_SOCKET.t_flags], TF_SENTFIN ; if we sent a fin, set the sentfin flag
@@:
 
mov edx, [eax + TCP_SOCKET.SND_NXT]
cmp edx, [eax + TCP_SOCKET.SND_MAX] ; is this a retransmission?
jbe @f
mov [eax + TCP_SOCKET.SND_MAX], edx ; [eax + TCP_SOCKET.SND_NXT] from before we updated it
 
cmp [eax + TCP_SOCKET.t_rtt], 0 ; are we currently timing anything?
je @f
mov [eax + TCP_SOCKET.t_rtt], 1 ; nope, start transmission timer
mov [eax + TCP_SOCKET.t_rtseq], edi
inc [TCPS_segstimed]
@@:
 
; set retransmission timer if not already set, and not doing an ACK or keepalive probe
 
test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission
jnz .retransmit_set
 
cmp edx, [eax + TCP_SOCKET.SND_UNA] ; edx is still [eax + TCP_SOCKET.SND_NXT]
je .retransmit_set
 
mov edx, [eax + TCP_SOCKET.t_rxtcur]
mov [eax + TCP_SOCKET.timer_retransmission], edx
or [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission
 
test [eax + TCP_SOCKET.timer_flags], timer_flag_persist
jz .retransmit_set
and [eax + TCP_SOCKET.timer_flags], not timer_flag_persist
mov [eax + TCP_SOCKET.t_rxtshift], 0
.retransmit_set:
 
;--------------------
; Create the checksum
 
xor dx, dx
test [ebx + NET_DEVICE.hwacc], NET_HWACC_TCP_IPv4_OUT
jnz .checksum_ok
 
tcp_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP)
 
.checksum_ok:
mov [esi + TCP_header.Checksum], dx
 
;----------------
; Send the packet
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: Sending with device %x\n", ebx
call [ebx + NET_DEVICE.transmit]
jnz .send_error
 
;---------------
; Ok, data sent!
 
pop ecx
pop eax
 
call net_ptr_to_num4
inc [TCP_segments_tx + edi]
inc [TCPS_sndtotal]
 
; update advertised receive window
 
mov ecx, [rcv_window]
test ecx, ecx
jz @f
add ecx, [eax + TCP_SOCKET.RCV_NXT]
cmp ecx, [eax + TCP_SOCKET.RCV_ADV]
jbe @f
mov [eax + TCP_SOCKET.RCV_ADV], ecx
@@:
 
; update last ack sent
 
push [eax + TCP_SOCKET.RCV_NXT]
pop [eax + TCP_SOCKET.last_ack_sent]
 
; clear the ACK flags
 
and [eax + TCP_SOCKET.t_flags], not (TF_ACKNOW + TF_DELACK)
 
;--------------
; unlock socket
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: unlocking socket 0x%x\n", eax
 
push eax
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
pop eax
 
;-----------------------------
; Check if we need more output
 
test [temp_bits], TCP_BIT_SENDALOT
jnz tcp_output.again
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_send: success!\n"
 
xor eax, eax
ret
 
 
.ip_error:
pop ecx
add esp, ecx
add esp, 4
pop eax
 
mov [eax + TCP_SOCKET.timer_retransmission], TCP_time_re_min
or [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission
 
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
 
DEBUGF DEBUG_NETWORK_ERROR, "TCP_send: IP error\n"
 
or eax, -1
ret
 
 
.send_error:
add esp, 4
pop eax
 
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
 
DEBUGF DEBUG_NETWORK_ERROR, "TCP_send: sending failed\n"
 
or eax, -2
ret
 
 
endp
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/tcp_subr.inc
0,0 → 1,619
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2020. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Based on the code of 4.4BSD ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
align 4
iglobal
TCP_backoff db 0,1,2,3,4,5,6,6,6,6,6,6,6
endg
 
macro tcp_checksum IP1, IP2 {
 
;-------------
; Pseudoheader
 
; protocol type
mov edx, IP_PROTO_TCP
 
; source address
add dl, byte [IP1+1]
adc dh, byte [IP1+0]
adc dl, byte [IP1+3]
adc dh, byte [IP1+2]
 
; destination address
adc dl, byte [IP2+1]
adc dh, byte [IP2+0]
adc dl, byte [IP2+3]
adc dh, byte [IP2+2]
 
; size
adc dl, cl
adc dh, ch
 
adc edx, 0
 
;---------------------
; Real header and data
 
push esi
call checksum_1
call checksum_2
pop esi
 
} ; returns in dx only
 
 
 
 
macro tcp_sendseqinit ptr {
 
push edi ;;;; FIXME: i dont like this static use of edi
mov edi, [ptr + TCP_SOCKET.ISS]
mov [ptr + TCP_SOCKET.SND_UP], edi
mov [ptr + TCP_SOCKET.SND_MAX], edi
mov [ptr + TCP_SOCKET.SND_NXT], edi
mov [ptr + TCP_SOCKET.SND_UNA], edi
pop edi
 
}
 
 
 
macro tcp_rcvseqinit ptr {
 
push edi
mov edi, [ptr + TCP_SOCKET.IRS]
inc edi ; SYN ocupies a sequence number
mov [ptr + TCP_SOCKET.RCV_NXT], edi
mov [ptr + TCP_SOCKET.RCV_ADV], edi
pop edi
 
}
 
 
 
macro tcp_init_socket socket {
 
; new tcp control block
 
mov [socket + TCP_SOCKET.t_maxseg], TCP_mss_default
mov [socket + TCP_SOCKET.t_flags], TF_REQ_SCALE or TF_REQ_TSTMP
 
mov [socket + TCP_SOCKET.t_srtt], TCP_time_srtt_default
mov [socket + TCP_SOCKET.t_rttvar], TCP_time_rtt_default * 4
mov [socket + TCP_SOCKET.t_rttmin], TCP_time_re_min
;;; TODO: TCP_time_rangeset
 
mov [socket + TCP_SOCKET.SND_CWND], TCP_max_win shl TCP_max_winshift
mov [socket + TCP_SOCKET.SND_SSTHRESH], TCP_max_win shl TCP_max_winshift
 
mov [socket + TCP_SOCKET.RCV_SCALE], 0
mov [socket + TCP_SOCKET.SND_SCALE], 0
 
}
 
 
;-----------------------------------------------------------------;
; ;
; tcp_pull_out_of_band ;
; ;
; IN: eax = ? ;
; ebx = socket ptr ;
; edx = tcp packet ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_pull_out_of_band:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "tcp_pull_out_of_band\n"
 
;;;; 1282-1305
 
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; tcp_drop ;
; ;
; IN: eax = socket ptr ;
; ebx = error number ;
; ;
; OUT: eax = socket ptr ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_drop:
 
;;; TODO: check if error code is "Connection timed out' and handle accordingly
 
DEBUGF DEBUG_NETWORK_VERBOSE, "tcp_drop: %x\n", eax
 
cmp [eax + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED
jb .no_syn_received
 
mov [eax + TCP_SOCKET.t_state], TCPS_CLOSED
 
push eax
call tcp_output
pop eax
 
inc [TCPS_drops]
 
mov [eax + SOCKET.errorcode], ebx
jmp tcp_close
 
.no_syn_received:
inc [TCPS_conndrops]
 
mov [eax + SOCKET.errorcode], ebx
jmp tcp_close
 
 
;-----------------------------------------------------------------;
; ;
; tcp_disconnect ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: eax = socket ptr / 0 ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_disconnect:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_disconnect: %x\n", eax
 
cmp [eax + TCP_SOCKET.t_state], TCPS_ESTABLISHED
jb tcp_close ; Connection not yet synchronised, just get rid of the socket
 
test [eax + SOCKET.options], SO_LINGER
jz .nolinger
 
; TODO: implement LINGER
; cmp [eax + SOCKET.so_linger], 0
; je TCP_drop
 
.nolinger:
call socket_is_disconnecting
 
push eax
add eax, STREAM_SOCKET.rcv
mov ecx, [eax + RING_BUFFER.size]
call socket_ring_free
pop eax
 
call tcp_usrclosed
 
test eax, eax
jz @f
push eax
call tcp_output
pop eax
@@:
ret
 
 
;-----------------------------------------------------------------;
; ;
; tcp_close ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_close:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_close: %x\n", eax
 
;;; TODO: update RTT and mean deviation
;;; TODO: update slow start threshold
 
call socket_is_disconnected
call socket_free
 
inc [TCPS_closed]
 
xor eax, eax
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; tcp_outflags ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: edx = flags ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_outflags:
 
mov edx, [eax + TCP_SOCKET.t_state]
movzx edx, byte[edx + .flaglist]
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_outflags: socket=%x flags=%x\n", eax, dl
 
ret
 
.flaglist:
db TH_RST + TH_ACK ; TCPS_CLOSED
db 0 ; TCPS_LISTEN
db TH_SYN ; TCPS_SYN_SENT
db TH_SYN + TH_ACK ; TCPS_SYN_RECEIVED
db TH_ACK ; TCPS_ESTABLISHED
db TH_ACK ; TCPS_CLOSE_WAIT
db TH_FIN + TH_ACK ; TCPS_FIN_WAIT_1
db TH_FIN + TH_ACK ; TCPS_CLOSING
db TH_FIN + TH_ACK ; TCPS_LAST_ACK
db TH_ACK ; TCPS_FIN_WAIT_2
db TH_ACK ; TCPS_TIME_WAIT
 
 
;-----------------------------------------------------------------;
; ;
; TCP_respond: Fast way to send an ACK/RST/keepalive segment. ;
; ;
; IN: ebx = socket ptr ;
; cl = flags ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_respond:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_respond_socket: socket=%x flags=%x\n", ebx, cl
 
;---------------------
; Create the IP packet
 
push cx ebx
mov edx, [ebx + IP_SOCKET.LocalIP]
mov edi, [ebx + IP_SOCKET.RemoteIP]
mov al, [ebx + IP_SOCKET.ttl]
mov ah, IP_PROTO_TCP
mov ecx, sizeof.TCP_header
mov ebx, [ebx + IP_SOCKET.device]
call ipv4_output
jz .error
pop esi cx
push eax
 
;-----------------------------------------------
; Fill in the TCP header by using the socket ptr
 
mov ax, [esi + TCP_SOCKET.LocalPort]
stosw
mov ax, [esi + TCP_SOCKET.RemotePort]
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 (TCP_header.DataOffset)
stosb
mov al, cl
stosb
mov eax, SOCKET_BUFFER_SIZE
sub eax, [esi + STREAM_SOCKET.rcv.size]
cmp eax, TCP_max_win
jbe .lessthanmax
mov eax, TCP_max_win
.lessthanmax:
mov cl, [esi + TCP_SOCKET.RCV_SCALE]
shr eax, cl
 
xchg al, ah
stosw ; window
xor eax, eax
stosd ; checksum + urgentpointer
 
;---------------------
; Fill in the checksum
 
.checksum:
sub edi, sizeof.TCP_header
mov ecx, sizeof.TCP_header
xchg esi, edi
tcp_checksum (edi + IP_SOCKET.LocalIP), (edi + IP_SOCKET.RemoteIP)
mov [esi+TCP_header.Checksum], dx
 
;--------------------
; And send the segment
 
call [ebx + NET_DEVICE.transmit]
test eax, eax
jnz @f
call net_ptr_to_num4
inc [TCP_segments_tx + edi]
@@:
ret
 
.error:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_respond_socket: failed\n"
add esp, 2 + 4
 
ret
 
 
;-----------------------------------------------------------------;
; ;
; tcp_respond_segment ;
; ;
; IN: ebx = device ptr ;
; edx = segment ptr (a previously received segment) ;
; edi = ptr to IPv4 header ;
; cl = flags ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_respond_segment:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_respond_segment: frame=%x flags=%x\n", edx, cl
 
;---------------------
; Create the IP packet
 
push cx edx
mov edx, [edi + IPv4_header.DestinationAddress]
mov edi, [edi + IPv4_header.SourceAddress]
mov ecx, sizeof.TCP_header
mov ax, IP_PROTO_TCP shl 8 + 128
call ipv4_output
jz .error
pop esi cx
 
push eax
 
;---------------------------------------------------
; Fill in the TCP header by using a received segment
 
mov ax, [esi + TCP_header.DestinationPort]
stosw
mov ax, [esi + TCP_header.SourcePort]
stosw
mov eax, [esi + TCP_header.AckNumber]
bswap eax
stosd
xor eax, eax
stosd
mov al, 0x50 ; Dataoffset: 20 bytes (sizeof.TCP_header/4 shl 4)
stosb
mov al, cl
stosb
mov ax, 1280
rol ax, 8
stosw ; window
xor eax, eax
stosd ; checksum + urgentpointer
 
;---------------------
; Fill in the checksum
 
lea esi, [edi - sizeof.TCP_header]
mov ecx, sizeof.TCP_header
tcp_checksum (esi - sizeof.IPv4_header + IPv4_header.DestinationAddress),\ ; FIXME
(esi - sizeof.IPv4_header + IPv4_header.SourceAddress)
mov [esi + TCP_header.Checksum], dx
 
;--------------------
; And send the segment
 
call [ebx + NET_DEVICE.transmit]
test eax, eax
jnz @f
call net_ptr_to_num4
inc [TCP_segments_tx + edi]
@@:
ret
 
.error:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_respond_segment: failed\n"
add esp, 2+4
 
ret
 
 
macro tcpt_rangeset timer, value, min, max {
 
local .min
local .max
local .done
 
cmp value, min
jb .min
cmp value, max
ja .max
mov timer, value
jmp .done
.min:
mov timer, min
jmp .done
.max:
mov timer, max
.done:
}
 
;-----------------------------------------------------------------;
; ;
; tcp_set_persist ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_set_persist:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_set_persist\n"
 
; First, check if retransmit timer is not set, retransmit and persist are mutually exclusive
 
test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission
jnz .exit
 
; calculate RTO
push ebx
mov ebx, [eax + TCP_SOCKET.t_srtt]
shr ebx, 2
add ebx, [eax + TCP_SOCKET.t_rttvar]
shr ebx, 1
 
mov cl, [eax + TCP_SOCKET.t_rxtshift]
shl ebx, cl
 
; Start/restart persistance timer.
 
tcpt_rangeset [eax + TCP_SOCKET.timer_persist], ebx, TCP_time_pers_min, TCP_time_pers_max
or [ebx + TCP_SOCKET.timer_flags], timer_flag_persist
pop ebx
 
cmp [eax + TCP_SOCKET.t_rxtshift], TCP_max_rxtshift
jae @f
inc [eax + TCP_SOCKET.t_rxtshift]
@@:
.exit:
 
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; tcp_xmit_timer: Calculate new smoothed RTT. ;
; ;
; IN: eax = rtt ;
; ebx = socket ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_xmit_timer:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_xmit_timer: socket=0x%x rtt=%d0ms\n", ebx, eax
 
inc [TCPS_rttupdated]
 
cmp [ebx + TCP_SOCKET.t_rtt], 0
je .no_rtt_yet
 
; srtt is stored as a fixed point with 3 bits after the binary point.
; The following magic is equivalent of the smoothing algorithm in rfc793 with an alpha of .875
; (srtt = rtt/8 + srtt*7/8 in fixed point)
; Adjust rtt to origin 0.
 
push ecx
mov ecx, [ebx + TCP_SOCKET.t_srtt]
shr ecx, TCP_RTT_SHIFT
sub eax, ecx
dec eax
pop ecx
 
add [ebx + TCP_SOCKET.t_srtt], eax
ja @f
mov [ebx + TCP_SOCKET.t_srtt], 1
@@:
 
; We accumulate a smoothed rtt variance (actually, a smoothed mean difference),
; then set the retransmit timer to smoothed rtt + 4 times the smoothed variance.
; rttvar is stored as fixed point with 2 bits after the binary point.
; The following is equivalent to rfc793 smoothing with an alpha of .75
; (rttvar = rttvar*3/4 + delta/4) (delta = eax)
 
; get abs(eax)
push edx
cdq
xor eax, edx
sub eax, edx
 
mov edx, [ebx + TCP_SOCKET.t_rttvar]
shr edx, TCP_RTTVAR_SHIFT
sub eax, edx
pop edx
 
add [ebx + TCP_SOCKET.t_rttvar], eax
ja @f
mov [ebx + TCP_SOCKET.t_rttvar], 1
@@:
ret
 
 
.no_rtt_yet:
push ecx
mov ecx, eax
shl ecx, TCP_RTT_SHIFT
mov [ebx + TCP_SOCKET.t_srtt], ecx
 
shl eax, TCP_RTTVAR_SHIFT - 1
mov [ebx + TCP_SOCKET.t_rttvar], eax
pop ecx
 
ret
 
 
;-----------------------------------------------------------------;
; ;
; tcp_mss: Update maximum segment size ;
; ;
; IN: eax = max segment size ;
; ebx = socket ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_mss:
 
cmp eax, 1420 ; FIXME
jbe @f
mov eax, 1420
@@:
mov [ebx + TCP_SOCKET.t_maxseg], eax
 
 
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; tcp_reassemble ;
; ;
; IN: ebx = socket ptr ;
; edx = segment ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_reassemble:
 
;;;;; TODO
 
ret
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/tcp_input.inc
0,0 → 1,1927
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2020. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Based on the algorithms used in 4.4BSD ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
TCP_BIT_NEEDOUTPUT = 1 shl 0
TCP_BIT_TIMESTAMP = 1 shl 1
TCP_BIT_DROPSOCKET = 1 shl 2
TCP_BIT_FIN_IS_ACKED = 1 shl 3
 
;-----------------------------------------------------------------;
; ;
; TCP_input: Add a segment to the incoming TCP queue. ;
; ;
; IN: [esp] = ptr to buffer ;
; ebx = ptr to device struct ;
; ecx = TCP segment size ;
; edx = ptr to IPv4 header ;
; esi = ptr to TCP segment ;
; edi = interface number*4 ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_input:
 
; record the current time
push [timer_ticks] ; in 1/100 seconds
push ebx ecx esi edx ; mind the order (see TCP_queue_entry struct)
mov esi, esp
 
push edi
add_to_queue TCP_queue, TCP_QUEUE_SIZE, sizeof.TCP_queue_entry, .fail
pop edi
add esp, sizeof.TCP_queue_entry
 
inc [TCP_segments_rx + edi]
 
xor edx, edx
mov eax, [TCP_input_event]
mov ebx, [eax + EVENT.id]
xor esi, esi
call raise_event
 
ret
 
.fail:
pop edi
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP incoming queue is full, discarding packet!\n"
 
call net_ptr_to_num4
inc [TCP_segments_missed + edi]
 
add esp, sizeof.TCP_queue_entry - 4
call net_buff_free
ret
 
 
;-----------------------------------------------------------------;
; ;
; TCP_process_input: Process segments from the incoming TCP queue.;
; ;
; IN: / ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
proc tcp_process_input
 
locals
dataoffset dd ?
timestamp dd ?
temp_bits db ?
device dd ?
endl
 
xor esi, esi
mov ecx, MANUAL_DESTROY
call create_event
mov [TCP_input_event], eax
 
.wait:
mov eax, [TCP_input_event]
mov ebx, [eax + EVENT.id]
call wait_event
 
.loop:
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]
mov [device], ebx
mov ecx, [esi + TCP_queue_entry.segment_size]
mov edi, [esi + TCP_queue_entry.ip_ptr] ; ptr to ipv4 header
mov esi, [esi + TCP_queue_entry.segment_ptr] ; change esi last
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: size=%u time=%d\n", ecx, [timer_ticks]
 
mov edx, esi
 
; Verify the checksum (if not already done by hw)
 
test [ebx + NET_DEVICE.hwacc], NET_HWACC_TCP_IPv4_IN
jnz .checksum_ok
 
push ecx esi
pushw [esi + TCP_header.Checksum]
mov [esi + TCP_header.Checksum], 0
tcp_checksum (edi+IPv4_header.SourceAddress), (edi+IPv4_header.DestinationAddress)
pop cx ; previous checksum
cmp cx, dx
pop edx ecx
jne .drop_no_socket
.checksum_ok:
 
; Verify the data offset
 
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
 
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
 
;-------------------------------------------
; Convert Big-endian values to little endian
 
ntohd [edx + TCP_header.SequenceNumber]
ntohd [edx + TCP_header.AckNumber]
 
ntohw [edx + TCP_header.Window]
ntohw [edx + TCP_header.UrgentPointer]
 
;-----------------------------------------------------------------------------------
;
; Find the socket pointer
;
;-----------------------------------------------------------------------------------
 
; IP Packet TCP Destination Port = local Port
; (IP Packet SenderAddress = Remote IP) OR (Remote IP = 0)
; (IP Packet TCP Source Port = remote Port) OR (remote Port = 0)
 
.findpcb:
pusha
mov ecx, socket_mutex
call mutex_lock
popa
 
mov ebx, net_sockets
mov si, [edx + TCP_header.DestinationPort]
 
.socket_loop:
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .no_socket ;respond_seg_reset
 
cmp [ebx + SOCKET.Domain], AF_INET4
jne .socket_loop
 
cmp [ebx + SOCKET.Protocol], IP_PROTO_TCP
jne .socket_loop
 
cmp [ebx + TCP_SOCKET.LocalPort], si
jne .socket_loop
 
mov eax, [ebx + IP_SOCKET.RemoteIP]
cmp eax, [edi + IPv4_header.SourceAddress]
je @f
test eax, eax
jnz .socket_loop
@@:
 
mov ax, [ebx + TCP_SOCKET.RemotePort]
cmp [edx + TCP_header.SourcePort], ax
je .found_socket
test ax, ax
jnz .socket_loop
.found_socket: ; ebx now contains the socketpointer
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: socket ptr=%x state=%u flags=%x\n", ebx, [ebx + TCP_SOCKET.t_state], [edx + TCP_header.Flags]:2
 
;----------------------------
; Check if socket isnt closed
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_CLOSED
je .drop_no_socket
 
;----------------
; Lock the socket
 
pusha
lea ecx, [ebx + SOCKET.mutex]
call mutex_lock
popa
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: socket locked\n"
 
;---------------------------
; disable all temporary bits
 
mov [temp_bits], 0
 
;---------------------------------------
; unscale the window into a 32 bit value
 
movzx eax, [edx + TCP_header.Window]
push ecx
mov cl, [ebx + TCP_SOCKET.SND_SCALE]
shl eax, cl
mov dword[edx + TCP_header.Window], eax ; word after window is checksum, we dont need checksum anymore
pop ecx
 
;-----------------------------------------------------------------------------------
;
; Accept incoming connections
;
;-----------------------------------------------------------------------------------
 
test [ebx + SOCKET.options], SO_ACCEPTCON
jz .no_accept
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Accepting new connection\n"
 
; Unlock current socket
 
pusha
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
popa
 
; Fork it
 
push ecx edx esi edi
call socket_fork
pop edi esi edx ecx
 
test eax, eax
jz .drop_no_socket
 
; Success! Use the new socket from now on (it is already locked)
 
mov ebx, eax
 
mov [temp_bits], TCP_BIT_DROPSOCKET
 
push [edi + IPv4_header.DestinationAddress]
pop [ebx + IP_SOCKET.LocalIP]
 
push [edx + TCP_header.DestinationPort]
pop [ebx + TCP_SOCKET.LocalPort]
 
mov [ebx + TCP_SOCKET.t_state], TCPS_LISTEN
.no_accept:
 
 
;-------------------------------------
; Reset idle timer and keepalive timer
 
mov [ebx + TCP_SOCKET.t_idle], 0
mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_idle
or [ebx + TCP_SOCKET.timer_flags], timer_flag_keepalive
 
;-----------------------------------------------------------------------------------
;
; 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
 
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"
 
add ecx, edx
lea esi, [edx + sizeof.TCP_header]
 
.opt_loop:
cmp esi, ecx ; are we scanning outside of header?
jae .no_options
lodsb
cmp al, TCP_OPT_EOL ; end of option list?
je .no_options
cmp al, TCP_OPT_NOP
je .opt_loop
cmp al, TCP_OPT_MAXSEG
je .opt_maxseg
cmp al, TCP_OPT_WINDOW
je .opt_window
cmp al, TCP_OPT_SACK_PERMIT
je .opt_sack_permit
; cmp al, TCP_OPT_SACK
; je .opt_sack
cmp al, TCP_OPT_TIMESTAMP
je .opt_timestamp
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: unknown option:%u\n", al
jmp .no_options ; If we reach here, some unknown options were received, skip them all!
 
.opt_maxseg:
lodsb
cmp al, 4
jne .no_options ; error occured, ignore all options!
 
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", eax
call tcp_mss
@@:
jmp .opt_loop
 
 
.opt_window:
lodsb
cmp al, 3
jne .no_options
 
test [edx + TCP_header.Flags], TH_SYN
jz @f
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Got window scale option\n"
or [ebx + TCP_SOCKET.t_flags], TF_RCVD_SCALE
 
lodsb
mov [ebx + TCP_SOCKET.SND_SCALE], al
;;;;; TODO
 
@@:
jmp .opt_loop
 
 
.opt_sack_permit:
lodsb
cmp al, 2
jne .no_options
 
test [edx + TCP_header.Flags], TH_SYN
jz @f
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Selective Acknowledgement permitted\n"
or [ebx + TCP_SOCKET.t_flags], TF_SACK_PERMIT
 
@@:
jmp .opt_loop
 
 
.opt_timestamp:
lodsb
cmp al, 10 ; length must be 10
jne .no_options
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Got timestamp option\n"
 
test [edx + TCP_header.Flags], TH_SYN
jz @f
or [ebx + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
@@:
 
lodsd
bswap eax
mov [ebx + TCP_SOCKET.ts_val], eax
lodsd ; timestamp echo reply
mov [ebx + TCP_SOCKET.ts_ecr], eax
or [temp_bits], TCP_BIT_TIMESTAMP
 
; Since we have a timestamp, lets do the paws test right away!
 
test [edx + TCP_header.Flags], TH_RST
jnz .no_paws
 
mov eax, [ebx + TCP_SOCKET.ts_recent]
test eax, eax
jz .no_paws
cmp eax, [ebx + TCP_SOCKET.ts_val]
jbe .no_paws
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: PAWS: detected an old segment\n"
 
mov eax, [timestamp]
sub eax, [ebx + TCP_SOCKET.ts_recent_age]
 
pop ecx
cmp eax, TCP_PAWS_IDLE
jle .paws_drop
push ecx
mov [ebx + TCP_SOCKET.ts_recent], 0 ; timestamp was invalid, fix it.
.no_paws:
jmp .opt_loop
 
.paws_drop:
inc [TCPS_rcvduppack]
add [TCPS_rcvdupbyte], ecx
inc [TCPS_pawsdrop]
jmp .drop_after_ack
 
.no_options:
 
pop ecx
 
;-----------------------------------------------------------------------------------
;
; Header prediction
;
;-----------------------------------------------------------------------------------
 
; According to Van Jacobson, there are two common cases for an uni-directional data transfer.
;
; General rule: the packets has no control flags, is in-sequence,
; window width didnt change and we're not retransmitting.
;
; Second rules:
; - If the length is 0 and the ACK moved forward, we're the sender side of the transfer.
; In this case we'll free the ACK'ed data and notify higher levels that we have free space in buffer
;
; - 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 [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED
jnz .not_uni_xfer
 
test [edx + TCP_header.Flags], TH_SYN + TH_FIN + TH_RST + TH_URG
jnz .not_uni_xfer
 
test [edx + TCP_header.Flags], TH_ACK
jz .not_uni_xfer
 
mov eax, [edx + TCP_header.SequenceNumber]
cmp eax, [ebx + TCP_SOCKET.RCV_NXT]
jne .not_uni_xfer
 
mov eax, dword[edx + TCP_header.Window]
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jne .not_uni_xfer
 
mov eax, [ebx + TCP_SOCKET.SND_NXT]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jne .not_uni_xfer
 
;---------------------------------------
; 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.
 
test ecx, ecx
jnz .not_sender
 
; - 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]
jb .not_uni_xfer
 
; - The acknowledgment field in the segment is less than or equal to the maximum sequence number sent.
 
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
ja .not_uni_xfer
 
; - The acknowledgment field in the segment is greater than the largest unacknowledged sequence number.
 
sub eax, [ebx + TCP_SOCKET.SND_UNA]
jbe .not_uni_xfer
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Header prediction: we are sender\n"
 
;---------------------------------
; Packet is a pure ACK, process it
 
inc [TCPS_predack]
 
inc [TCPS_rcvackpack]
add [TCPS_rcvackbyte], eax
 
; Delete acknowledged bytes from send buffer
 
pusha
mov ecx, eax
lea eax, [ebx + STREAM_SOCKET.snd]
call socket_ring_free
popa
 
; Update RTT estimators
 
test [temp_bits], TCP_BIT_TIMESTAMP
jz .no_timestamp_rtt
mov eax, [timestamp]
sub eax, [ebx + TCP_SOCKET.ts_ecr]
inc eax
call tcp_xmit_timer
jmp .rtt_done
.no_timestamp_rtt:
 
cmp [ebx + TCP_SOCKET.t_rtt], 0
je .rtt_done
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.t_rtseq]
jbe .rtt_done
mov eax, [ebx + TCP_SOCKET.t_rtt]
call tcp_xmit_timer
.rtt_done:
 
; update window pointers
 
mov eax, [edx + TCP_header.AckNumber]
mov [ebx + TCP_SOCKET.SND_UNA], eax
 
; Stop retransmit timer
 
and [ebx + TCP_SOCKET.timer_flags], not timer_flag_retransmission
 
; Unlock the socket
 
pusha
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
popa
 
; Awaken waiting processes
 
mov eax, ebx
call socket_notify
 
; Generate more output
 
call tcp_output
 
jmp .drop_no_socket
 
;-------------------------------------------------
; maybe we are the receiver in the uni-xfer then..
 
.not_sender:
 
; - The amount of data in the segment is greater than 0 (data count is in ecx)
; - The acknowledgment field equals the largest unacknowledged sequence number. This means no data is acknowledged by this segment.
 
mov eax, [edx + TCP_header.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.
 
cmp [ebx + TCP_SOCKET.seg_next], 0
jne .not_uni_xfer
 
; Complete processing of received data
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Header prediction: we are receiving %u bytes\n", ecx
 
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
 
or [ebx + TCP_SOCKET.t_flags], TF_DELACK ; Set delayed ack flag
 
jmp .drop
 
 
;-----------------------------------------------------------------------------------
;
; TCP segment processing, the slow way
;
;-----------------------------------------------------------------------------------
 
.not_uni_xfer:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Header prediction failed\n"
 
; Calculate receive window size
 
push edx
mov eax, SOCKET_BUFFER_SIZE
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
@@:
DEBUGF DEBUG_NETWORK_VERBOSE, "Receive window size=%d\n", eax
mov [ebx + TCP_SOCKET.RCV_WND], eax
pop edx
 
; If we are in listen or syn_sent state, go to that specific code right away
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_LISTEN
je .state_listen
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_SYN_SENT
je .state_syn_sent
 
;-----------------------------------------------------------------------------------
;
; Trim any data not in window
;
;-----------------------------------------------------------------------------------
 
;-------------------------------------------------
; 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
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: %u bytes duplicate data!\n", eax
 
; Check for duplicate SYN
 
test [edx + TCP_header.Flags], TH_SYN
jz .no_dup_syn
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: got duplicate syn\n"
 
and [edx + TCP_header.Flags], not (TH_SYN)
inc [edx + TCP_header.SequenceNumber]
 
cmp [edx + TCP_header.UrgentPointer], 1
jbe @f
dec [edx + TCP_header.UrgentPointer]
jmp .dup_syn
@@:
and [edx + TCP_header.Flags], not (TH_URG)
.dup_syn:
dec eax
.no_dup_syn:
 
;-----------------------------------
; Check for entire duplicate segment
 
cmp eax, ecx ; eax holds number of bytes to drop, ecx is data size
jb .no_complete_dup
jnz @f
test [edx + TCP_header.Flags], TH_FIN
jnz .no_complete_dup
@@:
 
; Any valid FIN must be to the left of the window.
; At this point the FIN must be out of sequence or a duplicate, drop it
 
and [edx + TCP_header.Flags], not TH_FIN
 
; send an ACK to resynchronize and drop any data.
; But keep on processing for RST or ACK
 
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
mov eax, ecx
 
inc [TCPS_rcvduppack]
add [TCPS_rcvdupbyte], eax
jmp .dup_processed
.no_complete_dup:
inc [TCPS_rcvpartduppack]
add [TCPS_rcvpartdupbyte], eax
.dup_processed:
 
;-----------------------------------------------
; Remove duplicate data and update urgent offset
 
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
 
sub [edx + TCP_header.UrgentPointer], ax
jg @f
and [edx + TCP_header.Flags], not (TH_URG)
mov [edx + TCP_header.UrgentPointer], 0
@@:
.no_duplicate:
 
;--------------------------------------------------
; Handle data that arrives after process terminates
 
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
test ecx, ecx
jz .not_terminated
 
mov eax, ebx
call tcp_close
inc [TCPS_rcvafterclose]
jmp .respond_seg_reset
.not_terminated:
 
;----------------------------------------
; Remove data beyond right edge of window
 
mov eax, [edx + TCP_header.SequenceNumber]
add eax, ecx
sub eax, [ebx + TCP_SOCKET.RCV_NXT]
sub eax, [ebx + TCP_SOCKET.RCV_WND] ; eax now holds the number of bytes to drop
jle .no_excess_data
 
DEBUGF DEBUG_NETWORK_VERBOSE, "%d bytes beyond right edge of window\n", eax
 
inc [TCPS_rcvpackafterwin]
 
cmp eax, ecx
jl .dont_drop_all
 
add [TCPS_rcvbyteafterwin], ecx
 
;----------------------------------------------------------------------------------------------------
; If a new connection request is received while in TIME_WAIT, drop the old connection and start over,
; if the sequence numbers are above the previous ones
 
test [edx + TCP_header.Flags], TH_SYN
jz .no_new_request
cmp [ebx + TCP_SOCKET.t_state], TCPS_TIME_WAIT
jne .no_new_request
; mov edx, [ebx + TCP_SOCKET.RCV_NXT]
; cmp edx, [edx + TCP_header.SequenceNumber]
; add edx, 64000 ; TCP_ISSINCR FIXME
mov eax, ebx
call tcp_close
jmp .findpcb ; FIXME: skip code for unscaling window, ...
.no_new_request:
 
; 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 esi, [edx + TCP_header.SequenceNumber]
cmp esi, [ebx + TCP_SOCKET.RCV_NXT]
jne .drop_after_ack
 
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
inc [TCPS_rcvwinprobe]
.dont_drop_all:
add [TCPS_rcvbyteafterwin], eax
DEBUGF DEBUG_NETWORK_VERBOSE, "Trimming %u bytes from the right of the window\n"
 
; remove data from the right side of window (decrease data length)
 
sub ecx, eax
and [edx + TCP_header.Flags], not (TH_PUSH or TH_FIN)
.no_excess_data:
 
;-----------------------------------------------------------------------------------
;
; Record timestamp
;
;-----------------------------------------------------------------------------------
 
; If last ACK falls within this segments sequence numbers, record its timestamp
 
test [temp_bits], TCP_BIT_TIMESTAMP
jz .no_timestamp
mov eax, [ebx + TCP_SOCKET.last_ack_sent]
sub eax, [edx + TCP_header.SequenceNumber]
jb .no_timestamp
test [edx + TCP_header.Flags], TH_SYN or TH_FIN ; SYN and FIN occupy one byte
jz @f
dec eax
@@:
sub eax, ecx
jae .no_timestamp
 
DEBUGF DEBUG_NETWORK_VERBOSE, "Recording timestamp\n"
 
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
.no_timestamp:
 
;-----------------------------------------------------------------------------------
;
; Process RST flag
;
;-----------------------------------------------------------------------------------
 
test [edx + TCP_header.Flags], TH_RST
jz .no_rst
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Got an RST flag\n"
 
mov eax, [ebx + TCP_SOCKET.t_state]
shl eax, 2
jmp dword [eax + .rst_sw_list]
 
;-----------------------------------------------------------------------------------
.rst_sw_list:
dd .no_rst ; TCPS_CLOSED
dd .no_rst ; TCPS_LISTEN
dd .no_rst ; TCPS_SYN_SENT
dd .econnrefused ; TCPS_SYN_RECEIVED
dd .econnreset ; TCPS_ESTABLISHED
dd .econnreset ; TCPS_CLOSE_WAIT
dd .econnreset ; TCPS_FIN_WAIT_1
dd .rst_close ; TCPS_CLOSING
dd .rst_close ; TCPS_LAST_ACK
dd .econnreset ; TCPS_FIN_WAIT_2
dd .rst_close ; TCPS_TIME_WAIT
 
;-----------------------------------------------------------------------------------
.econnrefused:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Connection refused\n"
mov [ebx + SOCKET.errorcode], ECONNREFUSED
jmp .close
 
;-----------------------------------------------------------------------------------
.econnreset:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Connection reset\n"
mov [ebx + SOCKET.errorcode], ECONNRESET
.close:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Closing connection\n"
mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSED
inc [TCPS_drops]
 
 
jmp .drop
 
;-----------------------------------------------------------------------------------
.rst_close:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Closing with reset\n"
jmp .unlock_and_close
 
;-----------------------------------------------------------------------------------
.no_rst:
 
;-----------------------------------------------------------------------------------
;
; Handle SYN-full and ACK-less segments
;
;-----------------------------------------------------------------------------------
 
; If a SYN is in the window, then this is an error so we send an RST and drop the connection
 
test [edx + TCP_header.Flags], TH_SYN
jz .not_syn_full
 
mov eax, ebx
mov ebx, ECONNRESET
call tcp_drop
jmp .drop_with_reset
.not_syn_full:
 
; If ACK bit is off, we drop the segment and return
 
test [edx + TCP_header.Flags], TH_ACK
jz .drop
 
;----------------------------------------------------------------------------------
;
; ACK processing for SYN_RECEIVED state
;
;----------------------------------------------------------------------------------
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED
jb .ack_processed ; states: closed, listen, syn_sent
ja .no_syn_rcv ; established, fin_wait_1, fin_wait_2, close_wait, closing, last_ack, time_wait
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: state=syn_received\n"
 
mov eax, [edx + TCP_header.AckNumber]
cmp [ebx + TCP_SOCKET.SND_UNA], eax
ja .drop_with_reset
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
ja .drop_with_reset
 
inc [TCPS_connects]
 
mov eax, ebx
call socket_is_connected
mov [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED
 
; Do window scaling?
 
test [ebx + TCP_SOCKET.t_flags], TF_RCVD_SCALE
jz @f
test [ebx + TCP_SOCKET.t_flags], TF_REQ_SCALE
jz @f
 
push word[ebx + TCP_SOCKET.requested_s_scale] ; Set send and receive scale factors to the received values
pop word[ebx + TCP_SOCKET.SND_SCALE]
@@:
 
call tcp_reassemble
 
mov eax, [edx + TCP_header.SequenceNumber]
dec eax
mov [ebx + TCP_SOCKET.SND_WL1], eax
.no_syn_rcv:
 
;-----------------------------------------------------------------------------------
;
; ACK processing for SYN_RECEIVED state and higher
;
;-----------------------------------------------------------------------------------
 
;-------------------------
; Check for duplicate ACKs
 
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
ja .dup_ack_complete
 
test ecx, ecx
jnz .reset_dupacks
 
mov eax, dword[edx + TCP_header.Window]
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jne .reset_dupacks
 
inc [TCPS_rcvdupack]
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Processing duplicate ACK\n"
 
; If we have outstanding data, other than a window probe, this is a completely duplicate ACK
; (window info didnt change) The ACK is the biggest we've seen and we've seen exactly our rexmt threshold of them,
; assume a packet has been dropped and retransmit it. Kludge snd_nxt & the congestion window so we send only this one packet.
 
test [ebx + TCP_SOCKET.timer_flags], timer_flag_retransmission
jz .reset_dupacks
 
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
jne .reset_dupacks
 
; Increment dupplicat ACK counter
; If it reaches the threshold, re-transmit the missing segment
 
inc [ebx + TCP_SOCKET.t_dupacks]
cmp [ebx + TCP_SOCKET.t_dupacks], TCP_re_xmit_thresh
jb .dup_ack_complete
ja .another_lost
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Re-transmitting lost segment\n"
 
push [ebx + TCP_SOCKET.SND_NXT] ; >>>>
 
mov eax, [ebx + TCP_SOCKET.SND_WND]
cmp eax, [ebx + TCP_SOCKET.SND_CWND]
jbe @f
mov eax, [ebx + TCP_SOCKET.SND_CWND]
@@:
shr eax, 1
push edx
xor edx, edx
div [ebx + TCP_SOCKET.t_maxseg]
cmp eax, 2
ja @f
xor eax, eax
mov al, 2
@@:
mul [ebx + TCP_SOCKET.t_maxseg]
pop edx
mov [ebx + TCP_SOCKET.SND_SSTHRESH], eax
 
and [ebx + TCP_SOCKET.timer_flags], not timer_flag_retransmission ; turn off retransmission timer
mov [ebx + TCP_SOCKET.t_rtt], 0
mov eax, [edx + TCP_header.AckNumber]
mov [ebx + TCP_SOCKET.SND_NXT], eax
mov eax, [ebx + TCP_SOCKET.t_maxseg]
mov [ebx + TCP_SOCKET.SND_CWND], eax
 
; Unlock the socket
 
push ebx
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
 
; retransmit missing segment
 
mov eax, [esp]
call tcp_output
 
; Lock the socket again
 
mov ecx, [esp]
add ecx, SOCKET.mutex
call mutex_lock
pop ebx
 
; Continue processing
 
xor edx, edx
mov eax, [ebx + TCP_SOCKET.t_maxseg]
mul [ebx + TCP_SOCKET.t_dupacks]
add eax, [ebx + TCP_SOCKET.SND_SSTHRESH]
mov [ebx + TCP_SOCKET.SND_CWND], eax
 
pop eax ; <<<<
cmp eax, [ebx + TCP_SOCKET.SND_NXT]
jb @f
mov [ebx + TCP_SOCKET.SND_NXT], eax
@@:
jmp .drop
 
.another_lost:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Increasing congestion window\n"
 
mov eax, [ebx + TCP_SOCKET.t_maxseg]
add [ebx + TCP_SOCKET.SND_CWND], eax
 
; Unlock the socket
 
push ebx
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
 
; retransmit missing segment, again
 
mov eax, [esp]
call tcp_output
 
; Lock the socket again
 
mov ecx, [esp]
add ecx, SOCKET.mutex
call mutex_lock
pop ebx
 
; And drop the incoming segment
 
jmp .drop
 
.reset_dupacks: ; We got a new ACK, reset duplicate ACK counter
mov [ebx + TCP_SOCKET.t_dupacks], 0
jmp .ack_processed
 
.dup_ack_complete:
 
;-------------------------------------------------
; If the congestion window was inflated to account
; for the other side's cached packets, retract it
 
mov eax, [ebx + TCP_SOCKET.SND_SSTHRESH]
cmp eax, [ebx + TCP_SOCKET.SND_CWND]
ja @f
cmp [ebx + TCP_SOCKET.t_dupacks], TCP_re_xmit_thresh
jbe @f
mov [ebx + TCP_SOCKET.SND_CWND], eax
@@:
 
mov [ebx + TCP_SOCKET.t_dupacks], 0
 
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jbe @f
inc [TCPS_rcvacktoomuch]
jmp .drop_after_ack
@@:
 
mov edi, [edx + TCP_header.AckNumber]
sub edi, [ebx + TCP_SOCKET.SND_UNA] ; now we got the number of acked bytes in edi
inc [TCPS_rcvackpack]
add [TCPS_rcvackbyte], edi
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: acceptable ACK for %u bytes\n", edi
 
;-----------------------------------------------------------------------------------
;
; RTT measurements and retransmission timer
;
;-----------------------------------------------------------------------------------
 
; If we have a timestamp, update smoothed RTT
 
test [temp_bits], TCP_BIT_TIMESTAMP
jz .timestamp_not_present
mov eax, [timestamp]
sub eax, [ebx + TCP_SOCKET.ts_ecr]
inc eax
call tcp_xmit_timer
jmp .rtt_done_
 
; If no timestamp but transmit timer is running and timed sequence number was acked,
; update smoothed RTT. Since we now have an RTT measurement, cancel the timer backoff
; (Phil Karn's retransmit algo)
; Recompute the initial retransmit timer
 
.timestamp_not_present:
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.t_rtseq]
jbe .rtt_done_
mov eax, [ebx + TCP_SOCKET.t_rtt]
test eax, eax
jz .rtt_done_
call tcp_xmit_timer
.rtt_done_:
 
; If all outstanding data is acked, stop retransmit timer and remember to restart (more output or persist)
; If there is more data to be acked, restart retransmit timer, using current (possible backed-off) value.
 
mov eax, [ebx + TCP_SOCKET.SND_MAX]
cmp eax, [edx + TCP_header.AckNumber]
jne .more_data
and [ebx + TCP_SOCKET.timer_flags], not timer_flag_retransmission
or [temp_bits], TCP_BIT_NEEDOUTPUT
jmp .no_restart
.more_data:
test [ebx + TCP_SOCKET.timer_flags], timer_flag_persist
jnz .no_restart
 
mov eax, [ebx + TCP_SOCKET.t_rxtcur]
mov [ebx + TCP_SOCKET.timer_retransmission], eax
or [ebx + TCP_SOCKET.timer_flags], timer_flag_retransmission
.no_restart:
 
;-----------------------------------------------------------------------------------
;
; Open congestion window in response to ACKs
;
;-----------------------------------------------------------------------------------
 
; If the window gives us less then sstresh packets in flight, open exponentially.
; Otherwise, open lineary
 
mov esi, [ebx + TCP_SOCKET.SND_CWND]
mov eax, [ebx + TCP_SOCKET.t_maxseg]
cmp esi, [ebx + TCP_SOCKET.SND_SSTHRESH]
jbe @f
push edx
push eax
mul eax ; t_maxseg*t_maxseg
div esi ; t_maxseg*t_maxseg/snd_cwnd
pop edx ; t_maxseg
shr edx, 3 ; t_maxseg/8
add eax, edx ; t_maxseg*t_maxseg/snd_cwnd + t_maxseg/8
pop edx
@@:
add esi, eax
 
push ecx
mov cl, [ebx + TCP_SOCKET.SND_SCALE]
mov eax, TCP_max_win
shl eax, cl
pop ecx
 
cmp esi, eax
jbe @f
mov esi, eax
@@:
mov [ebx + TCP_SOCKET.SND_CWND], esi
 
;-----------------------------------------------------------------------------------
;
; Remove acknowledged data from send buffer
;
;-----------------------------------------------------------------------------------
 
; If the number of bytes acknowledged exceeds the number of bytes on the send buffer,
; snd_wnd is decremented by the number of bytes in the send buffer and TCP knows
; that its FIN has been ACKed. (FIN occupies 1 byte in the sequence number space)
 
cmp edi, [ebx + STREAM_SOCKET.snd.size]
jbe .no_fin_ack
 
; Drop all data in output buffer
 
push ecx edx ebx
mov ecx, [ebx + STREAM_SOCKET.snd.size]
sub [ebx + TCP_SOCKET.SND_WND], ecx
lea eax, [ebx + STREAM_SOCKET.snd]
call socket_ring_free
pop ebx edx ecx
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: our FIN is acked\n"
or [temp_bits], TCP_BIT_FIN_IS_ACKED
jmp .ack_complete
.no_fin_ack:
 
; Drop acknowledged data
 
push ecx edx ebx
mov ecx, edi
lea eax, [ebx + STREAM_SOCKET.snd]
call socket_ring_free
pop ebx
sub [ebx + TCP_SOCKET.SND_WND], ecx
pop edx ecx
.ack_complete:
 
;-----------------------------------------------------------------------------------
;
; Wake up process waiting on send buffer
;
;-----------------------------------------------------------------------------------
 
mov eax, ebx
call socket_notify
 
; Update TCPS
 
mov eax, [edx + TCP_header.AckNumber]
mov [ebx + TCP_SOCKET.SND_UNA], eax
cmp eax, [ebx + TCP_SOCKET.SND_NXT]
jb @f
mov [ebx + TCP_SOCKET.SND_NXT], eax
@@:
 
;-----------------------------------------------------------------------------------
;
; State specific ACK handeling
;
;-----------------------------------------------------------------------------------
 
mov eax, [ebx + TCP_SOCKET.t_state]
jmp dword[.ack_sw_list+eax*4]
 
.ack_sw_list:
dd .ack_processed ; TCPS_CLOSED
dd .ack_processed ; TCPS_LISTEN
dd .ack_processed ; TCPS_SYN_SENT
dd .ack_processed ; TCPS_SYN_RECEIVED
dd .ack_processed ; TCPS_ESTABLISHED
dd .ack_processed ; TCPS_CLOSE_WAIT
dd .ack_fw1 ; TCPS_FIN_WAIT_1
dd .ack_c ; TCPS_CLOSING
dd .ack_la ; TCPS_LAST_ACK
dd .ack_processed ; TCPS_FIN_WAIT_2
dd .ack_tw ; TCPS_TIMED_WAIT
 
;-----------------------------------------------------------------------------------
.ack_fw1:
; If our FIN is now acked, enter FIN_WAIT_2
 
test [temp_bits], TCP_BIT_FIN_IS_ACKED
jz .ack_processed
 
; If we can't receive any more data, then closing user can proceed.
; Starting the timer is contrary to the specification, but if we dont get a FIN,
; we'll hang forever.
 
test [ebx + SOCKET.state], SS_CANTRCVMORE
jz @f
mov eax, ebx
call socket_is_disconnected
mov [ebx + TCP_SOCKET.timer_timed_wait], TCP_time_max_idle
or [ebx + TCP_SOCKET.timer_flags], timer_flag_wait
@@:
mov [ebx + TCP_SOCKET.t_state], TCPS_FIN_WAIT_2
jmp .ack_processed
 
;-----------------------------------------------------------------------------------
.ack_c:
; Enter the TIME_WAIT state if our FIN is acked in CLOSED state.
 
test [temp_bits], TCP_BIT_FIN_IS_ACKED
jz .ack_processed
 
mov [ebx + TCP_SOCKET.t_state], TCPS_TIME_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
mov eax, ebx
call socket_is_disconnected
jmp .ack_processed
 
;-----------------------------------------------------------------------------------
.ack_la:
; In LAST_ACK state, we may still be waiting for data to drain and/or to be acked.
; If our FIN is acked however, enter CLOSED state and return.
 
test [temp_bits], TCP_BIT_FIN_IS_ACKED
jz .ack_processed
 
.unlock_and_close:
push ebx
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
pop eax
 
call tcp_close
jmp .drop_no_socket
 
;-----------------------------------------------------------------------------------
.ack_tw:
; In TIME_WAIT state the only thing that should arrive is a retransmission of the remote FIN.
; Acknowledge it and restart the FINACK timer
 
mov [ebx + TCP_SOCKET.timer_timed_wait], 2*TCP_time_MSL
or [ebx + TCP_SOCKET.timer_flags], timer_flag_2msl
jmp .drop_after_ack
 
;-----------------------------------------------------------------------------------
;
; Initiation of Passive Open?
;
;-----------------------------------------------------------------------------------
 
.state_listen:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: state=listen\n"
 
test [edx + TCP_header.Flags], TH_RST
jnz .drop
 
test [edx + TCP_header.Flags], TH_ACK
jnz .drop_with_reset
 
test [edx + TCP_header.Flags], TH_SYN
jz .drop
 
inc [TCPS_accepts]
 
;;; TODO: check if it's a broadcast or multicast, and drop if so
 
;-------------------------------------------
; Processing of SYN received in LISTEN state
 
push [edi + IPv4_header.SourceAddress]
pop [ebx + IP_SOCKET.RemoteIP]
 
push [edx + TCP_header.SourcePort]
pop [ebx + TCP_SOCKET.RemotePort]
 
push [edx + TCP_header.SequenceNumber]
pop [ebx + TCP_SOCKET.IRS]
 
mov eax, [TCP_sequence_num]
add [TCP_sequence_num], TCP_ISSINCR / 2
mov [ebx + TCP_SOCKET.ISS], eax
mov [ebx + TCP_SOCKET.SND_NXT], eax
 
tcp_sendseqinit ebx
tcp_rcvseqinit ebx
 
mov [ebx + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED
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
 
lea eax, [ebx + STREAM_SOCKET.snd]
call socket_ring_create
test eax, eax
jz .drop
 
lea eax, [ebx + STREAM_SOCKET.rcv]
call socket_ring_create
test eax, eax
jz .drop
 
and [temp_bits], not TCP_BIT_DROPSOCKET
 
pusha
mov eax, ebx
call socket_notify
popa
 
jmp .trim
 
;-----------------------------------------------------------------------------------
;
; Completion of active open?
;
;-----------------------------------------------------------------------------------
 
.state_syn_sent:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: state=syn_sent\n"
 
test [edx + TCP_header.Flags], TH_ACK
jz @f
 
mov eax, [edx + TCP_header.AckNumber]
cmp eax, [ebx + TCP_SOCKET.ISS]
jbe .drop_with_reset
 
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
ja .drop_with_reset
@@:
 
test [edx + TCP_header.Flags], TH_RST
jz @f
 
test [edx + TCP_header.Flags], TH_ACK
jz .drop
 
mov eax, ebx
mov ebx, ECONNREFUSED
call tcp_drop
jmp .drop
@@:
 
;-----------------------------------------------------------------------------------
;
; Process received SYN in response to an active open
;
;-----------------------------------------------------------------------------------
 
test [edx + TCP_header.Flags], TH_SYN
jz .drop
 
test [edx + TCP_header.Flags], TH_ACK
jz @f
 
mov eax, [edx + TCP_header.AckNumber]
mov [ebx + TCP_SOCKET.SND_UNA], eax
cmp eax, [ebx + TCP_SOCKET.SND_NXT]
jbe @f
mov [ebx + TCP_SOCKET.SND_NXT], eax
 
and [ebx + TCP_SOCKET.timer_flags], not timer_flag_retransmission ; disable retransmission timer
@@:
 
push [edx + TCP_header.SequenceNumber]
pop [ebx + TCP_SOCKET.IRS]
 
tcp_rcvseqinit ebx
 
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
 
mov eax, [ebx + TCP_SOCKET.SND_UNA]
cmp eax, [ebx + TCP_SOCKET.ISS]
jbe .simultaneous_open
 
test [edx + TCP_header.Flags], TH_ACK
jz .simultaneous_open
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: active open\n"
 
inc [TCPS_connects]
 
; set socket state to connected
 
push eax
mov eax, ebx
call socket_is_connected
pop eax
mov [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED
 
; Do window scaling on this connection ?
 
mov eax, [ebx + TCP_SOCKET.t_flags]
and eax, TF_REQ_SCALE or TF_RCVD_SCALE
cmp eax, TF_REQ_SCALE or TF_RCVD_SCALE
jne .no_scaling
 
mov ax, word[ebx + TCP_SOCKET.requested_s_scale]
mov word[ebx + TCP_SOCKET.SND_SCALE], ax
.no_scaling:
 
;;; TODO: reassemble packets queue
 
; If we didnt have time to re-transmit the SYN,
; Use its rtt as our initial srtt & rtt var.
 
mov eax, [ebx + TCP_SOCKET.t_rtt]
test eax, eax
je .trim
call tcp_xmit_timer
jmp .trim
 
;-----------------------------------------------------------------------------------
;
; Simultaneous open (We have received a SYN but no ACK)
;
;-----------------------------------------------------------------------------------
 
.simultaneous_open:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: simultaneous open\n"
mov [ebx + TCP_SOCKET.t_state], TCPS_SYN_RECEIVED
 
;-----------------------------------------------------------------------------------
;
; Common processing for receipt of SYN
;
;-----------------------------------------------------------------------------------
 
.trim:
; Advance sequence number to correspond to first data byte.
; If data, trim to stay within window, dropping FIN if necessary
 
inc [edx + TCP_header.SequenceNumber]
 
; 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
inc [TCPS_rcvpackafterwin]
sub ecx, [ebx + TCP_SOCKET.RCV_WND]
add [TCPS_rcvbyteafterwin], ecx
 
and [edx + TCP_header.Flags], not (TH_FIN)
mov ecx, [ebx + TCP_SOCKET.RCV_WND]
 
.dont_trim:
mov eax, [edx + TCP_header.SequenceNumber]
mov [ebx + TCP_SOCKET.RCV_UP], eax
dec eax
mov [ebx + TCP_SOCKET.SND_WL1], eax
 
;-----------------------------------------------------------------------------------
;
; Update window information (step 6 in RFC793)
;
;-----------------------------------------------------------------------------------
 
.ack_processed:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: ACK processed\n"
 
; dont look at window if no ACK
 
test [edx + TCP_header.Flags], TH_ACK
jz .no_window_update
 
; Does the segment contain new data?
 
mov eax, [ebx + TCP_SOCKET.SND_WL1]
cmp eax, [edx + TCP_header.SequenceNumber]
jb .update_window
ja @f
 
; No new data but a new ACK ?
 
mov eax, [ebx + TCP_SOCKET.SND_WL2]
cmp eax, [edx + TCP_header.AckNumber]
jb .update_window
@@:
 
; No new data or ACK but advertised window is larger then current window?
 
mov eax, [ebx + TCP_SOCKET.SND_WL2]
cmp eax, [edx + TCP_header.AckNumber]
jne .no_window_update
 
mov eax, dword[edx + TCP_header.Window]
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jbe .no_window_update
 
 
; Keep track of pure window updates
.update_window:
test ecx, ecx
jnz @f
mov eax, [ebx + TCP_SOCKET.SND_WL2]
cmp eax, [edx + TCP_header.AckNumber]
jne @f
mov eax, dword[edx + TCP_header.Window]
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jbe @f
inc [TCPS_rcvwinupd]
@@:
 
mov eax, dword[edx + TCP_header.Window]
mov [ebx + TCP_SOCKET.SND_WND], eax
cmp eax, [ebx + TCP_SOCKET.max_sndwnd]
jbe @f
mov [ebx + TCP_SOCKET.max_sndwnd], eax
@@:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Updating window to %u\n", eax
 
push [edx + TCP_header.SequenceNumber]
pop [ebx + TCP_SOCKET.SND_WL1]
 
push [edx + TCP_header.AckNumber]
pop [ebx + TCP_SOCKET.SND_WL2]
 
or [temp_bits], TCP_BIT_NEEDOUTPUT
.no_window_update:
 
;-----------------------------------------------------------------------------------
;
; Process URG flag
;
;-----------------------------------------------------------------------------------
 
test [edx + TCP_header.Flags], TH_URG
jz .not_urgent
 
cmp [edx + TCP_header.UrgentPointer], 0
jz .not_urgent
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_TIME_WAIT
je .not_urgent
 
; Ignore bogus urgent offsets
 
movzx eax, [edx + TCP_header.UrgentPointer]
add eax, [ebx + STREAM_SOCKET.rcv.size]
cmp eax, SOCKET_BUFFER_SIZE
jbe .not_urgent
 
mov [edx + TCP_header.UrgentPointer], 0
and [edx + TCP_header.Flags], not (TH_URG)
jmp .do_data
 
.not_urgent:
 
; processing of received urgent pointer
 
;;; TODO (1051-1093)
 
;-----------------------------------------------------------------------------------
;
; Process the data
;
;-----------------------------------------------------------------------------------
 
.do_data:
cmp [ebx + TCP_SOCKET.t_state], TCPS_TIME_WAIT
jae .final_processing
 
test [edx + TCP_header.Flags], TH_FIN
jnz @f
 
test ecx, ecx
jz .final_processing
@@:
 
; The segment is in order?
 
mov eax, [edx + TCP_header.SequenceNumber]
cmp eax, [ebx + TCP_SOCKET.RCV_NXT]
jne .out_of_order
 
; The reassembly queue is empty?
 
cmp [ebx + TCP_SOCKET.seg_next], 0
jne .out_of_order
 
; The connection is established?
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_ESTABLISHED
jne .out_of_order
 
; Ok, lets do this.. Set delayed ACK flag and copy data into socket buffer
 
or [ebx + TCP_SOCKET.t_flags], TF_DELACK
 
pusha
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
popa
 
; Wake up the sleeping process
 
mov eax, ebx
call socket_notify
 
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]
 
; Uh-oh, some data is out of order, lets call TCP reassemble for help
 
call tcp_reassemble ;;; TODO!
 
; 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
 
jmp .final_processing ;;; HACK because of unimplemented reassembly queue!
.data_done:
 
;-----------------------------------------------------------------------------------
;
; Process FIN
;
;-----------------------------------------------------------------------------------
 
test [edx + TCP_header.Flags], TH_FIN
jz .final_processing
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Processing FIN\n"
 
cmp [ebx + TCP_SOCKET.t_state], TCPS_TIME_WAIT
jae .not_first_fin
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: First FIN for this connection\n"
 
mov eax, ebx
call socket_cant_recv_more
 
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
inc [ebx + TCP_SOCKET.RCV_NXT]
 
.not_first_fin:
mov eax, [ebx + TCP_SOCKET.t_state]
jmp dword[.fin_sw_list+eax*4]
 
.fin_sw_list:
dd .final_processing ; TCPS_CLOSED
dd .final_processing ; TCPS_LISTEN
dd .final_processing ; TCPS_SYN_SENT
dd .fin_syn_est ; TCPS_SYN_RECEIVED
dd .fin_syn_est ; TCPS_ESTABLISHED
dd .final_processing ; TCPS_CLOSE_WAIT
dd .fin_wait1 ; TCPS_FIN_WAIT_1
dd .final_processing ; TCPS_CLOSING
dd .final_processing ; TCPS_LAST_ACK
dd .fin_wait2 ; TCPS_FIN_WAIT_2
dd .fin_timed ; TCPS_TIMED_WAIT
 
;-----------------------------------------------------------------------------------
.fin_syn_est:
; In SYN_RECEIVED and ESTABLISHED state, enter the CLOSE_WAIT state
 
mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSE_WAIT
jmp .final_processing
 
;-----------------------------------------------------------------------------------
.fin_wait1:
; From FIN_WAIT_1 state, enter CLOSING state (our FIN has not been ACKed)
 
mov [ebx + TCP_SOCKET.t_state], TCPS_CLOSING
jmp .final_processing
 
;-----------------------------------------------------------------------------------
.fin_wait2:
; From FIN_WAIT_2 state, enter TIME_WAIT state and start the timer
 
mov [ebx + TCP_SOCKET.t_state], TCPS_TIME_WAIT
mov eax, ebx
call tcp_cancel_timers
call socket_is_disconnected
 
;-----------------------------------------------------------------------------------
.fin_timed:
; (re)start the 2 MSL timer
mov [ebx + TCP_SOCKET.timer_timed_wait], 2 * TCP_time_MSL
or [ebx + TCP_SOCKET.timer_flags], timer_flag_wait
 
;-----------------------------------------------------------------------------------
;
; Finally, drop the segment
;
;-----------------------------------------------------------------------------------
 
.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 .done
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: ACK now!\n"
 
.need_output:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: need output\n"
call tcp_output
 
.done:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: dumping\n"
 
call net_buff_free
jmp .loop
 
;-----------------------------------------------------------------------------------
;
; Drop segment, reply with an RST segment when needed
;
;-----------------------------------------------------------------------------------
 
;-----------------------------------------------------------------------------------
.drop_after_ack:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Drop after ACK\n"
 
push edx ebx
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
pop eax edx
 
test [edx + TCP_header.Flags], TH_RST
jnz .done
 
or [eax + TCP_SOCKET.t_flags], TF_ACKNOW
jmp .need_output
 
;-----------------------------------------------------------------------------------
.drop_with_reset:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Drop with reset\n"
 
push ebx edx
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
pop edx ebx
 
test [edx + TCP_header.Flags], TH_RST
jnz .done
 
; TODO: if its a multicast/broadcast, also drop
 
test [edx + TCP_header.Flags], TH_ACK
jnz .respond_ack
 
test [edx + TCP_header.Flags], TH_SYN
jnz .respond_syn
jmp .done
 
.respond_ack:
push ebx
mov cl, TH_RST
call tcp_respond
pop ebx
jmp .destroy_new_socket
 
.respond_syn:
push ebx
mov cl, TH_RST + TH_ACK
call tcp_respond
pop ebx
jmp .destroy_new_socket
 
;-----------------------------------------
; The connection has no associated socket
 
.no_socket:
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
.respond_seg_reset:
test [edx + TCP_header.Flags], TH_RST
jnz .drop_no_socket
 
; TODO: if its a multicast/broadcast, also drop
 
test [edx + TCP_header.Flags], TH_ACK
jnz .respond_seg_ack
 
test [edx + TCP_header.Flags], TH_SYN
jnz .respond_seg_syn
 
jmp .drop_no_socket
 
.respond_seg_ack:
mov cl, TH_RST
mov ebx, [device]
call tcp_respond_segment
jmp .drop_no_socket
 
.respond_seg_syn:
mov cl, TH_RST + TH_ACK
mov ebx, [device]
call tcp_respond_segment
jmp .drop_no_socket
 
;------------------------------------------------
; Unlock socket mutex and prepare to drop segment
 
.drop:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Dropping segment\n"
 
pusha
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
popa
 
;--------------------------------------------
; Destroy the newly created socket if needed
 
.destroy_new_socket:
test [temp_bits], TCP_BIT_DROPSOCKET
jz .drop_no_socket
 
mov eax, ebx
call socket_free
 
;------------------
; Drop the segment
 
.drop_no_socket:
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_input: Drop (no socket)\n"
 
call net_buff_free
jmp .loop
 
endp
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/ARP.inc
0,0 → 1,676
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2019. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; ARP.INC ;;
;; ;;
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Based on the work of [Johnny_B] and [smb] ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June- 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
ARP_NO_ENTRY = 0
ARP_VALID_MAPPING = 1
ARP_AWAITING_RESPONSE = 2
ARP_RESPONSE_TIMEOUT = 3
 
ARP_REQUEST_TTL = 31 ; 20 s
ARP_ENTRY_TTL = 937 ; 600 s
ARP_STATIC_ENTRY = -1
 
ARP_REQ_OPCODE = 0x0100 ; request
ARP_REP_OPCODE = 0x0200 ; reply
 
ARP_TABLE_SIZE = 20 ; Size of table
 
struct ARP_entry
 
IP dd ?
MAC dp ?
Status dw ?
TTL dw ?
 
ends
 
struct ARP_header
 
HardwareType dw ?
ProtocolType dw ?
HardwareSize db ?
ProtocolSize db ?
Opcode dw ?
SenderMAC dp ?
SenderIP dd ?
TargetMAC dp ?
TargetIP dd ?
 
ends
 
uglobal
align 4
 
ARP_table rb NET_DEVICES_MAX*(ARP_TABLE_SIZE * sizeof.ARP_entry)
 
ARP_entries rd NET_DEVICES_MAX
ARP_packets_tx rd NET_DEVICES_MAX
ARP_packets_rx rd NET_DEVICES_MAX
ARP_conflicts rd NET_DEVICES_MAX
 
 
endg
 
 
 
;-----------------------------------------------------------------;
; ;
; arp_init: Resets all ARP variables. ;
; ;
;-----------------------------------------------------------------;
macro arp_init {
 
xor eax, eax
mov edi, ARP_entries
mov ecx, 4*NET_DEVICES_MAX
rep stosd
 
}
 
;-----------------------------------------------------------------;
; ;
; 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
 
xor edi, edi
.loop_outer:
mov ecx, [ARP_entries + 4*edi]
test ecx, ecx
jz .exit
 
mov esi, (ARP_TABLE_SIZE * sizeof.ARP_entry)
imul esi, edi
add esi, ARP_table
.loop:
cmp [esi + ARP_entry.TTL], ARP_STATIC_ENTRY
je .next
 
dec [esi + ARP_entry.TTL]
jz .time_out
 
.next:
add esi, sizeof.ARP_entry
dec ecx
jnz .loop
jmp .exit
 
.time_out:
cmp [esi + ARP_entry.Status], ARP_AWAITING_RESPONSE
je .response_timeout
 
push esi edi ecx
call arp_del_entry
pop ecx edi esi
 
jmp .next
 
.response_timeout:
mov [esi + ARP_entry.Status], ARP_RESPONSE_TIMEOUT
mov [esi + ARP_entry.TTL], 10
 
jmp .next
 
.exit:
inc edi
cmp edi, NET_DEVICES_MAX
jb .loop_outer
 
}
 
 
;-----------------------------------------------------------------;
; ;
; arp_input ;
; ;
; IN: [esp] = Pointer to buffer ;
; [esp+4] = size of buffer ;
; ecx = packet size (without ethernet header) ;
; edx = packet ptr ;
; ebx = device ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
arp_input:
 
;-----------------------------------------
; Check validity and print some debug info
 
cmp ecx, sizeof.ARP_header
jb .exit
 
call net_ptr_to_num4
cmp edi, -1
jz .exit
 
inc [ARP_packets_rx + edi] ; update stats
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: got packet from %u.%u.%u.%u (device*4=%u)\n",\
[edx + ARP_header.SenderIP]:1, [edx + ARP_header.SenderIP + 1]:1,\
[edx + ARP_header.SenderIP + 2]:1, [edx + ARP_header.SenderIP + 3]:1, edi
 
;------------------------------
; First, check for IP collision
 
mov eax, [edx + ARP_header.SenderIP]
cmp eax, [IPv4_address + edi]
je .collision
 
;---------------------
; Handle reply packets
 
cmp [edx + ARP_header.Opcode], ARP_REP_OPCODE
jne .maybe_request
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: It's a reply\n"
 
mov ecx, [ARP_entries + edi]
test ecx, ecx
jz .exit
 
mov esi, edi
imul esi, (ARP_TABLE_SIZE * sizeof.ARP_entry)/4
add esi, ARP_table
.loop:
cmp [esi + ARP_entry.IP], eax
je .gotit
add esi, sizeof.ARP_entry
dec ecx
jnz .loop
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: no matching entry found\n"
jmp .exit
 
.gotit:
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: found matching entry\n"
 
cmp [esi + ARP_entry.TTL], ARP_STATIC_ENTRY ; if it is a static entry, dont touch it
je .exit
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: updating entry\n"
 
mov [esi + ARP_entry.Status], ARP_VALID_MAPPING
mov [esi + ARP_entry.TTL], ARP_ENTRY_TTL
 
mov eax, dword [edx + ARP_header.SenderMAC]
mov dword [esi + ARP_entry.MAC], eax
mov cx, word [edx + ARP_header.SenderMAC + 4]
mov word [esi + ARP_entry.MAC + 4], cx
 
jmp .exit
 
;-----------------------
; Handle request packets
 
.maybe_request:
cmp [edx + ARP_header.Opcode], ARP_REQ_OPCODE
jne .exit
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: its a request\n"
 
mov eax, [IPv4_address + edi]
cmp eax, [edx + ARP_header.TargetIP] ; Is it looking for my IP address?
jne .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)
 
lea esi, [edx + ARP_header.SenderMAC]
lea edi, [edx + ARP_header.TargetMAC]
movsd ; Move Sender Mac to Dest MAC
movsw ;
movsd ; Move sender IP to Dest IP
 
pop esi
mov esi, [net_device_list + esi]
lea esi, [esi + ETH_DEVICE.mac]
lea edi, [edx + ARP_header.SenderMAC]
movsd ; Copy MAC address from in MAC_LIST
movsw ;
pop eax
stosd ; Write our IP
 
mov [edx + ARP_header.Opcode], ARP_REP_OPCODE
 
; Now, Fill in ETHERNET header
 
mov edi, [esp]
add edi, [edi + NET_BUFF.offset]
lea esi, [edx + ARP_header.TargetMAC]
movsd
movsw
lea esi, [edx + ARP_header.SenderMAC]
movsd
movsw
; mov ax , ETHER_ARP ; It's already there, I'm sure of it!
; stosw
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: Sending reply\n"
 
call [ebx + NET_DEVICE.transmit]
ret
 
.collision:
inc [ARP_conflicts + edi]
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: IP address conflict detected!\n"
 
.exit:
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_input: exiting\n"
call net_buff_free
ret
 
;-----------------------------------------------------------------;
; ;
; arp_output_request ;
; ;
; IN: ebx = device ptr ;
; eax = IP ;
; ;
; OUT: scratched: probably everything ;
; ;
;-----------------------------------------------------------------;
align 4
arp_output_request:
 
push eax
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_output_request: ip=%u.%u.%u.%u device=0x%x\n",\
[esp]:1, [esp + 1]:1, [esp + 2]:1, [esp + 3]:1, ebx
 
mov ax, ETHER_PROTO_ARP
mov ecx, sizeof.ARP_header
mov edx, ETH_BROADCAST ; broadcast mac
call eth_output
jz .exit
 
mov [edi + ARP_header.HardwareType], 0x0100 ; Ethernet
mov [edi + ARP_header.ProtocolType], 0x0008 ; IP
mov [edi + ARP_header.HardwareSize], 6 ; MAC-addr length
mov [edi + ARP_header.ProtocolSize], 4 ; IP-addr length
mov [edi + ARP_header.Opcode], ARP_REQ_OPCODE ; Request
 
add edi, ARP_header.SenderMAC
lea esi, [ebx + ETH_DEVICE.mac] ; SenderMac
movsw ;
movsd ;
 
push edi
call net_ptr_to_num4
inc [ARP_packets_tx + edi] ; assume we will succeed
lea esi, [IPv4_address + edi] ; SenderIP
pop edi
movsd
 
mov esi, ETH_BROADCAST ; DestMac
movsw ;
movsd ;
popd [edi] ; DestIP
 
push eax
call [ebx + NET_DEVICE.transmit]
ret
 
.exit:
add esp, 4
DEBUGF DEBUG_NETWORK_ERROR, "ARP_output_request: send failed\n"
ret
 
 
;-----------------------------------------------------------------;
; ;
; arp_add_entry: Add or update an entry in the ARP table. ;
; ;
; IN: esi = ptr to entry (can easily be made on the stack) ;
; edi = device num*4 ;
; ;
; OUT: eax = entry number on success ;
; eax = -1 on error ;
; esi = ptr to newly created entry ;
; ;
;-----------------------------------------------------------------;
align 4
arp_add_entry:
 
; TODO: use a mutex to lock ARP table
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_add_entry: device=%u\n", edi
 
mov ecx, [ARP_entries + edi]
cmp ecx, ARP_TABLE_SIZE ; list full ?
jae .full
 
; From this point on, we can only fail if IP has a static entry, or if table is corrupt.
 
inc [ARP_entries + edi] ; assume we will succeed
 
push edi
xor ecx, ecx
imul edi, ARP_TABLE_SIZE*sizeof.ARP_entry/4
add edi, ARP_table
mov eax, [esi + ARP_entry.IP]
.loop:
cmp [edi + ARP_entry.Status], ARP_NO_ENTRY ; is this slot empty?
je .add
 
cmp [edi + ARP_entry.IP], eax ; if not, check if it doesnt collide
jne .maybe_next
 
cmp [edi + ARP_entry.TTL], ARP_STATIC_ENTRY ; ok, its the same IP, update it if not static
jne .add
 
DEBUGF DEBUG_NETWORK_ERROR, "ARP_add_entry: failed, IP already has a static entry\n"
jmp .error
 
.maybe_next: ; try the next slot
add edi, sizeof.ARP_entry
inc ecx
cmp ecx, ARP_TABLE_SIZE
jb .loop
 
.add:
push ecx
mov ecx, sizeof.ARP_entry/2
rep movsw
pop ecx
lea esi, [edi - sizeof.ARP_entry]
pop edi
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_add_entry: entry=%u\n", ecx
 
ret
 
.error:
pop edi
dec [ARP_entries + edi]
DEBUGF DEBUG_NETWORK_ERROR, "ARP_add_entry_failed\n"
.full:
mov eax, -1
ret
 
 
;-----------------------------------------------------------------;
; ;
; arp_del_entry: Remove an entry from the ARP table. ;
; ;
; IN: esi = ptr to arp entry ;
; edi = device number ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
arp_del_entry:
 
; TODO: use a mutex to lock ARP table
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_del_entry: entry=0x%x entrys=%u\n", esi, [ARP_entries + 4*edi]
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_del_entry: IP=%u.%u.%u.%u\n", \
[esi + ARP_entry.IP]:1, [esi + ARP_entry.IP + 1]:1, [esi + ARP_entry.IP + 2]:1, [esi + ARP_entry.IP + 3]:1
 
push edi
imul edi, (ARP_TABLE_SIZE) * sizeof.ARP_entry
lea ecx, [ARP_table + (ARP_TABLE_SIZE - 1) * sizeof.ARP_entry + edi]
sub ecx, esi
shr ecx, 1
 
; move all trailing entries, sizeof.ARP_entry bytes to left.
mov edi, esi
add esi, sizeof.ARP_entry
rep movsw
 
; now add an empty entry to the end (erasing previous one)
xor eax, eax
mov ecx, sizeof.ARP_entry/2
rep stosw
 
pop edi
dec [ARP_entries + 4*edi]
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_del_entry: success\n"
 
ret
 
 
 
 
 
;-----------------------------------------------------------------;
; ;
; arp_ip_to_mac: Translate an IP address to a MAC address. ;
; ;
; IN: eax = IPv4 address ;
; edi = device number * 4 ;
; ;
; OUT: eax = -1 on error ;
; eax = -2 when request send ;
; eax = first two bytes of mac on success ;
; ebx = last four bytes of mac on success ;
; edi = unchanged ;
; ;
;-----------------------------------------------------------------;
align 4
arp_ip_to_mac:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_IP_to_MAC: %u.%u", al, ah
rol eax, 16
DEBUGF DEBUG_NETWORK_VERBOSE, ".%u.%u device*4: %u\n", al, ah, edi
rol eax, 16
 
cmp eax, 0xffffffff
je .broadcast
 
;--------------------------------
; Try to find the IP in ARP_table
 
mov ecx, [ARP_entries + edi]
test ecx, ecx
jz .not_in_list
mov esi, edi
imul esi, (sizeof.ARP_entry * ARP_TABLE_SIZE)/4
add esi, ARP_table + ARP_entry.IP
.scan_loop:
cmp [esi], eax
je .found_it
add esi, sizeof.ARP_entry
dec ecx
jnz .scan_loop
 
.not_in_list:
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_IP_to_MAC: preparing for ARP request\n"
 
push eax edi ; save IP for ARP_output_request
; Now craft the ARP entry on the stack
pushw ARP_REQUEST_TTL ; TTL
pushw ARP_AWAITING_RESPONSE ; status
pushd 0 ; mac
pushw 0
pushd eax ; IP
mov esi, esp
 
; Add it to the list
call arp_add_entry
 
; Delete the temporary entry
add esp, sizeof.ARP_entry ; clear the entry from stack
 
; If we could not add it to the list, give up
cmp eax, -1 ; did ARP_add_entry fail?
je .full
 
;-----------------------------------------------
; At this point, we got an ARP entry in the list
 
; Now send a request packet on the network
pop edi eax ; IP in eax, device number in ebx, for ARP_output_request
 
push esi edi
mov ebx, [net_device_list + edi]
call arp_output_request
pop edi esi
.found_it:
cmp [esi + ARP_entry.Status], ARP_VALID_MAPPING ; Does it have a MAC assigned?
je .valid
 
if ARP_BLOCK
 
cmp [esi + ARP_entry.Status], ARP_AWAITING_RESPONSE ; Are we waiting for reply from remote end?
jne .give_up
push esi
mov esi, 10 ; wait 10 ms
call delay_ms
pop esi
jmp .found_it ; now check again
 
else
 
jmp .give_up
 
end if
 
.valid:
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_IP_to_MAC: found MAC\n"
movzx eax, word[esi + ARP_entry.MAC]
mov ebx, dword[esi + ARP_entry.MAC + 2]
ret
 
.full:
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_IP_to_MAC: table is full!\n"
add esp, 8
.give_up:
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_IP_to_MAC: entry has no valid mapping!\n"
mov eax, -1
ret
 
.broadcast:
mov eax, 0x0000ffff
mov ebx, 0xffffffff
ret
 
 
;-----------------------------------------------------------------;
; ;
; arp_api: Part of system function 76. ;
; ;
; IN: bl = subfunction number ;
; bh = device number ;
; ecx, edx, .. depends on subfunction ;
; ;
; OUT: depends on subfunction ;
; ;
;-----------------------------------------------------------------;
align 4
arp_api:
 
movzx eax, bh
shl eax, 2
 
and ebx, 0xff
cmp ebx, .number
ja .error
jmp dword [.table + 4*ebx]
 
.table:
dd .packets_tx ; 0
dd .packets_rx ; 1
dd .entries ; 2
dd .read ; 3
dd .write ; 4
dd .remove ; 5
dd .send_announce ; 6
dd .conflicts ; 7
.number = ($ - .table) / 4 - 1
 
.error:
mov eax, -1
ret
 
.packets_tx:
mov eax, [ARP_packets_tx + eax]
ret
 
.packets_rx:
mov eax, [ARP_packets_rx + eax]
ret
 
.conflicts:
mov eax, [ARP_conflicts + eax]
ret
 
.entries:
mov eax, [ARP_entries + eax]
ret
 
.read:
cmp ecx, [ARP_entries + eax]
jae .error
shr eax, 2
imul eax, sizeof.ARP_entry*ARP_TABLE_SIZE
add eax, ARP_table
; edi = pointer to buffer
; ecx = # entry
imul ecx, sizeof.ARP_entry
lea esi, [eax + ecx]
mov ecx, sizeof.ARP_entry/2
rep movsw
 
xor eax, eax
ret
 
.write:
; esi = pointer to buffer
mov edi, eax
call arp_add_entry ; out: eax = entry number, -1 on error
ret
 
.remove:
; ecx = # entry
cmp ecx, [ARP_entries + eax]
jae .error
imul ecx, sizeof.ARP_entry
lea esi, [ARP_table + ecx]
mov edi, eax
shr edi, 2
call arp_del_entry
ret
 
.send_announce:
mov ebx, [net_device_list + eax]
mov eax, [IPv4_address + eax]
call arp_output_request ; now send a gratuitous ARP
ret
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/udp.inc
0,0 → 1,434
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2019. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; UDP.INC ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
 
struct UDP_header
 
SourcePort dw ?
DestinationPort dw ?
Length dw ? ; Length of (UDP Header + Data)
Checksum dw ?
 
ends
 
 
uglobal
align 4
 
UDP_packets_tx rd NET_DEVICES_MAX
UDP_packets_rx rd NET_DEVICES_MAX
 
endg
 
 
;-----------------------------------------------------------------;
; ;
; udp_init: This function resets all UDP variables ;
; ;
;-----------------------------------------------------------------;
macro udp_init {
 
xor eax, eax
mov edi, UDP_packets_tx
mov ecx, 2*NET_DEVICES_MAX
rep stosd
}
 
 
macro udp_checksum IP1, IP2 { ; esi = ptr to udp packet, ecx = packet size, destroys: ecx, edx
 
; Pseudoheader
mov edx, IP_PROTO_UDP
 
add dl, byte[IP1+1]
adc dh, byte[IP1+0]
adc dl, byte[IP1+3]
adc dh, byte[IP1+2]
 
adc dl, byte[IP2+1]
adc dh, byte[IP2+0]
adc dl, byte[IP2+3]
adc dh, byte[IP2+2]
 
adc dl, cl ; byte[esi+UDP_header.Length+1]
adc dh, ch ; byte[esi+UDP_header.Length+0]
 
; Done with pseudoheader, now do real header
adc dl, byte[esi+UDP_header.SourcePort+1]
adc dh, byte[esi+UDP_header.SourcePort+0]
 
adc dl, byte[esi+UDP_header.DestinationPort+1]
adc dh, byte[esi+UDP_header.DestinationPort+0]
 
adc dl, byte[esi+UDP_header.Length+1]
adc dh, byte[esi+UDP_header.Length+0]
 
adc edx, 0
 
; Done with header, now do data
push esi
movzx ecx, [esi+UDP_header.Length]
rol cx , 8
sub cx , sizeof.UDP_header
add esi, sizeof.UDP_header
 
call checksum_1
call checksum_2
pop esi
 
add [esi+UDP_header.Checksum], dx ; this final instruction will set or clear ZF :)
 
}
 
 
;-----------------------------------------------------------------;
; ;
; udp_input: Inject the UDP data in the application sockets. ;
; ;
; IN: [esp] = ptr to buffer ;
; ebx = ptr to device struct ;
; ecx = UDP packet size ;
; edx = ptr to IPv4 header ;
; esi = ptr to UDP packet data ;
; edi = interface number*4 ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
udp_input:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: size=%u\n", ecx
 
; First validate, checksum
 
neg [esi + UDP_header.Checksum] ; substract checksum from 0
jz .no_checksum ; if checksum is zero, it is considered valid
 
; otherwise, we will re-calculate the checksum and add it to this value, thus creating 0 when it is correct
 
mov eax, edx
udp_checksum (eax+IPv4_header.SourceAddress), (eax+IPv4_header.DestinationAddress)
jnz .checksum_mismatch
 
.no_checksum:
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: checksum ok\n"
 
; Convert length to little endian
 
rol [esi + UDP_header.Length], 8
 
; Look for a socket where
; IP Packet UDP Destination Port = local Port
; IP Packet SA = Remote IP
 
pusha
mov ecx, socket_mutex
call mutex_lock
popa
 
mov cx, [esi + UDP_header.SourcePort]
mov dx, [esi + UDP_header.DestinationPort]
mov eax, net_sockets
.next_socket:
mov eax, [eax + SOCKET.NextPtr]
or eax, eax
jz .unlock_dump
 
cmp [eax + SOCKET.Domain], AF_INET4
jne .next_socket
 
cmp [eax + SOCKET.Protocol], IP_PROTO_UDP
jne .next_socket
 
cmp [eax + UDP_SOCKET.LocalPort], dx
jne .next_socket
 
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: socket=%x\n", eax
 
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
;;; TODO: when packet is processed, check more sockets?!
 
; FIXME: check remote IP if possible
;
; cmp [eax + IP_SOCKET.RemoteIP], 0xffffffff
; je @f
; cmp [eax + IP_SOCKET.RemoteIP],
; jne .next_socket
; @@:
 
cmp [eax + UDP_SOCKET.RemotePort], 0
je .updateport
 
cmp [eax + UDP_SOCKET.RemotePort], cx
jne .dump
 
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
 
.updatesock:
inc [UDP_packets_rx + edi]
 
movzx ecx, [esi + UDP_header.Length]
sub ecx, sizeof.UDP_header
add esi, sizeof.UDP_header
 
jmp socket_input
 
.updateport:
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
 
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: new remote port=%x\n", cx ; FIXME: find a way to print big endian values with debugf
mov [eax + UDP_SOCKET.RemotePort], cx
jmp .updatesock
 
.unlock_dump:
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: no socket found\n"
jmp .dump
 
.checksum_mismatch:
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: checksum mismatch\n"
 
.dump:
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_input: dumping\n"
call net_buff_free
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; udp_output: Create an UDP packet. ;
; ;
; IN: eax = socket pointer ;
; ecx = number of bytes to send ;
; esi = pointer to data ;
; ;
; OUT: eax = -1 on error ;
; ;
;-----------------------------------------------------------------;
 
align 4
udp_output:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_output: socket=%x bytes=%u data_ptr=%x\n", eax, ecx, esi
 
mov dx, [eax + UDP_SOCKET.RemotePort]
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_output: remote port=%x, ", dx ; FIXME: find a way to print big endian values with debugf
rol edx, 16
mov dx, [eax + UDP_SOCKET.LocalPort]
DEBUGF DEBUG_NETWORK_VERBOSE, "local port=%x\n", dx
 
sub esp, 4 ; Data ptr will be placed here
push edx esi
mov ebx, [eax + IP_SOCKET.device]
mov edx, [eax + IP_SOCKET.LocalIP]
mov edi, [eax + IP_SOCKET.RemoteIP]
mov al, [eax + IP_SOCKET.ttl]
mov ah, IP_PROTO_UDP
add ecx, sizeof.UDP_header
call ipv4_output
jz .fail
mov [esp + 8], eax ; pointer to buffer start
 
mov [edi + UDP_header.Length], cx
rol [edi + UDP_header.Length], 8
 
pop esi
push edi ecx
sub ecx, sizeof.UDP_header
add edi, sizeof.UDP_header
shr ecx, 2
rep movsd
mov ecx, [esp]
and ecx, 3
rep movsb
pop ecx edi
 
pop dword [edi + UDP_header.SourcePort]
 
; Checksum
mov esi, edi
mov [edi + UDP_header.Checksum], 0
udp_checksum (edi-4), (edi-8) ; FIXME: IPv4 packet could have options..
 
DEBUGF DEBUG_NETWORK_VERBOSE, "UDP_output: sending with device %x\n", ebx
call [ebx + NET_DEVICE.transmit]
test eax, eax
jnz @f
call net_ptr_to_num4
inc [UDP_packets_tx + edi]
@@:
 
ret
 
.fail:
DEBUGF DEBUG_NETWORK_ERROR, "UDP_output: failed\n"
add esp, 4+4+8
or eax, -1
ret
 
 
 
 
;-----------------------------------------------------------------;
; ;
; udp_connect ;
; ;
; IN: eax = socket pointer ;
; ;
; OUT: eax = 0 on success ;
; eax = -1 on error ;
; ebx = error code on error ;
; ;
;-----------------------------------------------------------------;
align 4
udp_connect:
 
test [eax + SOCKET.state], SS_ISCONNECTED
jz @f
call udp_disconnect
@@:
 
push eax edx
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
pop edx eax
 
; Fill in remote port and IP
pushw [edx + 2]
pop [eax + UDP_SOCKET.RemotePort]
 
pushd [edx + 4]
pop [eax + UDP_SOCKET.RemoteIP]
 
; Find route to host
pusha
push eax
mov ebx, [eax + UDP_SOCKET.device]
mov edx, [eax + UDP_SOCKET.LocalIP]
mov eax, [eax + UDP_SOCKET.RemoteIP]
call ipv4_route
test eax, eax
jz .enoroute
pop eax
mov ebx, [net_device_list + edi]
mov [eax + UDP_SOCKET.device], ebx
mov [eax + UDP_SOCKET.LocalIP], edx
popa
 
; Find a local port, if user didnt define one
cmp [eax + UDP_SOCKET.LocalPort], 0
jne @f
call socket_find_port
@@:
 
push eax
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
pop eax
 
call socket_is_connected
 
xor eax, eax
ret
 
.enoroute:
pop eax
 
push eax
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
pop eax
 
popa
xor eax, eax
dec eax
mov ebx, EADDRNOTAVAIL
ret
 
 
;-----------------------------------------------------------------;
; ;
; UDP_disconnect ;
; ;
; IN: eax = socket pointer ;
; ;
; OUT: eax = socket pointer ;
; ;
;-----------------------------------------------------------------;
align 4
udp_disconnect:
 
; TODO: remove the pending received data
 
call socket_is_disconnected
 
ret
 
 
 
 
 
;-----------------------------------------------------------------;
; ;
; UDP_api: Part of system function 76 ;
; ;
; IN: bl = subfunction number in bl ;
; bh = device number in bh ;
; ecx, edx, .. depends on subfunction ;
; ;
; OUT: depends on subfunction ;
; ;
;-----------------------------------------------------------------;
align 4
udp_api:
 
movzx eax, bh
shl eax, 2
 
test bl, bl
jz .packets_tx ; 0
dec bl
jz .packets_rx ; 1
 
.error:
mov eax, -1
ret
 
.packets_tx:
mov eax, [UDP_packets_tx + eax]
ret
 
.packets_rx:
mov eax, [UDP_packets_rx + eax]
ret
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/IPv4.inc
0,0 → 1,1161
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2019. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; IPv4.INC ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Based on the work of [Johnny_B] and [smb] ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
IPv4_MAX_FRAGMENTS = 64
IPv4_MAX_ROUTES = 64
 
IPv4_ROUTE_FLAG_UP = 1 shl 0
IPv4_ROUTE_FLAG_GATEWAY = 1 shl 1
IPv4_ROUTE_FLAG_HOST = 1 shl 2
IPv4_ROUTE_FLAG_D = 1 shl 3 ; Route was created by a redirect
IPv4_ROUTE_FLAG_M = 1 shl 4 ; Route was modified by a redirect
 
struct IPv4_header
 
VersionAndIHL db ? ; Version[0-3 bits] and IHL(header length)[4-7 bits]
TypeOfService db ? ; precedence [7-5] minimize delay [4], maximize throughput [3], maximize riliability [2] minimize momentary cost [1] and zero [0]
TotalLength dw ?
Identification dw ?
FlagsAndFragmentOffset dw ? ; Flags[0-2] and FragmentOffset[3-15]
TimeToLive db ? ;
Protocol db ?
HeaderChecksum dw ?
SourceAddress dd ?
DestinationAddress dd ?
 
ends
 
struct IPv4_FRAGMENT_slot
 
ttl dw ? ; Time to live for this entry, 0 for empty slot's
id dw ? ; Identification field from IP header
SrcIP dd ? ; .. from IP header
DstIP dd ? ; .. from IP header
ptr dd ? ; Pointer to first packet
 
ends
 
struct IPv4_FRAGMENT_entry ; This structure will replace the ethernet header in fragmented ip packets
 
PrevPtr dd ? ; Pointer to previous fragment entry (-1 for first packet)
NextPtr dd ? ; Pointer to next fragment entry (-1 for last packet)
Owner dd ? ; Pointer to structure of driver
rb 2 ; to match ethernet header size ;;; FIXME
; Ip header begins here (we will need the IP header to re-construct the complete packet)
ends
 
;struct IPv4_ROUTE
;
; Destination dd ?
; Gateway dd ?
; Flags dd ?
; Use dd ?
; Interface dd ?
;
;ends
 
uglobal
align 4
 
IPv4_address rd NET_DEVICES_MAX
IPv4_subnet rd NET_DEVICES_MAX
IPv4_nameserver rd NET_DEVICES_MAX
IPv4_gateway rd NET_DEVICES_MAX
IPv4_broadcast rd NET_DEVICES_MAX
 
IPv4_packets_tx rd NET_DEVICES_MAX
IPv4_packets_rx rd NET_DEVICES_MAX
IPv4_packets_dumped rd NET_DEVICES_MAX
 
IPv4_fragments rb IPv4_MAX_FRAGMENTS * sizeof.IPv4_FRAGMENT_slot
 
; IPv4_routes rd IPv4_MAX_ROUTES * sizeof.IPv4_ROUTE
 
endg
 
 
;-----------------------------------------------------------------;
; ;
; ipv4_init: Resets all IPv4 variables ;
; ;
;-----------------------------------------------------------------;
macro ipv4_init {
 
xor eax, eax
mov edi, IPv4_address
mov ecx, 7*NET_DEVICES_MAX + (sizeof.IPv4_FRAGMENT_slot*IPv4_MAX_FRAGMENTS)/4
rep stosd
 
}
 
 
;-----------------------------------------------------------------;
; ;
; Decrease TimeToLive of all fragment slots ;
; ;
;-----------------------------------------------------------------;
macro ipv4_decrease_fragment_ttls {
 
local .loop, .next
 
mov esi, IPv4_fragments
mov ecx, IPv4_MAX_FRAGMENTS
.loop:
cmp [esi + IPv4_FRAGMENT_slot.ttl], 0
je .next
dec [esi + IPv4_FRAGMENT_slot.ttl]
jz .died
.next:
add esi, sizeof.IPv4_FRAGMENT_slot
dec ecx
jnz .loop
jmp .done
 
.died:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4 Fragment slot timed-out!\n"
;;; TODO: clear all entry's of timed-out slot
jmp .next
 
.done:
}
 
 
 
macro ipv4_checksum ptr {
 
; This is the fast procedure to create or check an 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
 
}
 
 
 
;-----------------------------------------------------------------;
; ;
; ipv4_input: Check if IPv4 Packet isnt damaged and call ;
; appropriate handler. (TCP/UDP/ICMP/..) ;
; We will also re-construct fragmented packets. ;
; ;
; IN: Pointer to buffer in [esp] ;
; pointer to device struct in ebx ;
; pointer to IPv4 header in edx ;
; size of IPv4 packet in ecx ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
ipv4_input:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: packet from %u.%u.%u.%u ",\
[edx + IPv4_header.SourceAddress + 0]:1,[edx + IPv4_header.SourceAddress + 1]:1,\
[edx + IPv4_header.SourceAddress + 2]:1,[edx + IPv4_header.SourceAddress + 3]:1
DEBUGF DEBUG_NETWORK_VERBOSE, "to %u.%u.%u.%u\n",\
[edx + IPv4_header.DestinationAddress + 0]:1,[edx + IPv4_header.DestinationAddress + 1]:1,\
[edx + IPv4_header.DestinationAddress + 2]:1,[edx + IPv4_header.DestinationAddress + 3]:1
 
call net_ptr_to_num4
cmp edi, -1
je .invalid_device
 
;-------------------------------
; re-calculate the checksum
 
ipv4_checksum edx
jnz .dump ; if checksum isn't valid then dump packet
 
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Checksum ok\n"
 
;--------------------------------
; Check if destination IP matches
 
; local ip (Using RFC1122 strong end system model)
mov eax, [edx + IPv4_header.DestinationAddress]
cmp eax, [IPv4_address + edi]
je .ip_ok
 
; network layer broadcast
cmp eax, [IPv4_broadcast + edi]
je .ip_ok
 
; physical layer broadcast (255.255.255.255)
cmp eax, 0xffffffff
je .ip_ok
 
; multicast (224.0.0.0/4 = 224.0.0.0 to 239.255.255.255)
and eax, 0x0fffffff
cmp eax, 224
je .ip_ok
 
; maybe we just dont have an IP yet and should accept everything on the IP level
cmp [IPv4_address + edi], 0
je .ip_ok
 
; or it's just not meant for us.. :(
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Destination address does not match!\n"
jmp .dump
 
;------------------------
; Now we can update stats
 
.ip_ok:
inc [IPv4_packets_rx + edi]
 
;----------------------------------
; Check if the packet is fragmented
 
test [edx + IPv4_header.FlagsAndFragmentOffset], 1 shl 5 ; Is 'more fragments' flag set ?
jnz .has_fragments ; If so, we definately have a fragmented packet
 
test [edx + IPv4_header.FlagsAndFragmentOffset], 0xff1f ; If flag is not set, but there is a fragment offset, the packet is last in series of fragmented packets
jnz .is_last_fragment
 
;-------------------------------------------------------------------
; No, it's just a regular IP packet, pass it to the higher protocols
 
.handle_it: ; We reach here if packet hasnt been fragmented, or when it already has been re-constructed
 
movzx esi, [edx + IPv4_header.VersionAndIHL] ; Calculate Header length by using IHL field
and esi, 0x0000000f ;
shl esi, 2 ;
 
movzx ecx, [edx + IPv4_header.TotalLength] ; Calculate length of encapsulated Packet
xchg cl, ch ;
sub ecx, esi ;
 
mov al, [edx + IPv4_header.Protocol]
add esi, edx ; make esi ptr to data
 
cmp al, IP_PROTO_TCP
je tcp_input
 
cmp al, IP_PROTO_UDP
je udp_input
 
cmp al, IP_PROTO_ICMP
je icmp_input
 
;-------------------------------
; Look for a matching RAW socket
pusha
mov ecx, socket_mutex
call mutex_lock
popa
 
add ecx, esi
sub ecx, edx
mov esi, edx
movzx edx, al
mov eax, net_sockets
.next_socket:
mov eax, [eax + SOCKET.NextPtr]
or eax, eax
jz .dump_unlock
 
cmp [eax + SOCKET.Domain], AF_INET4
jne .next_socket
 
cmp [eax + SOCKET.Protocol], edx
jne .next_socket
 
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: found matching RAW socket: 0x%x\n", eax
 
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
 
jmp socket_input
 
.dump_unlock:
 
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: unknown protocol %u\n", al
 
.dump:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: dumping\n"
inc [IPv4_packets_dumped + edi]
call net_buff_free
ret
 
.invalid_device:
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_input: packet originated from invalid device\n"
call net_buff_free
ret
 
 
;---------------------------
; Fragmented packet handler
 
 
.has_fragments:
movzx eax, [edx + IPv4_header.FlagsAndFragmentOffset]
xchg al, ah
shl ax, 3
 
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: fragmented packet offset=%u id=%x ptr=0x%x\n", ax, [edx + IPv4_header.Identification]:4, edx
 
test ax, ax ; Is this the first packet of the fragment?
jz .is_first_fragment
 
 
;-------------------------------------------------------
; We have a fragmented IP packet, but it's not the first
 
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Middle fragment packet received!\n"
 
call ipv4_find_fragment_slot
cmp esi, -1
je .dump
 
mov [esi + IPv4_FRAGMENT_slot.ttl], 15 ; Reset the ttl
mov esi, [esi + IPv4_FRAGMENT_slot.ptr]
or edi, -1
.find_last_entry: ; The following routine will try to find the last entry
cmp edi, [esi + IPv4_FRAGMENT_entry.PrevPtr]
jne .destroy_slot ; Damn, something screwed up, remove the whole slot (and free buffers too if possible!)
mov edi, esi
mov esi, [esi + IPv4_FRAGMENT_entry.NextPtr]
cmp esi, -1
jne .find_last_entry
; We found the last entry (pointer is now in edi)
; We are going to overwrite the ethernet header in received packet with a FRAGMENT_entry structure
 
pop eax ; pointer to packet
mov [edi + IPv4_FRAGMENT_entry.NextPtr], eax ; update pointer of previous entry to the new entry
mov [eax + IPv4_FRAGMENT_entry.NextPtr], -1
mov [eax + IPv4_FRAGMENT_entry.PrevPtr], edi
mov [eax + IPv4_FRAGMENT_entry.Owner], ebx
 
ret
 
 
;------------------------------------
; We have received the first fragment
 
.is_first_fragment:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: First fragment packet received!\n"
; try to locate a free slot..
mov ecx, IPv4_MAX_FRAGMENTS
mov esi, IPv4_fragments
.find_free_slot:
cmp word [esi + IPv4_FRAGMENT_slot.ttl], 0
je .found_free_slot
add esi, sizeof.IPv4_FRAGMENT_slot
loop .find_free_slot
jmp .dump ; If no free slot was found, dump the packet
 
.found_free_slot: ; We found a free slot, let's fill in the FRAGMENT_slot structure
mov [esi + IPv4_FRAGMENT_slot.ttl], 15 ; RFC recommends 15 secs as ttl
mov ax, [edx + IPv4_header.Identification]
mov [esi + IPv4_FRAGMENT_slot.id], ax
mov eax, [edx + IPv4_header.SourceAddress]
mov [esi + IPv4_FRAGMENT_slot.SrcIP], eax
mov eax, [edx + IPv4_header.DestinationAddress]
mov [esi + IPv4_FRAGMENT_slot.DstIP], eax
pop eax
mov [esi + IPv4_FRAGMENT_slot.ptr], eax
; Now, replace ethernet header in original buffer with a FRAGMENT_entry structure
mov [eax + IPv4_FRAGMENT_entry.NextPtr], -1
mov [eax + IPv4_FRAGMENT_entry.PrevPtr], -1
mov [eax + IPv4_FRAGMENT_entry.Owner], ebx
 
ret
 
 
;-----------------------------------
; We have received the last fragment
 
.is_last_fragment:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Last fragment packet received!\n"
 
call ipv4_find_fragment_slot
cmp esi, -1
je .dump
 
mov esi, [esi + IPv4_FRAGMENT_slot.ptr] ; We found the first entry, let's calculate total size of the packet in eax, so we can allocate a buffer
push esi
xor eax, eax
or edi, -1
 
.count_bytes:
cmp [esi + IPv4_FRAGMENT_entry.PrevPtr], edi
jne .destroy_slot_pop ; Damn, something screwed up, remove the whole slot (and free buffers too if possible!)
mov cx, [esi + sizeof.IPv4_FRAGMENT_entry + IPv4_header.TotalLength] ; Add total length
xchg cl, ch
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Packet size=%u\n", cx
add ax, cx
movzx cx, [esi + sizeof.IPv4_FRAGMENT_entry + IPv4_header.VersionAndIHL] ; Sub Header length
and cx, 0x000F
shl cx, 2
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Header size=%u\n", cx
sub ax, cx
mov edi, esi
mov esi, [esi + IPv4_FRAGMENT_entry.NextPtr]
cmp esi, -1
jne .count_bytes
 
mov esi, [esp+4]
mov [edi + IPv4_FRAGMENT_entry.NextPtr], esi ; Add this packet to the chain, this simplifies the following code
mov [esi + IPv4_FRAGMENT_entry.NextPtr], -1
mov [esi + IPv4_FRAGMENT_entry.PrevPtr], edi
mov [esi + IPv4_FRAGMENT_entry.Owner], ebx
 
mov cx, [edx + IPv4_header.TotalLength] ; Note: This time we dont substract Header length
xchg cl, ch
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Packet size=%u\n", cx
add ax, cx
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Total Received data size=%u\n", eax
 
push eax
mov ax, [edx + IPv4_header.FlagsAndFragmentOffset]
xchg al, ah
shl ax, 3
add cx, ax
pop eax
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Total Fragment size=%u\n", ecx
 
cmp ax, cx
jne .destroy_slot_pop
 
push eax
push eax
call kernel_alloc
test eax, eax
je .destroy_slot_pop ; If we dont have enough space to allocate the buffer, discard all packets in slot
mov edx, [esp+4] ; Get pointer to first fragment entry back in edx
 
.rebuild_packet_loop:
movzx ecx, [edx + sizeof.IPv4_FRAGMENT_entry + IPv4_header.FlagsAndFragmentOffset] ; Calculate the fragment offset
xchg cl, ch ; intel byte order
shl cx, 3 ; multiply by 8 and clear first 3 bits
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Fragment offset=%u\n", cx
 
lea edi, [eax + ecx] ; Notice that edi will be equal to eax for first fragment
movzx ebx, [edx + sizeof.IPv4_FRAGMENT_entry + IPv4_header.VersionAndIHL] ; Find header size (in ebx) of fragment
and bx, 0x000F ;
shl bx, 2 ;
 
lea esi, [edx + sizeof.IPv4_FRAGMENT_entry] ; Set esi to the correct begin of fragment
movzx ecx, [edx + sizeof.IPv4_FRAGMENT_entry + IPv4_header.TotalLength] ; Calculate total length of fragment
xchg cl, ch ; intel byte order
 
cmp edi, eax ; Is this packet the first fragment ?
je .first_fragment
sub cx, bx ; If not, dont copy the header
add esi, ebx ;
.first_fragment:
 
 
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Copying %u bytes from 0x%x to 0x%x\n", ecx, esi, edi
push cx ; First copy dword-wise, then byte-wise
shr cx, 2 ;
rep movsd ;
pop cx ;
and cx, 3 ;
rep movsb ;
 
push eax
push [edx + IPv4_FRAGMENT_entry.Owner] ; we need to remeber the owner, in case this is the last packet
push [edx + IPv4_FRAGMENT_entry.NextPtr] ; Set edx to the next pointer
push edx ; Push pointer to fragment onto stack
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Next Fragment: 0x%x\n", edx
call net_buff_free ; free the previous fragment buffer (this uses the value from stack)
pop edx ebx eax
cmp edx, -1 ; Check if it is last fragment in chain
jne .rebuild_packet_loop
 
pop ecx
xchg cl, ch
mov edx, eax
mov [edx + IPv4_header.TotalLength], cx
add esp, 12
xchg cl, ch
push ecx edx ; size and pointer
jmp .handle_it ; edx = buf ptr, ecx = size, [esp] buf ptr, [esp+4], total size, ebx=device ptr
 
.destroy_slot_pop:
add esp, 4
.destroy_slot:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Destroy fragment slot!\n"
; TODO!
jmp .dump
 
 
 
 
 
;-----------------------------------------------------------------;
; ;
; ipv4_find_fragment_slot ;
; ;
; IN: pointer to fragmented packet in edx ;
; ;
; OUT: pointer to slot in esi, -1 on error ;
; ;
;-----------------------------------------------------------------;
align 4
ipv4_find_fragment_slot:
 
;;; TODO: the RFC says we should check protocol number too
 
push eax ebx ecx edx
mov ax, [edx + IPv4_header.Identification]
mov ecx, IPv4_MAX_FRAGMENTS
mov esi, IPv4_fragments
mov ebx, [edx + IPv4_header.SourceAddress]
mov edx, [edx + IPv4_header.DestinationAddress]
.find_slot:
cmp [esi + IPv4_FRAGMENT_slot.id], ax
jne .try_next
cmp [esi + IPv4_FRAGMENT_slot.SrcIP], ebx
jne .try_next
cmp [esi + IPv4_FRAGMENT_slot.DstIP], edx
je .found_slot
.try_next:
add esi, sizeof.IPv4_FRAGMENT_slot
loop .find_slot
 
or esi, -1
.found_slot:
pop edx ecx ebx eax
ret
 
 
;------------------------------------------------------------------;
; ;
; ipv4_output ;
; ;
; IN: al = protocol ;
; ah = TTL ;
; ebx = device ptr (or 0 to let IP layer decide) ;
; ecx = data length ;
; edx = Source IP ;
; edi = Destination IP ;
; ;
; OUT: eax = pointer to buffer start ;
; eax = 0 on error ;
; ebx = device ptr (send packet through this device) ;
; ecx = data length ;
; edx = size of complete frame ;
; edi = start of IPv4 payload ;
; ;
;------------------------------------------------------------------;
align 4
ipv4_output:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_output: size=%u ip=0x%x\n", ecx, edi
 
cmp ecx, 65500 ; Max IPv4 packet size
ja .too_large
 
push ecx ax edi
mov eax, edi
call ipv4_route ; outputs device number in edi, dest ip in eax, source IP in edx
test eax, eax
jz .no_route
push edx
test edi, edi
jz .loopback
 
call arp_ip_to_mac
test eax, 0xffff0000 ; error bits
jnz .arp_error
push ebx ; push the mac onto the stack
push ax
 
inc [IPv4_packets_tx + edi] ; update stats
 
mov ax, ETHER_PROTO_IPv4
mov ebx, [net_device_list + edi]
mov ecx, [esp + 6 + 8 + 2]
add ecx, sizeof.IPv4_header
mov edx, esp
call eth_output
jz .eth_error
add esp, 6 ; pop the mac out of the stack
 
.continue:
xchg cl, ch ; internet byte order
mov [edi + IPv4_header.VersionAndIHL], 0x45 ; IPv4, normal length (no Optional header)
mov [edi + IPv4_header.TypeOfService], 0 ; nothing special, just plain ip packet
mov [edi + IPv4_header.TotalLength], cx
mov [edi + IPv4_header.Identification], 0 ; fragment id: FIXME
mov [edi + IPv4_header.FlagsAndFragmentOffset], 0
 
mov [edi + IPv4_header.HeaderChecksum], 0
popd [edi + IPv4_header.SourceAddress]
popd [edi + IPv4_header.DestinationAddress]
 
pop word[edi + IPv4_header.TimeToLive] ; ttl shl 8 + protocol
; [edi + IPv4_header.Protocol]
 
pop ecx
 
ipv4_checksum edi
add edi, sizeof.IPv4_header
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_output: success!\n"
ret
 
.eth_error:
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_output: ethernet error\n"
add esp, 3*4+2+6
xor eax, eax
ret
 
.no_route:
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_output: No route to host!\n"
add esp, 2*4+2
xor eax, eax
ret
 
.arp_error:
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_output: ARP error=%x\n", eax
add esp, 4
pop eax
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_output: ip=0x%x\n", eax
add esp, 4+2
xor eax, eax
ret
 
.too_large:
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_output: Packet too large!\n"
xor eax, eax
ret
 
.loopback:
inc [IPv4_packets_tx + edi] ; update stats
 
mov dword [esp], eax ; set source IP to dest IP
mov ecx, [esp + 10]
add ecx, sizeof.IPv4_header
mov edi, AF_INET4
call loop_output
jmp .continue
 
 
 
 
;------------------------------------------------------------------;
; ;
; ipv4_output_raw ;
; ;
; IN: eax = socket ptr ;
; ecx = data length ;
; esi = data ptr ;
; ;
; OUT: eax = -1 on error ;
; ;
;------------------------------------------------------------------;
align 4
ipv4_output_raw:
 
DEBUGF 1,"IPv4_output_raw: size=%u ptr=%x socket=%x\n", ecx, esi, eax
 
sub esp, 8
push esi eax
 
call ipv4_route
call arp_ip_to_mac
 
test eax, 0xffff0000 ; error bits
jnz .arp_error
 
push ebx ; push the mac
push ax
 
inc [IPv4_packets_tx + 4*edi]
mov ax, ETHER_PROTO_IPv4
mov ebx, [net_device_list + 4*edi]
mov ecx, [esp + 6 + 4]
add ecx, sizeof.IPv4_header
mov edx, esp
call eth_output
jz .error
add esp, 6 ; pop the mac
 
mov dword[esp+4+4], edx
mov dword[esp+4+4+4], eax
 
pop eax esi
;; TODO: check socket options if we should add header, or just compute checksum
 
push edi ecx
rep movsb
pop ecx edi
 
; [edi + IPv4_header.VersionAndIHL] ; IPv4, normal length (no Optional header)
; [edi + IPv4_header.TypeOfService] ; nothing special, just plain ip packet
; [edi + IPv4_header.TotalLength]
; [edi + IPv4_header.TotalLength] ; internet byte order
; [edi + IPv4_header.FlagsAndFragmentOffset]
 
mov [edi + IPv4_header.HeaderChecksum], 0
 
; [edi + IPv4_header.TimeToLive] ; ttl shl 8 + protocol
; [edi + IPv4_header.Protocol]
; [edi + IPv4_header.Identification] ; fragment id
; [edi + IPv4_header.SourceAddress]
; [edi + IPv4_header.DestinationAddress]
 
ipv4_checksum edi ;;;; todo: checksum for IP packet with options!
add edi, sizeof.IPv4_header
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_output_raw: device=%x\n", ebx
call [ebx + NET_DEVICE.transmit]
ret
 
.error:
add esp, 6+8+4+4
mov ebx, ENOBUFS ; FIXME: NOBUFS or MSGSIZE error
or eax, -1
ret
 
.arp_error:
add esp, 8+4+4
mov ebx, ENOTCONN
or eax, -1
ret
 
 
;-----------------------------------------------------------------;
; ;
; ipv4_fragment ;
; ;
; IN: [esp] = ptr to packet buffer to fragment ;
; edi = ptrr to ip header in that buffer ;
; ebx = device ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
proc ipv4_fragment stdcall buffer
 
locals
offset dd ?
headerlength dd ?
headerptr dd ?
dataptr dd ?
remaining dd ?
segmentsize dd ?
endl
 
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_fragment\n"
 
; We must be able to put at least 8 bytes per segment
movzx eax, byte[edi] ; IHL
and eax, 0xf
shl eax, 2
mov [headerlength], eax
add eax, 8
mov ecx, [ebx + NET_DEVICE.mtu]
and ecx, not 11b
cmp ecx, eax
jb .fail
 
mov [edi + IPv4_header.HeaderChecksum], 0
 
mov [segmentsize], ecx
mov [headerptr], edi
movzx ecx, [edi + IPv4_header.TotalLength]
xchg cl, ch
sub ecx, [headerlength]
mov [remaining], ecx
mov [offset], 0
 
add edi, [headerlength]
mov [dataptr], edi
 
.loop:
DEBUGF DEBUG_NETWORK_VERBOSE, "Ipv4_fragment: new fragment"
 
mov ecx, [segmentsize]
cmp ecx, [remaining]
jbe @f
mov ecx, [remaining]
@@:
 
mov ax, ETHER_PROTO_IPv4
mov edx, [esp]
add edx, [edx + NET_BUFF.offset]
; add edx, ETH_header.DstMAC ; = 0
call ETH_output
jz .fail
 
push edi
mov edx, ecx
 
; copy header
mov esi, [headerptr]
mov ecx, [headerlength]
shr ecx, 2
rep movsd
 
; copy data
mov esi, [dataptr]
add esi, [offset]
mov ecx, edx
sub ecx, [headerlength]
shr ecx, 2
rep movsd
pop edi
 
; now, correct header
; packet length
mov ax, dx
xchg al, ah
mov [edi + IPv4_header.TotalLength], ax
 
; offset
mov eax, [offset]
xchg al, ah
 
sub edx, [headerlength]
sub [remaining], edx
je @f
jb .fail
or ah, 1 shl 2 ; more fragments
add [offset], edx
@@:
mov [edi + IPv4_header.FlagsAndFragmentOffset], ax
 
; Send the fragment
IPv4_checksum edi
call [ebx + NET_DEVICE.transmit]
 
cmp [remaining], 0
jne .loop
 
call NET_BUFF_free
ret
 
.fail:
DEBUGF DEBUG_NETWORK_ERROR, "Ipv4_fragment: failed\n"
call NET_BUFF_free
ret
 
endp
 
 
 
;-----------------------------------------------------------------;
; ;
; ipv4_route ;
; ;
; IN: eax = Destination IP ;
; ebx = outgoing device / 0 ;
; edx = Source IP ;
; ;
; OUT: eax = Destination IP (may be gateway), 0 on error ;
; edx = Source IP ;
; edi = device number*4 ;
; ;
; DESTROYED: ;
; ecx ;
; ;
;-----------------------------------------------------------------;
align 4
ipv4_route:
 
test ebx, ebx
jnz .got_device
 
; Broadcast does not need gateway
cmp eax, 0xffffffff
je .broadcast
 
xor edi, edi
.loop:
mov ebx, [IPv4_address + edi]
and ebx, [IPv4_subnet + edi]
jz .next
mov ecx, eax
and ecx, [IPv4_subnet + edi]
cmp ebx, ecx
je .got_it
.next:
add edi, 4
cmp edi, 4*NET_DEVICES_MAX
jb .loop
 
mov eax, [IPv4_gateway + 4] ; TODO: let user (or a user space daemon) configure default route
.broadcast:
mov edi, 4 ; TODO: same as above
.got_it:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_route: %u\n", edi
test edx, edx
jnz @f
mov edx, [IPv4_address + edi]
@@:
 
ret
 
.got_device:
; Validate device ptr and convert to device number
call net_ptr_to_num4
cmp edi, -1
je .fail
 
mov edx, [IPv4_address + edi] ; Source IP
 
; Broadcast does not need gateway
cmp eax, 0xffffffff
je @f
 
; Check if we should route to gateway or not
mov ebx, [IPv4_address + edi]
and ebx, [IPv4_subnet + edi]
mov ecx, eax
and ecx, [IPv4_subnet + edi]
cmp ecx, ebx
je @f
mov eax, [IPv4_gateway + edi]
@@:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_route: %u\n", edi
ret
 
.fail:
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_route failed\n"
xor eax, eax
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; ipv4_get_frgmnt_num ;
; ;
; IN: / ;
; ;
; OUT: ax = fragment number ;
; ;
;-----------------------------------------------------------------;
align 4
ipv4_get_frgmnt_num:
xor ax, ax ;;; TODO: replace this with real code
 
ret
 
 
;-----------------------------------------------------------------;
; ;
; ipv4_connect ;
; ;
; IN: eax = socket pointer ;
; ;
; OUT: eax = 0 on success ;
; eax = -1 on error ;
; ebx = error code on error ;
; ;
;-----------------------------------------------------------------;
align 4
ipv4_connect:
 
push eax edx
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
pop edx eax
 
; Fill in local IP
cmp [eax + IP_SOCKET.LocalIP], 0
jne @f
push [IPv4_address + 4] ; FIXME: use correct local IP
pop [eax + IP_SOCKET.LocalIP]
 
; Fill in remote IP
pushd [edx + 4]
pop [eax + IP_SOCKET.RemoteIP]
 
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
 
xor eax, eax
ret
 
 
;-----------------------------------------------------------------;
; ;
; ipv4_API: Part of system function 76. ;
; ;
; IN: bl = subfunction number ;
; bh = device number ;
; ecx, edx, .. depends on subfunction ;
; ;
; OUT: depends on subfunction ;
; ;
;-----------------------------------------------------------------;
align 4
ipv4_api:
 
movzx eax, bh
shl eax, 2
 
and ebx, 0x000000ff
cmp ebx, .number
ja .error
jmp dword [.table + 4*ebx]
 
.table:
dd .packets_tx ; 0
dd .packets_rx ; 1
dd .read_ip ; 2
dd .write_ip ; 3
dd .read_dns ; 4
dd .write_dns ; 5
dd .read_subnet ; 6
dd .write_subnet ; 7
dd .read_gateway ; 8
dd .write_gateway ; 9
.number = ($ - .table) / 4 - 1
 
.error:
mov eax, -1
ret
 
.packets_tx:
mov eax, [IPv4_packets_tx + eax]
ret
 
.packets_rx:
mov eax, [IPv4_packets_rx + eax]
ret
 
.read_ip:
mov eax, [IPv4_address + eax]
ret
 
.write_ip:
mov [IPv4_address + eax], ecx
mov edi, eax ; device number, we'll need it for ARP
 
; pre-calculate the local broadcast address
mov ebx, [IPv4_subnet + eax]
not ebx
or ebx, ecx
mov [IPv4_broadcast + eax], ebx
 
mov ebx, [net_device_list + eax]
mov eax, [IPv4_address + eax]
call arp_output_request ; now send a gratuitous ARP
 
call net_send_event
xor eax, eax
ret
 
.read_dns:
mov eax, [IPv4_nameserver + eax]
ret
 
.write_dns:
mov [IPv4_nameserver + eax], ecx
call net_send_event
xor eax, eax
ret
 
.read_subnet:
mov eax, [IPv4_subnet + eax]
ret
 
.write_subnet:
mov [IPv4_subnet + eax], ecx
 
; pre-calculate the local broadcast address
mov ebx, [IPv4_address + eax]
not ecx
or ecx, ebx
mov [IPv4_broadcast + eax], ecx
 
call net_send_event
xor eax, eax
ret
 
.read_gateway:
mov eax, [IPv4_gateway + eax]
ret
 
.write_gateway:
mov [IPv4_gateway + eax], ecx
 
call net_send_event
xor eax, eax
ret
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/PPPoE.inc
0,0 → 1,346
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2012-2019. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; PPPoE.INC ;;
;; ;;
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
 
struct PPPoE_frame
VersionAndType db ?
Code db ?
SessionID dw ?
Length dw ? ; Length of payload, does NOT include the length PPPoE header.
Payload rb 0
ends
 
uglobal
align 4
 
PPPoE_SID dw ?
PPPoE_MAC dp ?
 
endg
 
;-----------------------------------------------------------------;
; ;
; pppoe_init: Reset all pppoe variables ;
; ;
;-----------------------------------------------------------------;
macro pppoe_init {
 
call pppoe_stop_connection
 
}
 
 
;-----------------------------------------------------------------;
; ;
; pppoe_discovery_input ;
; ;
; IN: [esp] = ptr to buffer ;
; [esp+4] = size of buffer ;
; ebx = ptr to device struct ;
; ecx = size of PPP packet ;
; edx = ptr to PPP header ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
pppoe_discovery_input:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "PPPoE_discovery_input\n"
 
; First, find open PPPoE socket
 
pusha
mov ecx, socket_mutex
call mutex_lock
popa
 
mov eax, net_sockets
 
.next_socket:
mov eax, [eax + SOCKET.NextPtr]
or eax, eax
jz .dump
 
cmp [eax + SOCKET.Domain], AF_PPP
jne .next_socket
 
cmp [eax + SOCKET.Protocol], PPP_PROTO_ETHERNET
jne .next_socket
 
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
; Now, send it to the this socket
 
mov ecx, [esp + 4]
mov esi, [esp]
 
jmp socket_input
 
.dump:
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
DEBUGF DEBUG_NETWORK_VERBOSE, 'PPPoE_discovery_input: dumping\n'
call net_buff_free
ret
 
 
;-----------------------------------------------------------------;
; ;
; pppoe_discovery_output ;
; ;
; IN: eax = socket pointer ;
; ecx = number of bytes to send ;
; esi = pointer to data ;
; ;
;-----------------------------------------------------------------;
align 4
pppoe_discovery_output:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "PPPoE_discovery_output: socket=%x buffer=%x size=%d\n", eax, esi, ecx
 
; RFC2516: An entire PADI packet (including the PPPoE header) MUST NOT
; exceed 1484 octets.
cmp ecx, 1484 + 14
ja .bad
 
; Check that device exists and is ethernet device
mov ebx, [eax + SOCKET.device]
 
cmp ebx, NET_DEVICES_MAX
ja .bad
 
mov ebx, [net_device_list + 4*ebx]
test ebx, ebx
jz .bad
 
cmp [ebx + NET_DEVICE.device_type], NET_DEVICE_ETH
jne .bad
 
DEBUGF DEBUG_NETWORK_VERBOSE, "PPPoE_discovery_output: device=%x\n", ebx
 
; Create packet.
push ecx esi
;;;; FIXME stdcall kernel_alloc, 1500
pop esi ecx
test eax, eax
jz .bad
 
mov edx, ecx
mov edi, eax
rep movsb
 
cmp edx, 60 ; Min ETH size
ja @f
mov edx, 60
@@:
 
push edx eax ; size and packet ptr for driver send proc
 
; Overwrite source MAC and protocol type
lea edi, [eax + ETH_header.SrcMAC]
lea esi, [ebx + ETH_DEVICE.mac]
movsd
movsw
cmp word[edi], ETHER_PROTO_PPP_SESSION ; Allow only PPP_discovery, or LCP
je @f
mov ax, ETHER_PROTO_PPP_DISCOVERY
stosw
@@:
 
; And send the packet
call [ebx + NET_DEVICE.transmit]
 
xor eax, eax
ret
 
.bad:
or eax, -1
ret
 
 
;-----------------------------------------------------------------;
; ;
; pppoe_session_input ;
; ;
; IN: [esp] = ptr to buffer ;
; [esp+4] = size of buffer ;
; ebx = ptr to device struct ;
; edx = ptr to PPP header ;
; ecx = size of PPP packet ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
pppoe_session_input:
 
cmp [edx + PPPoE_frame.VersionAndType], 0x11
jne .dump
 
cmp [edx + PPPoE_frame.Code], 0x00
jne .dump
 
movzx ecx, [edx + PPPoE_frame.Length]
xchg cl, ch
 
mov ax, [edx + PPPoE_frame.SessionID]
DEBUGF DEBUG_NETWORK_VERBOSE, "PPPoE_input: session ID=%x, length=%u\n", ax, cx
cmp ax, [PPPoE_SID]
jne .dump
 
mov ax, word [edx + PPPoE_frame.Payload]
add edx, PPPoE_frame.Payload + 2
 
cmp ax, PPP_PROTO_IPv4
je ipv4_input
 
; cmp ax, PPP_PROTO_IPv6
; je ipv6_input
 
jmp pppoe_discovery_input ; Send LCP,CHAP,CBCP,... packets to the PPP dialer
DEBUGF DEBUG_NETWORK_VERBOSE, "PPPoE_input: Unknown protocol=%x\n", ax
 
.dump:
DEBUGF DEBUG_NETWORK_VERBOSE, "PPPoE_input: dumping\n"
call net_buff_free
ret
 
 
 
;-----------------------------------------------------------------;
; ;
; pppoe_output ;
; ;
; IN: ax = protocol ;
; ebx = device ptr ;
; ecx = packet size ;
; ;
; OUT: eax = buffer start ;
; eax = 0 on error ;
; ebx = device ptr ;
; ecx = packet size ;
; edx = size of complete buffer ;
; edi = start of PPP payload ;
; ;
;-----------------------------------------------------------------;
align 4
pppoe_output:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "PPPoE_output: size=%u device=%x\n", ecx, ebx
 
pushw ax
pushw [PPPoE_SID]
 
mov ax, ETHER_PROTO_PPP_SESSION
add ecx, PPPoE_frame.Payload + 2
lea edx, [PPPoE_MAC]
call eth_output
jz .eth_error
 
sub ecx, PPPoE_frame.Payload
mov [edi + PPPoE_frame.VersionAndType], 0x11
mov [edi + PPPoE_frame.Code], 0
popw [edi + PPPoE_frame.SessionID]
xchg cl, ch
mov [edi + PPPoE_frame.Length], cx
xchg cl, ch
 
pop word [edi + PPPoE_frame.Payload]
 
sub ecx, 2
add edi, PPPoE_frame.Payload + 2
 
DEBUGF DEBUG_NETWORK_VERBOSE, "PPPoE_output: success!\n"
ret
 
 
.eth_error:
add esp, 4
xor eax, eax
ret
 
align 4
pppoe_start_connection:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "PPPoE_start_connection: %x\n", cx
 
cmp [PPPoE_SID], 0
jne .fail
 
mov [PPPoE_SID], cx
mov dword [PPPoE_MAC], edx
mov word [PPPoE_MAC + 4], si
 
xor eax, eax
ret
 
.fail:
or eax, -1
ret
 
 
align 4
pppoe_stop_connection:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "PPPoE_stop_connection\n"
 
xor eax, eax
mov [PPPoE_SID], ax
mov dword [PPPoE_MAC], eax
mov word [PPPoE_MAC + 4], ax
 
ret
 
 
;-----------------------------------------------------------------;
; ;
; pppoe_api: Part of system function 76 ;
; ;
; IN: subfunction number in bl ;
; device number in bh ;
; ecx, edx, .. depends on subfunction ;
; ;
; OUT: ;
; ;
;-----------------------------------------------------------------;
align 4
pppoe_api:
 
movzx eax, bh
shl eax, 2
 
and ebx, 0xff
cmp ebx, .number
ja .error
jmp dword [.table + 4*ebx]
 
.table:
dd pppoe_start_connection ; 0
dd pppoe_stop_connection ; 1
.number = ($ - .table) / 4 - 1
 
.error:
mov eax, -1
ret
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/tcp_usreq.inc
0,0 → 1,237
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2019. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Based on the code of 4.4BSD ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
 
;-----------------------------------------------------------------;
; ;
; tcp_usrclosed ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_usrclosed:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_usrclosed: %x\n", eax
 
push ebx
mov ebx, [eax + TCP_SOCKET.t_state]
mov ebx, dword [.switch + ebx*4]
jmp ebx
 
.switch:
dd .close ; TCPS_CLOSED
dd .close ; TCPS_LISTEN
dd .close ; TCPS_SYN_SENT
dd .wait1 ; TCPS_SYN_RECEIVED
dd .wait1 ; TCPS_ESTABLISHED
dd .last_ack ; TCPS_CLOSE_WAIT
dd .ret ; TCPS_FIN_WAIT_1
dd .ret ; TCPS_CLOSING
dd .ret ; TCPS_LAST_ACK
dd .disc ; TCPS_FIN_WAIT_2
dd .disc ; TCPS_TIMED_WAIT
 
.close:
mov [eax + TCP_SOCKET.t_state], TCPS_CLOSED
call tcp_close
pop ebx
ret
 
.wait1:
mov [eax + TCP_SOCKET.t_state], TCPS_FIN_WAIT_1
pop ebx
ret
 
.last_ack:
mov [eax + TCP_SOCKET.t_state], TCPS_LAST_ACK
pop ebx
ret
 
.disc:
call socket_is_disconnected
.ret:
pop ebx
ret
 
 
;-----------------------------------------------------------------;
; ;
; tcp_connect ;
; ;
; IN: eax = socket ptr ;
; ;
; OUT: eax = 0 on success ;
; eax = -1 on error ;
; ebx = error code on error ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_connect:
 
test [eax + SOCKET.state], SS_ISCONNECTED
jnz .eisconn
 
push eax edx
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
 
mov ebx, eax
lea eax, [ebx + STREAM_SOCKET.snd]
call socket_ring_create
test eax, eax
jz .nomem
 
lea eax, [ebx + STREAM_SOCKET.rcv]
call socket_ring_create
test eax, eax
jz .nomem
pop edx eax
 
; Fill in remote port and IP
pushw [edx + 2]
pop [eax + TCP_SOCKET.RemotePort]
 
pushd [edx + 4]
pop [eax + TCP_SOCKET.RemoteIP]
 
; Find route to host
pusha
push eax
mov ebx, [eax + TCP_SOCKET.device]
mov edx, [eax + TCP_SOCKET.LocalIP]
mov eax, [eax + TCP_SOCKET.RemoteIP]
call ipv4_route
test eax, eax
jz .enoroute
pop eax
mov ebx, [net_device_list + edi]
mov [eax + TCP_SOCKET.device], ebx
mov [eax + TCP_SOCKET.LocalIP], edx
popa
 
; Find a local port, if user didnt define one
cmp [eax + TCP_SOCKET.LocalPort], 0
jne @f
call socket_find_port
@@:
 
; Compute window scaling factor
push ecx
xor ecx, ecx
mov ebx, TCP_max_win
@@:
cmp ebx, SOCKET_BUFFER_SIZE
ja @f
shl ebx, 1
inc ecx
cmp ecx, TCP_max_winshift
jb @r
@@:
mov [eax + TCP_SOCKET.request_r_scale], cl
pop ecx
 
call socket_is_connecting
inc [TCPS_connattempt]
 
mov [eax + TCP_SOCKET.timer_persist], 0
mov [eax + TCP_SOCKET.t_state], TCPS_SYN_SENT
 
mov [eax + TCP_SOCKET.timer_keepalive], TCP_time_keep_init
 
push [TCP_sequence_num]
add [TCP_sequence_num], TCP_ISSINCR/2
pop [eax + TCP_SOCKET.ISS]
 
tcp_sendseqinit eax
 
push eax
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
pop eax
 
; Now send the SYN packet to remote end
push eax
call tcp_output
pop eax
 
test [eax + SOCKET.options], SO_NONBLOCK
jz .waitforit
 
xor eax, eax
dec eax
mov ebx, EINPROGRESS
ret
 
.nomem:
pop edx eax
xor eax, eax
dec eax
mov ebx, ENOMEM
ret
 
.eisconn:
xor eax, eax
dec eax
mov ebx, EISCONN
ret
 
.enoroute:
pop eax
popa
xor eax, eax
dec eax
mov ebx, EADDRNOTAVAIL
ret
 
.waitforit:
push eax
stdcall timer_hs, TCP_time_connect, 0, .timeout, eax
pop ebx
mov [ebx + TCP_SOCKET.timer_connect], eax
mov eax, ebx
 
.loop:
cmp [eax + SOCKET.errorcode], 0
jne .fail
cmp [eax + TCP_SOCKET.t_state], TCPS_ESTABLISHED
je .established
 
call socket_block
jmp .loop
 
.timeout:
mov eax, [esp+4]
mov [eax + SOCKET.errorcode], ETIMEDOUT
and [eax + SOCKET.state], not SS_ISCONNECTING
call socket_notify
ret 4
 
.fail:
mov ebx, [eax + SOCKET.errorcode]
mov [eax + SOCKET.errorcode], 0 ; Clear the error, we only need to send it to the caller once
xor eax, eax
dec eax
ret
 
.established:
stdcall cancel_timer_hs, [eax + TCP_SOCKET.timer_connect]
xor eax, eax
ret
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/icmp.inc
0,0 → 1,456
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2019. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; ICMP.INC ;;
;; ;;
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Based on the work of [Johnny_B] and [smb] ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
; ICMP types & codes
 
ICMP_ECHOREPLY = 0 ; echo reply message
 
ICMP_UNREACH = 3
ICMP_UNREACH_NET = 0 ; bad net
ICMP_UNREACH_HOST = 1 ; bad host
ICMP_UNREACH_PROTOCOL = 2 ; bad protocol
ICMP_UNREACH_PORT = 3 ; bad port
ICMP_UNREACH_NEEDFRAG = 4 ; IP_DF caused drop
ICMP_UNREACH_SRCFAIL = 5 ; src route failed
ICMP_UNREACH_NET_UNKNOWN = 6 ; unknown net
ICMP_UNREACH_HOST_UNKNOWN = 7 ; unknown host
ICMP_UNREACH_ISOLATED = 8 ; src host isolated
ICMP_UNREACH_NET_PROHIB = 9 ; prohibited access
ICMP_UNREACH_HOST_PROHIB = 10 ; ditto
ICMP_UNREACH_TOSNET = 11 ; bad tos for net
ICMP_UNREACH_TOSHOST = 12 ; bad tos for host
ICMP_UNREACH_FILTER_PROHIB = 13 ; admin prohib
ICMP_UNREACH_HOST_PRECEDENCE = 14 ; host prec vio.
ICMP_UNREACH_PRECEDENCE_CUTOFF = 15 ; prec cutoff
 
ICMP_SOURCEQUENCH = 4 ; Packet lost, slow down
 
ICMP_REDIRECT = 5 ; shorter route, codes:
ICMP_REDIRECT_NET = 0 ; for network
ICMP_REDIRECT_HOST = 1 ; for host
ICMP_REDIRECT_TOSNET = 2 ; for tos and net
ICMP_REDIRECT_TOSHOST = 3 ; for tos and host
 
ICMP_ALTHOSTADDR = 6 ; alternate host address
ICMP_ECHO = 8 ; echo service
ICMP_ROUTERADVERT = 9 ; router advertisement
ICMP_ROUTERADVERT_NORMAL = 0 ; normal advertisement
ICMP_ROUTERADVERT_NOROUTE_COMMON= 16 ; selective routing
 
ICMP_ROUTERSOLICIT = 10 ; router solicitation
ICMP_TIMXCEED = 11 ; time exceeded, code:
ICMP_TIMXCEED_INTRANS = 0 ; ttl==0 in transit
ICMP_TIMXCEED_REASS = 1 ; ttl==0 in reass
 
ICMP_PARAMPROB = 12 ; ip header bad
ICMP_PARAMPROB_ERRATPTR = 0 ; error at param ptr
ICMP_PARAMPROB_OPTABSENT = 1 ; req. opt. absent
ICMP_PARAMPROB_LENGTH = 2 ; bad length
 
ICMP_TSTAMP = 13 ; timestamp request
ICMP_TSTAMPREPLY = 14 ; timestamp reply
ICMP_IREQ = 15 ; information request
ICMP_IREQREPLY = 16 ; information reply
ICMP_MASKREQ = 17 ; address mask request
ICMP_MASKREPLY = 18 ; address mask reply
ICMP_TRACEROUTE = 30 ; traceroute
ICMP_DATACONVERR = 31 ; data conversion error
ICMP_MOBILE_REDIRECT = 32 ; mobile host redirect
ICMP_IPV6_WHEREAREYOU = 33 ; IPv6 where-are-you
ICMP_IPV6_IAMHERE = 34 ; IPv6 i-am-here
ICMP_MOBILE_REGREQUEST = 35 ; mobile registration req
ICMP_MOBILE_REGREPLY = 36 ; mobile registreation reply
ICMP_SKIP = 39 ; SKIP
 
ICMP_PHOTURIS = 40 ; Photuris
ICMP_PHOTURIS_UNKNOWN_INDEX = 1 ; unknown sec index
ICMP_PHOTURIS_AUTH_FAILED = 2 ; auth failed
ICMP_PHOTURIS_DECRYPT_FAILED = 3 ; decrypt failed
 
 
struct ICMP_header
 
Type db ?
Code db ?
Checksum dw ?
Identifier dw ?
SequenceNumber dw ?
 
ends
 
 
uglobal
align 4
 
ICMP_packets_tx rd NET_DEVICES_MAX
ICMP_packets_rx rd NET_DEVICES_MAX
 
endg
 
 
 
;-----------------------------------------------------------------;
; ;
; ICMP_init ;
; ;
;-----------------------------------------------------------------;
 
macro icmp_init {
 
xor eax, eax
mov edi, ICMP_packets_tx
mov ecx, 2*NET_DEVICES_MAX
rep stosd
 
}
 
 
;-----------------------------------------------------------------;
; ;
; icmp_input: Send a reply's to an ICMP echo or insert packets ;
; into socket. ;
; ;
; IN: [esp] = ptr to buffer ;
; ebx = ptr to device struct ;
; ecx = ICMP Packet size ;
; edx = ptr to IPv4 header ;
; esi = ptr to ICMP Packet data ;
; edi = interface number*4 ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
icmp_input:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ICMP_input\n"
 
; Dump all multicasts and broadcasts
mov eax, [IPv4_address + edi]
cmp eax, [edx + IPv4_header.DestinationAddress]
jne .dump
 
; Check the checksum
push esi ecx edx
push [esi + ICMP_header.Checksum]
mov [esi + ICMP_header.Checksum], 0
xor edx, edx
call checksum_1
call checksum_2
pop si
cmp dx, si
pop edx ecx esi
jne .checksum_mismatch
 
DEBUGF DEBUG_NETWORK_VERBOSE, "ICMP_input: Checksum OK\n"
 
; Update stats
inc [ICMP_packets_rx + edi]
 
; Is this an echo request?
cmp [esi + ICMP_header.Type], ICMP_ECHO
je .echo_request
 
; Look for an open ICMP socket
pusha
mov ecx, socket_mutex
call mutex_lock
popa
 
add ecx, esi
sub ecx, edx
mov esi, edx
mov eax, net_sockets
.next_socket:
mov eax, [eax + SOCKET.NextPtr]
or eax, eax
jz .dump_
 
cmp [eax + SOCKET.Domain], AF_INET4
jne .next_socket
 
cmp [eax + SOCKET.Protocol], IP_PROTO_ICMP
jne .next_socket
 
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
DEBUGF DEBUG_NETWORK_VERBOSE, "socket=%x\n", eax
 
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
 
jmp socket_input
 
 
 
.echo_request:
 
; We'll reuse the packet so we can create the response as fast as possible
DEBUGF DEBUG_NETWORK_VERBOSE, "ICMP echo request\n"
 
; Change Packet type to reply
mov [esi + ICMP_header.Type], ICMP_ECHOREPLY
 
mov eax, [esp]
mov esi, [eax + NET_BUFF.offset]
add esi, eax
 
; Check frame type
cmp [eax + NET_BUFF.type], NET_BUFF_ETH
jne .not_ethernet
 
; exchange dest and source MAC in ETH header
push dword [esi + ETH_header.DstMAC]
push dword [esi + ETH_header.SrcMAC]
pop dword [esi + ETH_header.DstMAC]
pop dword [esi + ETH_header.SrcMAC]
push word [esi + ETH_header.DstMAC + 4]
push word [esi + ETH_header.SrcMAC + 4]
pop word [esi + ETH_header.DstMAC + 4]
pop word [esi + ETH_header.SrcMAC + 4]
add esi, sizeof.ETH_header
 
.not_ethernet:
; Exchange dest and source address in IP header
push [esi + IPv4_header.SourceAddress]
push [esi + IPv4_header.DestinationAddress]
pop [esi + IPv4_header.SourceAddress]
pop [esi + IPv4_header.DestinationAddress]
 
; Calculate IP header length
movzx ecx, [esi + IPv4_header.VersionAndIHL]
and ecx, 0x0f
shl cx, 2
mov edi, ecx ; put it in edi for later
 
; Calculate IP checksum
mov eax, esi
mov [eax + IPv4_header.HeaderChecksum], 0
xor edx, edx
call checksum_1
call checksum_2
mov [eax + IPv4_header.HeaderChecksum], dx
 
; Calculate ICMP packet length
movzx ecx, [eax + IPv4_header.TotalLength]
xchg ch, cl
sub ecx, edi ; IP packet length - IP header length = ICMP packet length
 
; Calculate ICMP checkSum
mov eax, esi
mov [esi + ICMP_header.Checksum], 0
xor edx, edx
call checksum_1
call checksum_2
mov [eax + ICMP_header.Checksum], dx
 
; Transmit the frame
DEBUGF DEBUG_NETWORK_VERBOSE, "ICMP transmitting reply\n"
call [ebx + NET_DEVICE.transmit]
test eax, eax
jnz @f
DEBUGF DEBUG_NETWORK_VERBOSE, "ICMP transmit failed\n"
call net_ptr_to_num4
inc [ICMP_packets_tx + edi]
inc [IPv4_packets_tx + edi]
@@:
ret
 
.dump_:
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
 
DEBUGF DEBUG_NETWORK_ERROR, "ICMP_input: no socket found\n"
jmp .dump
 
.checksum_mismatch:
DEBUGF DEBUG_NETWORK_ERROR, "ICMP_input: checksum mismatch\n"
 
.dump:
DEBUGF DEBUG_NETWORK_VERBOSE, "ICMP_input: dumping\n"
call net_buff_free
ret
 
 
if 0
;-----------------------------------------------------------------;
; ;
; icmp_output ;
; ;
; IN: eax = dest ip ;
; bh = type ;
; bl = code ;
; ecx = data length ;
; edx = source ip ;
; esi = data offset ;
; edi = identifier shl 16 + sequence number ;
; ;
;-----------------------------------------------------------------;
align 4
icmp_output:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "Creating ICMP Packet\n"
 
push esi edi bx
add ecx, sizeof.ICMP_header
mov di, IP_PROTO_ICMP SHL 8 + 128 ; TTL
call IPv4_output
jz .exit
 
DEBUGF DEBUG_NETWORK_VERBOSE, "full icmp packet size: %u\n", edx
 
pop word [edi + ICMP_header.Type] ; Write both type and code bytes at once
pop dword [edi + ICMP_header.Identifier] ; identifier and sequence number
mov [edi + ICMP_header.Checksum], 0
 
push ebx ecx edx
mov esi, edi
xor edx, edx
call checksum_1
call checksum_2
mov [edi + ICMP_header.Checksum], dx
pop edx ecx ebx esi
 
sub ecx, sizeof.ICMP_header
add edi, sizeof.ICMP_header
push cx
shr cx, 2
rep movsd
pop cx
and cx, 3
rep movsb
 
sub edi, edx ;;; TODO: find a better way to remember start of packet
push edx edi
DEBUGF DEBUG_NETWORK_VERBOSE, "Sending ICMP Packet\n"
call [ebx + NET_DEVICE.transmit]
test eax, eax
jnz @f
call NET_ptr_to_num4
inc [ICMP_PACKETS_TX + edi]
@@:
ret
.exit:
DEBUGF DEBUG_NETWORK_ERROR, "Creating ICMP Packet failed\n"
add esp, 2*4 + 2
ret
end if
 
 
 
 
;-----------------------------------------------------------------;
; ;
; icmp_output_raw ;
; ;
; IN: eax = socket ptr ;
; ecx = data length ;
; edx = data pointer ;
; ;
;-----------------------------------------------------------------;
align 4
icmp_output_raw:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "Creating ICMP Packet for socket %x, data ptr=%x\n", eax, edx
 
push edx
mov ebx, [eax + IP_SOCKET.device]
mov edx, [eax + IP_SOCKET.LocalIP]
mov edi, [eax + IP_SOCKET.RemoteIP]
mov al, [eax + IP_SOCKET.ttl]
mov ah, IP_PROTO_ICMP
call ipv4_output
jz .fail
 
pop esi
push eax
 
push edi ecx
DEBUGF DEBUG_NETWORK_VERBOSE, "copying %u bytes from %x to %x\n", ecx, esi, edi
rep movsb
pop ecx edi
 
mov [edi + ICMP_header.Checksum], 0
 
mov esi, edi
xor edx, edx
call checksum_1
call checksum_2
mov [edi + ICMP_header.Checksum], dx
 
DEBUGF DEBUG_NETWORK_VERBOSE, "Sending ICMP Packet\n"
call [ebx + NET_DEVICE.transmit]
test eax, eax
jnz @f
call net_ptr_to_num4
inc [ICMP_packets_tx + edi]
@@:
ret
 
.fail:
pop edx
DEBUGF DEBUG_NETWORK_ERROR, "Creating ICMP Packet failed\n"
or eax, -1
mov ebx, EMSGSIZE ;;; FIXME
ret
 
 
 
 
;-----------------------------------------------------------------;
; ;
; icmp_api: Part of system function 76. ;
; ;
; IN: bl = subfunction number ;
; bh = device number ;
; ecx, edx, .. depends on subfunction ;
; ;
; OUT: depends on subfunction ;
; ;
;-----------------------------------------------------------------;
align 4
icmp_api:
 
movzx eax, bh
shl eax, 2
 
test bl, bl
jz .packets_tx ; 0
dec bl
jz .packets_rx ; 1
 
.error:
mov eax, -1
ret
 
.packets_tx:
mov eax, [ICMP_packets_tx + eax]
ret
 
.packets_rx:
mov eax, [ICMP_packets_rx + eax]
ret
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/tcp_timer.inc
0,0 → 1,192
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2017. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Based on the code of 4.4BSD ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
timer_flag_retransmission = 1 shl 0
timer_flag_keepalive = 1 shl 1
timer_flag_2msl = 1 shl 2
timer_flag_persist = 1 shl 3
timer_flag_wait = 1 shl 4
 
 
macro tcp_timer_160ms {
 
local .loop
local .exit
 
mov ebx, net_sockets
.loop:
mov ebx, [ebx + SOCKET.NextPtr]
test ebx, ebx
jz .exit
 
cmp [ebx + SOCKET.Domain], AF_INET4
jne .loop
cmp [ebx + SOCKET.Protocol], IP_PROTO_TCP
jne .loop
test [ebx + TCP_SOCKET.t_flags], TF_DELACK
jz .loop
 
and [ebx + TCP_SOCKET.t_flags], not (TF_DELACK)
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
 
push ebx
mov eax, ebx
call tcp_output
pop ebx
 
inc [TCPS_delack] ; update stats
 
jmp .loop
 
.exit:
 
}
 
 
align 4
proc tcp_timer_640ms
 
xor esi, esi
mov ecx, MANUAL_DESTROY
call create_event
mov [TCP_timer1_event], eax
 
.wait:
mov eax, [TCP_timer1_event]
mov ebx, [eax + EVENT.id]
call wait_event
 
; Update TCP sequence number
 
add [TCP_sequence_num], 64000
 
; Scan through all the active TCP sockets, decrementing all active timers
; When a timer reaches zero, run its handler.
 
mov eax, net_sockets
.loop:
mov eax, [eax + SOCKET.NextPtr]
.check_only:
or eax, eax
jz .wait
 
cmp [eax + SOCKET.Domain], AF_INET4
jne .loop
 
cmp [eax + SOCKET.Protocol], IP_PROTO_TCP
jne .loop
 
inc [eax + TCP_SOCKET.t_idle]
 
test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission
jz .check_more2
dec [eax + TCP_SOCKET.timer_retransmission]
jnz .check_more2
 
DEBUGF DEBUG_NETWORK_VERBOSE, "socket %x: Retransmission timer expired\n", eax
 
push eax
call tcp_output
pop eax
 
.check_more2:
test [eax + TCP_SOCKET.timer_flags], timer_flag_keepalive
jz .check_more3
dec [eax + TCP_SOCKET.timer_keepalive]
jnz .check_more3
 
DEBUGF DEBUG_NETWORK_VERBOSE, "socket %x: Keepalive expired\n", eax
 
cmp [eax + TCP_SOCKET.state], TCPS_ESTABLISHED
ja .dont_kill
 
push eax
call tcp_disconnect
pop eax
jmp .loop
 
.dont_kill:
test [eax + SOCKET.options], SO_KEEPALIVE
jz .reset_keepalive
 
push eax
mov ebx, eax
xor cl, cl
call tcp_respond ; send keepalive
pop eax
mov [eax + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval
jmp .check_more3
 
.reset_keepalive:
mov [eax + TCP_SOCKET.timer_keepalive], TCP_time_keep_idle
 
.check_more3:
test [eax + TCP_SOCKET.timer_flags], timer_flag_2msl
jz .check_more5
dec [eax + TCP_SOCKET.timer_timed_wait]
jnz .check_more5
 
DEBUGF DEBUG_NETWORK_VERBOSE, "socket %x: 2MSL timer expired\n", eax
 
.check_more5:
test [eax + TCP_SOCKET.timer_flags], timer_flag_persist
jz .check_more6
dec [eax + TCP_SOCKET.timer_persist]
jnz .check_more6
 
DEBUGF DEBUG_NETWORK_VERBOSE, "socket %x: persist timer expired\n", eax
 
call tcp_set_persist
or [eax + TCP_SOCKET.t_flags], TF_FORCE
push eax
call tcp_output
pop eax
and [eax + TCP_SOCKET.t_flags], not TF_FORCE
 
.check_more6:
test [eax + TCP_SOCKET.timer_flags], timer_flag_wait
jz .loop
dec [eax + TCP_SOCKET.timer_timed_wait]
jnz .loop
 
DEBUGF DEBUG_NETWORK_VERBOSE, "socket %x: timed wait timer expired\n", eax
 
push [eax + SOCKET.NextPtr]
call tcp_close
pop eax
 
jmp .check_only
 
endp
 
 
;-----------------------------------------------------------------;
; ;
; TCP_cancel_timers ;
; ;
; IN: eax = socket ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
tcp_cancel_timers:
 
mov [eax + TCP_SOCKET.timer_flags], 0
 
ret
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/tcp.inc
0,0 → 1,293
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2017. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; Part of the TCP/IP network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; Based on the code of 4.4BSD ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
; Socket states
TCPS_CLOSED = 0
TCPS_LISTEN = 1
TCPS_SYN_SENT = 2
TCPS_SYN_RECEIVED = 3
TCPS_ESTABLISHED = 4
TCPS_CLOSE_WAIT = 5
TCPS_FIN_WAIT_1 = 6
TCPS_CLOSING = 7
TCPS_LAST_ACK = 8
TCPS_FIN_WAIT_2 = 9
TCPS_TIME_WAIT = 10
 
; Socket Flags
TF_ACKNOW = 1 shl 0 ; ack peer immediately
TF_DELACK = 1 shl 1 ; ack, but try to delay it
TF_NODELAY = 1 shl 2 ; don't delay packets to coalesce
TF_NOOPT = 1 shl 3 ; don't use tcp options
TF_SENTFIN = 1 shl 4 ; have sent FIN
TF_REQ_SCALE = 1 shl 5 ; have/will request window scaling
TF_RCVD_SCALE = 1 shl 6 ; other side has requested scaling
TF_REQ_TSTMP = 1 shl 7 ; have/will request timestamps
TF_RCVD_TSTMP = 1 shl 8 ; a timestamp was received in SYN
TF_SACK_PERMIT = 1 shl 9 ; other side said I could SACK
 
TF_FORCE = 1 shl 16 ; force to send a segment
 
; Segment flags
TH_FIN = 1 shl 0
TH_SYN = 1 shl 1
TH_RST = 1 shl 2
TH_PUSH = 1 shl 3
TH_ACK = 1 shl 4
TH_URG = 1 shl 5
 
; Segment header options
TCP_OPT_EOL = 0 ; End of option list.
TCP_OPT_NOP = 1 ; No-Operation.
TCP_OPT_MAXSEG = 2 ; Maximum Segment Size.
TCP_OPT_WINDOW = 3 ; window scale
TCP_OPT_SACK_PERMIT = 4 ; Selective Acknowledgement
TCP_OPT_SACK = 5
TCP_OPT_TIMESTAMP = 8
 
; Fundamental timer values
TCP_time_MSL = 47 ; max segment lifetime (30s)
TCP_time_re_min = 2 ; min retransmission (1,28s)
TCP_time_re_max = 100 ; max retransmission (64s)
TCP_time_pers_min = 8 ; min persist (5,12s)
TCP_time_pers_max = 94 ; max persist (60,16s)
TCP_time_keep_init = 118 ; connection establishment (75,52s)
TCP_time_keep_idle = 4608 ; idle time before 1st probe (2h)
TCP_time_keep_interval = 118 ; between probes when no response (75,52s)
TCP_time_rtt_default = 5 ; default Round Trip Time (3,2s)
TCP_time_srtt_default = 0 ;
TCP_time_max_idle = 8*TCP_time_keep_interval ; FIXME
 
TCP_time_connect = 300 ; in 1/100s (default=3s)
 
; timer constants
TCP_max_rxtshift = 12 ; max retransmissions waiting for ACK
TCP_max_keepcnt = 8 ; max keepalive probes
 
;
TCP_max_winshift = 14
TCP_max_win = 65535
 
TCP_re_xmit_thresh = 3
 
TCP_mss_default = 1480 ; default max segment size
 
; smoothed round trip time and estimated variance are stored as fixed point numbers,
; shifted by the value below.
; With these scales, srtt has 3 bits to the right of the binary point, and thus an "alpha"
; of .875. rttvar has 2 bits to the right and thus "alpha" of 0.75
TCP_RTT_SHIFT = 3
TCP_RTTVAR_SHIFT = 2
 
TCP_PAWS_IDLE = 24*24*60*60*100 ; 24 days, in 1/100 seconds
 
TCP_QUEUE_SIZE = 50
 
TCP_ISSINCR = 128000
 
struct TCP_header
 
SourcePort dw ?
DestinationPort dw ?
SequenceNumber dd ?
AckNumber dd ?
DataOffset db ? ; DataOffset[0-3 bits] and Reserved[4-7]
Flags db ? ; Reserved[0-1 bits]|URG|ACK|PSH|RST|SYN|FIN
Window dw ?
Checksum dw ?
UrgentPointer dw ?
 
ends
 
struct TCP_queue_entry
 
ip_ptr dd ?
segment_ptr dd ?
segment_size dd ?
device_ptr dd ?
timestamp dd ?
buffer_ptr dd ?
 
ends
 
uglobal
align 4
 
TCP_segments_tx rd NET_DEVICES_MAX
TCP_segments_rx rd NET_DEVICES_MAX
TCP_segments_missed rd NET_DEVICES_MAX
TCP_segments_dumped rd NET_DEVICES_MAX
; TCP_bytes_rx rq NET_DEVICES_MAX
; TCP_bytes_tx rq NET_DEVICES_MAX
TCP_sequence_num dd ?
TCP_queue rd (TCP_QUEUE_SIZE*sizeof.TCP_queue_entry + sizeof.queue)/4
TCP_input_event dd ?
TCP_timer1_event dd ?
endg
 
uglobal
align 4
 
TCPS_accepts dd ? ; #SYNs received in LISTEN state
TCPS_closed dd ? ; #connections closed (includes drops)
TCPS_connattempt dd ? ; #connections initiated (calls to connect)
TCPS_conndrops dd ? ; #embryonic connections dropped (before SYN received)
TCPS_connects dd ? ; #connections established actively or passively
TCPS_delack dd ? ; #delayed ACKs sent
TCPS_drops dd ? ; #connections dropped (after SYN received)
TCPS_keepdrops dd ? ; #connections dropped in keepalive (established or awaiting SYN)
TCPS_keepprobe dd ? ; #keepalive probes sent
TCPS_keeptimeo dd ? ; #times keepalive timer or connections-establishment timer expire
TCPS_pawsdrop dd ? ; #segments dropped due to PAWS
TCPS_pcbcachemiss dd ? ; #times PCB cache comparison fails
TCPS_persisttimeo dd ? ; #times persist timer expires
TCPS_predack dd ? ; #times header prediction correct for ACKs
TCPS_preddat dd ? ; #times header prediction correct for data packets
TCPS_rcvackbyte dd ? ; #bytes ACKed by received ACKs
TCPS_rcvackpack dd ? ; #received ACK packets
TCPS_rcvacktoomuch dd ? ; #received ACKs for unsent data
TCPS_rcvafterclose dd ? ; #packets received after connection closed
TCPS_rcvbadoff dd ? ; #packets received with invalid header length
TCPS_rcvbadsum dd ? ; #packets received with checksum errors
TCPS_rcvbyte dd ? ; #bytes received in sequence
TCPS_rcvbyteafterwin dd ? ; #bytes received beyond advertised window
TCPS_rcvdupack dd ? ; #duplicate ACKs received
TCPS_rcvdupbyte dd ? ; #bytes receivedin completely duplicate packets
TCPS_rcvduppack dd ? ; #packets received with completely duplicate bytes
TCPS_rcvoobyte dd ? ; #out-of-order bytes received
TCPS_rcvoopack dd ? ; #out-of-order packets received
TCPS_rcvpack dd ? ; #packets received in sequence
TCPS_rcvpackafterwin dd ? ; #packets with some data beyond advertised window
TCPS_rcvpartdupbyte dd ? ; #duplicate bytes in part-duplicate packets
TCPS_rcvpartduppack dd ? ; #packets with some duplicate data
TCPS_rcvshort dd ? ; #packets received too short
TCPS_rcvtotal dd ? ; #total packets received
TCPS_rcvwinprobe dd ? ; #window probe packets received
TCPS_rcvwinupd dd ? ; #received window update packets
TCPS_rexmttimeo dd ? ; #retransmission timeouts
TCPS_rttupdated dd ? ; #times RTT estimators updated
TCPS_segstimed dd ? ; #segments for which TCP tried to measure RTT
TCPS_sndacks dd ? ; #ACK-only packets sent (data length = 0)
TCPS_sndbyte dd ? ; #data bytes sent
TCPS_sndctrl dd ? ; #control (SYN, FIN, RST) packets sent (data length = 0)
TCPS_sndpack dd ? ; #data packets sent (data length > 0)
TCPS_sndprobe dd ? ; #window probes sent (1 byte of data forced by persist timer)
TCPS_sndrexmitbyte dd ? ; #data bytes retransmitted
TCPS_sndrexmitpack dd ? ; #data packets retransmitted
TCPS_sndtotal dd ? ; total #packets sent
TCPS_sndurg dd ? ; #packets sent with URG-only (data length=0)
TCPS_sndwinup dd ? ; #window update-only packets sent (data length=0)
TCPS_timeoutdrop dd ? ; #connections dropped in retransmission timeout
 
endg
 
 
;-----------------------------------------------------------------;
; ;
; TCP_init: Resets all TCP variables. ;
; ;
;-----------------------------------------------------------------;
macro tcp_init {
 
xor eax, eax
mov edi, TCP_segments_tx
mov ecx, (6*NET_DEVICES_MAX)
rep stosd
 
pseudo_random eax
mov [TCP_sequence_num], eax
 
init_queue TCP_queue
 
movi ebx, 1
mov ecx, tcp_process_input
call new_sys_threads
test eax, eax
jns @f
DEBUGF DEBUG_NETWORK_ERROR,'K : cannot create kernel thread for TCP input, error %d\n', eax
@@:
 
movi ebx, 1
mov ecx, tcp_timer_640ms
call new_sys_threads
test eax, eax
jns @f
DEBUGF DEBUG_NETWORK_ERROR,'K : cannot create kernel thread for TCP timer, error %d\n', eax
@@:
 
}
 
 
include 'tcp_timer.inc'
include 'tcp_subr.inc'
include 'tcp_usreq.inc'
include 'tcp_input.inc'
include 'tcp_output.inc'
 
 
;------------------------------------------------------------------;
; ;
; tcp_api: Part of system function 76 ;
; ;
; IN: bl = subfunction number ;
; bh = device number ;
; ecx, edx, .. depends on subfunction ;
; ;
; OUT: depends on subfunction ;
; ;
;------------------------------------------------------------------;
align 4
tcp_api:
 
movzx eax, bh
shl eax, 2
 
test bl, bl
jz .packets_tx ; 0
dec bl
jz .packets_rx ; 1
dec bl
jz .packets_missed ; 2
dec bl
jz .packets_dumped ; 3
dec bl
jz .packets_queued ; 4
 
.error:
mov eax, -1
ret
 
.packets_tx:
mov eax, [TCP_segments_tx + eax]
ret
 
.packets_rx:
mov eax, [TCP_segments_rx + eax]
ret
 
.packets_missed:
mov eax, [TCP_segments_missed + eax]
ret
 
.packets_dumped:
mov eax, [TCP_segments_dumped + eax]
ret
 
.packets_queued:
mov eax, [TCP_queue + queue.size]
ret
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/queue.inc
0,0 → 1,120
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; queue.inc ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
; The Queues implemented by these macros form a ring-buffer.
; The data to these queue's always looks like this:
;
; At top, you have the queue struct, wich has the size (number of currently queued packets, read and write pointers.
; This struct is followed by a number of slots wich you can read and write to using the macros.
; How these slots look like is up to you to chose, normally they should have at least a pointer to where the real data is.
; (you can see some examples below)
 
 
struct queue
 
size dd ? ; number of queued packets in this queue
w_ptr dd ? ; current writing pointer in queue
r_ptr dd ? ; current reading pointer
 
ends
 
; The following macros share these inputs:
 
; ptr = pointer to where the queue data is located
; size = number of slots/entrys in the queue
; entry_size = size of one slot, in bytes
; failaddr = the address where macro will jump to when there is no data in the queue
 
; additionally, add_to_queue requires you to set esi to the data wich you want to queue
; get_from_queue on the other hand will return a pointer in esi, to the entry you're interessed in
; PS: macros WILL destroy ecx and edi
 
macro add_to_queue ptr, size, entry_size, failaddr {
 
local .ok, .no_wrap
 
spin_lock_irqsave
 
cmp [ptr + queue.size], size ; Check if queue isnt full
jb .ok
 
spin_unlock_irqrestore
jmp failaddr
 
.ok:
inc [ptr + queue.size] ; if not full, queue one more
 
mov edi, [ptr + queue.w_ptr] ; Current write pointer (FIFO!)
mov ecx, entry_size/4 ; Write the queue entry
rep movsd ;
 
lea ecx, [size*entry_size+ptr+sizeof.queue]
cmp edi, ecx ; entry size
jb .no_wrap
 
sub edi, size*entry_size
.no_wrap:
mov [ptr + queue.w_ptr], edi
 
spin_unlock_irqrestore
 
}
 
 
 
macro get_from_queue ptr, size, entry_size, failaddr {
 
local .ok, .no_wrap
 
spin_lock_irqsave
 
cmp [ptr + queue.size], 0 ; any packets queued?
ja .ok
 
spin_unlock_irqrestore
jmp failaddr
 
.ok:
dec [ptr + queue.size] ; if so, dequeue one
 
mov esi, [ptr + queue.r_ptr]
push esi
 
add esi, entry_size
 
lea ecx, [size*entry_size+ptr+sizeof.queue]
cmp esi, ecx ; entry size
jb .no_wrap
 
sub esi, size*entry_size
 
.no_wrap:
mov dword [ptr + queue.r_ptr], esi
 
pop esi
 
spin_unlock_irqrestore
 
}
 
macro init_queue ptr {
 
mov [ptr + queue.size] , 0
lea edi, [ptr + sizeof.queue]
mov [ptr + queue.w_ptr], edi
mov [ptr + queue.r_ptr], edi
 
}
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/IPv6.inc
0,0 → 1,290
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2012-2015. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; IPv6.INC ;;
;; ;;
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision$
 
 
struct IPv6_header
 
VersionTrafficFlow dd ? ; Version[0-3], Traffic class[4-11], Flow Label [12-31]
PayloadLength dw ? ; 16 bits, unsigned length of payload (extension headers are part of this)
NextHeader db ? ; Values are same as in IPv4 'Protocol' field
HopLimit db ? ; Decremented by every node, packet is discarded when it reaches 0
SourceAddress rd 4 ; 128-bit addresses
DestinationAddress rd 4 ;
Payload rb 0
 
ends
 
 
uglobal
align 4
 
IPv6:
.addresses rd 4*NET_DEVICES_MAX
.subnet rd 4*NET_DEVICES_MAX
.dns rd 4*NET_DEVICES_MAX
.gateway rd 4*NET_DEVICES_MAX
 
.packets_tx rd NET_DEVICES_MAX
.packets_rx rd NET_DEVICES_MAX
 
endg
 
 
;-----------------------------------------------------------------;
; ;
; ipv6_init: Resets all IPv6 variables ;
; ;
;-----------------------------------------------------------------;
macro ipv6_init {
 
xor eax, eax
mov edi, IPv6
mov ecx, (4*4*4+2*4)MAX_IP
rep stosd
 
}
 
 
 
;-----------------------------------------------------------------;
; ;
; ipv6_input: Check if IPv6 Packet isnt damaged and call ;
; appropriate handler. (TCP/UDP/ICMP/..) ;
; We will also re-construct fragmented packets ;
; ;
; IN: [esp] = ptr to buffer ;
; [esp+4] = size of buffer ;
; ebx = ptr to device struct ;
; edx = ptr to IPv6 header ;
; ecx = size of IPv6 packet ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
ipv6_input:
 
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv6_input from: %x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x\n",\
[edx + IPv6_header.SourceAddress + 0]:2,[edx + IPv6_header.SourceAddress + 1]:2,\
[edx + IPv6_header.SourceAddress + 2]:2,[edx + IPv6_header.SourceAddress + 3]:2,\
[edx + IPv6_header.SourceAddress + 4]:2,[edx + IPv6_header.SourceAddress + 5]:2,\
[edx + IPv6_header.SourceAddress + 6]:2,[edx + IPv6_header.SourceAddress + 7]:2,\
[edx + IPv6_header.SourceAddress + 8]:2,[edx + IPv6_header.SourceAddress + 9]:2,\
[edx + IPv6_header.SourceAddress + 10]:2,[edx + IPv6_header.SourceAddress + 11]:2,\
[edx + IPv6_header.SourceAddress + 12]:2,[edx + IPv6_header.SourceAddress + 13]:2,\
[edx + IPv6_header.SourceAddress + 14]:2,[edx + IPv6_header.SourceAddress + 15]:2
 
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv6_input to: %x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x\n",\
[edx + IPv6_header.DestinationAddress + 0]:2,[edx + IPv6_header.DestinationAddress + 1]:2,\
[edx + IPv6_header.DestinationAddress + 2]:2,[edx + IPv6_header.DestinationAddress + 3]:2,\
[edx + IPv6_header.DestinationAddress + 4]:2,[edx + IPv6_header.DestinationAddress + 5]:2,\
[edx + IPv6_header.DestinationAddress + 6]:2,[edx + IPv6_header.DestinationAddress + 7]:2,\
[edx + IPv6_header.DestinationAddress + 8]:2,[edx + IPv6_header.DestinationAddress + 9]:2,\
[edx + IPv6_header.DestinationAddress + 10]:2,[edx + IPv6_header.DestinationAddress + 11]:2,\
[edx + IPv6_header.DestinationAddress + 12]:2,[edx + IPv6_header.DestinationAddress + 13]:2,\
[edx + IPv6_header.DestinationAddress + 14]:2,[edx + IPv6_header.DestinationAddress + 15]:2
 
sub ecx, sizeof.IPv6_header
jb .dump
 
cmp cx, [edx + IPv6_header.PayloadLength]
jb .dump
 
;-------------------------------------------------------------------
; No, it's just a regular IP packet, pass it to the higher protocols
 
.handle_it:
movzx ecx, [edx + IPv6_header.PayloadLength]
lea edi, [edx + IPv6_header.SourceAddress] ; make edi ptr to source and dest IPv6 address
lea esi, [edx + IPv6_header.Payload] ; make esi ptr to data
mov al, [edx + IPv6_header.NextHeader]
 
.scan:
cmp al, 59 ; no next
je .dump
 
cmp al, 0
je .hop_by_hop
 
cmp al, 43
je .routing
 
cmp al, 44
je .fragment
 
cmp al, 60
je .dest_opts
 
; cmp al, IP_PROTO_TCP
; je TCP_input
 
; cmp al, IP_PROTO_UDP
; je UDP_input
 
; cmp al, 58
; je ICMP6_input
 
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv6_input - unknown protocol: %u\n", al
 
.dump:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv6_input - dumping\n"
call net_buff_free
ret
 
.dump_options:
add esp, 2+4+4
jmp .dump
 
.nextheader:
pop esi
pop ecx
pop ax
jmp .scan
 
;-------------------------
; Hop-by-Hop
 
.hop_by_hop:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv6_input - hop by hop\n"
pushw [esi] ; 8 bit identifier for option type
movzx eax, byte[esi + 1] ; Hdr Ext Len
inc eax ; first 8 octets not counted
shl eax, 3 ; * 8
sub ecx, eax
push ecx
add eax, esi
push eax
inc esi
inc esi
 
mov al, [esi]
 
cmp al, 0
je .pad_1
 
cmp al, 1
je .pad_n
 
; TODO: check with other known options
 
; unknown option.. discard packet or not?
; check highest two bits
test al, 0xc0 ; discard packet
jnz .dump_options
 
.pad_n:
movzx eax, byte[esi + 1]
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv6_input - pad %u\n", eax
inc esi
inc esi
add esi, eax
sub ecx, eax
jmp .hop_by_hop
 
.pad_1:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv6_input - pad 1\n"
inc esi
dec ecx
jmp .hop_by_hop
 
 
 
.dest_opts:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv6_input - dest opts\n"
jmp .nextheader
 
.routing:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv6_input - routing\n"
pushw [esi] ; 8 bit identifier for option type
movzx eax, byte[esi + 1] ; Hdr Ext Len
inc eax ; first 8 octets not counted
shl eax, 3 ; * 8
sub ecx, eax
push ecx
add eax, esi
push eax
inc esi
inc esi
 
cmp al, 0
je .pad_1
 
cmp al, 1
je .pad_n
 
mov al, [esi] ; routing type
 
jmp .nextheader
 
.fragment:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv6_input - fragment\n"
 
jmp .nextheader
 
 
 
 
 
 
;-----------------------------------------------------------------;
; ;
; ipv6_api: Part of system function 76 ;
; ;
; IN: bl = subfunction number ;
; bh = device number ;
; ecx, edx, .. depends on subfunction ;
; ;
; OUT: depends on subfunction ;
; ;
;-----------------------------------------------------------------;
align 4
ipv6_api:
 
movzx eax, bh
shl eax, 2
 
and ebx, 0x000000ff
cmp ebx, .number
ja .error
jmp dword [.table + 4*ebx]
 
.table:
dd .packets_tx ; 0
dd .packets_rx ; 1
; dd .read_ip ; 2
; dd .write_ip ; 3
; dd .read_dns ; 4
; dd .write_dns ; 5
; dd .read_subnet ; 6
; dd .write_subnet ; 7
; dd .read_gateway ; 8
; dd .write_gateway ; 9
.number = ($ - .table) / 4 - 1
 
.error:
mov eax, -1
ret
 
.packets_tx:
mov eax, [IPv6.packets_tx + eax]
ret
 
.packets_rx:
mov eax, [IPv6.packets_rx + eax]
ret
 
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
Added: svn:keywords
+Revision
\ No newline at end of property
/kernel/branches/kolibri-ahci/network/.
Property changes:
Added: svn:ignore
+*.mnt
+lang.inc
+*.bat
+out.txt
+scin*
+*.obj