Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 1513 → Rev 1514

/kernel/branches/net/network/ARP.inc
1,6 → 1,6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; ARP.INC ;;
12,7 → 12,7
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; Version 2, June- 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
242,7 → 242,7
mov eax, [IP_LIST+4*edi] ; senderIP
push eax
 
mov edi, [ETH_DRV_LIST + 4*edi]
mov edi, [NET_DRV_LIST + 4*edi]
lea eax, [edi + ETH_DEVICE.mac]
mov ebx, ETH_BROADCAST
mov ecx, 60 ; minimum packet size
249,8 → 249,7
mov edx, edi ;;;
mov di , ETHER_ARP
call ETH_create_packet
cmp edi, -1
je .exit
jz .exit
 
mov ecx, eax
 
275,7 → 274,7
DEBUGF 1,"ARP Packet for device %x created successfully\n", ebx
 
push edx ecx
jmp ETH_sender
jmp NET_send
 
.exit:
add esp, 8
523,7 → 522,7
cmp word [edx + ARP_Packet.Opcode], ARP_REQ_OPCODE ; Is this a request packet?
jne .exit
 
call ETH_struc2dev
call NET_ptr_to_num
DEBUGF 1,"ARP Request packet through device: %u\n", edi
inc [ARP_PACKETS_RX+4*edi]
cmp edi, -1
549,7 → 548,7
movsd ; Move sender IP to Dest IP
 
pop esi
mov esi, [ETH_DRV_LIST + 4*esi]
mov esi, [NET_DRV_LIST + 4*esi]
lea esi, [esi + ETH_DEVICE.mac]
lea edi, [edx + ARP_Packet.SenderMAC]
movsd ; Copy MAC address from in MAC_LIST
573,7 → 572,7
 
DEBUGF 1,"ARP_Handler - Sending reply \n"
 
jmp ETH_sender ; And send it!
jmp NET_send ; And send it!
 
.exit:
call kernel_free
/kernel/branches/net/network/IPv4.inc
1,6 → 1,6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; IPv4.INC ;;
18,16 → 18,13
 
$Revision$
 
; IP underlying protocols numbers
 
ETHER_IPv4 equ 0x0008 ; Reversed from 0800 for intel
 
MAX_FRAGMENTS equ 16
MAX_FRAGMENTS equ 64
MAX_IP equ MAX_NET_DEVICES
IP_MAX_INTERFACES equ MAX_IP
 
struct IPv4_Packet
.VersionAndIHL db ? ; Version[0-3 bits] and IHL(header length)[4-7 bits]
.TypeOfService db ?
.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]
58,13 → 55,15
 
align 4
uglobal
BROADCAST dd ?
 
IP_LIST rd MAX_IP
SUBNET_LIST rd MAX_IP
DNS_LIST rd MAX_IP
GATEWAY_LIST rd MAX_IP
 
IP_PACKETS_TX rd MAX_IP
IP_PACKETS_RX rd MAX_IP
 
FRAGMENT_LIST rb MAX_FRAGMENTS*FRAGMENT_slot.size
endg
 
83,11 → 82,11
IPv4_init:
 
or eax, -1
mov edi, BROADCAST
mov ecx, 4*MAX_IP+1
mov edi, IP_LIST
mov ecx, 4*MAX_IP
rep stosd
 
xor eax, eax
inc eax
mov edi, FRAGMENT_LIST
mov ecx, FRAGMENT_slot.size*MAX_FRAGMENTS/4 + 2*MAX_IP
rep stosd
114,11 → 113,13
;-----------------------------------------------------------------
align 4
IPv4_handler: ; TODO: implement handler for IP options
; TODO2: add code for IPv4 sockets (raw sockets)
; TODO2: add code for raw sockets
 
DEBUGF 1,"IP_Handler - start\n"
DEBUGF 1,"IPv4_Handler, packet from: %u.%u.%u.%u ",\
[edx + IPv4_Packet.SourceAddress]:1,[edx + IPv4_Packet.SourceAddress + 1]:1,[edx + IPv4_Packet.SourceAddress + 2]:1,[edx + IPv4_Packet.SourceAddress + 3]:1
DEBUGF 1,"to: %u.%u.%u.%u\n",\
[edx + IPv4_Packet.DestinationAddress]:1,[edx + IPv4_Packet.DestinationAddress + 1]:1,[edx + IPv4_Packet.DestinationAddress + 2]:1,[edx + IPv4_Packet.DestinationAddress + 3]:1
 
 
;-------------------------------------------
; Check if the packet still has time to live
 
133,61 → 134,66
cmp al , 0x05 ; IHL!= 5*4(20 bytes)
jnz .has_options
 
 
;-------------------------------
; Now, re-calcualte the checksum
; Now, re-calculate the checksum
 
; Re-calculate checksum
push edx ebx
mov esi, edx
call IPv4_checksum
pop ebx edx
 
; now see if it was correct
cmp [edx + IPv4_Packet.HeaderChecksum], 0
jne .dump ; if checksum isn't valid then dump packet
 
DEBUGF 1,"IPv4 Checksum is correct\n"
 
;-------------------------------------------------------
; Time to find out what interface this packet belongs to
;-----------------------------------
; Check if destination IP is correct
 
; Therefore we will scan the current list of IP's
call NET_ptr_to_num
shl edi, 2
 
mov eax, [edx + IPv4_Packet.DestinationAddress]
mov edi, BROADCAST
mov ecx, MAX_IP+1
; check if it matches local ip
 
.find_ip_loop:
cmp eax, dword [edi]
jz .ip_ok
add edi, 4
dec ecx
jnz .find_ip_loop
mov eax, dword[IP_LIST+edi]
cmp [edx + IPv4_Packet.DestinationAddress], eax
je .ip_ok
 
; it was not on the list, perhaps it's a loopback ?
; check for broadcast
 
mov eax, dword[SUBNET_LIST+edi]
not eax
test eax, 127 shl 24 ; 127.x.x.x
jz .ip_ok
or eax, dword[IP_LIST+edi]
cmp [edx + IPv4_Packet.DestinationAddress], eax
je .ip_ok
 
; TODO: we need to check for broadcasts (other then 255.255.255.255)
; or a special broadcast
 
DEBUGF 2,"Destination address does not match!\n"
cmp [edx + IPv4_Packet.DestinationAddress], -1
je .ip_ok
 
jmp .dump
; ; maybe it's a multicast then
;
; mov eax, [edx + IPv4_Packet.DestinationAddress]
; and eax, 0xff000000
; cmp eax, 224 shl 24
; je .ip_ok
 
; or a loopback address
 
;---------------------------------------------------
; Now we can update stats and find the device number
cmp eax, 127 shl 24
je .ip_ok
 
.ip_ok:
call ETH_struc2dev ; TODO: make this work on other protocols too!
inc [IP_PACKETS_RX+4*edi]
DEBUGF 1,"Packet comes from %u.%u.%u.%u\n",\
[edx + IPv4_Packet.SourceAddress]:1,[edx + IPv4_Packet.SourceAddress + 1]:1,[edx + IPv4_Packet.SourceAddress + 2]:1,[edx + IPv4_Packet.SourceAddress + 3]:1
; or it's not meant for us..
 
DEBUGF 2,"Destination address does not match!\n"
jmp .dump
 
;------------------------
; Now we can update stats
 
.ip_ok:
inc [IP_PACKETS_RX+edi]
 
