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