/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 |