;----------------------------------
; Check if the packet is fragmented
198,14 → 204,12
test [edx + IPv4_Packet.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 eax, byte [edx + IPv4_Packet.VersionAndIHL] ; Calculate Header length by using IHL field
and eax, 0x0000000F ;
and eax, 0x0000000f ;
shl eax, 2 ;
movzx ecx, word [edx + IPv4_Packet.TotalLength] ; Calculate length of encapsulated Packet
xchg cl , ch ;
220,13 → 224,13
pop edx ; Offset to data (tcp/udp/icmp/.. Packet)
 
cmp al , IP_PROTO_TCP
je TCP_handler
je TCP_input
 
cmp al , IP_PROTO_UDP
je UDP_handler
je UDP_input
 
cmp al , IP_PROTO_ICMP
je ICMP_handler
je ICMP_input
 
DEBUGF 2,"unknown Internet protocol: %u\n", al
 
351,7 → 355,7
cmp esi, -1
jne .count_bytes
 
mov esi, [esp+4] ;;;
mov esi, [esp+4]
mov [edi + FRAGMENT_entry.NextPtr], esi ; Add this packet to the chain, this simplifies the following code
mov [esi + FRAGMENT_entry.NextPtr], -1
mov [esi + FRAGMENT_entry.PrevPtr], edi
463,7 → 467,7
;
; find fragment slot
;
; IN: pointer to fragmented packet in edx ; TODO: the RFC says we should check protocol too
; IN: pointer to fragmented packet in edx
; OUT: pointer to slot in edi, -1 on error
;
;-----------------------------------------------------------------
470,6 → 474,8
align 4
IPv4_find_fragment_slot:
 
;;; TODO: the RFC says we should check protocol number too
 
push eax ebx ecx edx
mov ax , word [edx + IPv4_Packet.Identification]
mov ecx, MAX_FRAGMENTS
514,7 → 520,7
dec [esi + FRAGMENT_slot.ttl]
jnz .try_next
DEBUGF 1,"Fragment slot timed-out!\n"
; TODO: clear all entry's of timed-out slot
;;; TODO: clear all entry's of timed-out slot
.try_next:
add esi, 4
loop .loop
524,14 → 530,40
 
 
 
;-----------------------------------------------------------------
;------------------------------------------------------------------
;
;
; IN: dword [esp] = pointer to packet to be fragmented
; dword [esp+4] = buffer size
; edx = pointer to IPv4 header in that packet
; ecx = data length
; ebx = device structure
;
; OUT: /
;
;------------------------------------------------------------------
align 4
IPv4_fragment:
 
;;; TODO: write code here
 
 
call kernel_free
add esp, 4
 
ret
 
 
 
 
;------------------------------------------------------------------
;
; Create_IPv4_Packet
;
; IN: eax = dest ip
; ebx = source ip
; ecx = data length
; dx = fragment id
; dx = fragment id ;;;;
; di = protocol
;
; OUT: eax = pointer to buffer start
538,19 → 570,18
; ebx = pointer to device struct (needed for sending procedure)
; ecx = unchanged (packet size of embedded data)
; edx = size of complete buffer
; esi = pointer to sending procedure
; edi = pointer to start of data (-1 on error)
; edi = pointer to start of data (0 on error)
;
;----------------------------------------------------------------- ;;; TODO: create fragmented packets
;------------------------------------------------------------------
align 4
IPv4_create_packet:
 
DEBUGF 1,"Create IPv4 Packet (size=%u)\n", ecx
 
cmp ecx, 1480
cmp ecx, 65500 ; Max IPv4 packet size
jg .exit_
 
test ebx, ebx ; if dest ip = 0
test ebx, ebx ; if source ip = 0
jnz .ip_ok ; and local ip is valid
; use local ip instead
cmp [IP_LIST],0xffffffff ;
582,16 → 613,17
.send:
call IPv4_dest_to_dev
inc [IP_PACKETS_TX+4*edi]
mov edx, [ETH_DRV_LIST + 4*edi]
mov edx, [NET_DRV_LIST + 4*edi]
lea eax, [edx + ETH_DEVICE.mac]
mov ebx, esp
mov ecx, [esp+18] ;; 18 or 22 ??
add ecx, IPv4_Packet.DataOrOptional
mov di , ETHER_IPv4
call ETH_create_packet ; TODO: figure out a way to make this work with other protocols too
;;; TODO: detect if packet is too large for ethernet, if so, call IPv4_fragment
call ETH_create_packet ;;; TODO: figure out a way to make this work with other protocols too
add esp, 6
cmp edi, -1
je .exit
test edi, edi
jz .exit
 
mov [edi + IPv4_Packet.VersionAndIHL], 0x45 ; IPv4, normal length (no Optional header)
mov [edi + IPv4_Packet.TypeOfService], 0
622,12 → 654,12
 
.not_found:
DEBUGF 1,"Create IPv4 Packet - ARP entry not found!\n"
; TODO: QUEUE the packet to resend later!
;;;;;;
.exit:
add esp, 16
.exit_:
DEBUGF 1,"Create IPv4 Packet - failed\n"
or edi, -1
and edi, 0
ret
 
 
/kernel/branches/net/network/ethernet.inc
1,6 → 1,6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; ETHERNET.INC ;;
16,17 → 16,18
 
$Revision$
 
MAX_ETH_DEVICES equ MAX_NET_DEVICES
ETH_QUEUE_SIZE equ 16
 
struct ETH_FRAME
.DstMAC dp ? ; destination MAC-address [6 bytes]
.SrcMAC dp ? ; source MAC-address [6 bytes]
.Type dw ? ; type of the upper-layer protocol [2 bytes]
.Data: ; data [46-1500 bytes]
.DstMAC dp ? ; destination MAC-address
.SrcMAC dp ? ; source MAC-address
.Type dw ? ; type of the upper-layer protocol
.Data: ; data (46-1500 bytes for a normal packet)
ends
 
struct ETH_DEVICE
virtual at NET_DEVICE.end
 
ETH_DEVICE:
.unload dd ?
.reset dd ?
.transmit dd ?
39,19 → 40,12
.bytes_rx dq ?
.packets_tx dd ?
.packets_rx dd ?
.mode dd ? ; This dword contains cable status (10mbit/100mbit, full/half duplex, auto negotiation or not,..)
.mode dd ?
.name dd ?
.mac dp ?
ends ; the rest of the device struct depends on the type of device
 
struct eth_queue_entry
.owner dd ?
.data_ptr dd ?
.data_size dd ?
.size:
ends
end virtual
 
 
align 4
iglobal
 
60,13 → 54,7
 
align 4
uglobal
 
ETH_RUNNING dd ?
ETH_DRV_LIST rd MAX_ETH_DEVICES
ETH_IN_QUEUE rd 3*ETH_QUEUE_SIZE+3
if QUEUE_BEFORE_SENDING
ETH_OUT_QUEUE rd 3*ETH_QUEUE_SIZE+3
end if
endg
 
 
83,128 → 71,13
align 4
ETH_init:
 
xor eax, eax
mov edi, ETH_RUNNING
mov ecx, (1+MAX_ETH_DEVICES)
rep stosd
mov [ETH_RUNNING], 0
 
init_queue ETH_IN_QUEUE
 
if QUEUE_BEFORE_SENDING
init_queue ETH_OUT_QUEUE
end if
 
ret
 
 
;-----------------------------------------------------------------
;
; ETH_Add_Device:
;
; This function is called by ethernet drivers,
; to register each running ethernet device to the kernel
;
; IN: Pointer to device structure in ebx
; OUT: Device num in eax, -1 on error
;
;-----------------------------------------------------------------
align 4
ETH_add_device:
 
DEBUGF 1,"ETH_Add_Device: %x ", ebx
 
mov eax, [ETH_RUNNING]
cmp eax, MAX_ETH_DEVICES
jge .error
 
test eax, eax
jnz .notfirst
mov dword [ETH_IN_QUEUE], eax
if QUEUE_BEFORE_SENDING
mov dword [ETH_OUT_QUEUE], eax
end if
.notfirst:
 
mov eax, ebx
mov ecx, MAX_ETH_DEVICES ; We need to check whole list because a device may be removed without re-organizing list
mov edi, ETH_DRV_LIST
 
repne scasd ; See if device is already in the list
jz .error
 
xor eax, eax
mov ecx, MAX_ETH_DEVICES
mov edi, ETH_DRV_LIST
 
repne scasd ; Find empty spot in the list
jnz .error
 
sub edi, 4
mov [edi], ebx ; add device to list
 
sub edi, ETH_DRV_LIST ; edi = 4*device num Calculate device number in eax
mov eax, edi ; edx = 4*device num
shr eax, 2
 
inc [ETH_RUNNING] ; Indicate that one more ethernet device is up and running
 
DEBUGF 1,"- succes: %u\n",eax
ret
 
.error:
or eax, -1
DEBUGF 2,"Adding ETH device failed\n"
ret
 
 
 
 
;-----------------------------------------------------------------
;
; ETH_Remove_Device:
;
; This function is called by ethernet drivers,
; to unregister ethernet devices from the kernel
;
; IN: Pointer to device structure in ebx
; OUT: eax: -1 on error
;
;-----------------------------------------------------------------
align 4
ETH_remove_device:
 
cmp [ETH_RUNNING], 0
je .error
 
mov eax, ebx
mov ecx, MAX_ETH_DEVICES
mov edi, ETH_DRV_LIST
 
repne scasd
jnz .error
 
xor eax, eax
mov dword [edi-4], eax
 
dec [ETH_RUNNING]
jnz .notlast
 
mov dword [ETH_IN_QUEUE], ETH_QUEUE_SIZE
if QUEUE_BEFORE_SENDING
mov dword [ETH_OUT_QUEUE], ETH_QUEUE_SIZE
end if
 
.notlast:
ret
 
.error:
or eax, -1
ret
 
 
 
;-----------------------------------------------------------------
;
; ETH_Receiver:
;
; This function is called by ethernet drivers,
218,57 → 91,8
;-----------------------------------------------------------------
align 4
ETH_receiver:
 
; DEBUGF 1,"ETH_Receiver: "
; push ebx
; mov esi, esp
; add_to_queue ETH_IN_QUEUE, ETH_QUEUE_SIZE, eth_queue_entry.size, .fail
; DEBUGF 1,"Queued packet successfully\n"
; add esp, 4*3
;
; ret
;
; .fail:
; DEBUGF 1,"ETH_IN_QUEUE is full!\n"
; add esp, 4
; call kernel_free
; add esp, 4
;
; ret
;
;
;
;;-----------------------------------------------------------------
;;
;; ETH_Handler:
;;
;; Handles all queued eth packets (called from kernel's main_loop)
;;
;; IN: /
;; OUT: /
;;
;;-----------------------------------------------------------------
;align 4
;ETH_handler:
;
; get_from_queue ETH_IN_QUEUE, ETH_QUEUE_SIZE, eth_queue_entry.size, .gohome
;
; push ETH_handler
;
; lodsd
; mov ebx, eax
; lodsd
; mov ecx, eax
; lodsd
; xchg eax, ecx
; push ecx
; push eax
 
 
;-----------------------------
mov eax, [esp]
mov ecx, [esp+4]
;-----------------------------
 
DEBUGF 1,"ETH_Handler - size: %u\n", ecx
cmp ecx, 60 ; check packet length
290,136 → 114,23
DEBUGF 2,"ETH_Handler - dumping\n"
call kernel_free
add esp, 4
 
.gohome:
ret ; return to get more from queue / to caller
 
 
 
 
align 4
ETH_handler:
ret
 
 
;-----------------------------------------------------------------
;
; ETH_sender:
;
; This function sends an ethernet packet to the correct driver.
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; pointer to device struct in ebx
; OUT: /
;
;-----------------------------------------------------------------
align 4
ETH_sender:
if QUEUE_BEFORE_SENDING
DEBUGF 1,"ETH_Sender: queuing for device: %x, %u bytes\n", [esp], [esp + 4]
 
push ebx
mov esi, esp
add_to_queue ETH_OUT_QUEUE, ETH_QUEUE_SIZE, eth_queue_entry.size, .fail
DEBUGF 1,"Queued packet successfully\n"
add esp, 3*4
 
ret
 
.fail:
DEBUGF 1,"ETH_OUT_QUEUE is full!\n"
add esp, 4
call kernel_free
add esp, 4
 
ret
 
 
 
;-----------------------------------------------------------------
;
; ETH_send_queued:
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
align 4
ETH_send_queued:
 
get_from_queue ETH_OUT_QUEUE, ETH_QUEUE_SIZE, eth_queue_entry.size, .gohome
 
push ETH_send_queued ; this will cause the procedure to check for more packets
; when a single packet is handled
 
mov ebx, [esi]
pushd [esi + 8]
pushd [esi + 4]
 
DEBUGF 1,"dequeued packet for device %x\n", ebx
end if
call [ebx+ETH_DEVICE.transmit] ; we will return to get_from_queue macro after transmitting packet
call kernel_free
add esp, 4 ; pop (balance stack)
 
.gohome:
ret
 
 
;-----------------------------------------------------------------
;
; ETH_struc2dev
;
; IN: pointer to device struct in ebx
;
; OUT: edi is -1 on error, device number otherwise
;
;-----------------------------------------------------------------
align 4
ETH_struc2dev:
push ecx
 
mov ecx, MAX_ETH_DEVICES
mov edi, ETH_DRV_LIST
 
 
.loop:
cmp ebx, [edi]
jz .found
add edi, 4
dec ecx
jnz .loop
 
or edi, -1
 
pop ecx
ret
 
.found:
sub edi, ETH_DRV_LIST
shr edi, 2
 
pop ecx
ret
 
 
;-----------------------------------------------------------------
;
; ETH_create_packet
;
; IN: pointer to source mac in eax
; pointer to destination mac in ebx
; packet size in ecx
; device number in edx
; protocol in di
; IN: eax = pointer to source mac
; ebx = pointer to destination mac
; ecx = packet size
; edx = device number
; di = protocol
;
; OUT: edi is -1 on error, pointer to buffer otherwise
; eax points to buffer start
; ebx is pointer to device structure
; ecx is unchanged (packet size of embedded data)
; edx is size of complete buffer
; esi points to procedure wich needs to be called to send packet
; OUT: edi = 0 on error, pointer to buffer otherwise
; eax = buffer start
; ebx = to device structure
; ecx = unchanged (packet size of embedded data)
; edx = size of complete buffer
;
;-----------------------------------------------------------------
align 4
427,7 → 138,7
 
DEBUGF 1,"Creating Ethernet Packet (size=%u): \n", ecx
 
cmp ecx, 1500
cmp ecx, 1500 ;;;
jg .exit
 
push ecx di eax ebx edx
436,13 → 147,13
push ecx
push ecx
call kernel_alloc
test eax, eax
mov edi, eax
test edi, edi
jz .pop_exit
 
pop ecx
pop edx
 
mov edi, eax
pop esi
movsd
movsw
455,10 → 166,9
lea eax, [edi - ETH_FRAME.Data] ; Set eax to buffer start
mov edx, ecx ; Set ebx to complete buffer size
pop ecx
mov esi, ETH_sender
 
xor ebx, ebx ;;;; TODO: Fixme
mov ebx, [ETH_DRV_LIST + ebx]
mov ebx, [NET_DRV_LIST + ebx]
 
cmp edx, 46 + ETH_FRAME.Data ; If data size is less then 46, add padding bytes
jg .continue
471,12 → 181,12
.pop_exit:
DEBUGF 2,"Out of ram space!!\n"
add esp, 18
or edi,-1
and edi, 0
ret
 
.exit:
DEBUGF 2,"Packet too large!\n"
or edi, -1
and edi, 0
ret
 
 
497,9 → 207,20
align 4
ETH_API:
 
cmp bh, MAX_NET_DEVICES
jg .error
movzx eax, bh
shl eax, 2
 
cmp bl, 7
jz .out_queue
cmp bl, 6
jz .in_queue
 
mov eax, dword [NET_DRV_LIST + eax]
cmp [eax + NET_DEVICE.type], NET_TYPE_ETH
jne .error
 
test bl, bl
jz .packets_tx ; 0
dec bl
512,31 → 233,22
jz .read_mac ; 4
dec bl
jz .write_mac ; 5
dec bl
jz .in_queue ; 6
dec bl
jz .out_queue ; 7
 
.error:
mov eax, -1
DEBUGF 2,"Device is not ethernet type\n"
or eax, -1
ret
 
.packets_tx:
add eax, ETH_DRV_LIST
mov eax, dword [eax]
mov eax, dword [eax + ETH_DEVICE.packets_tx]
 
ret
 
.packets_rx:
add eax, ETH_DRV_LIST
mov eax, dword [eax]
mov eax, dword [eax + ETH_DEVICE.packets_rx]
ret
 
.bytes_tx:
add eax, ETH_DRV_LIST
mov eax, dword [eax]
mov ebx, dword [eax + ETH_DEVICE.bytes_tx + 4]
mov eax, dword [eax + ETH_DEVICE.bytes_tx]
mov [esp+20+4], ebx ; TODO: fix this ugly code
543,8 → 255,6
ret
 
.bytes_rx:
add eax, ETH_DRV_LIST
mov eax, dword [eax]
mov ebx, dword [eax + ETH_DEVICE.bytes_rx + 4]
mov eax, dword [eax + ETH_DEVICE.bytes_rx]
mov [esp+20+4], ebx ; TODO: fix this ugly code
552,11 → 262,6
 
 
.read_mac:
add eax, ETH_DRV_LIST
mov eax, [eax]
; push eax
; call dword [eax + ETH_DEVICE.get_MAC]
; pop eax
movzx ebx, word [eax + ETH_DEVICE.mac]
mov eax, dword [eax + ETH_DEVICE.mac + 2]
mov [esp+20+4], ebx ; TODO: fix this ugly code
565,22 → 270,24
.write_mac:
push ecx
push dx
add eax, ETH_DRV_LIST
mov eax, [eax]
mov eax, dword [eax + ETH_DEVICE.set_MAC]
mov eax, [eax + ETH_DEVICE.set_MAC]
call eax
ret
 
.in_queue:
if ETH_QUEUE
add eax, ETH_IN_QUEUE
mov eax, [eax + queue.size]
else
or eax, -1
end if
ret
 
.out_queue:
if QUEUE_BEFORE_SENDING
if ETH_QUEUE
add eax, ETH_OUT_QUEUE
mov eax, [eax + queue.size]
else
mov eax, -1
or eax, -1
end if
ret
/kernel/branches/net/network/icmp.inc
1,6 → 1,6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; ICMP.INC ;;
130,10 → 130,10
 
;-----------------------------------------------------------------
;
; ICMP_Handler:
; ICMP_input:
;
; this procedure will send reply's to ICMP echo's
; and insert packets into sockets when needed ;;; TODO: update this to work with fragmented packets too!
; This procedure will send reply's to ICMP echo's
; and insert packets into sockets when needed
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
144,20 → 144,22
;
;-----------------------------------------------------------------
align 4
ICMP_handler: ;TODO: works only on pure ethernet right now !
ICMP_input:
 
DEBUGF 1,"ICMP_Handler - buf:%x size:%x dev:%x, size:%x, buf:%x\n", [esp], [esp+4], ebx, ecx, edx
;;; TODO: works only on pure ethernet right now !
 
DEBUGF 1,"ICMP_Handler - start\n"
cmp byte [edx + ICMP_Packet.Type], ICMP_ECHO ; Is this an echo request?
jne .check_sockets
 
;;; TODO: check checksum!
 
DEBUGF 1,"ICMP_Handler - is echo request, through device:%x\n", ebx
DEBUGF 1,"ICMP_Handler - echo request\n"
 
mov byte [edx + ICMP_Packet.Type], ICMP_ECHOREPLY ; Change Packet type to reply
mov word [edx + ICMP_Packet.Checksum], 0 ; Set checksum to 0, needed to calculate new checksum
 
call ETH_struc2dev
call NET_ptr_to_num
cmp edi,-1
je .dump
inc [ICMP_PACKETS_RX+4*edi]
207,12 → 209,12
pop ecx edx ebx
mov word [edx + ICMP_Packet.Checksum], ax
 
jmp ETH_sender ; Send the reply
jmp NET_send ; Send the reply
; and return to caller of this proc
 
 
 
 
 
.check_sockets:
; TODO: validate the header & checksum.
 
222,12 → 224,12
.try_more:
mov ax , [edx + ICMP_Packet.Identifier]
.next_socket:
mov esi, [esi + SOCKET_head.NextPtr]
mov esi, [esi + SOCKET.NextPtr]
or esi, esi
jz .dump
cmp [esi + SOCKET_head.Type], IP_PROTO_ICMP
cmp [esi + SOCKET.Type], IP_PROTO_ICMP
jne .next_socket
cmp [esi + SOCKET_head.end + IPv4_SOCKET.end + ICMP_SOCKET.Identifier], ax
cmp [esi + ICMP_SOCKET.Identifier], ax
jne .next_socket
 
call IPv4_dest_to_dev
237,7 → 239,7
 
DEBUGF 1,"Found valid ICMP packet for socket %x\n", esi
 
lea ebx, [esi + SOCKET_head.lock]
lea ebx, [esi + SOCKET.lock]
call wait_mutex
 
; Now, assign data to socket. We have socket address in esi.
249,7 → 251,7
add esp, 4
sub edx, esi
mov edi, edx
jmp socket_internal_receiver
;;; jmp SOCKET_input
 
.dump:
DEBUGF 1,"ICMP_Handler - dumping\n"
262,59 → 264,6
 
;-----------------------------------------------------------------
;
; ICMP_Handler_fragments:
;
; Called by IP_handler,
; this procedure will send reply's to ICMP echo's etc
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; pointer to device struct in ebx
; ICMP Packet size in ecx
; pointer to ICMP Packet data in edx
; OUT: /
;
;-----------------------------------------------------------------
align 4
ICMP_handler_fragments: ; works only on pure ethernet right now !
 
DEBUGF 1,"ICMP_Handler_fragments - start\n"
 
cmp ecx, 65500
jg .dump
 
cmp byte [edx + ICMP_Packet.Type], ICMP_ECHO ; Is this an echo request? discard if not
jne .dump
 
mov esi, [esp]
 
sub ecx, ICMP_Packet.Data
mov eax, [esi + IPv4_Packet.SourceAddress]
mov ebx, [esi + IPv4_Packet.DestinationAddress]
push word [esi + IPv4_Packet.Identification]
 
mov di , [edx + ICMP_Packet.Identifier]
shl edi, 16
mov di , [edx + ICMP_Packet.SequenceNumber]
 
mov esi, edx
add esi, ICMP_Packet.Data
pop dx
shl edx, 16
mov dx , ICMP_ECHOREPLY shl 8 + 0 ; Type + Code
 
call ICMP_create_packet
 
.dump:
DEBUGF 1,"ICMP_Handler_fragments - end\n"
 
call kernel_free
add esp, 4 ; pop (balance stack)
ret
 
 
;-----------------------------------------------------------------
;
; Note: ICMP only works on top of IP protocol :)
;
; inputs:
330,7 → 279,7
;
;-----------------------------------------------------------------
align 4
ICMP_create_packet:
ICMP_output:
 
DEBUGF 1,"Create ICMP Packet\n"
 
341,10 → 290,8
shr edx, 16
 
call IPv4_create_packet
jz .exit
 
cmp edi, -1
je .exit
 
DEBUGF 1,"full icmp packet size: %u\n", edx
 
pop eax
372,7 → 319,7
and cx , 3
rep movsb
 
sub edi, edx ;; TODO: find a better way to remember start of packet
sub edi, edx ;;; TODO: find a better way to remember start of packet
mov ecx, [ebx + ETH_DEVICE.transmit]
push edx edi ecx
DEBUGF 1,"Sending ICMP Packet\n"
/kernel/branches/net/network/queue.inc
1,6 → 1,6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; queue.inc ;;
14,7 → 14,7
 
$Revision$
 
; The Queues implemented by these macros for a sort of ring-buffer.
; 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.
/kernel/branches/net/network/socket.inc
1,6 → 1,6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; SOCKET.INC ;;
15,102 → 15,177
 
$Revision$
 
struct SOCKET_head
virtual at 0
 
SOCKET:
.NextPtr dd ? ; pointer to next socket in list
.PrevPtr dd ? ; pointer to previous socket in list
.Number dd ? ; socket number (unique within single process)
.Number dd ? ; socket number
 
.lock dd ? ; lock mutex
 
.PID dd ? ; application process id
.Domain dd ? ; INET/UNIX/..
.Type dd ? ; RAW/UDP/TCP/...
.Protocol dd ? ; ICMP/IPv4/ARP/
.lock dd ? ; lock mutex
.errorcode dd ?
 
.options dd ?
.SO_SND.SB_CC dd ? ;;;;; socket options: number of bytes in socket
.SO_RCV.SB_CC dd ?
.state dd ? ;;;;;;;;;
 
.end:
ends
end virtual
 
struct IPv4_SOCKET
virtual at SOCKET.end
 
IP_SOCKET:
 
.LocalIP dd ?
rd 3 ; for IPv6 addresses
 
.RemoteIP dd ?
.SequenceNumber dd ?
rd 3 ; for IPv6 addresses
 
; todo: add options (for func 8 and 9)
.end:
end virtual
 
virtual at SOCKET.end
 
SOCKET_virtual:
 
.ConnectedTo dd ? ; Socket number of other socket this one is connected to
 
.end:
ends
end virtual
 
struct TCP_SOCKET
virtual at IP_SOCKET.end
 
TCP_SOCKET:
 
.LocalPort dw ? ; In INET byte order
.RemotePort dw ? ; In INET byte order
 
.backlog dw ? ; Backlog
.backlog_cur dw ? ; current size of queue for un-accept-ed connections
.last_ack_number dd ? ; used only to let application know that ACK has been received
; todo: may be use SND_UNA instead
; todo: may be use events which allow additional information instead
; todo: may be count acknowledged bytes (at least it has obvious sense)
 
.OrigRemoteIP dd ? ; original remote IP address (used to reset to LISTEN state)
.OrigRemotePort dw ? ; original remote port (used to reset to LISTEN state)
.wndsizeTimer dd ? ; window size timer
 
; Transmission control block
.state dd ? ; TCB state
.timer dd ? ; TCB timer (seconds)
.t_state dd ? ; TCB state
.t_timer dd ? ; TCB timer (seconds)
.t_rxtshift dd ?
.t_rxtcur dd ?
.t_dupacks dd ?
.t_maxseg dd ?
.t_force dd ?
.t_flags dd ?
 
.ISS dd ? ; initial send sequence number
.IRS dd ? ; initial receive sequence number
;---------------
; 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 ?
.SND_WL1 dd ? ; window minus one
.SND_WL2 dd ? ;
.ISS dd ? ; initial send sequence number
.SND_WND dd ? ; send window
 
; receive sequence
.RCV_WND dw ? ; receive window
.RCV_NXT dd ? ; next receive sequence number to use
.RCV_WND dd ? ; receive window
.SEG_LEN dd ? ; segment length
.SEG_WND dd ? ; segment window
.RCV_UP dd ?
.IRS dd ? ; initial receive sequence number
 
.flags db ? ; packet flags
;---------------------
; Additional variables
 
; receive variables
.RCV_ADV dd ?
 
; retransmit variables
.SND_MAX dd ?
 
; congestion control
.SND_CWND dd ?
.SND_SSTHRESH dd ?
 
;----------------------
; Transmit timing stuff
 
.t_idle dd ?
.t_rtt dd ?
.t_rtseq dd ?
.t_srtt dd ?
.t_rttvar dd ?
.t_rttmin dd ?
.max_sndwnd dd ?
 
;-----------------
; Out-of-band data
 
.t_oobflags dd ?
.t_iobc dd ?
.t_softerror dd ?
 
 
;---------
; RFC 1323
 
.SND_SCALE db ? ; Scale factor
.RCV_SCALE db ?
.request_r_scale db ?
.requested_s_scale dd ?
 
.ts_recent dd ?
.ts_recent_age dd ?
.last_ack_sent dd ?
 
.end:
ends
end virtual
 
struct UDP_SOCKET
virtual at IP_SOCKET.end
 
UDP_SOCKET:
 
.LocalPort dw ? ; In INET byte order
.RemotePort dw ? ; In INET byte order
.firstpacket db ?
 
.end:
ends
end virtual
 
struct ICMP_SOCKET
virtual at IP_SOCKET.end
 
ICMP_SOCKET:
 
.Identifier dw ? ;
 
.end:
end virtual
 
ends
 
struct IPC_SOCKET
 
.ConnectedTo dd ? ; Socket number of other socket this one is connected to
 
.end:
 
ends
 
struct socket_queue_entry
; .owner dd ?
.data_ptr dd ?
.buf_ptr dd ?
.data_size dd ?
.offset dd ?
.size:
ends
 
 
MAX_backlog equ 20 ; backlog for stream sockets
SOCKETBUFFSIZE equ 4096 ; in bytes
 
SOCKET_QUEUE_SIZE equ 10 ; maximum number ofincoming packets queued for 1 socket
SOCKET_QUEUE_LOCATION equ 2048 ; the incoming packet queue for sockets is placed in the socket struct itself, at this location from start
; the incoming packet queue for sockets is placed in the socket struct itself, at this location from start
SOCKET_QUEUE_LOCATION equ SOCKETBUFFSIZE - SOCKET_QUEUE_SIZE*socket_queue_entry.size - queue.data
 
uglobal
net_sockets rd 2
net_sockets rd 4
last_UDP_port dw ? ; These values give the number of the last used ephemeral port
last_TCP_port dw ? ;
endg
129,8 → 204,10
align 4
socket_init:
 
mov [net_sockets], 0
mov [net_sockets + 4], 0
xor eax, eax
mov edi, net_sockets
mov ecx, 4
rep stosd
 
mov [last_UDP_port], MIN_EPHEMERAL_PORT
mov [last_TCP_port], MIN_EPHEMERAL_PORT
145,26 → 222,26
;-----------------------------------------------------------------
align 4
sys_socket:
and ebx, 0x000000FF ; should i remove this line ?
cmp bl , 8 ; highest possible number
cmp ebx, 8 ; highest possible number
jg s_error
lea ebx, [.table + 4*ebx]
jmp dword [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_recv ; 7
dd socket_get_opt ; 8
; dd socket_set_opt ; 9
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_get_opt ; 8
; dd SOCKET_set_opt ; 9
 
 
s_error:
DEBUGF 1,"socket error\n"
mov dword [esp+32],-1
 
ret
174,7 → 251,6
;
; SOCKET_open
;
;
; IN: domain in ecx
; type in edx
; protocol in esi
182,45 → 258,19
;
;-----------------------------------------------------------------
align 4
socket_open:
SOCKET_open:
 
DEBUGF 1,"socket_open: domain: %u, type: %u",ecx, edx
DEBUGF 1,"socket_open: domain: %u, type: %u protocol: %x\n", ecx, edx, esi
 
call net_socket_alloc
or eax, eax
call SOCKET_alloc
jz s_error
 
mov [eax + SOCKET_head.Domain], ecx
mov [eax + SOCKET_head.Type], edx
mov [eax + SOCKET_head.Protocol], esi
mov [eax + SOCKET.Domain], ecx
mov [eax + SOCKET.Type], edx
mov [eax + SOCKET.Protocol], esi
 
cmp ecx, AF_INET4
je .af_inet4
mov [esp+32], edi
 
jmp .done
 
 
.af_inet4:
 
cmp edx, IP_PROTO_TCP
je .tcp
 
jmp .done
 
.tcp:
 
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_CLOSED
 
pseudo_random ebx
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.ISS], ebx
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT], ebx
 
.done:
stdcall net_socket_addr_to_num, eax
DEBUGF 1,", socketnumber: %u\n", eax
 
mov [esp+32], eax
 
ret
 
 
236,12 → 286,11
;
;-----------------------------------------------------------------
align 4
socket_bind:
SOCKET_bind:
 
DEBUGF 1,"Socket_bind: socknum: %u sockaddr: %x, length: %u, ",ecx,edx,esi
DEBUGF 1,"socket_bind: socknum: %u sockaddr: %x, length: %u\n", ecx, edx, esi
 
stdcall net_socket_num_to_addr, ecx
cmp eax, -1
call SOCKET_num_to_ptr
jz s_error
 
cmp esi, 2
264,37 → 313,37
 
.af_inet4:
 
DEBUGF 1,"af_inet4\n"
 
cmp esi, 6
jl s_error
 
mov ecx, [eax + SOCKET_head.Type]
mov ecx, [eax + SOCKET.Type]
 
mov bx, word [edx + 2]
DEBUGF 1,"local port: %x ",bx
test bx, bx
jz .find_free
 
call socket_check_port
test bx, bx
je s_error
call SOCKET_check_port
; test bx, bx
jz s_error
jmp .got_port
 
.find_free:
call SOCKET_find_port
; test bx, bx
jz s_error
 
call socket_find_port
test bx, bx
je s_error
 
.got_port:
DEBUGF 1,"using port: %x ",bx
mov word [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.LocalPort], bx
DEBUGF 1,"using local port: %u", bx
mov word [eax + UDP_SOCKET.LocalPort], bx
 
mov ebx, dword [edx + 4]
mov dword [eax + SOCKET_head.end + IPv4_SOCKET.LocalIP], ebx
mov dword [eax + IP_SOCKET.LocalIP], ebx
 
DEBUGF 1,"local ip: %u.%u.%u.%u\n",\
[eax + SOCKET_head.end + IPv4_SOCKET.LocalIP + 0]:1,[eax + SOCKET_head.end + IPv4_SOCKET.LocalIP + 1]:1,\
[eax + SOCKET_head.end + IPv4_SOCKET.LocalIP + 2]:1,[eax + SOCKET_head.end + IPv4_SOCKET.LocalIP + 3]:1
[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
306,7 → 355,6
;
; SOCKET_connect
;
;
; IN: socket number in ecx
; pointer to sockaddr struct in edx
; length of that struct in esi
314,12 → 362,11
;
;-----------------------------------------------------------------
align 4
socket_connect:
SOCKET_connect:
 
DEBUGF 1,"Socket_connect: socknum: %u sockaddr: %x, length: %u,",ecx,edx,esi
DEBUGF 1,"socket_connect: socknum: %u sockaddr: %x, length: %u\n", ecx, edx, esi
 
stdcall net_socket_num_to_addr, ecx
cmp eax, -1
call SOCKET_num_to_ptr
jz s_error
 
cmp esi, 8
332,23 → 379,22
 
.af_inet4:
 
cmp [eax + SOCKET_head.Type], IP_PROTO_UDP
cmp [eax + SOCKET.Type], IP_PROTO_UDP
je .udp
 
cmp [eax + SOCKET_head.Type], IP_PROTO_TCP
cmp [eax + SOCKET.Type], IP_PROTO_TCP
je .tcp
 
jmp s_error
 
.udp:
 
mov bx , word [edx + 2]
mov word [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.RemotePort], bx
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.firstpacket], 0
DEBUGF 1,"remote port: %x ",bx
mov word [eax + UDP_SOCKET.RemotePort], bx
mov [eax + UDP_SOCKET.firstpacket], 0
DEBUGF 1,"remote port: %u ",bx
 
mov ebx, dword [edx + 4]
mov dword [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP], ebx
mov dword [eax + IP_SOCKET.RemoteIP], ebx
DEBUGF 1,"remote ip: %u.%u.%u.%u\n",[edx+4]:1,[edx+5]:1,[edx+6]:1,[edx+7]:1
 
mov dword [esp+32],0
356,50 → 402,45
 
 
.tcp:
; TODO: set sequence number to random value
; set sequence number
 
lea ebx, [eax + SOCKET_head.lock]
mov ebx, [TCP_sequence_num]
add [TCP_sequence_num], 6400
mov [eax + TCP_SOCKET.ISS], ebx
 
lea ebx, [eax + SOCKET.lock]
call wait_mutex
 
; fill in remote port and IP
 
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer], 0 ; Reset the window timer.
; TODO: figure out WTF this is
;;;;;; mov [eax + TCP_SOCKET.wndsizeTimer], 0 ; Reset the window timer.
 
mov bx , word [edx + 2]
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RemotePort], bx
DEBUGF 1,"remote port: %x ",bx
mov [eax + TCP_SOCKET.RemotePort], bx
DEBUGF 1,"remote port: %u ",bx
 
mov ebx, dword [edx + 4]
mov [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP], ebx
mov [eax + IP_SOCKET.RemoteIP], ebx
 
; check if local port and IP is ok
 
cmp [eax + SOCKET_head.end + IPv4_SOCKET.LocalIP], 0
cmp [eax + IP_SOCKET.LocalIP], 0
jne @f
push [IP_LIST] ; device zero = default
pop [eax + SOCKET_head.end + IPv4_SOCKET.LocalIP]
push [IP_LIST] ;;;;; device zero = default
pop [eax + IP_SOCKET.LocalIP]
@@:
 
cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort], 0
cmp [eax + TCP_SOCKET.LocalPort], 0
jne @f
 
mov ecx, [eax + SOCKET_head.Type]
call socket_find_port
test bx, bx
jz s_error
 
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort], bx
call SOCKET_find_port
@@:
 
; mov [eax + TCP_SOCKET.t_state], TCB_SYN_SENT
call TCP_output
 
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_SYN_SENT
; now say hello to the remote tcp socket
mov [eax + SOCKET.lock], 0
 
mov bl, TH_SYN
xor ecx, ecx
call TCP_send
 
mov dword [esp+32],0
mov dword [esp+32], 0 ; success!
ret
 
 
407,7 → 448,6
;
; SOCKET_listen
;
;
; IN: socket number in ecx
; backlog in edx
; OUT: eax is socket num, -1 on error
414,29 → 454,32
;
;-----------------------------------------------------------------
align 4
socket_listen:
SOCKET_listen:
 
DEBUGF 1,"Socket_listen: socknum: %u backlog: %u\n",ecx,edx
 
stdcall net_socket_num_to_addr, ecx
cmp eax, -1
call SOCKET_num_to_ptr
jz s_error
 
cmp word [eax + SOCKET_head.Domain], AF_INET4
cmp word [eax + SOCKET.Domain], AF_INET4
jne s_error
 
cmp [eax + SOCKET_head.Type], IP_PROTO_TCP
cmp [eax + SOCKET.Type], IP_PROTO_TCP
jne s_error
 
; TODO: check local port number
 
cmp edx, MAX_backlog
jb .ok
mov dx , MAX_backlog
jle .ok
mov edx, MAX_backlog
.ok:
 
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog], dx
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_LISTEN
mov [eax + TCP_SOCKET.backlog], dx
mov [eax + TCP_SOCKET.t_state], TCB_LISTEN
or [eax + SOCKET.options], SO_ACCEPTCON
 
mov dword [esp+32], 0
 
ret
 
 
444,7 → 487,6
;
; SOCKET_accept
;
;
; IN: socket number in ecx
; addr in edx
; addrlen in esi
452,16 → 494,14
;
;-----------------------------------------------------------------
align 4
socket_accept:
SOCKET_accept:
 
DEBUGF 1,"Socket_accept: socknum: %u sockaddr: %x, length: %u\n",ecx,edx,esi
 
stdcall net_socket_num_to_addr, ecx
or eax, eax
call SOCKET_num_to_ptr
jz s_error
mov esi, eax
 
cmp word [esi + SOCKET_head.Domain], AF_INET4
cmp word [eax + SOCKET.Domain], AF_INET4
je .af_inet4
 
jmp s_error
468,7 → 508,7
 
.af_inet4:
 
cmp [esi + SOCKET_head.Type], IP_PROTO_TCP
cmp [eax + SOCKET.Type], IP_PROTO_TCP
je .tcp
 
jmp s_error
475,19 → 515,24
 
.tcp:
 
lea ebx, [esi + SOCKET_head.lock]
lea ebx, [eax + SOCKET.lock]
call wait_mutex
movzx eax, [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog_cur]
test eax, eax
 
movzx ebx, [eax + TCP_SOCKET.backlog_cur]
test ebx, ebx
jz .unlock_err
dec [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog_cur]
mov eax, [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.end + (eax-1)*4]
mov [esi + SOCKET_head.lock], 0
stdcall net_socket_addr_to_num, eax
mov [esp+32], eax
 
dec [eax + TCP_SOCKET.backlog_cur]
mov eax, [eax + TCP_SOCKET.end + (ebx-1)*4]
mov [eax + SOCKET.lock], 0
mov dword [esp+32], 0
 
call TCP_output ;;;;;
 
ret
 
.unlock_err:
mov [esi + SOCKET_head.lock], 0
mov [eax + SOCKET.lock], 0
jmp s_error
 
 
495,94 → 540,55
;
; SOCKET_close
;
;
; IN: socket number in ecx
; OUT: eax is socket num, -1 on error
;
;-----------------------------------------------------------------
align 4
socket_close:
SOCKET_close:
 
DEBUGF 1,"Socket_close: socknum: %u\n",ecx
DEBUGF 1,"socket_close: socknum: %u\n", ecx
 
stdcall net_socket_num_to_addr, ecx
or eax, eax
call SOCKET_num_to_ptr
jz s_error
 
cmp [eax + SOCKET_head.Domain], AF_INET4
cmp [eax + SOCKET.Domain], AF_INET4
jne s_error
 
cmp [eax + SOCKET_head.Type], IP_PROTO_UDP
je .udp
cmp [eax + SOCKET.Type], IP_PROTO_UDP
je .free
 
cmp [eax + SOCKET_head.Type], IP_PROTO_ICMP
je .icmp
cmp [eax + SOCKET.Type], IP_PROTO_ICMP
je .free
 
cmp [eax + SOCKET_head.Type], IP_PROTO_TCP
cmp [eax + SOCKET.Type], IP_PROTO_TCP
je .tcp
 
jmp s_error
 
.udp:
.tcp:
test [eax + TCP_SOCKET.t_state], TCB_SYN_RECEIVED ;;;;;;
jz .free
 
stdcall net_socket_free, eax
call TCP_output
 
mov dword [esp+32],0
ret
 
 
.icmp:
 
 
 
ret
 
.tcp:
; state must be LISTEN, SYN_SENT, CLOSED or maybe even invalid
; so, we may destroy the socket
.free:
call SOCKET_free
mov dword [esp+32],0
 
; first, remove all resend entries for this socket
 
call TCP_remove_socket
 
; cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_LISTEN
; je .destroy_tcb
; cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_SYN_SENT
; je .destroy_tcb
; cmp [eac + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_CLOSED
; je .destroy_tcb
cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_SYN_RECEIVED
je .fin_wait
cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_ESTABLISHED
je .fin_wait
cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_CLOSE_WAIT
je .last_ack
 
stdcall net_socket_free, ebx
 
ret
 
 
.last_ack:
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_LAST_ACK
jmp .send_fin
 
.fin_wait:
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_FIN_WAIT_1
 
.send_fin:
mov bl, TH_FIN + TH_ACK
xor ecx, ecx
call TCP_send
 
ret
 
 
 
 
;-----------------------------------------------------------------
;
; SOCKET_receive
;
;
; IN: socket number in ecx
; addr to buffer in edx
; length of buffer in esi
591,22 → 597,23
;
;-----------------------------------------------------------------
align 4
socket_recv:
SOCKET_receive:
 
DEBUGF 1,"Socket_receive: socknum: %u bufaddr: %x, buflength: %u, flags: %x\n",ecx,edx,esi,edi
stdcall net_socket_num_to_addr, ecx ; get real socket address
or eax, eax
DEBUGF 1,"socket_receive: socknum: %u bufaddr: %x, buflength: %u, flags: %x\n", ecx, edx, esi, edi
 
call SOCKET_num_to_ptr
jz s_error
 
mov ebx, esi
get_from_queue (eax + SOCKET_QUEUE_LOCATION),\
SOCKET_QUEUE_SIZE,\
socket_queue_entry.size,\
s_error
; destroys esi and ecx
 
DEBUGF 1,"Socket pointer: %x\n", eax
mov edi, edx ; addr to buffer
 
get_from_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, socket_queue_entry.size, s_error ; destroys esi and ecx
 
mov edi, edx ; addr to buffer
mov ecx, [esi + socket_queue_entry.data_size]
 
DEBUGF 1,"Got %u bytes of data\n", ecx
 
cmp ecx, ebx
613,15 → 620,14
jle .large_enough
DEBUGF 1,"Buffer too small...\n"
jmp s_error
 
.large_enough:
 
push [esi + socket_queue_entry.data_ptr] ; save the buffer addr so we can clear it later
mov esi, [esi + socket_queue_entry.offset]
add esi, [esp] ; calculate the real data offset
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 1,"Source buffer: %x, real addr: %x\n", [esp], esi
mov dword[esp+32+4], ecx ; return number of bytes copied in ebx
 
mov dword[esp+32+4], ecx ; return number of bytes copied
 
; copy the data
shr ecx, 1
jnc .nb
movsb
632,9 → 638,12
jz .nd
rep movsd
.nd:
; remove the packet ;;; TODO: only if it is empty!!
 
call kernel_free ; todo: check if ALL applications had the chance to receive data
;;;; call TCP_output ; only if it is tcp
 
call kernel_free
 
ret
 
 
651,130 → 660,76
;
;-----------------------------------------------------------------
align 4
socket_send:
SOCKET_send:
 
DEBUGF 1,"Socket_send: socknum: %u sockaddr: %x, length: %u, flags: %x, ",ecx,edx,esi,edi
DEBUGF 1,"socket_send: socknum: %u sockaddr: %x, length: %u, flags: %x\n", ecx, edx, esi, edi
 
stdcall net_socket_num_to_addr, ecx ; get real socket address
or eax, eax
call SOCKET_num_to_ptr
jz s_error
 
cmp word [eax + SOCKET_head.Domain], AF_INET4
cmp word [eax + SOCKET.Domain], AF_INET4
je .af_inet4
 
jmp s_error
 
.af_inet4:
DEBUGF 1,"Socket type:%u\n", [eax + SOCKET_head.Type]:4
DEBUGF 1,"af_inet4\n"
 
cmp [eax + SOCKET_head.Type], IP_PROTO_TCP
cmp [eax + SOCKET.Type], IP_PROTO_TCP
je .tcp
 
cmp [eax + SOCKET_head.Type], IP_PROTO_UDP
cmp [eax + SOCKET.Type], IP_PROTO_UDP
je .udp
 
cmp [eax + SOCKET_head.Type], SOCK_RAW
je .raw
 
jmp s_error
 
.udp:
DEBUGF 1,"type: UDP\n"
 
DEBUGF 1,"type: UDP, "
 
cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.LocalPort],0
; check if local port is valid
cmp [eax + UDP_SOCKET.LocalPort], 0
jne @f
 
push esi
mov ecx, [eax + SOCKET_head.Type]
call socket_find_port
test bx, bx
pop esi
je s_error
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.LocalPort], bx
call SOCKET_find_port
jz s_error
 
; Now, send the packet
@@:
 
mov ecx, esi
mov esi, edx
 
call UDP_socket_send
call UDP_output
 
and dword [esp+32], 0
mov dword [esp+32], 0
ret
 
.tcp:
DEBUGF 1,"type: TCP\n"
 
cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort],0
; check if local port is valid
cmp [eax + TCP_SOCKET.LocalPort], 0
jne @f
 
push esi
mov ecx, [eax + SOCKET_head.Type]
call socket_find_port
test bx, bx
pop esi
je s_error
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort], bx
call SOCKET_find_port
jz s_error
 
@@:
;;;; TODO: queue the data
 
mov ecx, esi
mov esi, edx
mov bl, TH_PUSH + TH_ACK
call TCP_output
 
call TCP_send
 
mov [esp+32], eax
ret
 
.raw:
cmp [eax + SOCKET_head.Protocol], IP_PROTO_IP
je .raw_ip
 
cmp [eax + SOCKET_head.Protocol], IP_PROTO_ICMP
je .raw_icmp
 
jmp s_error
 
 
.raw_ip:
 
;;;;;;
 
mov [esp+32], eax
ret
 
 
.raw_icmp:
 
; sub ecx, ICMP_Packet.Data
; mov esi, edx
; push ax
; call IPv4_get_frgmnt_num
; mov dx, ax
; pop ax
; shl edx, 16
; mov dh , [esi + ICMP_Packet.Type]
; mov dl , [esi + ICMP_Packet.Code]
; mov di , [esi + ICMP_Packet.Identifier]
; mov [eax + SOCKET.LocalPort], di ; Set localport to the identifier number, so we can receive reply's
; shl edi, 16
; mov di , [esi + ICMP_Packet.SequenceNumber]
; add esi, ICMP_Packet.Data
; mov ebx, [eax + SOCKET.LocalIP]
; mov eax, [eax + SOCKET.RemoteIP]
; call ICMP_create_packet
 
mov [esp+32], eax
ret
 
;-----------------------------------------------------------------
;
; SOCKET_get_options
;
;
; IN: socket number in ecx
; edx points to the options:
; IN: ecx = socket number
; edx = pointer to the options:
; dd level, optname, optval, optlen
; OUT: -1 on error
;
784,29 → 739,34
;
;-----------------------------------------------------------------
align 4
socket_get_opt:
SOCKET_get_opt:
 
DEBUGF 1,"socket_get_opt\n"
 
call SOCKET_num_to_ptr
jz s_error
 
cmp dword [edx], IP_PROTO_TCP
jnz .unknown
jnz s_error
cmp dword [edx+4], -2
jz @f
cmp dword [edx+4], -3
jnz .unknown
jnz s_error
@@:
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 + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.last_ack_number]
cmp dword [edx+4], -2
jz @f
mov ecx, [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state]
; 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
815,70 → 775,65
@@:
mov dword [esp+32], 0
ret
.fail:
.unknown:
mov dword [esp+32], -1
ret
 
 
;-----------------------------------------------------------------
;
; SOCKET_find_free_port (local port)
; SOCKET_find_port
;
; works with INET byte order
; Fills 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: type in ecx (TCP/UDP)
; OUT: bx = 0 on error, portnumber otherwise
; IN: eax = socket pointer
; OUT: /
;
;-----------------------------------------------------------------
align 4
socket_find_port:
SOCKET_find_port:
 
DEBUGF 1,"Socket_find_free_port\n"
DEBUGF 1,"socket_find_free_port\n"
 
cmp ecx, IP_PROTO_UDP
push ebx esi ecx
 
cmp [eax + SOCKET.Type], IP_PROTO_UDP
je .udp
 
cmp ecx, IP_PROTO_TCP
cmp [eax + SOCKET.Type], IP_PROTO_TCP
je .tcp
 
jmp .error
 
.done:
mov [eax + UDP_SOCKET.LocalPort], bx
.error:
pop ecx esi ebx
ret
 
.udp:
mov bx, [last_UDP_port]
je .continue
call .findit
mov [last_UDP_port], bx
jmp .done
 
.tcp:
mov bx, [last_TCP_port]
call .findit
mov [last_TCP_port], bx
jmp .done
 
 
.continue:
.restart:
mov bx, MIN_EPHEMERAL_PORT
.findit:
inc bx
 
.check_only:
mov esi, net_sockets
 
.next_socket:
mov esi, [esi + SOCKET_head.NextPtr]
or esi, esi
jz .port_ok
 
cmp [esi + SOCKET_head.Type], ecx
jne .next_socket
 
rol bx, 8
cmp [esi + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.LocalPort], bx
rol bx, 8 ; this doesnt change the zero flag, does it ?
jne .next_socket
 
cmp bx, MAX_EPHEMERAL_PORT
jle .continue
jz .restart
 
; todo: WRAP!
; mov [last_UDP_port], MIN_EPHEMERAL_PORT
.exit:
xor ebx, ebx
call SOCKET_check_port
jz .findit
 
.port_ok:
rol bx, 8
ret
 
 
885,33 → 840,43
 
;-----------------------------------------------------------------
;
; SOCKET_check_port (local port)
; SOCKET_check_port
;
; works with INET byte order
; Checks if a local port number is unused
; If the proposed port number is unused, it is filled in in the socket structure
;
; IN: type in ecx (TCP/UDP)
; port to check in bx
; OUT: bx = 0 on error, unchanged otherwise
; IN: eax = socket ptr (to find out if its a TCP/UDP socket)
; bx = proposed socket number
;
; OUT: ZF = cleared on error
;
;-----------------------------------------------------------------
align 4
socket_check_port:
SOCKET_check_port:
 
DEBUGF 1,"socket_check_port\n"
 
mov ecx, [eax + SOCKET.Type]
mov esi, net_sockets
 
.next_socket:
mov esi, [esi + SOCKET_head.NextPtr]
mov esi, [esi + SOCKET.NextPtr]
or esi, esi
jz .port_ok
 
cmp [esi + SOCKET_head.Type], ecx
cmp [esi + SOCKET.Type], ecx
jne .next_socket
 
cmp [esi + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.LocalPort], bx
cmp [esi + UDP_SOCKET.LocalPort], bx
jne .next_socket
 
xor ebx, ebx
DEBUGF 1,"local port %u already in use\n", bx
ret
 
.port_ok:
mov [eax + UDP_SOCKET.LocalPort], bx
or bx, bx ; set the zero-flag
 
ret
 
 
918,243 → 883,409
 
;-----------------------------------------------------------------
;
; SOCKET_internal_receiver
; SOCKET_input
;
; Updates a socket with received data
;
; Note: the mutex must already be set !
; Note: the mutex should already be set !
;
; IN: eax = socket ptr
; ecx = size
; esi = pointer to buffer
; edi = offset
; ebx = pointer to device struct
; ecx = data size
; esi = ptr to data
; [esp] = ptr to buf
; [esp + 4] = buf size
;
; OUT: xxx
; OUT: /
;
;-----------------------------------------------------------------
align 4
socket_internal_receiver:
SOCKET_input:
 
DEBUGF 1,"Internal socket receiver: buffer %x, offset: %x size=%u socket: %x\n", esi, edi, ecx, eax
DEBUGF 1,"socket_input: socket=%x, data=%x size=%u\n", eax, esi, ecx
 
push edi ; offset
push ecx ; size
push esi ; data_ptr
mov dword[esp+4], ecx
push esi
mov esi, esp
add_to_queue (eax + SOCKET_QUEUE_LOCATION), SOCKET_QUEUE_SIZE, socket_queue_entry.size, notify_network_event.full
 
add_to_queue (eax + SOCKET_QUEUE_LOCATION),\
SOCKET_QUEUE_SIZE,\
socket_queue_entry.size,\
SOCKET_input.full
 
DEBUGF 1,"Queued packet successfully\n"
add esp, socket_queue_entry.size
mov [eax + SOCKET.lock], 0
jmp SOCKET_notify_owner
 
mov [eax + SOCKET_head.lock], 0
.full:
DEBUGF 2,"Socket %x is full!\n", eax
mov [eax + SOCKET.lock], 0
call kernel_free
add esp, 8
 
notify_network_event:
; flag an event to the application
mov edx, [eax + SOCKET_head.PID] ; get socket owner PID
ret
 
;-----------------------------------------------------------------
;
; SOCKET_notify_owner
;
; notify's the owner of a socket that something happened
;
; IN: eax = socket ptr
; OUT: /
;
;-----------------------------------------------------------------
align 4
SOCKET_notify_owner:
 
DEBUGF 1,"socket_notify_owner\n"
 
call SOCKET_check
jz .error
 
push ecx eax esi
 
; socket exists, now try to flag an event to the application
 
mov eax, [eax + SOCKET.PID]
mov ecx, 1
mov esi, TASK_DATA + TASKDATA.pid
 
.next_pid:
cmp [esi], edx
cmp [esi], eax
je .found_pid
inc ecx
add esi, 0x20
cmp ecx, [TASK_COUNT]
jbe .next_pid
ret
 
; PID not found, TODO: close socket!
 
jmp .error2
 
.found_pid:
shl ecx, 8
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK ; stack event
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK
mov [check_idle_semaphore], 200
ret
 
.full:
DEBUGF 2,"Socket %x is full!\n",eax
mov [eax + SOCKET_head.lock], 0
call kernel_free
add esp, 8
DEBUGF 1,"owner notified\n"
 
.error2:
pop esi eax ecx
.error:
 
ret
 
 
 
;--------------------------------------------------------------------
;
; SOCKET_alloc
;
; Allocate memory for socket data and put new socket into the list
; Newly created socket is initialized with calling PID and number and
; put into beginning of list (which is a fastest way).
;
; @return socket structure address in EAX
; IN: /
; OUT: eax = 0 on error, socket ptr otherwise
; edi = socket number
; ZF = cleared on error
;
proc net_socket_alloc stdcall uses ebx ecx edx edi
;--------------------------------------------------------------------
align 4
SOCKET_alloc:
 
push ecx ebx
 
stdcall kernel_alloc, SOCKETBUFFSIZE
DEBUGF 1, "K : net_socket_alloc (0x%x)\n", eax
; check if we can allocate needed amount of memory
DEBUGF 1, "socket_alloc: %x ", eax
or eax, eax
jz .exit
 
; zero-initialize allocated memory
push eax
push eax edi
mov edi, eax
 
mov ecx, SOCKETBUFFSIZE / 4
; cld
xor eax, eax
rep stosd
pop eax
pop edi eax
 
init_queue (eax + SOCKET_QUEUE_LOCATION)
 
; add socket to the list by changing pointers
mov ebx, net_sockets
push [ebx + SOCKET_head.NextPtr]
mov [ebx + SOCKET_head.NextPtr], eax
mov [eax + SOCKET_head.PrevPtr], ebx
pop ebx
mov [eax + SOCKET_head.NextPtr], ebx
or ebx, ebx
jz @f
mov [ebx + SOCKET_head.PrevPtr], eax
; find first free socket number and use it
 
@@: ; set socket owner PID to the one of calling process
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
mov [eax + SOCKET_head.PID], ebx
 
; find first free socket number and use it
;mov edx, ebx
mov ebx, net_sockets
xor ecx, ecx
.next_socket_number:
inc ecx
.next_socket:
mov ebx, [ebx + SOCKET_head.NextPtr]
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .last_socket_number
cmp [ebx + SOCKET_head.Number], ecx
jz .last_socket
cmp [ebx + SOCKET.Number], ecx
jne .next_socket
;cmp [ebx + SOCKET.PID], edx
;jne .next_socket
mov ebx, net_sockets
jmp .next_socket_number
 
.last_socket_number:
mov [eax + SOCKET_head.Number], ecx
.last_socket:
mov [eax + SOCKET.Number], ecx
 
DEBUGF 1, "(number: %u)\n", ecx
 
; Fill in PID
mov ebx, [TASK_BASE]
mov ebx, [ebx + TASKDATA.pid]
 
mov [eax + SOCKET.PID], ebx
 
; add socket to the list by changing pointers
 
mov ebx, [net_sockets + SOCKET.NextPtr]
 
mov [eax + SOCKET.PrevPtr], net_sockets
mov [eax + SOCKET.NextPtr], ebx
 
or ebx, ebx
jz @f
add ebx, SOCKET.lock ; lock the next socket
call wait_mutex
sub ebx, SOCKET.lock
mov [ebx + SOCKET.PrevPtr], eax
mov [ebx + SOCKET.lock], 0
@@:
 
mov [net_sockets + SOCKET.NextPtr], eax
 
mov edi, ecx
or eax, eax ; used to clear zero flag
.exit:
pop ebx ecx
 
ret
endp
 
; Free socket data memory and pop socket off the list
 
;----------------------------------------------------
;
; @param sockAddr is a socket structure address
; SOCKET_free
;
proc net_socket_free stdcall uses ebx ecx edx, sockAddr:DWORD
mov eax, [sockAddr]
DEBUGF 1, "K : net_socket_free (0x%x)\n", eax
; check if we got something similar to socket structure address
or eax, eax
jz .error
; Free socket data memory and remove socket from the list
;
; IN: eax = socket ptr
; OUT: /
;
;----------------------------------------------------
align 4
SOCKET_free:
 
; make sure sockAddr is one of the socket addresses in the list
mov ebx, net_sockets
;mov ecx, [TASK_BASE]
;mov ecx, [ecx + TASKDATA.pid]
.next_socket:
mov ebx, [ebx + SOCKET_head.NextPtr]
or ebx, ebx
DEBUGF 1, "socket_free: %x\n", eax
 
call SOCKET_check
jz .error
cmp ebx, eax
jne .next_socket
;cmp [ebx + SOCKET.PID], ecx
;jne .next_socket
 
; okay, we found the correct one
; remove it from the list first, changing pointers
mov ebx, [eax + SOCKET_head.NextPtr]
mov eax, [eax + SOCKET_head.PrevPtr]
mov [eax + SOCKET_head.NextPtr], ebx
or ebx, ebx
push ebx
lea ebx, [eax + SOCKET.lock]
call wait_mutex
 
DEBUGF 1, "freeing socket..\n"
 
push eax ; this will be passed to kernel_free
mov ebx, [eax + SOCKET.NextPtr]
mov eax, [eax + SOCKET.PrevPtr]
 
DEBUGF 1, "linking socket %x to socket %x\n", eax, ebx
 
test eax, eax
jz @f
mov [ebx + SOCKET_head.PrevPtr], eax
mov [eax + SOCKET.NextPtr], ebx
@@:
 
lea ebx, [eax + SOCKET_head.lock]
call wait_mutex
test ebx, ebx
jz @f
mov [ebx + SOCKET.PrevPtr], eax
@@:
 
@@: ; and finally free the memory structure used
stdcall kernel_free, [sockAddr]
ret
call kernel_free
pop ebx
 
DEBUGF 1, "socket is gone!\n"
 
.error:
DEBUGF 1, "K : failed\n"
ret
endp
 
 
;---------------------------------------------------
;
; SOCKET_num_to_ptr
;
; Get socket structure address by its number
; Scan through sockets list to find the socket with specified number.
; This proc uses SOCKET.PID indirectly to check if socket is owned by
; calling process.
;
; @param sockNum is a socket number
; @return socket structure address or 0 (not found) in EAX
; IN: ecx = socket number
; OUT: ecx = 0 on error, socket ptr otherwise
; ZF = set on error
;
proc net_socket_num_to_addr stdcall uses ebx ecx, sockNum:DWORD
mov eax, [sockNum]
; check if we got something similar to socket number
or eax, eax
jz .error
;---------------------------------------------------
align 4
SOCKET_num_to_ptr:
 
; scan through sockets list
mov ebx, net_sockets
;mov ecx, [TASK_BASE]
;mov ecx, [ecx + TASKDATA.pid]
DEBUGF 1,"socket_num_to_ptr: %u ", ecx
 
mov eax, net_sockets
 
.next_socket:
mov ebx, [ebx + SOCKET_head.NextPtr]
or ebx, ebx
mov eax, [eax + SOCKET.NextPtr]
or eax, eax
jz .error
cmp [ebx + SOCKET_head.Number], eax
cmp [eax + SOCKET.Number], ecx
jne .next_socket
;cmp [ebx + SOCKET.PID], ecx
;jne .next_socket
 
; okay, we found the correct one
mov eax, ebx
ret
test eax, eax
 
DEBUGF 1,"(%x)\n", eax
.error:
xor eax, eax
ret
endp
 
; Get socket number by its structure address
; Scan through sockets list to find the socket with specified address.
; This proc uses SOCKET.PID indirectly to check if socket is owned by
; calling process.
 
;---------------------------------------------------
;
; @param sockAddr is a socket structure address
; @return socket number (SOCKET.Number) or 0 (not found) in EAX
; SOCKET_ptr_to_num
;
proc net_socket_addr_to_num stdcall uses ebx ecx, sockAddr:DWORD
mov eax, [sockAddr]
; check if we got something similar to socket structure address
or eax, eax
; Get socket number by its address
;
; IN: eax = socket ptr
; OUT: eax = 0 on error, socket num otherwise
; ZF = set on error
;
;---------------------------------------------------
align 4
SOCKET_ptr_to_num:
 
DEBUGF 1,"socket_ptr_to_num: %x ", eax
 
call SOCKET_check
jz .error
 
; scan through sockets list
mov eax, [eax + SOCKET.Number]
 
DEBUGF 1,"(%u)\n", eax
 
.error:
ret
 
 
;---------------------------------------------------
;
; SOCKET_check
;
; checks if the given value is really a socket ptr
;
; IN: eax = socket ptr
; OUT: eax = 0 on error, unchanged otherwise
; ZF = set on error
;
;---------------------------------------------------
align 4
SOCKET_check:
 
DEBUGF 1,"socket_check\n"
 
push ebx
mov ebx, net_sockets
;mov ecx, [TASK_BASE]
;mov ecx, [ecx + TASKDATA.pid]
 
.next_socket:
mov ebx, [ebx + SOCKET_head.NextPtr]
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .error
jz .done
cmp ebx, eax
jne .next_socket
;cmp [ebx + SOCKET.PID], ecx
;jne .next_socket
jnz .next_socket
 
; okay, we found the correct one
mov eax, [ebx + SOCKET_head.Number]
.done:
mov eax, ebx
test eax, eax
pop ebx
 
ret
 
.error:
xor eax, eax
 
 
;---------------------------------------------------
;
; SOCKET_check_owner
;
; checks if the caller application owns the socket
;
; IN: eax = socket ptr
; OUT: ZF = true/false
;
;---------------------------------------------------
align 4
SOCKET_check_owner:
 
DEBUGF 1,"socket_check_owner\n"
 
push ebx
mov ebx, [TASK_BASE]
mov ebx, [ecx + TASKDATA.pid]
cmp [eax + SOCKET.PID], ebx
pop ebx
 
ret
endp
 
 
 
 
;---------------------------------------------------
;
; 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
;
; IN: eax = pid
; OUT: /
;
;------------------------------------------------------
align 4
SOCKET_process_end:
 
DEBUGF 1,"socket_process_end: %x\n", eax
 
push ebx
mov ebx, net_sockets
 
.next_socket:
 
mov ebx, [ebx + SOCKET.NextPtr]
.test_socket:
test ebx, ebx
jz .done
 
cmp [ebx + SOCKET.PID], eax
jne .next_socket
 
DEBUGF 1,"closing socket %x", eax, ebx
 
mov [ebx + SOCKET.PID], 0
 
cmp [ebx + SOCKET.Type], IP_PROTO_UDP
je .udp
 
cmp [ebx + SOCKET.Type], IP_PROTO_TCP
je .tcp
 
jmp .next_socket ; kill all sockets for given PID
 
.udp:
mov eax, ebx
mov ebx, [ebx + SOCKET.NextPtr]
call SOCKET_free
jmp .test_socket
 
.tcp:
 
jmp .next_socket
 
.done:
pop ebx
 
ret
/kernel/branches/net/network/stack.inc
1,6 → 1,6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; STACK.INC ;;
23,19 → 23,22
__DEBUG_LEVEL__ equ 1 ; this sets the debug level for network part of kernel
 
uglobal
last_1sTick db ?
last_1hsTick dd ?
net_10ms dd ?
net_tmr_count dw ?
endg
 
MAX_NET_DEVICES equ 16
QUEUE_BEFORE_SENDING equ 0 ; 1 or 0 (enable or disable) currently only affects ethernet
 
ETH_QUEUE equ 0 ; 1 = enable / 0 = disable
 
MIN_EPHEMERAL_PORT equ 49152
MAX_EPHEMERAL_PORT equ 61000
 
ETHER equ 1337 ; TODO: find another value for this (how does it work in posix ?)
; Ethernet protocol numbers
ETHER_ARP equ 0x0608
ETHER_IPv4 equ 0x0008 ; Reversed from 0800 for intel
 
;Protocol family
AF_UNSPEC equ 0
AF_UNIX equ 1
AF_INET4 equ 2
46,9 → 49,10
;AF_BRIDGE equ 7
;AF_AAL5 equ 8
;AF_X25 equ 9
;AF_INET6 equ 10
AF_INET6 equ 10
;AF_MAX equ 12
 
; Internet protocol numbers
IP_PROTO_IP equ 0
IP_PROTO_ICMP equ 1
IP_PROTO_TCP equ 6
55,54 → 59,31
IP_PROTO_UDP equ 17
 
; Socket types
SOCK_STREAM = 1
SOCK_DGRAM = 2
SOCK_RAW = 3
SOCK_STREAM equ 1
SOCK_DGRAM equ 2
SOCK_RAW equ 3
 
TCB_LISTEN equ 1
TCB_SYN_SENT equ 2
TCB_SYN_RECEIVED equ 3
TCB_ESTABLISHED equ 4
TCB_FIN_WAIT_1 equ 5
TCB_FIN_WAIT_2 equ 6
TCB_CLOSE_WAIT equ 7
TCB_CLOSING equ 8
TCB_LAST_ACK equ 9
TCB_TIMED_WAIT equ 10
TCB_CLOSED equ 11
; Socket options
SO_ACCEPTCON equ 1
 
TH_FIN equ 1 shl 0
TH_SYN equ 1 shl 1
TH_RST equ 1 shl 2
TH_PUSH equ 1 shl 3
TH_ACK equ 1 shl 4
TH_URG equ 1 shl 5
SOCKET_MAXDATA equ 4096
 
; Network driver types
NET_TYPE_ETH equ 1
NET_TYPE_SLIP equ 2
 
macro inc_INET reg {
 
add byte [reg + 3], 1
adc byte [reg + 2], 0
adc byte [reg + 1], 0
adc byte [reg], 0
virtual at 0
 
}
NET_DEVICE:
.type dd ?
.end:
 
end virtual
 
macro add_INET reg {
add byte [reg + 3], cl
adc byte [reg + 2], ch
adc byte [reg + 1], 0
adc byte [reg], 0
rol ecx, 16
add byte [reg + 1], cl
adc byte [reg], ch
rol ecx, 16
}
 
 
; Exactly as it says..
macro pseudo_random reg {
 
add reg, [esp]
rol reg, 5
xor reg, [timer_ticks]
109,23 → 90,48
imul reg, 214013
xor reg, 0xdeadbeef
rol reg, 9
}
 
pushd reg
mov word [esp], 0x8080 ; kernel heap start addr (os_stack)
xor reg, [esp]
add esp, 4
macro ntohld reg {
 
rol word reg, 8
rol dword reg, 16
rol word reg, 8
 
}
 
macro ntohlw reg {
 
rol word reg, 8
 
}
 
 
include "queue.inc"
 
include "ethernet.inc"
;include "slip.inc"
 
include "ARP.inc"
include "IPv4.inc"
include "ethernet.inc"
include "socket.inc"
 
include "icmp.inc"
include "udp.inc"
include "tcp.inc"
include "udp.inc"
include "icmp.inc"
 
include "socket.inc"
 
 
 
align 4
uglobal
 
NET_RUNNING dd ?
NET_DRV_LIST rd MAX_NET_DEVICES
 
endg
 
 
;-----------------------------------------------------------------
;
; stack_init
139,28 → 145,37
align 4
stack_init:
 
; Init the network drivers list
 
xor eax, eax
mov edi, NET_RUNNING
mov ecx, MAX_NET_DEVICES + 1
rep stosd
 
; Call other init procedures
 
call ETH_init
; call SLIP_init
 
call IPv4_init
call ICMP_init
 
call ARP_init
call UDP_init
call TCP_init
call ICMP_init
 
call socket_init
 
mov al, 0 ; set up 1s timer
out 0x70, al
in al, 0x71
mov [last_1sTick], al
mov [net_tmr_count], 0
 
ret
 
 
 
;-----------------------------------------------------------------
;
; stack_handler
;
; This function calls all network init procedures
; This function is called in kernel loop
;
; IN: /
; OUT: /
169,43 → 184,232
align 4
stack_handler:
 
cmp [ETH_RUNNING], 0
cmp [NET_RUNNING], 0
je .exit
 
; Test for 10ms tick
mov eax, [timer_ticks]
cmp eax, [last_1hsTick]
cmp eax, [net_10ms]
je .exit
mov [net_10ms], eax
 
mov [last_1hsTick], eax
 
call ETH_handler ; handle all queued ethernet packets
if QUEUE_BEFORE_SENDING
if ETH_QUEUE
call ETH_handler
call ETH_send_queued
end if
call TCP_send_queued
call TCP_10ms
 
.sec_tick:
inc [net_tmr_count]
cmp [net_tmr_count], 50
je .500ms
cmp [net_tmr_count], 100
jne .exit
 
; Test for 1 second tick
mov al, 0
out 0x70, al
in al, 0x71
cmp al, [last_1sTick]
je .exit
 
mov [last_1sTick], al
 
call ARP_decrease_entry_ttls
call IPv4_decrease_fragment_ttls
call TCP_decrease_socket_ttls
call TCP_timer_1000ms
 
mov [net_tmr_count], 0
 
.500ms:
call TCP_500ms
 
.exit:
ret
 
 
 
;-----------------------------------------------------------------
;
; NET_Add_Device:
;
; This function is called by the network drivers,
; to register each running NIC to the kernel
;
; IN: Pointer to device structure in ebx
; OUT: Device num in eax, -1 on error
;
;-----------------------------------------------------------------
align 4
NET_add_device:
 
DEBUGF 1,"NET_Add_Device: %x\n", ebx
 
mov eax, [NET_RUNNING]
cmp eax, MAX_NET_DEVICES
jge .error
 
;----------------------------------
; Check if device is already listed
mov eax, ebx
mov ecx, MAX_NET_DEVICES ; We need to check whole list because a device may be removed without re-organizing list
mov edi, NET_DRV_LIST
 
repne scasd ; See if device is already in the list
jz .error
 
;----------------------------
; Find empty slot in the list
xor eax, eax
mov ecx, MAX_NET_DEVICES
mov edi, NET_DRV_LIST
 
repne scasd
jnz .error
 
sub edi, 4
 
cmp [ebx + NET_DEVICE.type], NET_TYPE_ETH
je .ethernet
 
cmp [ebx + NET_DEVICE.type], NET_TYPE_SLIP
je .slip
 
DEBUGF 1,"Unknown network device type: %u\n", [ebx + NET_DEVICE.type]
jmp .error
 
.ethernet:
DEBUGF 1,"Trying to add an ethernet driver\n"
 
inc [ETH_RUNNING] ; Indicate that one more ethernet device is up and running
jmp .add_it
 
.slip:
DEBUGF 1,"Trying to add a slip driver\n"
;;;;
jmp .error
 
 
.add_it:
 
;-----------------------------
; Add device to the found slot
mov [edi], ebx ; add device to list
 
sub edi, NET_DRV_LIST ; Calculate device number in eax
mov eax, edi ;
shr eax, 2
 
inc [NET_RUNNING] ; Indicate that one more network device is up and running
 
DEBUGF 1,"Device number: %u\n",eax
ret
 
.error:
or eax, -1
DEBUGF 2,"Adding network device failed\n"
ret
 
 
 
;-----------------------------------------------------------------
;
; NET_Remove_Device:
;
; This function is called by etwork drivers,
; to unregister network devices from the kernel
;
; IN: Pointer to device structure in ebx
; OUT: eax: -1 on error
;
;-----------------------------------------------------------------
align 4
NET_remove_device:
 
cmp [NET_RUNNING], 0
je .error
 
;----------------------------
; Find the driver in the list
 
mov eax, ebx
mov ecx, MAX_NET_DEVICES
mov edi, NET_DRV_LIST
 
repne scasd
jnz .error
 
;------------------------
; Remove it from the list
 
xor eax, eax
mov dword [edi-4], eax
 
dec [NET_RUNNING]
ret
 
.error:
or eax, -1
ret
 
 
 
;-----------------------------------------------------------------
;
; NET_ptr_to_num
;
; IN: ebx = ptr to device struct
; OUT: edi = -1 on error, device number otherwise
;
;-----------------------------------------------------------------
align 4
NET_ptr_to_num:
push ecx
 
mov ecx, MAX_NET_DEVICES
mov edi, NET_DRV_LIST
 
.loop:
cmp ebx, [edi]
jz .found
add edi, 4
dec ecx
jnz .loop
 
; repnz scasd could work too if eax is used instead of ebx!
 
or edi, -1
 
pop ecx
ret
 
.found:
sub edi, NET_DRV_LIST
shr edi, 2
 
pop ecx
ret
 
 
 
 
;--------------------------
;
; NET_send
;
; IN: ebx = ptr to device struct
; [esp] = data ptr
; [esp + 4] = data size
;
; OUT: /
;
;--------------------------
align 4
NET_send:
 
call [ebx + ETH_DEVICE.transmit] ;;;;
 
;;; TODO:check if packet was sent ok
 
call kernel_free
add esp, 4
ret
 
 
 
 
;-----------------------------------------------------------------
;
; checksum_1
;
; This is the first of two functions needed to calculate a checksum.
333,7 → 537,7
cmp ebx, -1
jne @f
 
mov eax, [ETH_RUNNING]
mov eax, [NET_RUNNING]
jmp .return
 
@@:
344,12 → 548,12
and esi, 0x0000ff00
shr esi, 6
 
cmp dword [esi + ETH_DRV_LIST], 0 ; check if driver is running
cmp dword [esi + NET_DRV_LIST], 0 ; check if driver is running
je .doesnt_exist
 
test bl, bl ; 0 = Get device type (ethernet/token ring/...)
jnz @f
; todo
 
xor eax, eax
jmp .return
 
358,7 → 562,7
dec bl ; 1 = Get device name
jnz @f
 
mov esi, [esi + ETH_DRV_LIST]
mov esi, [esi + NET_DRV_LIST]
mov esi, [esi + ETH_DEVICE.name]
mov edi, ecx
 
373,7 → 577,7
dec bl ; 2 = Reset the device
jnz @f
 
mov esi, [esi + ETH_DRV_LIST]
mov esi, [esi + NET_DRV_LIST]
call [esi + ETH_DEVICE.reset]
jmp .return
 
382,7 → 586,7
dec bl ; 3 = Stop driver for this device
jnz @f
 
mov esi, [esi + ETH_DRV_LIST]
mov esi, [esi + NET_DRV_LIST]
call [esi + ETH_DEVICE.unload]
jmp .return
 
407,7 → 611,7
 
;----------------------------------------------------------------
;
; System Function To work with Protocols (75)
; System function to work with protocols (75)
;
;----------------------------------------------------------------
align 4
417,8 → 621,8
 
mov esi, ebx
and esi, 0x0000ff00
shr esi, 6
cmp dword [esi + ETH_DRV_LIST], 0 ; check if driver is running TODO: check other lists too
shr esi, 6 ; now we have the device num * 4 in esi
cmp dword [esi + NET_DRV_LIST], 0 ; check if driver is running
je .doesnt_exist
 
push .return ; return address (we will be using jumps instead of calls)
441,7 → 645,7
cmp ax , ETHER_ARP
je ARP_API
 
cmp ax , ETHER
cmp ax , 1337
je ETH_API
 
add esp, 4 ; if we reached here, no function was called, so we need to balance stack
/kernel/branches/net/network/tcp.inc
1,6 → 1,6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; TCP.INC ;;
8,25 → 8,57
;; Part of the tcp/ip network stack for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; Inspired by the TCP code of Mike Hibbit for MenuetOS ;;
;; ;;
;; Based on the code of 4.4BSD ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
$Revision$
 
TCP_RETRIES equ 5 ; Number of times to resend a Packet
TCP_PACKET_TTL equ 50 ; resend if not replied to in 1/100 s
TCP_SOCKET_TTL equ 10 ; # of secs to wait before closing socket
TCP_QUEUE_SIZE equ 16
; Socket states
TCB_CLOSED equ 0
TCB_LISTEN equ 1
TCB_SYN_SENT equ 2
TCB_SYN_RECEIVED equ 3
TCB_ESTABLISHED equ 4
TCB_CLOSE_WAIT equ 5
TCB_FIN_WAIT_1 equ 6
TCB_CLOSING equ 7
TCB_LAST_ACK equ 8
TCB_FIN_WAIT_2 equ 9
TCB_TIMED_WAIT equ 10
 
TCP_MAX_ACKS equ 16
; Socket Flags
TF_ACKNOW equ 1 shl 0 ; ack peer immediately
TF_DELACK equ 1 shl 1 ; ack, but try to delay it
TF_NODELAY equ 1 shl 2 ; don't delay packets to coalesce
TF_NOOPT equ 1 shl 3 ; don't use tcp options
TF_SENTFIN equ 1 shl 4 ; have sent FIN
TF_REQ_SCALE equ 1 shl 5 ; have/will request window scaling
TF_RCVD_SCALE equ 1 shl 6 ; other side has requested scaling
TF_REQ_TSTMP equ 1 shl 7 ; have/will request timestamps
TF_RCVD_TSTMP equ 1 shl 8 ; a timestamp was received in SYN
TF_SACK_PERMIT equ 1 shl 9 ; other side said I could SACK
 
; Segment flags
TH_FIN equ 1 shl 0
TH_SYN equ 1 shl 1
TH_RST equ 1 shl 2
TH_PUSH equ 1 shl 3
TH_ACK equ 1 shl 4
TH_URG equ 1 shl 5
 
struct TCP_Packet
; Segment header options
TCP_OPT_EOL equ 0 ; End of option list.
TCP_OPT_NOP equ 1 ; No-Operation.
TCP_OPT_MAXSEG equ 2 ; Maximum Segment Size.
TCP_OPT_WINDOW equ 3 ; window scale
TCP_OPT_TIMESTAMP equ 8
 
struct TCP_segment
.SourcePort dw ?
.DestinationPort dw ?
.SequenceNumber dd ?
36,15 → 68,13
.Window dw ?
.Checksum dw ?
.UrgentPointer dw ?
; .Options rb 3
; .Padding db ?
.Data:
.Data: ; ..or options
ends
 
struct tcp_in_queue_entry
.data_ptr dd ?
.data_size dd ?
.offset dd ? ; TODO: replace this in code by absolute address isntead of relative offset
.offset dd ?
.size:
ends
 
51,47 → 81,20
struct tcp_out_queue_entry
.data_ptr dd ?
.data_size dd ?
.ttl dd ?
.retries dd ?
.owner dd ?
.sendproc dd ?
.seq_num dd ?
.socket dd ?
 
.size:
ends
 
align 4
uglobal
TCP_PACKETS_TX rd MAX_IP
TCP_PACKETS_RX rd MAX_IP
 
TCP_IN_QUEUE rd (tcp_in_queue_entry.size*TCP_QUEUE_SIZE+queue.data)/4
TCP_OUT_QUEUE dd ?, ?
rd (tcp_out_queue_entry.size*TCP_QUEUE_SIZE)/4
 
TCP_ACKS dd ?
TCP_ACK_LIST rd 3*TCP_MAX_ACKS
TCP_segments_tx rd IP_MAX_INTERFACES
TCP_segments_rx rd IP_MAX_INTERFACES
TCP_bytes_rx rq IP_MAX_INTERFACES
TCP_bytes_tx rq IP_MAX_INTERFACES
TCP_sequence_num dd ?
endg
 
align 4
iglobal
TCPstateHandler:
 
dd stateTCB_LISTEN
dd stateTCB_SYN_SENT
dd stateTCB_SYN_RECEIVED
dd stateTCB_ESTABLISHED
dd stateTCB_FIN_WAIT_1
dd stateTCB_FIN_WAIT_2
dd stateTCB_CLOSE_WAIT
dd stateTCB_CLOSING
dd stateTCB_LAST_ACK
dd stateTCB_TIME_WAIT
dd stateTCB_CLOSED
 
endg
 
 
;-----------------------------------------------------------------
;
; TCP_init
106,73 → 109,60
TCP_init:
 
xor eax, eax
mov edi, TCP_PACKETS_TX
mov ecx, 2*MAX_IP
mov edi, TCP_segments_tx
mov ecx, (6*IP_MAX_INTERFACES)
rep stosd
 
init_queue TCP_IN_QUEUE
mov [TCP_sequence_num],1
 
; tcp_out_queue is a special type of queue:
; The first dword is a counter of total packets queued.
; The remaining bytes are socket 'slots' wich use tcp_out_queue_entry data structure.
; An empty slot is know by the fact that tcp_out_queue_entry.data_ptr (first dword of the slot) is set to 0
; There are TCP_OUT_QUEUE_SIZE number of slots
 
xor eax, eax
mov esi, TCP_OUT_QUEUE
mov ecx, TCP_QUEUE_SIZE*tcp_out_queue_entry/4+2+2+3*TCP_MAX_ACKS
rep stosd
 
ret
 
 
;-----------------------------------------------------------------
;
; TCP_decrease_socket_ttls
; decrease socket ttls
;
; IN: /
; OUT: /
;
; destroys: eax
;
;-----------------------------------------------------------------
align 4
TCP_decrease_socket_ttls:
; scan through all the sockets, decrementing active timers
TCP_timer_1000ms:
; scan through all the active TCP sockets, decrementing active timers
 
mov ebx, net_sockets
cmp [ebx + SOCKET_head.NextPtr], 0
je .exit
.next_socket:
mov ebx, [ebx + SOCKET_head.NextPtr]
or ebx, ebx
mov eax, net_sockets
.loop:
mov eax, [eax + SOCKET.NextPtr]
.check_only:
or eax, eax
jz .exit
 
cmp [ebx + SOCKET_head.Type], IP_PROTO_TCP
jne .next_socket
cmp [eax + SOCKET.Type], IP_PROTO_TCP
jne .loop
 
; DEBUGF 1, "K : %x-%x: %x-%x-%x-%u\n", [ebx + SOCKET.PID]:2, [ebx + SOCKET.Number]:2, [ebx + SOCKET.LocalPort]:4, [ebx + SOCKET.RemoteIP], [ebx + SOCKET.RemotePort]:4, [ebx + SOCKET.state]
 
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.timer], 0
cmp [eax + TCP_SOCKET.t_timer], 0
jne .decrement_tcb
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer], 0
;;;;;; cmp [eax + TCP_SOCKET.wndsizeTimer], 0
jne .decrement_wnd
jmp .next_socket
jmp .loop
 
.decrement_tcb:
; decrement it, delete socket if TCB timer = 0 & socket in timewait state
dec [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.timer]
jnz .next_socket
dec [eax + TCP_SOCKET.t_timer]
jnz .loop
 
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_TIMED_WAIT
jne .next_socket
cmp [eax + TCP_SOCKET.t_state], TCB_TIMED_WAIT
jne .loop
 
push [ebx + SOCKET_head.PrevPtr]
stdcall net_socket_free, ebx
pop ebx
jmp .next_socket
push [eax + SOCKET.NextPtr]
call SOCKET_free
pop eax
jmp .check_only
 
.decrement_wnd:
dec [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer]
jmp .next_socket
;;;;;; dec [eax + TCP_SOCKET.wndsizeTimer]
 
.exit:
ret
179,1067 → 169,1815
 
 
 
;-----------------------------------------------------------------
;----------------------
;
; TCP_send_queued:
; TCP_500ms
;
; Decreases 'ttl' of tcp packets queued.
; if 'ttl' reaches 0, resend the packet and decrease 'retries'
; if 'retries' reaches zero, remove the queued packet
;
; IN: /
; OUT: /
;
;-----------------------------------------------------------------
;----------------------
align 4
TCP_send_queued:
TCP_500ms:
 
cmp [TCP_OUT_QUEUE], 0
je .exit
add [TCP_sequence_num], 64000
 
mov ebx, TCP_OUT_QUEUE+4
call wait_mutex
 
mov eax, TCP_QUEUE_SIZE
mov ecx, [TCP_OUT_QUEUE]
mov esi, TCP_OUT_QUEUE+8
 
.loop:
cmp [esi + tcp_out_queue_entry.data_ptr], 0
jnz .found_one
add esi, tcp_out_queue_entry.size
loop .loop
.exit:
mov [TCP_OUT_QUEUE+4], 0
ret
 
.found_one:
dec [esi + tcp_out_queue_entry.ttl]
jz .send_it
cmp [esi + tcp_out_queue_entry.data_ptr], -1
jz .is_ack
.find_next:
add esi, tcp_out_queue_entry.size
dec eax
jz .exit
test ecx, ecx
jnz .loop
mov [TCP_OUT_QUEUE+4], 0
ret
 
.send_it:
pusha
mov ebx, [esi + tcp_out_queue_entry.owner]
pushd [esi + tcp_out_queue_entry.data_size]
pushd [esi + tcp_out_queue_entry.data_ptr]
DEBUGF 1,"Now sending TCP packet %x, size: %u, owner: %x, sendproc %x\n", [esp], [esp+4], ebx, [esi + tcp_out_queue_entry.sendproc]
inc [TCP_PACKETS_TX]
call [esi + tcp_out_queue_entry.sendproc]
add esp, 8
popa
 
dec [esi + tcp_out_queue_entry.retries]
jz .remove_it
;----------------------
;
; TCP_10ms
;
;----------------------
align 4
TCP_10ms:
 
mov [esi + tcp_out_queue_entry.ttl], TCP_PACKET_TTL
jmp .find_next
; todo: decrease timers
 
.remove_it:
push [esi + tcp_out_queue_entry.data_ptr]
mov [esi + tcp_out_queue_entry.data_ptr], 0
call kernel_free
dec [TCP_OUT_QUEUE]
jmp .find_next
ret
 
.is_ack:
pusha
mov eax, [esi + tcp_out_queue_entry.socket]
mov ebx, [esi + tcp_out_queue_entry.owner]
mov ecx, [esi + tcp_out_queue_entry.size]
call TCP_send_ack
popa
mov [esi + tcp_out_queue_entry.data_ptr], 0
dec [TCP_OUT_QUEUE]
jmp .find_next
 
 
 
;-----------------------------------------------------------------
;
; TCP_handler:
; TCP_input:
;
; Called by IPv4_handler,
; this procedure will inject the tcp data diagrams in the application sockets.
; IN: [esp] = ptr to buffer
; [esp+4] = buffer size
; ebx = ptr to device struct
; ecx = segment size
; edx = ptr to TCP segment
;
; IN: Pointer to buffer in [esp]
; size of buffer in [esp+4]
; pointer to device struct in ebx
; TCP Packet size in ecx
; pointer to TCP Packet in edx
; SourceAddres (IPv4) in esi
; esi = ipv4 source address
; edi = ipv4 dest address
;
; OUT: /
;
;-----------------------------------------------------------------
align 4
TCP_handler :
TCP_input:
 
DEBUGF 1,"TCP_Handler\n"
DEBUGF 1,"TCP_input\n"
 
; TODO: validate checksum
; Offset must be greater than or equal to the size of the standard TCP header (20) and less than or equal to the TCP length.
 
; Find a matching socket for received packet, all following expressions must be valid:
;
movzx eax, [edx + TCP_segment.DataOffset]
and eax, 0xf0
shr al , 2
 
DEBUGF 1,"data offset: %u\n", eax
 
cmp eax, 20
jl .drop
 
cmp eax, ecx
jg .drop
 
;-------------------------------
; Now, re-calculate the checksum
 
push eax edx ebx
 
push edi
push esi
mov esi, edx
call TCP_checksum ; this destroys edx, ecx and esi (but not edi! :)
 
pop ebx edx eax
 
cmp [edx + TCP_segment.Checksum], 0
jnz .drop
 
DEBUGF 1,"Checksum is correct\n"
 
;-----------------------------------------------------------------------------------------
; Check if this packet has a timestamp option (We do it here so we can process it quickly)
 
cmp eax, 20 + 12 ; Timestamp option is 12 bytes
jl .no_timestamp
je .is_ok
 
cmp byte [edx + TCP_segment.Data + 12], 0 ; end of option list
jne .no_timestamp
 
.is_ok:
test [edx + TCP_segment.Flags], TH_SYN ; SYN flag must not be set
jnz .no_timestamp
 
cmp dword [edx + TCP_segment.Data], 0x0101080a ; Timestamp header
jne .no_timestamp
 
DEBUGF 1,"timestamp ok\n"
 
; TODO: Parse the options
; TODO: Set a Bit in the TCP to tell all options are parsed
 
ret
 
.no_timestamp:
 
;-------------------------------------------
; Convert Big-endian values to little endian
 
ntohld [edx + TCP_segment.SequenceNumber]
ntohld [edx + TCP_segment.AckNumber]
 
ntohlw [edx + TCP_segment.Window]
ntohlw [edx + TCP_segment.UrgentPointer]
 
;------------------------------------------------------------
; Next thing to do is find the TCB (thus, the socket pointer)
 
; IP Packet TCP Destination Port = local Port
; (IP Packet SA = Remote IP) OR (Remote IP = 0)
; (IP Packet SenderAddress = Remote IP) OR (Remote IP = 0)
; (IP Packet TCP Source Port = remote Port) OR (remote Port = 0)
 
mov ebx, net_sockets
 
.socket_loop:
mov ebx, [ebx + SOCKET_head.NextPtr]
mov ebx, [ebx + SOCKET.NextPtr]
or ebx, ebx
jz .dump
jz .drop_with_reset
 
mov ax, [edx + TCP_Packet.DestinationPort]
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort], ax
cmp [ebx + SOCKET.Type], IP_PROTO_TCP
jne .socket_loop
 
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.RemoteIP]
mov ax, [edx + TCP_segment.DestinationPort]
cmp [ebx + TCP_SOCKET.LocalPort], ax
jne .socket_loop
 
mov eax, [ebx + IP_SOCKET.RemoteIP]
cmp eax, esi
je @f
test eax, eax
jne .socket_loop
jnz .socket_loop
@@:
 
mov ax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RemotePort]
cmp [edx + TCP_Packet.SourcePort] , ax
mov ax, [ebx + TCP_SOCKET.RemotePort]
cmp [edx + TCP_segment.SourcePort] , ax
je .found_socket
test ax, ax
jnz .socket_loop
.found_socket:
DEBUGF 1,"Found valid socket for packet\n"
DEBUGF 1,"Socket ptr: %x\n", ebx
 
inc [TCP_PACKETS_RX]
; ebx now contains the pointer to the socket
 
add ebx, SOCKET_head.lock
;----------------------------
; Check if socket isnt closed
 
cmp [TCP_SOCKET.t_state], TCB_CLOSED
je .drop
 
;----------------
; Lock the socket
 
add ebx, SOCKET.lock ; TODO: figure out if we should lock now already
call wait_mutex
sub ebx, SOCKET_head.lock
sub ebx, SOCKET.lock
 
;-------------------------------
; ebx is pointer to socket
; ecx is size of tcp packet
; edx is pointer to tcp packet
;---------------------------------------
; unscale the window into a 32 bit value ;;;;;;
 
; calculate header length
movzx eax, [edx + TCP_Packet.DataOffset]
movzx eax, [edx + TCP_segment.Window]
xchg al, ah
 
test [edx + TCP_segment.Flags], TH_SYN
jnz .no_syn
 
mov cl , [ebx + TCP_SOCKET.SND_SCALE]
shl eax, cl
 
.no_syn:
 
;-----------------------------------
; Is this socket a listening socket?
 
; If so, create a new socket
 
test [ebx + SOCKET.options], SO_ACCEPTCON
jz .no_accept_conn
 
 
; TODO: create a new socket
 
 
.no_accept_conn:
 
;----------------------------
; Compute window scale factor
 
 
; TODO
 
 
;-------------------------------------
; Reset idle timer and keepalive timer
 
 
; TODO
 
;-----------------------------------------
; Process TCP options if not in LISTEN state
 
test [ebx + TCP_SOCKET.t_state], TCB_LISTEN
jz .dont_do_options
 
call TCP_do_options
 
.dont_do_options:
 
;-----------------------------------------------------------------------
; Time to do some header prediction (Original Principle by Van Jacobson)
 
 
; There are two common cases for an uni-directional data transfer.
;
; General rule: the packets has no control flags, is in-sequence,
; 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 [TCP_SOCKET.t_state], TCB_ESTABLISHED
jnz .not_uni_xfer
 
test [TCP_segment.Flags], TH_SYN + TH_FIN + TH_RST + TH_URG
jnz .not_uni_xfer
 
test [TCP_segment.Flags], TH_ACK
jz .not_uni_xfer
 
mov eax, [edx + TCP_segment.SequenceNumber]
cmp eax, [ebx + TCP_SOCKET.RCV_NXT]
jne .not_uni_xfer
 
movzx eax, [edx + TCP_segment.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
 
;-------------------------------------------------------------------------------
; If last ACK falls within this segment's sequence number, record the timestamp.
 
; TODO: check if it has a timestamp
 
 
 
 
;---------------------------------------
; check if we are sender in the uni-xfer
 
; If the following 4 conditions are all true, this segment is a pure ACK.
;
; - The segment contains no data (ti_len is 0).
 
movzx eax, [edx + TCP_segment.DataOffset]
and eax, 11110000b
shr eax, 2
DEBUGF 1,"TCP header size: %u\n", eax
sub ecx, eax
jnz .not_sender
 
;-------------------------------
; ecx is size of tcp data
; - The acknowledgment field in the segment (ti_ack) is greater than the largest unacknowledged sequence number (snd_una).
; Since this test is "greater than" and not "greater than or equal to," it is true only if some positive amount of data is acknowledged by the ACK.
 
; as a Packet has been received, update the TCB timer
mov eax, [edx + TCP_segment.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
jle .not_uni_xfer
 
; If the received Packet has an ACK bit set, remove any Packets in the resend queue that this received Packet acknowledges
test [edx + TCP_Packet.Flags], TH_ACK
jz .no_ack ; No ACK, so no data yet
; - The acknowledgment field in the segment (ti_ack) is less than or equal to the maximum sequence number sent (snd_max).
 
; Calculate ACK number, in intel byte order
mov edi, [edx + TCP_Packet.AckNumber]
bswap edi
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.last_ack_number], edi
DEBUGF 1,"Setting last_ack_number to %u\n", edi
; mov eax, [edx + TCP_segment.Ack]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jg .not_uni_xfer
 
; Dequeue all acknowledged packets
cmp [TCP_OUT_QUEUE], 0 ; first, check if any packets are queued at all
je .no_ack
; - The congestion window (snd_cwnd) is greater than or equal to the current send window (snd_wnd).
; 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.
 
push ebx
mov ebx, TCP_OUT_QUEUE+4
call wait_mutex
pop ebx
mov eax, [ebx + TCP_SOCKET.SND_CWND]
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jl .not_uni_xfer
 
push ecx
DEBUGF 1,"Removing all queued packets with smaller ACK\n"
mov ecx, TCP_QUEUE_SIZE
mov esi, TCP_OUT_QUEUE+8
.loop:
cmp [esi + tcp_out_queue_entry.data_ptr], 0
je .maybe_next
DEBUGF 1,"Header prediction: we are sender\n"
 
cmp [esi + tcp_out_queue_entry.socket], ebx
jne .maybe_next
;---------------------------------
; Packet is a pure ACK, process it
 
cmp [esi + tcp_out_queue_entry.seq_num], edi
jg .maybe_next
; Update RTT estimators
 
DEBUGF 1,"Removing a queued packet\n"
; Delete acknowledged bytes from send buffer
 
push [esi + tcp_out_queue_entry.data_ptr]
mov [esi + tcp_out_queue_entry.data_ptr], 0
dec [TCP_OUT_QUEUE]
call kernel_free
; Stop retransmit timer
 
.maybe_next:
add esi, tcp_out_queue_entry.size
loop .loop
; Awaken waiting processes
 
mov [TCP_OUT_QUEUE+4], 0
pop ecx
; Generate more output
 
; Now call the correct handler, depending on the socket state
.no_ack:
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state]
 
cmp eax, TCB_LISTEN
jb .dump
cmp eax, TCB_CLOSED
ja .dump
 
call dword [TCPstateHandler+eax*4-4]
 
.dump:
DEBUGF 1,"Dumping TCP packet\n"
call kernel_free
add esp, 4 ; pop (balance stack)
jmp .drop
 
ret
 
 
 
;-----------------------------------------------------------------
;
; TCP_send (Assumes socket mutex set)
;
; IN: eax = socket pointer
; bl = flags
; ecx = number of bytes to send, may be set to 0 (single ACK)
; esi = pointer to data
;
;-----------------------------------------------------------------
;-------------------------------------------------
; maybe we are the receiver in the uni-xfer then..
 
.not_sender:
; The amount of data in the segment (ti_len) is greater than 0 (data count is in ecx)
 
 
; The acknowledgment field (ti_ack) equals the largest unacknowledged sequence number. This means no data is acknowledged by this segment.
mov eax, [edx + TCP_segment.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
jne .not_uni_xfer
 
; The reassembly list of out-of-order segments for the connection is empty (seg_next equals tp).
;;;;
jnz .not_uni_xfer
 
; There is room in the receive buffer for the data in the segment.
;;;;
jnz .not_uni_xfer
 
;-------------------------------------
; Complete processing of received data
 
DEBUGF 1,"header prediction: we are receiver\nreceiving %u bytes of data\n", ecx
 
; The next expected receive sequence number (rcv_nxt) is incremented by the number of bytes of data.
 
add [ebx + TCP_SOCKET.RCV_NXT], ecx
 
; Add the data to the socket buffer
 
; The receiving process is awakened (by sorwakeup).
 
; The delayed-ACK flag is set and the input processing is complete.
 
jmp .drop
 
 
 
 
 
;----------------------------------------------------
; Header prediction failed, doing it the slow way..
 
.not_uni_xfer:
 
DEBUGF 1,"Header prediction failed\n"
 
;------------------------
; calculate header length ;;;;; we already calculated this before!
movzx eax, [edx + TCP_segment.DataOffset]
and eax, 0xf0
shr eax, 2
 
; Update edx to point to data..
add edx, eax
; ..and ecx to give data size
sub ecx, eax
 
;------------------------------
; Calculate receive window size
 
;;;;
 
 
;-------------------------
; TCP slow input procedure
 
DEBUGF 1,"TCP slow input procedure\n"
 
cmp [eax + TCP_SOCKET.t_state], TCB_LISTEN
je .LISTEN
 
cmp [eax + TCP_SOCKET.t_state], TCB_SYN_SENT
je .SYN_SENT
 
 
;--------------------------------------------
; Protection Against Wrapped Sequence Numbers
 
 
; First, check timestamp if present
 
;;;; TODO
 
; Then, check if at least some bytes of data are within window
 
;;;; TODO
 
jmp .trim_then_step6
 
align 4
TCP_send:
.LISTEN:
 
DEBUGF 1,"Creating TCP packet, socket: %x, flags: %x\n",eax, bl
DEBUGF 1,"TCP state: listen\n"
 
mov di , IP_PROTO_TCP
add ecx, TCP_Packet.Data
test [edx + TCP_segment.Flags], TH_RST
jnz .drop
 
push ecx bx eax esi
; Create an IPv4 Packet of the correct size
mov ebx, [eax + SOCKET_head.end + IPv4_SOCKET.LocalIP]
mov eax, [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP]
test [edx + TCP_segment.Flags], TH_ACK
jnz .drop_with_reset
 
call IPv4_create_packet
cmp edi, -1
je .fail
test [edx + TCP_segment.Flags], TH_SYN
jz .drop
 
; If there is any data, copy it first
pop esi
push edi
add edi, TCP_Packet.Data
sub ecx, TCP_Packet.Data
; TODO: check if it's a broadcast or multicast, and drop if so
 
shr ecx, 1
jnc .nb
movsb
.nb: shr ecx, 1
jnc .nw
movsw
.nw: test ecx, ecx
jz .nd
;;; 28.6
 
; create a new socket and fill in the nescessary variables
 
;; Exit if backlog queue is full
; mov ax, [ebx + TCP_SOCKET.backlog_cur]
; cmp ax, [ebx + TCP_SOCKET.backlog]
; jae .exit
 
; Allocate new socket
call SOCKET_alloc
;;; jz .fail
 
; Copy structure from current socket to new, (including lock!)
; We start at PID to reserve the socket num, and the 2 pointers at beginning of socket
lea esi, [edx + SOCKET.PID]
lea edi, [eax + SOCKET.PID]
mov ecx, (TCP_SOCKET.end - SOCKET.PID + 3)/4
rep movsd
.nd:
pop edi
 
; Fill in the TCP header
pop esi
;; Push pointer to new socket to queue
; movzx ecx, [ebx + TCP_SOCKET.backlog_cur]
; inc [ebx + TCP_SOCKET.backlog_cur]
; mov [ebx + TCP_SOCKET.end + ecx*4], eax
 
; fill in tcp sequence number
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT]
pop [edi + TCP_Packet.SequenceNumber]
mov [eax + IP_SOCKET.RemoteIP], esi ; IP source address
 
; Fill in local and remote ports
push dword [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort]
pop dword [edi + TCP_Packet.SourcePort]
mov cx, [edx + TCP_segment.SourcePort]
mov [eax + TCP_SOCKET.RemotePort], cx
 
; Acknumber
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
pop [edi + TCP_Packet.AckNumber]
mov ecx, [edx + TCP_segment.SequenceNumber]
mov [eax + TCP_SOCKET.IRS], ecx
 
; Fill in other tcp options
pop cx
mov [edi + TCP_Packet.Flags], cl
mov [edi + TCP_Packet.Window], 0x0005 ; 1280 bytes
mov [edi + TCP_Packet.UrgentPointer], 0
mov [edi + TCP_Packet.DataOffset], 0x50
mov [edi + TCP_Packet.Checksum], 0
mov ecx, [eax + TCP_SOCKET.ISS]
mov [eax + TCP_SOCKET.SND_NXT], ecx
 
; Get size of total packet back in ecx
pop ecx
; Push pointer to and size of total packet (needed for send procedure)
push edx eax
; push socket number (for TCP_add_to_queue)
push esi
jmp .trim_then_step6
 
; Now, calculate the checksum
xchg cl, ch
pushw cx
xchg cl, ch
pushw IP_PROTO_TCP shl 8
pushd [edi-4] ; destination address ; TODO: fix this, IPv4 packet could have options..
pushd [edi-8] ; source address
 
xor edx, edx
mov esi, edi
call checksum_1
mov ecx, 12
mov esi, esp
call checksum_1
; and store it in TCP header
call checksum_2
mov [edi + TCP_Packet.Checksum], dx
add esp, 10 ; remove the pseudoheader from stack
 
DEBUGF 1,"Sending TCP Packet to device %x\n", ebx
mov edx, [edi + TCP_Packet.SequenceNumber]
bswap edx
mov esi, [ebx + ETH_DEVICE.transmit]
align 4
.SYN_SENT:
 
pop cx ; get the length from packet, back from pseudoheader
pop edi
DEBUGF 1,"TCP state: syn_sent\n"
 
cmp cx, TCP_Packet.Data shl 8 ; if the packet has no data
je .only_one ; send it only once
test [edx + TCP_segment.Flags], TH_ACK
jz @f
 
and ecx, 0x0000ffff
xchg cl, ch
sub cx, TCP_Packet.Data
mov eax, [edx + TCP_segment.AckNumber]
cmp eax, [ebx + TCP_SOCKET.ISS]
jle .drop_with_reset
 
add_INET (edi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT) ; todo: this should only happen when packet was queued successful
mov ecx, TCP_RETRIES
mov eax, [edx + TCP_segment.AckNumber]
cmp eax, [ebx + TCP_SOCKET.SND_MAX]
jg .drop_with_reset
@@:
 
jmp .go_for_it
 
.only_one:
mov ecx, 1
.go_for_it:
test [edx + TCP_segment.Flags], TH_RST
jz @f
 
mov [edi + SOCKET_head.lock], 0
jmp TCP_queue ; At last send the packet!
test [edx + TCP_segment.Flags], TH_ACK
jz .drop
 
.fail:
add esp, 2+4
or eax, -1
ret
;tp = tcp_drop(tp, ECONNREFUSED)
 
jmp .drop
@@:
 
;-----------------------------------------------------------------
;
; Queue a TCP packet for sending
;
; IN: [esp] pointer to buffer
; [esp + 4] size of buffer
; ebx = driver struct
; edx = sequence number of this packet in intel byte order
; esi = sender proc
; edi = socket number
test [edx + TCP_segment.Flags], TH_SYN
jz .drop
 
; OUT: /
;
;-----------------------------------------------------------------
; now, process received SYN in response to an active open
test [edx + TCP_segment.Flags], TH_ACK
jz @f
 
mov eax, [edx + TCP_segment.AckNumber]
mov [ebx + TCP_SOCKET.SND_UNA], eax
 
mov eax, [ebx + TCP_SOCKET.SND_UNA]
cmp eax, [ebx + TCP_SOCKET.SND_NXT]
jle @f
mov [ebx + TCP_SOCKET.SND_NXT], eax
 
 
; TODO: turn off retransmission timer
 
mov eax, [edx + TCP_segment.SequenceNumber]
mov [ebx + TCP_SOCKET.IRS], eax
 
; TODO: set socket state to connected
 
mov [ebx + TCP_SOCKET.t_state], TCB_ESTABLISHED
 
; TODO: check if we should scale the connection (567-572)
; TODO: update RTT estimators
 
 
@@:
 
; We have received a syn but no ACK, so we are having a simultaneous open..
mov [ebx + TCP_SOCKET.t_state], TCB_SYN_RECEIVED
 
;-------------------------------------
; Common processing for receipt of SYN
 
.trimthenstep6:
 
inc [edx + TCP_segment.SequenceNumber]
 
cmp cx, [ebx + TCP_SOCKET.RCV_WND]
jle @f
 
movzx eax, cx
sub ax, [ebx + TCP_SOCKET.RCV_WND]
; TODO: 592
mov cx, [ebx + TCP_SOCKET.RCV_WND]
; TODO...
@@:
;;;;;
;;; jmp .step6
 
 
 
 
 
align 4
TCP_queue:
.trim_then_step6:
 
DEBUGF 1,"Adding packet to TCP queue, buffer: %x, size: %u, driver: %x, acknum: %u\n", [esp], [esp+4], ebx, edx
DEBUGF 1,"Trim, then step 6\n"
 
cmp [TCP_OUT_QUEUE], TCP_QUEUE_SIZE
jge .full
;----------------------------
; trim any data not in window
 
push ebx
mov ebx, TCP_OUT_QUEUE+4
call wait_mutex
pop ebx
mov eax, [ebx + TCP_SOCKET.RCV_NXT]
sub eax, [edx + TCP_segment.SequenceNumber]
 
mov ecx, TCP_QUEUE_SIZE
mov eax, TCP_OUT_QUEUE+8
.loop:
cmp [eax + tcp_out_queue_entry.data_ptr], 0
je .found_it
add eax, tcp_out_queue_entry.size
loop .loop
test eax, eax
jz .no_drop
 
add esp, 4
.full: ; silently discard the packet
DEBUGF 1,"TCP queue is full!\n"
test [edx + TCP_segment.Flags], TH_SYN
jz .no_drop
 
and [edx + TCP_segment.Flags], not (TH_SYN)
inc [edx + TCP_segment.SequenceNumber]
 
cmp [edx + TCP_segment.UrgentPointer], 1
jl @f
 
dec [edx + TCP_segment.UrgentPointer]
 
jmp .no_drop
@@:
 
and [edx + TCP_segment.Flags], not (TH_URG)
dec eax
 
.no_drop:
 
; eax holds number of bytes to drop
 
 
;----------------------------------
; Check for entire duplicate packet
 
cmp eax, ecx
jge .duplicate
 
;;; TODO: figure 28.30
 
;; inc [TCP_segments_rx]
 
;; add dword [TCP_bytes_rx], ecx
;; adc dword [TCP_bytes_rx+4], 0
 
;------------------------
; Check for duplicate FIN
 
test [edx + TCP_segment.Flags], TH_FIN
jz @f
inc ecx
cmp eax, ecx
dec ecx
jne @f
 
mov eax, ecx
and [edx + TCP_segment.Flags], not TH_FIN
;;; TODO: set ACKNOW flag
 
jmp .no_duplicate
@@:
 
; Handle the case when a bound socket connects to itself
; Allow packets with a SYN and an ACKto continue with the processing
 
 
;-------------------------------------
; Generate duplicate ACK if nescessary
 
; This code also handles simultaneous half-open or self-connects
 
test eax, eax
jnz .drop_after_ack
 
cmp [edx + TCP_segment.Flags], TH_ACK
jz .drop_after_ack
 
.duplicate:
 
;----------------------------------------
; Update statistics for duplicate packets
 
;;; TODO
 
;;; DROP the packet ??
 
.no_duplicate:
 
;-----------------------------------------------
; Remove duplicate data and update urgent offset
 
add [edx + TCP_segment.SequenceNumber], eax
 
;;; TODO
 
sub [edx + TCP_segment.UrgentPointer], ax
jg @f
 
and [edx + TCP_segment.Flags], not (TH_URG)
mov [edx + TCP_segment.UrgentPointer], 0
@@:
 
;--------------------------------------------------
; Handle data that arrives after process terminates
 
cmp [ebx + SOCKET.PID], 0
jge @f
 
cmp [ebx + TCP_SOCKET.t_state], TCB_CLOSE_WAIT
jle @f
 
test ecx, ecx
jz @f
 
;;; Close the socket
;;; update stats
 
jmp .drop_with_reset
 
@@:
 
;----------------------------------------
; Remove data beyond right edge of window
 
mov eax, [edx + TCP_segment.SequenceNumber]
add eax, ecx
sub eax, [ebx + TCP_SOCKET.RCV_NXT]
sub ax, [ebx + TCP_SOCKET.RCV_WND]
 
; eax now holds the number of bytes to drop
 
jle .no_excess_data
 
;;; TODO: update stats
 
cmp eax, ecx
jl .dont_drop_all
 
;;; TODO 700-736
 
.dont_drop_all:
 
.no_excess_data:
 
 
;-----------------
; Record timestamp
 
;;; TODO 737-746
 
;------------------
; Process RST flags
 
test [edx + TCP_segment.Flags], TH_RST
jz .rst_skip
 
mov eax, [ebx + TCP_SOCKET.t_state]
shl eax, 2
jmp dword [eax + .rst_sw_list]
 
.rst_sw_list:
dd .rst_skip ;TCB_CLOSED
dd .rst_skip ;TCB_LISTEN
dd .rst_skip ;TCB_SYN_SENT
dd .econnrefused ;TCB_SYN_RECEIVED
dd .econnreset ;TCB_ESTABLISHED
dd .econnreset ;TCB_CLOSE_WAIT
dd .econnreset ;TCB_FIN_WAIT_1
dd .rst_close ;TCB_CLOSING
dd .rst_close ;TCB_LAST_ACK
dd .econnreset ;TCB_FIN_WAIT_2
dd .rst_close ;TCB_TIMED_WAIT
 
.econnrefused:
 
;;; TODO: debug info
 
jmp .close
 
.econnreset:
 
;;; TODO: debug info
.close:
 
;;; update stats
 
.rst_close:
 
;;; Close the socket
jmp .drop
 
.rst_skip:
 
;--------------------------------------
; handle SYN-full and ACK-less segments
 
test [edx + TCP_segment.Flags], TH_SYN
jz @f
 
;;; tcp_drop ( ECONNRESET)
jmp .drop_with_reset
 
test [edx + TCP_segment.Flags], TH_ACK
jz .drop
 
;----------------
; Process the ACK
 
cmp [ebx + TCP_SOCKET.t_state], TCB_SYN_RECEIVED
jg .ack_dup
jl .ack_nodup
 
; dd .ack_nodup ;TCB_CLOSED
; dd .ack_nodup ;TCB_LISTEN
; dd .ack_nodup ;TCB_SYN_SENT
; dd .ack_syn_rcvd ;TCB_SYN_RECEIVED
; dd .ack_dup ;TCB_ESTABLISHED
; dd .ack_dup ;TCB_CLOSE_WAIT
; dd .ack_dup ;TCB_FIN_WAIT_1
; dd .ack_dup ;TCB_CLOSING
; dd .ack_dup ;TCB_LAST_ACK
; dd .ack_dup ;TCB_FIN_WAIT_2
; dd .ack_dup ;TCB_TIMED_WAIT
 
;;;;;
 
.ack_dup:
 
;;;;
 
.ack_nodup:
 
;;;; 887
 
;-------------------------------------------------
; If the congestion window was infalted to account
; for the other side's cached packets, retrace it
 
;;;; 888 - 902
 
 
;------------------------------------------
; RTT measurements and retransmission timer
 
;;;;; 903 - 926
 
 
;-------------------------------------------
; Open congestion window in response to ACKs
 
;;;;
 
 
;------------------------------------------
; Remove acknowledged data from send buffer
 
;;;; 943 - 956
 
;---------------------------------------
; Wake up process waiting on send buffer
 
;;;;;
 
mov eax, [ebx + TCP_SOCKET.t_state]
shl eax, 2
jmp dword [eax + .ACK_sw_list]
 
.ACK_sw_list:
dd .step6 ;TCB_CLOSED
dd .step6 ;TCB_LISTEN
dd .step6 ;TCB_SYN_SENT
dd .step6 ;TCB_SYN_RECEIVED
dd .step6 ;TCB_ESTABLISHED
dd .step6 ;TCB_CLOSE_WAIT
dd ._963 ;TCB_FIN_WAIT_1
dd ._958 ;TCB_CLOSING
dd ._999 ;TCB_LAST_ACK
dd .step6 ;TCB_FIN_WAIT_2
dd ._1010 ;TCB_TIMED_WAIT
 
 
._963:
 
 
jmp .step6
 
 
._958:
 
jmp .step6
 
._999:
 
jmp .step6
 
 
._1010:
 
jmp .step6
 
 
 
align 4
.step6:
 
DEBUGF 1,"step 6\n"
 
;--------------------------
; update window information
 
test [edx + TCP_segment.Flags], TH_ACK
jz .no_window_update
 
mov eax, [ebx + TCP_SOCKET.SND_WL1]
cmp eax, [edx + TCP_segment.SequenceNumber]
 
;;;; 1021
 
;----------------------------------
; Keep track of pure window updates
 
test ecx, ecx
jz @f
 
mov eax, [ebx + TCP_SOCKET.SND_WL2]
cmp eax, [edx + TCP_segment.AckNumber]
jne @f
 
;; mov eax, tiwin
cmp eax, [ebx + TCP_SOCKET.SND_WND]
jle @f
 
;;; update stats
 
@@:
 
;; mov eax, incoming window
cmp eax, [ebx + TCP_SOCKET.max_sndwnd]
jle @f
mov [ebx + TCP_SOCKET.max_sndwnd], eax
@@:
mov [ebx + TCP_SOCKET.SND_WND], eax
 
mov eax, [edx + TCP_segment.SequenceNumber]
mov [ebx + TCP_SOCKET.SND_WL1], eax
 
mov eax, [edx + TCP_segment.AckNumber]
mov [ebx + TCP_SOCKET.SND_WL2], eax
 
;;; needoutput = 1
 
.no_window_update:
 
 
;-----------------
; process URG flag
 
test [edx + TCP_segment.Flags], TH_URG
jz .not_urgent
 
cmp [edx + TCP_segment.UrgentPointer], 0
jz .not_urgent
 
cmp [ebx + TCP_SOCKET.t_state], TCB_TIMED_WAIT
je .not_urgent
 
; Ignore bogus urgent offsets
 
;;; 1040-1050
 
movzx eax, [edx + TCP_segment.UrgentPointer]
add eax, [ebx + SOCKET.SO_RCV.SB_CC]
cmp eax, SOCKET_MAXDATA
jle .not_urgent
 
mov [edx + TCP_segment.UrgentPointer], 0
and [edx + TCP_segment.Flags], not (TH_URG)
jmp .do_data
 
.not_urgent:
 
;--------------------------------------
; processing of received urgent pointer
 
;;; 1051-1093
 
align 4
.do_data:
 
DEBUGF 1,"Do data:\n"
 
; process the data in the segment
 
test [edx + TCP_segment.Flags], TH_FIN
jz .process_fin
 
test [ebx + TCP_SOCKET.t_state], TCB_FIN_WAIT_1 ;;;;;
jge .dont_do_data
 
DEBUGF 1,"Processing data in segment\n"
 
;;; NOW, process the data
 
jmp .final_processing
 
 
.dont_do_data:
 
 
;---------------
; FIN processing
 
.process_fin:
 
DEBUGF 1,"Processing FIN\n"
 
mov eax, [ebx + TCP_SOCKET.t_state]
shl eax, 2
jmp dword [eax + .FIN_sw_list]
 
.FIN_sw_list:
dd .no_fin ;TCB_CLOSED
dd .no_fin ;TCB_LISTEN
dd .no_fin ;TCB_SYN_SENT
dd ._1131 ;TCB_SYN_RECEIVED
dd ._1131 ;TCB_ESTABLISHED
dd .no_fin ;TCB_CLOSE_WAIT
dd ._1139 ;TCB_FIN_WAIT_1
dd .no_fin ;TCB_CLOSING
dd .no_fin ;TCB_LAST_ACK
dd ._1147 ;TCB_FIN_WAIT_2
dd ._1156 ;TCB_TIMED_WAIT
 
 
 
._1131:
 
._1139:
 
._1147:
 
._1156:
 
 
.no_fin:
 
;-----------------
; Final processing
 
.final_processing:
 
DEBUGF 1,"Final processing\n"
 
;;; if debug enabled, output packet
 
;test ;;;needoutput = 1
;jnz .outputnow
 
test [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
jz .ret
 
.outputnow:
call TCP_output
 
.ret:
mov [ebx + SOCKET.lock], 0
 
call kernel_free
add esp, 4
ret 4
 
ret
;------------------------------------------
; Generate an ACK, droping incoming segment
 
.found_it: ; eax points to empty queue entry
align 4
.drop_after_ack:
 
mov [eax + tcp_out_queue_entry.retries], TCP_RETRIES
pop [eax + tcp_out_queue_entry.data_ptr]
pop [eax + tcp_out_queue_entry.data_size]
mov [eax + tcp_out_queue_entry.ttl], 1 ; send immediately
mov [eax + tcp_out_queue_entry.owner], ebx
mov [eax + tcp_out_queue_entry.sendproc], esi
mov [eax + tcp_out_queue_entry.seq_num], edx
mov [eax + tcp_out_queue_entry.socket], edi
DEBUGF 1,"Drop after ACK\n"
 
inc [TCP_OUT_QUEUE]
test [edx + TCP_segment.Flags], TH_RST
jnz .drop
 
sub eax, TCP_OUT_QUEUE+8
shr eax, 5
DEBUGF 1,"Added to queue in pos %u, total queued packets: %u\n", eax, [TCP_OUT_QUEUE+8]
and [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
 
mov [TCP_OUT_QUEUE+4], 0
call TCP_output
 
ret
mov [ebx + SOCKET.lock], 0
 
call kernel_free
ret 4
 
;-----------------------------------------------------------------
 
;-------------------------------------------
; Generate an RST, dropping incoming segment
 
align 4
.drop_with_reset:
 
DEBUGF 1,"Drop with reset\n"
 
test [edx + TCP_segment.Flags], TH_RST
jnz .drop
 
;;; if its a multicast/broadcast, also drop
 
test [edx + TCP_segment.Flags], TH_ACK
jnz .respond_ack
 
test [edx + TCP_segment.Flags], TH_SYN
jnz .respond_syn
 
mov [ebx + SOCKET.lock], 0
 
call kernel_free
ret 4
 
.respond_ack:
 
;;;;
 
call TCP_respond
 
jmp .destroy_new_socket
 
 
.respond_syn:
 
;;;;
 
call TCP_respond
 
jmp .destroy_new_socket
 
;-----
; Drop
 
align 4
.drop:
 
DEBUGF 1,"Dropping packet\n"
 
;;;; If debugging options are enabled, output the packet somwhere
 
.destroy_new_socket:
 
;;;; kill the newly created socket
 
mov [ebx + SOCKET.lock], 0
 
call kernel_free
ret 4
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
 
;---------------------
;
; IN: ebx = socket
; ecx = ack number
; TCP_do_options
;
; OUT: /
;
;-----------------------------------------------------------------
;-------------------
 
align 4
TCP_queue_ack:
TCP_do_options:
 
DEBUGF 1,"Adding ACK to TCP queue, socket: %x, acknum: %u\n", ebx, ecx
DEBUGF 1,"TCP_do_options\n"
 
cmp [TCP_OUT_QUEUE], TCP_QUEUE_SIZE
jge .full
push eax
sub eax, 20
jz .no_options
 
push ebx ecx
mov ebx, TCP_OUT_QUEUE+4
call wait_mutex
lea esi, [edx + TCP_segment.Data]
 
mov ecx, TCP_QUEUE_SIZE
mov eax, TCP_OUT_QUEUE+8
 
;-------------------------------------------
; Begin the loop by checking for EOL and NOP
 
.loop:
cmp [eax + tcp_out_queue_entry.data_ptr], 0
je .found_it
add eax, tcp_out_queue_entry.size
loop .loop
 
add esp, 8
.full: ; silently discard the packet
DEBUGF 1,"TCP queue is full!\n"
ret
cmp byte [esi], TCP_OPT_EOL ; end of option list?
jz .no_options
 
.found_it: ; eax points to empty queue entry
cmp byte [esi], TCP_OPT_NOP ; nop ?
;;; cmove edi, 1 ; if so, set option size to 1
jz .continue ; and continue scanning
 
pop [eax + tcp_out_queue_entry.data_size] ; ACK number
mov [eax + tcp_out_queue_entry.data_ptr], -1 ; ACK packet
pop [eax + tcp_out_queue_entry.socket]
mov [eax + tcp_out_queue_entry.retries], 1
mov [eax + tcp_out_queue_entry.ttl], 20 ; 200 ms
;------------------
; We have an option
 
inc [TCP_OUT_QUEUE]
movzx edi, byte [esi + 1] ; get the length of this option in edi
 
sub eax, TCP_OUT_QUEUE+8
shr eax, 5
DEBUGF 1,"Added to queue in pos %u, total queued packets: %u\n", eax, [TCP_OUT_QUEUE+8]
 
mov [TCP_OUT_QUEUE+4], 0
;--------------------------------------
; Check for Maximum segment size option
 
ret
cmp byte [esi], TCP_OPT_MAXSEG
jne .no_maxseg
 
cmp edi, 4 ; option length
jne .continue
 
; IN: eax = socket pointer
; ebx = device structure
; ecx = ack number
test [edx + TCP_segment.Flags], TH_SYN
jz .continue
 
align 4
TCP_send_ack:
; Now parse the option...
 
DEBUGF 1,"Creating TCP ACK packet, socket: %x, acknum: %x\n", eax, ecx
jmp .continue
 
push ecx eax
.no_maxseg:
 
mov di , IP_PROTO_TCP
mov ecx, TCP_Packet.Data
; Create an IPv4 Packet of the correct size
mov ebx, [eax + SOCKET_head.end + IPv4_SOCKET.LocalIP]
mov eax, [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP]
;------------------------
; Check for Window option
 
call IPv4_create_packet
cmp edi, -1
je .fail
cmp byte [esi], TCP_OPT_WINDOW
jne .no_window
 
pop ecx
cmp edi, 3 ; option length
jne .continue
 
; fill in tcp sequence number
push [ecx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT]
pop [edi + TCP_Packet.SequenceNumber]
test [edx + TCP_segment.Flags], TH_SYN
jz .continue
 
; Fill in local and remote ports
push dword [ecx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort]
pop dword [edi + TCP_Packet.SourcePort]
; ...
 
; Acknumber
pop [edi + TCP_Packet.AckNumber]
jmp .continue
 
; Fill in other tcp options
mov [edi + TCP_Packet.Flags], TH_ACK
mov [edi + TCP_Packet.Window], 0x0005 ; 1280 bytes
mov [edi + TCP_Packet.UrgentPointer], 0
mov [edi + TCP_Packet.DataOffset], 0x50
mov [edi + TCP_Packet.Checksum], 0
.no_window:
 
; Push pointer to and size of total packet (needed for send procedure)
push edx eax esi
;---------------------------
; Check for Timestamp option
 
; Now, calculate the checksum
pushw TCP_Packet.Data shl 8
pushw IP_PROTO_TCP shl 8
pushd [edi-4] ; destination address ; TODO: fix this, IPv4 packet could have options..
pushd [edi-8] ; source address
cmp byte [esi], TCP_OPT_TIMESTAMP
jne .no_timestamp
 
xor edx, edx
mov ecx, 12
mov esi, esp
call checksum_1
call checksum_2
mov [edi + TCP_Packet.Checksum], dx
add esp, 12 ; remove the pseudoheader from stack
cmp edi, 10 ; option length
jne .continue
 
; ...
 
 
jmp .continue
 
.no_timestamp:
 
;----------------------------------
; Future options may be placed here
 
 
 
 
;------------------------------
; Continue scanning for options
 
.continue:
add esi, edi
sub eax, edi
jg .loop
 
.no_options:
 
pop eax
call eax
call kernel_free
add esp, 4 ; pop (balance stack)
 
ret
 
.fail:
add esp, 8
 
 
 
;---------------------------
;
; TCP_pull_out_of_band
;
; IN: eax =
; ebx = socket ptr
; edx = tcp packet ptr
;
; OUT: /
;
;---------------------------
 
align 4
TCP_pull_out_of_band:
 
DEBUGF 1,"TCP_pull_out_of_band\n"
 
;;;; 1282-1305
 
ret
 
 
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
 
 
 
;-----------------------------------------------------------------
;
; Remove all queued TCP packets for a specified socket
; TCP_output
;
; IN: eax = socket number
; IN: eax = socket pointer
;; esi = ptr to data
;; ecx = number of data bytes
;
; OUT: /
;
; destoys esi and ecx
;
;-----------------------------------------------------------------
 
align 4
TCP_remove_socket:
TCP_output:
 
cmp [TCP_OUT_QUEUE], 0
je .skip
DEBUGF 1,"TCP_output, socket: %x\n", eax
 
mov ebx, TCP_OUT_QUEUE+4
call wait_mutex
; 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 eax, TCP_QUEUE_SIZE
mov ecx, [TCP_OUT_QUEUE]
mov esi, TCP_OUT_QUEUE+8
mov ebx, [eax + TCP_SOCKET.SND_MAX]
cmp ebx, [eax + TCP_SOCKET.SND_UNA]
jne .not_idle
 
.loop:
cmp [esi + tcp_out_queue_entry.data_ptr], 0
jz .maybenext
cmp [esi + tcp_out_queue_entry.socket], eax
jnz .maybenext
mov ebx, [eax + TCP_SOCKET.t_idle]
cmp ebx, [eax + TCP_SOCKET.t_rxtcur]
jle .not_idle
 
push [esi + tcp_out_queue_entry.data_ptr]
mov [esi + tcp_out_queue_entry.data_ptr], 0
dec [TCP_OUT_QUEUE]
call kernel_free
; 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.
 
.maybenext:
add esi, tcp_out_queue_entry.size
loop .loop
mov ebx, [eax + TCP_SOCKET.t_maxseg]
mov [eax + TCP_SOCKET.SND_CWND], ebx
 
mov [TCP_OUT_QUEUE+4], 0
.skip:
ret
.not_idle:
.again:
mov ebx, [eax + TCP_SOCKET.SND_NXT] ; calculate offset
sub ebx, [eax + TCP_SOCKET.SND_UNA] ;
 
mov ecx, [eax + TCP_SOCKET.SND_WND] ; determine window
cmp ecx, [eax + TCP_SOCKET.SND_CWND] ;
jl @f ;
mov ecx, [eax + TCP_SOCKET.SND_CWND] ;
@@: ;
 
call TCP_outflags
 
; 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_force], -1
jz .no_persist_timeout
 
;---------- TCB state handlers start here
test ecx, ecx
jnz .no_zero_window
 
cmp ebx, [eax + SOCKET.SO_SND.SB_CC]
jge @f
 
and dl, not (TH_FIN) ; clear the FIN flag ??? how can it be set before?
 
@@:
inc ecx
jmp .no_persist_timeout
 
align 4
stateTCB_LISTEN:
.no_zero_window:
 
DEBUGF 1,"TCBStateHandler: Listen\n"
;;; mov [eax + TCP_SOCKET.t_timer....TCPT_PERSIST], 0
mov [eax + TCP_SOCKET.t_rxtshift], 0
 
test [edx + TCP_Packet.Flags], TH_SYN ; SYN packet? => send syn+ack, open new socket and set connection to established
jz .exit
; Exit if backlog queue is full
mov ax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog_cur]
cmp ax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog]
jae .exit
; Allocate new socket
push esi edi
call net_socket_alloc
test eax, eax
jz .fail
; Copy structure from current socket to new, including lock
lea esi, [ebx + SOCKET_head.PID] ; yes, PID must also be copied
lea edi, [eax + SOCKET_head.PID]
mov ecx, ((SOCKET_head.end - SOCKET_head.PID) + IPv4_SOCKET.end + TCP_SOCKET.end + 3)/4
rep movsd
pop edi esi
; Push pointer to new socket to queue
movzx ecx, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog_cur]
inc [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog_cur]
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.end + ecx*4], eax
.no_persist_timeout:
 
mov [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP], esi ; IP source address
mov cx, [edx + TCP_Packet.SourcePort]
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RemotePort], cx
mov ecx, [edx + TCP_Packet.SequenceNumber]
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.IRS], ecx
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT], ecx
lea esi, [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
inc_INET esi ; RCV.NXT
mov ecx, [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.ISS]
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT], ecx
;;;106
 
mov [ebx + SOCKET_head.lock], 0
mov esi, [eax + SOCKET.SO_SND.SB_CC]
cmp esi, ecx
jl @f
mov esi, ecx
@@:
sub esi, ebx
 
push eax
; Now construct the response
mov bl, TH_SYN + TH_ACK
xor ecx, ecx
call TCP_send
pop eax
cmp esi, -1
jne .not_minus_one
 
mov [eax + SOCKET_head.lock], 0
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_SYN_RECEIVED
call notify_network_event
ret
; If FIN has been set, but not ACKed, and we havent been called to retransmit,
; len (esi) will be -1
; Otherwise, window shrank after we sent into it.
; If window shrank to 0, cancel pending retransmit and pull SND_NXT back to (closed) window
; We will enter persist state below.
; If window didn't close completely, just wait for an ACK
 
.exit:
mov [ebx + SOCKET_head.lock], 0
ret
xor esi, esi
 
.fail:
add esp, 8
mov [ebx + SOCKET_head.lock], 0
ret
test ecx, ecx
jnz @f
 
;;; mov [eax + TCP_SOCKET.t_timer..TCPT_REXMT], 0
 
align 4
stateTCB_SYN_SENT:
push [eax + TCP_SOCKET.SND_UNA]
pop [eax + TCP_SOCKET.SND_NXT]
@@:
 
DEBUGF 1,"TCBStateHandler: Syn_Sent\n"
.not_minus_one:
 
; We are awaiting an ACK to our SYN, with a SYM
; Look at control flags - expecting an ACK
;;; 124
 
mov al, [edx + TCP_Packet.Flags]
cmp esi, [eax + TCP_SOCKET.t_maxseg]
jle @f
 
test al, TH_RST
jnz .reset ; jump if RST bit set
mov esi, [eax + TCP_SOCKET.t_maxseg]
;sendalot = 1
 
push [edx + TCP_Packet.SequenceNumber] ;;
pop [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT] ;;
inc_INET (ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT) ;;
@@:
 
;;; 128
 
push [edx + TCP_Packet.AckNumber] ;;;;;;
pop [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT] ;;;;;;
mov edi, [eax + TCP_SOCKET.SND_NXT]
add edi, esi ; len
sub edi, [eax + TCP_SOCKET.SND_UNA]
add edi, [eax + SOCKET.SO_SND.SB_CC]
cmp edi, 0
jle @f
 
and al, TH_SYN + TH_ACK
jz .exit ; jump if none of the following is set: RST, SYN, ACK
and dl, not (TH_FIN) ; clear the FIN flag
 
test al, TH_ACK
jz .onlysyn ; jump if only SYN bit is set
@@:
 
; If we arrived here, SYN and ACK are set
 
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_ESTABLISHED
pushw TH_ACK
;;;; 130 TODO: set window (ecx) to space in send buffer
 
.send: ; Send an ACK
mov eax, ebx
pop bx
push eax
xor ecx, ecx
call TCP_send
pop ebx
 
.exit:
mov [ebx + SOCKET_head.lock], 0
ret
;------------------------------
; Sender silly window avoidance
 
.reset:
; TODO: ....
test esi, esi
jz .zero_length
 
; remove all queued TCP packets for this connection !
 
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_CLOSED
mov [ebx + SOCKET_head.lock], 0
ret
cmp esi, [eax + TCP_SOCKET.t_maxseg]
je .send
 
.onlysyn:
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_SYN_RECEIVED
pushw TH_SYN + TH_ACK
jmp .send
;;; TODO: 144-145
 
test [eax + TCP_SOCKET.t_force], -1
jnz .send
 
;;; TODO: 149..152
 
align 4
stateTCB_SYN_RECEIVED:
.zero_length:
 
DEBUGF 1,"TCBStateHandler: Syn_received\n"
 
test [edx + TCP_Packet.Flags], TH_RST ; reset connection? => LISTEN
jz .check_ack
;----------------------------------------
; Check if a window update should be sent
 
push [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.OrigRemotePort]
pop [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RemotePort]
push [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.OrigRemoteIP]
pop [ebx + SOCKET_head.end + IPv4_SOCKET.RemoteIP]
cmp ecx, 0 ; window
jle .no_window
 
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_LISTEN
jmp .exit
;;; TODO 154-172
 
.check_ack:
test [edx + TCP_Packet.Flags], TH_ACK ; ACK? => connection established!
jz .exit
.no_window:
 
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_ESTABLISHED
mov eax, ebx
call notify_network_event
;--------------------------
; Should a segment be sent?
 
.exit:
mov [ebx + SOCKET_head.lock], 0
ret
test [ebx + TCP_SOCKET.t_flags], TF_ACKNOW
jnz .send
 
test dl, TH_SYN + TH_RST
jnz .send
 
if 0
mov eax, [ebx + TCP_SOCKET.SND_UP]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
jg .send
 
test dl, TH_FIN
jz .enter_persist
 
align 4
stateTCB_ESTABLISHED:
test [ebx + TCP_SOCKET.t_flags], TF_SENTFIN
jnz .send
 
DEBUGF 1,"TCBStateHandler: Established\n"
mov eax, [ebx + TCP_SOCKET.SND_NXT]
cmp eax, [ebx + TCP_SOCKET.SND_UNA]
je .send
 
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
bswap eax
DEBUGF 1,"RCV_NXT is set to:%u\n", eax
bswap eax
cmp eax, [edx + TCP_Packet.SequenceNumber]
jne .exit ;;;;;;
;--------------------
; Enter persist state
 
; check if we received an ACK
test [edx + TCP_Packet.Flags], TH_ACK
jz .no_ack
.enter_persist:
 
mov ax, [edx + TCP_Packet.Window]
xchg al, ah
cmp ax, 1024
ja @f
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer], 1
@@:
.no_ack:
DEBUGF 1,"Entering pesist state\n"
 
; Now, see if we received any data
test ecx, ecx
jz .nodata
 
; Calculate next sequencenumber
add_INET (ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT)
 
push edx
DEBUGF 1,"Got %u bytes data!\n", ecx
; calculate header length
movzx eax, [edx + TCP_Packet.DataOffset]
and eax, 11110000b
shr eax, 2
DEBUGF 1,"TCP header size: %u\n", eax
add edx, eax ; now edx points to data
;--------------------------------------
; No reason to send a segment, just ret
 
add esp, 4
pop esi ; pointer to buffer
add esp, 4
DEBUGF 1,"No reason to send a segment\n"
 
sub edx, esi
mov edi, edx ; offset
mov eax, ebx ; socket ptr
ret
 
call socket_internal_receiver ; Place the data from packet into socket
 
; lea ebx, [eax + SOCKET_head.lock]
; call wait_mutex
mov ebx, eax
pop edx
 
test [edx + TCP_Packet.Flags], TH_FIN + TH_RST
jz .ack
 
.nodata:
test [edx + TCP_Packet.Flags], TH_FIN + TH_RST
jz .exit
 
; Send an ACK to that fin, and enter closewait state
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_CLOSE_WAIT
; Remove all resend entries from the queue
mov eax, ebx
call TCP_remove_socket
;-----------------------------------------------
;
; Send a segment
;
; ebx = socket pointer
; dl = flags
;
;-----------------------------------------------
 
.ack:
push ebx
mov ecx, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
call TCP_queue_ack
pop ebx
.send:
 
.exit:
mov [ebx + SOCKET_head.lock], 0
ret
DEBUGF 1,"Preparing to send a segment\n"
 
xor edi, edi ; edi will contain the number of header option bytes
 
end if
;------------------------------------
; Send options with first SYN segment
 
test dl, TH_SYN
jz .no_options
 
align 4
stateTCB_ESTABLISHED:
mov eax, [ebx + TCP_SOCKET.ISS]
mov [ebx + TCP_SOCKET.SND_NXT], eax
 
DEBUGF 1,"TCBStateHandler: Established\n"
test [ebx + TCP_SOCKET.t_flags], TF_NOOPT
jnz .no_options
 
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT]
mov eax, TCP_OPT_MAXSEG shl 24 + 4 shl 16
mov ax, 1280 ;;;;;;
bswap eax
DEBUGF 1,"RCV_NXT is set to:%u\n", eax
push eax
 
mov di, 4
 
test [ebx + TCP_SOCKET.t_flags], TF_REQ_SCALE
jz .no_syn
 
test dl, TH_ACK
jnz .scale_opt
 
test [ebx + TCP_SOCKET.t_flags], TF_RCVD_SCALE
jz .no_syn
 
.scale_opt:
 
mov eax, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP
mov ah, byte [ebx + TCP_SOCKET.request_r_scale]
bswap eax
cmp eax, [edx + TCP_Packet.SequenceNumber]
jne .exit
push eax
 
; Calculate next sequencenumber
add_INET (ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT)
add di, 4
 
test [edx + TCP_Packet.Flags], TH_FIN + TH_RST
jnz .fin
.no_syn:
 
.check_ack:
test [edx + TCP_Packet.Flags], TH_ACK
jz .exit
;------------------------------------
; Make the timestamp option if needed
 
DEBUGF 1,"Received ACK\n"
; First, look at the incoming window. If this is less than or equal to 1024,
; Set the socket window timer to 1. This will stop an additional Packets being queued.
; ** I may need to tweak this value, since I do not know how many Packets are already queued
push ecx
mov cx, [edx + TCP_Packet.Window]
xchg cl, ch
cmp cx, 1024
ja @f
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer], 1
@@:
pop ecx
test [ebx + TCP_SOCKET.t_flags], TF_REQ_TSTMP
jz .no_timestamp
 
; Now, see if we received any data
test dl, TH_RST
jnz .no_timestamp
 
test dl, TH_ACK
jz .timestamp
 
test [ebx + TCP_SOCKET.t_flags], TF_RCVD_TSTMP
jz .no_timestamp
 
.timestamp:
 
DEBUGF 1,"Creating a timestamp\n"
 
push dword (TCP_OPT_TIMESTAMP shl 8 + 10 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24)
pushw 0
mov eax, [timer_ticks]
bswap eax
push eax
 
add di, 10
 
.no_timestamp:
 
;; TODO: check if we dont exceed the max segment size
 
.no_options:
add edi, TCP_segment.Data
 
;-----------------------------------
; Check if we have some data to send
 
;;; mov ecx, [huppeldepup]
 
test ecx, ecx
jz .exit
jz .no_data
 
DEBUGF 1,"Got %u bytes data!\n", ecx
; calculate header length
movzx eax, [edx + TCP_Packet.DataOffset]
and eax, 11110000b
shr eax, 2
DEBUGF 1,"TCP header size: %u\n", eax
add edx, eax ; now edx points to data
;;; 278-316
 
add esp, 4
pop esi ; pointer to buffer
add esp, 4
jmp .header
 
sub edx, esi
mov edi, edx ; offset
mov eax, ebx ; socket ptr
.no_data:
 
call socket_internal_receiver ; Place the data from packet into socket
;;; 317-338
 
lea ebx, [eax + SOCKET_head.lock]
call wait_mutex
mov ebx, eax
 
.ack:
mov eax, ebx
mov bl, TH_ACK
push eax
xor ecx, ecx
call TCP_send ; send the ack
pop ebx
.exit:
mov [ebx + SOCKET_head.lock], 0
;----------
 
push di dx ebx
 
add ecx, edi ; total TCP segment size
 
mov eax, [ebx + IP_SOCKET.RemoteIP]
mov ebx, [ebx + IP_SOCKET.LocalIP]
mov di , IP_PROTO_TCP
call IPv4_create_packet
 
;;;; jz .fail
 
push edx eax
call NET_send
ret
 
.fin: ; we received a FIN or RESET
; Remove all resend entries from the queue
mov ecx, TCP_QUEUE_SIZE
mov esi, TCP_OUT_QUEUE+4
;----------------
 
.removeloop:
cmp [esi + tcp_out_queue_entry.data_ptr], 0
je .maybe_next
 
; TODO: check if the packets belong to the same tcp connection !
;-------------------------------
; Now, create the 20-byte header
 
DEBUGF 1,"Removing a queued packet\n"
.header:
 
push [esi + tcp_out_queue_entry.data_ptr]
mov [esi + tcp_out_queue_entry.data_ptr], 0
dec [TCP_OUT_QUEUE]
call kernel_free
;-----------------------
; Fill in the TCP header
pop esi
 
.maybe_next:
add esi, tcp_out_queue_entry.size
loop .removeloop
push [esi + TCP_SOCKET.SND_NXT]
rol word [esp], 8
rol dword [esp], 16
pop [edi + TCP_segment.SequenceNumber]
 
; Send an ACK to that fin, and enter closewait state
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_CLOSING
jmp .check_ack
push [esi + TCP_SOCKET.RCV_NXT]
rol word [esp], 8
rol dword [esp], 16
pop [edi + TCP_segment.AckNumber]
 
push [esi + TCP_SOCKET.LocalPort]
rol word [esp], 8
pop [edi + TCP_segment.SourcePort]
 
align 4
stateTCB_FIN_WAIT_1:
push [esi + TCP_SOCKET.RemotePort]
rol word [esp], 8
pop [edi + TCP_segment.DestinationPort]
 
DEBUGF 1,"TCBStateHandler: Fin_wait_1\n"
 
; We can either receive an ACK of a fin, or a fin
mov al, [edx + TCP_Packet.Flags]
and al, TH_FIN + TH_ACK
mov [edi + TCP_segment.Window], 0x0005
; 1280 bytes
mov [edi + TCP_segment.UrgentPointer], 0
 
cmp al, TH_ACK
jne @f
mov [edi + TCP_segment.DataOffset], 0x50
 
; It was an ACK
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_FIN_WAIT_2
jmp .exit
mov [edi + TCP_segment.Flags], cl
 
@@: mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_CLOSING
cmp al, TH_FIN
je @f
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_TIMED_WAIT
mov [edi + TCP_segment.Checksum], 0
 
@@:
; Send an ACK
mov eax, ebx
mov bl, TH_ACK
push eax
xor ecx, ecx
call TCP_send
pop ebx
;-----
 
.exit:
mov [ebx + SOCKET_head.lock], 0
ret
 
;--------------
; Copy the data
 
pop esi
push edi
add edi, TCP_segment.Data ;;
sub ecx, TCP_segment.Data ;;;
 
align 4
stateTCB_FIN_WAIT_2:
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
test ecx, ecx
jz .nd
rep movsd
.nd:
pop edi
 
DEBUGF 1,"TCBStateHandler: Fin_wait_2\n"
;--------------------
; Create the checksum
 
test [edx + TCP_Packet.Flags], TH_FIN
jz .exit
push [ebx + IP_SOCKET.LocalIP]
push [ebx + IP_SOCKET.RemoteIP]
call TCP_checksum
 
; Change state, as we have a fin
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_TIMED_WAIT
;----------------
; Send the packet
 
; Send an ACK
mov eax, ebx
mov bl, TH_ACK
push eax
xor ecx, ecx
call TCP_send
pop ebx
;;;;;
 
.exit:
mov [ebx + SOCKET_head.lock], 0
 
DEBUGF 1,"Sending TCP Packet to device %x\n", ebx
mov esi, [ebx + ETH_DEVICE.transmit]
 
ret
 
 
 
;-------------------------
;
; TCP_outflags
;
; IN: eax = socket ptr
;
; OUT: edx = flags
;
;-------------------------
align 4
stateTCB_CLOSE_WAIT:
TCP_outflags:
 
DEBUGF 1,"TCBStateHandler: close_wait\n"
; Intentionally left empty
; socket_close_tcp handles this
mov edx, [eax + TCP_SOCKET.t_state]
movzx edx, byte [edx + .flaglist]
 
mov [ebx + SOCKET_head.lock], 0
DEBUGF 1,"TCP_outflags, socket: %x, flags: %x\n", eax, dl
 
ret
 
.flaglist:
 
db TH_RST + TH_ACK ; TCB_CLOSED
db 0 ; TCB_LISTEN
db TH_SYN ; TCB_SYN_SENT
db TH_SYN + TH_ACK ; TCB_SYN_RECEIVED
db TH_ACK ; TCB_ESTABLISHED
db TH_ACK ; TCB_CLOSE_WAIT
db TH_SYN + TH_ACK ; TCB_FIN_WAIT_1
db TH_SYN + TH_ACK ; TCB_CLOSING
db TH_SYN + TH_ACK ; TCB_LAST_ACK
db TH_ACK ; TCB_FIN_WAIT_2
db TH_ACK ; TCB_TIMED_WAIT
 
 
;-------------------------
;
; TCP_drop
;
; IN: eax = socket ptr
;
; OUT: /
;
;-------------------------
align 4
stateTCB_CLOSING:
TCP_drop:
 
DEBUGF 1,"TCBStateHandler: closingn\n"
DEBUGF 1,"TCP_drop\n"
 
; We can either receive an ACK of a fin, or a fin
test [edx + TCP_Packet.Flags], TH_ACK
jz .exit
; cmp [eax + TCP_SOCKET.t_state], TCB_SYN_RECEIVED
; jl .no_syn_received
 
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.state], TCB_TIMED_WAIT
mov [eax + TCP_SOCKET.t_state], TCB_CLOSED
 
.exit:
call TCP_output
 
mov [ebx + SOCKET_head.lock], 0
; .no_syn_received:
 
ret
 
 
align 4
stateTCB_LAST_ACK:
 
DEBUGF 1,"TCBStateHandler: last_ackn\n"
 
; Look at control flags - expecting an ACK
test [edx + TCP_Packet.Flags], TH_ACK
jz .exit
 
mov [ebx + SOCKET_head.lock], 0
;---------------------------------------
;
; TCP_respond
;
; The easy way to send a RST/ACK segment
;
; IN: eax = socket ptr
;
; OUT: /
;
;---------------------------------------
align 4
TCP_respond:
 
; delete the socket
stdcall net_socket_free, ebx
DEBUGF 1,"TCP_respond\n"
 
.exit:
ret
 
 
 
;-----------------------------------------------------------------
;
; TCP_checksum
;
; This is the fast procedure to create or check a UDP header
; - To create a new checksum, the checksum field must be set to 0 before computation
; - To check an existing checksum, leave the checksum as is,
; and it will be 0 after this procedure, if it was correct
;
; IN: push source ip
; push dest ip
;
; esi = packet ptr
;
; OUT: checksum is filled in in packet! (but also in dx)
;
;-----------------------------------------------------------------
align 4
stateTCB_TIME_WAIT:
TCP_checksum:
 
DEBUGF 1,"TCBStateHandler: time_wait\n"
;-------------
; Pseudoheader
 
mov [ebx + SOCKET_head.lock], 0
; protocol type
mov edx, IP_PROTO_TCP ; NO shl 8 here ! (it took me ages to figure this one out)
 
ret
; source address
add dl, [esp+1+4]
adc dh, [esp+0+4]
adc dl, [esp+3+4]
adc dh, [esp+2+4]
 
; destination address
adc dl, [esp+1+8]
adc dh, [esp+0+8]
adc dl, [esp+3+8]
adc dh, [esp+2+8]
 
align 4
stateTCB_CLOSED:
; size
adc dl, cl
adc dh, ch
 
DEBUGF 1,"TCBStateHandler: closed\n"
;---------------------
; Real header and data
 
mov [ebx + SOCKET_head.lock], 0
push esi
call checksum_1
call checksum_2
pop esi
 
ret
neg [esi+UDP_Packet.Checksum] ; zero will stay zero so we just get the checksum
add [esi+UDP_Packet.Checksum], dx ; , else we will get (new checksum - old checksum) in the end, wich should be 0 :)
 
ret 8 ; Remove the IPs from stack
 
 
 
 
;---------------------------------------------------------------------------
;
; TCP_API
1269,11 → 2007,11
ret
 
.packets_tx:
add eax, TCP_PACKETS_TX
add eax, TCP_segments_tx
mov eax, [eax]
ret
 
.packets_rx:
add eax, TCP_PACKETS_RX
add eax, TCP_segments_rx
mov eax, [eax]
ret
/kernel/branches/net/network/udp.inc
1,6 → 1,6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; UDP.INC ;;
55,12 → 55,11
ret
 
 
 
;-----------------------------------------------------------------
;
; UDP_Handler:
; UDP_input:
;
; Called by IPv4_handler,
; Called by IPv4_input,
; this procedure will inject the udp data diagrams in the application sockets.
;
; IN: Pointer to buffer in [esp]
76,23 → 75,20
;
;-----------------------------------------------------------------
align 4
UDP_handler:
UDP_input:
 
DEBUGF 1,"UDP_Handler, checksum:%x, size:%u\n", [edx+UDP_Packet.Checksum]:4, ecx
DEBUGF 1,"UDP_input, size:%u\n", ecx
 
; First validate, checksum:
cmp [edx + UDP_Packet.Checksum], 0
jz .no_checksum
 
xchg edi, esi ; save ipv4 source address so we can look it up later
xchg edi, esi ; save ipv4 source address to edi so we can use it later
 
push edx
 
push esi
push edi
push esi edi
mov esi, edx
call UDP_checksum ; this destroys edx, ecx and esi (but not edi! :)
 
call UDP_checksum ; this destroys edx, ecx and esi (but not edi...)
pop edx
 
cmp [edx + UDP_Packet.Checksum], 0
108,47 → 104,43
mov eax, net_sockets
.try_more:
mov si , [edx + UDP_Packet.DestinationPort] ; get the local port from the IP Packet's UDP header
rol si , 8
.next_socket:
mov eax, [eax + SOCKET_head.NextPtr]
mov eax, [eax + SOCKET.NextPtr]
or eax, eax
jz .dump
cmp [eax + SOCKET_head.Domain], AF_INET4
cmp [eax + SOCKET.Domain], AF_INET4
jne .next_socket
cmp [eax + SOCKET_head.Type], IP_PROTO_UDP
cmp [eax + SOCKET.Type], IP_PROTO_UDP
jne .next_socket
cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.LocalPort], si
cmp [eax + UDP_SOCKET.LocalPort], si
jne .next_socket
 
DEBUGF 1,"found socket with matching domain, type and localport\n"
DEBUGF 1,"using socket: %x\n", eax
 
; For dhcp, we must allow any remote server to respond.
; I will accept the first incoming response to be the one
; I bind to, if the socket is opened with a destination IP address of
; 255.255.255.255
cmp [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP], 0xffffffff
je .ok1
;;; TODO: when packet is processed, check more sockets!
 
cmp [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP], edi ; edi is IPv4 destination address
jne .try_more ; Quit if the source IP is not valid, check for more sockets with this IP/PORT combination
cmp [eax + IP_SOCKET.RemoteIP], 0xffffffff
je @f
cmp [eax + IP_SOCKET.RemoteIP], edi ; edi is the packets source address
jne .try_more
@@:
 
 
DEBUGF 1,"Remote Ip matches\n"
.ok1:
 
cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.firstpacket], 0
cmp [eax + UDP_SOCKET.firstpacket], 0
jz .updateport
 
mov si, [edx + UDP_Packet.SourcePort]
cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.RemotePort], si
rol si, 8
cmp [eax + UDP_SOCKET.RemotePort], si
jne .dump
 
push ebx
lea ebx, [eax + SOCKET_head.lock]
lea ebx, [eax + SOCKET.lock]
call wait_mutex
pop ebx
 
.ok2:
 
.updatesock:
inc [UDP_PACKETS_RX]
DEBUGF 1,"Found valid UDP packet for socket %x\n", eax
lea esi, [edx + UDP_Packet.Data]
movzx ecx, [edx + UDP_Packet.Length]
155,29 → 147,21
rol cx , 8
sub cx , UDP_Packet.Data
 
inc [UDP_PACKETS_RX]
jmp SOCKET_input
 
pop edi
add esp, 4
 
sub esi, edi
xchg esi, edi
jmp socket_internal_receiver
 
 
.updateport:
 
push ebx
lea ebx, [eax + SOCKET_head.lock]
lea ebx, [eax + SOCKET.lock]
call wait_mutex
pop ebx
 
mov si, [edx + UDP_Packet.SourcePort]
DEBUGF 1,"Changing remote port to: %x\n", si
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.RemotePort], si
inc [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.firstpacket]
rol si, 8
DEBUGF 1,"Changing remote port to: %u\n", si
mov [eax + UDP_SOCKET.RemotePort], si
inc [eax + UDP_SOCKET.firstpacket]
 
jmp .ok2
jmp .updatesock
 
 
.checksum_mismatch:
202,7 → 186,7
 
;-----------------------------------------------------------------
;
; UDP_socket_send
; UDP_output
;
; IN: eax = socket pointer
; ecx = number of bytes to send
211,27 → 195,31
;-----------------------------------------------------------------
 
align 4
UDP_socket_send:
UDP_output:
 
mov edx, dword [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.LocalPort] ; load local port and remote port at once
DEBUGF 1,"local port: %x, remote port: %x\n",\
[eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.LocalPort]:4,\
[eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.RemotePort]:4
mov ebx, [eax + SOCKET_head.end + IPv4_SOCKET.LocalIP]
mov eax, [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP]
DEBUGF 1,"UDP_output: socket:%x, bytes: %u, data ptr: %x\n", eax, ecx, esi
 
DEBUGF 1,"Create UDP Packet (size=%u)\n",ecx
mov dx, [eax + UDP_SOCKET.RemotePort]
DEBUGF 1,"remote port: %u\n", dx
rol dx, 8
rol edx, 16
mov dx, [eax + UDP_SOCKET.LocalPort]
DEBUGF 1,"local port: %u\n", dx
rol dx, 8
 
 
mov ebx, [eax + IP_SOCKET.LocalIP]
mov eax, [eax + IP_SOCKET.RemoteIP]
 
mov di , IP_PROTO_UDP
sub esp, 8 ; Data ptr and data size will be placed here
add ecx, UDP_Packet.Data
 
; TODO: fill in: dx = fragment id
;;; TODO: fragment id
 
push edx esi
call IPv4_create_packet ; TODO: figure out a way to choose between IPv4 and IPv6
cmp edi, -1
je .fail
call IPv4_create_packet
jz .fail
 
mov [esp + 8], eax ; pointer to buffer start
mov [esp + 8 + 4], edx ; buffer size
251,7 → 239,7
rep movsb
pop ecx edi
 
pop dword [edi + UDP_Packet.SourcePort] ; fill in both portnumbers
pop dword [edi + UDP_Packet.SourcePort]
mov [edi + UDP_Packet.Checksum], 0 ; set it to zero, to calculate checksum
 
; Checksum
263,10 → 251,10
inc [UDP_PACKETS_TX]
 
DEBUGF 1,"Sending UDP Packet to device %x\n", ebx
jmp ETH_sender
 
jmp NET_send
 
.fail:
; todo: queue the packet
add esp, 8+8
ret
 
275,7 → 263,7
 
;-----------------------------------------------------------------
;
; checksum_udp
; UDP_checksum
;
; This is the fast procedure to create or check a UDP header
; - To create a new checksum, the checksum field must be set to 0 before computation