/kernel/branches/net/kernel.asm |
---|
56,13 → 56,13 |
include 'macros.inc' |
$Revision: 1025 $ |
$Revision$ |
USE_COM_IRQ equ 1 ; make irq 3 and irq 4 available for PCI devices |
; Enabling the next line will enable serial output console |
;debug_com_base equ 0x3f8 ; 0x3f8 is com1, 0x2f8 is com2, 0x3e8 is com3, 0x2e8 is com4, no irq's are used |
debug_com_base equ 0x3f8 ; 0x3f8 is com1, 0x2f8 is com2, 0x3e8 is com3, 0x2e8 is com4, no irq's are used |
include "proc32.inc" |
include "kglobals.inc" |
/kernel/branches/net/network/ARP.inc |
---|
221,7 → 221,7 |
mov ecx, 60 ; minimum packet size |
mov edx, edi ;;; |
mov di , ETHER_ARP |
call ETH_create_Packet |
call ETH_create_packet |
cmp edi, -1 |
je .exit |
248,7 → 248,7 |
DEBUGF 1,"ARP Packet for device %x created successfully\n", ebx |
push edx ecx |
jmp ETH_Sender |
jmp ETH_sender |
.exit: |
add esp, 8 |
555,7 → 555,7 |
DEBUGF 1,"ARP_Handler - Sending reply \n" |
jmp ETH_Sender ; And send it! |
jmp ETH_sender ; And send it! |
.exit: |
call kernel_free |
/kernel/branches/net/network/IPv4.inc |
---|
3,7 → 3,7 |
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; IP.INC ;; |
;; IPv4.INC ;; |
;; ;; |
;; Part of the tcp/ip network stack for KolibriOS ;; |
;; ;; |
52,7 → 52,7 |
.PrevPtr dd ? ; Pointer to previous fragment entry (-1 for first packet) |
.NextPtr dd ? ; Pointer to next fragment entry (-1 for last packet) |
.Owner dd ? ; Pointer to structure of driver |
rb 2 ; to match ethernet header size |
rb 2 ; to match ethernet header size ; TODO: fix this hack |
.Data: ; Ip header begins here (we will need the IP header to re-construct the complete packet) |
ends |
99,10 → 99,9 |
;----------------------------------------------------------------- |
; |
; IP_Handler: |
; IPv4_Handler: |
; |
; Called by eth_handler, |
; will check if IP Packet isnt damaged |
; Will check if IP Packet isnt damaged |
; and call appropriate handler. (TCP/UDP/ICMP/..) |
; |
; It will also re-construct fragmented packets |
119,21 → 118,28 |
IPv4_handler: |
DEBUGF 1,"IP_Handler - start\n" |
mov cx , [edx + IPv4_Packet.HeaderChecksum] |
xchg ch , cl ; Get the checksum in intel format |
mov word [edx + IPv4_Packet.HeaderChecksum], 0 ; Clear checksum field to recalculating checksum |
push edx ebx |
movzx eax, byte [edx + IPv4_Packet.VersionAndIHL] ; Calculate Header length by using IHL field |
and eax, 0x0000000F ; |
shl eax, 2 ; |
; save checksum, and clear it in original packet |
mov di , [edx + IPv4_Packet.HeaderChecksum] |
DEBUGF 1,"checksum: %x\n",di |
mov word [edx + IPv4_Packet.HeaderChecksum], 0 |
push edx |
stdcall checksum_jb, edx, eax ; buf_ptr, buf_size |
pop edx |
cmp cx , ax |
jnz .dump ; if CHECKSUM isn't valid then dump Packet |
; Re-calculate checksum |
movzx ecx, byte [edx + IPv4_Packet.VersionAndIHL] ; Calculate Header length by using IHL field |
and ecx, 0x0000000F ; |
shl cx , 2 ; |
mov esi, edx |
xor edx, edx |
call checksum_1 |
call checksum_2 |
; now compare the two.. |
cmp dx, di |
pop ebx edx |
jne .dump ; if checksum isn't valid then dump packet |
mov eax, [edx + IPv4_Packet.DestinationAddress] |
mov edi, BROADCAST |
mov ecx, MAX_IP+1 |
183,10 → 189,14 |
add eax, edx |
push eax |
mov al , [edx + IPv4_Packet.Protocol] |
;----------------------- experimental |
mov esi, [edx + IPv4_Packet.SourceAddress] |
mov edi, [edx + IPv4_Packet.DestinationAddress] |
;----------------------- |
pop edx ; Offset to data (tcp/udp/icmp/.. Packet) |
cmp al , IP_PROTO_TCP |
; je TCP_handler |
je TCP_handler |
cmp al , IP_PROTO_UDP |
je UDP_handler |
385,14 → 395,21 |
movzx eax, byte [edx + IPv4_Packet.VersionAndIHL] ; Calculate Header length by using IHL field |
and ax, 0x000F ; |
shl ax, 2 ; |
sub ecx, eax ; |
sub ecx, eax |
add eax, edx |
push eax |
mov al , [edx + IPv4_Packet.Protocol] |
;----------------------- experimental |
mov esi, [edx + IPv4_Packet.SourceAddress] |
mov edi, [edx + IPv4_Packet.DestinationAddress] |
;----------------------- |
pop edx ; Offset to data (tcp/udp/icmp/.. Packet) |
cmp al , IP_PROTO_TCP |
; je TCP_handler |
je TCP_handler |
cmp al , IP_PROTO_UDP |
je UDP_handler |
493,12 → 510,12 |
; dx = fragment id |
; di = protocol |
; |
; OUT: eax points to buffer start |
; ebx is size of complete buffer |
; edi = pointer to start of data (-1 on error) |
; OUT: eax = pointer to buffer start |
; ebx = pointer to device struct (needed for sending procedure) |
; ecx = unchanged (packet size of embedded data) |
; edx = pointer to device struct (needed for sending procedure) |
; edx = size of complete buffer |
; esi = pointer to sending procedure |
; edi = pointer to start of data (-1 on error) |
; |
;----------------------------------------------------------------- |
550,7 → 567,7 |
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 |
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 |
571,14 → 588,18 |
pop ecx |
mov [edi + IPv4_Packet.DestinationAddress], ecx |
push eax |
stdcall checksum_jb, edi, IPv4_Packet.DataOrOptional ; buf_ptr, buf_size |
xchg al, ah |
mov [edi + IPv4_Packet.HeaderChecksum], ax |
pop eax ecx |
push eax ebx edx |
; calculate checksum |
xor edx, edx |
mov esi, edi |
mov ecx, IPv4_Packet.DataOrOptional |
call checksum_1 |
call checksum_2 |
mov [edi + IPv4_Packet.HeaderChecksum], dx |
pop edx ebx eax ecx |
add edi, IPv4_Packet.DataOrOptional |
DEBUGF 1,"IPv4 Packet for device %x created successfully\n", edx |
DEBUGF 1,"IPv4 Packet for device %x created successfully\n", ebx |
ret |
585,7 → 606,7 |
.not_found: |
DEBUGF 1,"Create IPv4 Packet - ARP entry not found!\n" |
; TODO: QUEUE! |
; TODO: QUEUE the packet to resend later! |
.exit: |
add esp, 16 |
.exit_: |
/kernel/branches/net/network/ethernet.inc |
---|
80,14 → 80,9 |
mov ecx, (1+MAX_ETH_DEVICES) |
rep stosd |
mov dword [ETH_IN_QUEUE], ETH_QUEUE_SIZE |
mov dword [ETH_IN_QUEUE+4], ETH_IN_QUEUE + queue.data |
mov dword [ETH_IN_QUEUE+8], ETH_IN_QUEUE + queue.data |
init_queue ETH_IN_QUEUE |
init_queue ETH_OUT_QUEUE |
mov dword [ETH_OUT_QUEUE], ETH_QUEUE_SIZE |
mov dword [ETH_OUT_QUEUE+4], ETH_OUT_QUEUE + queue.data |
mov dword [ETH_OUT_QUEUE+8], ETH_OUT_QUEUE + queue.data |
ret |
104,7 → 99,7 |
;--------------------------------------------------------- |
align 4 |
ETH_Add_Device: |
ETH_add_device: |
DEBUGF 1,"ETH_Add_Device: %x ", ebx |
122,7 → 117,6 |
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 |
cld |
repne scasd ; See if device is already in the list |
jz .error |
167,7 → 161,7 |
;-------------------------------- |
align 4 |
ETH_Remove_Device: |
ETH_remove_device: |
cmp [ETH_RUNNING], 0 |
je .error |
212,12 → 206,21 |
;------------------------------------------------------------- |
align 4 |
ETH_Receiver: |
DEBUGF 1,"ETH_Receiver \n" |
ETH_receiver: |
DEBUGF 1,"ETH_Receiver: " |
add_to_queue ETH_IN_QUEUE, ETH_QUEUE_SIZE, .gohome |
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 |
.gohome: |
.fail: |
DEBUGF 1,"ETH_IN_QUEUE is full!\n" |
add esp, 4 |
call kernel_free |
add esp, 4 |
ret |
238,8 → 241,19 |
align 4 |
ETH_handler: |
get_from_queue ETH_IN_QUEUE, ETH_QUEUE_SIZE, .gohome |
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 |
DEBUGF 1,"ETH_Handler - size: %u\n", ecx |
cmp ecx, 60 ; check packet length |
jl .dump |
262,13 → 276,13 |
add esp, 4 |
.gohome: |
ret ; return 1. to get more from queue / 2. to caller |
ret ; return to get more from queue / to caller |
;----------------------------------------------------------------- |
; |
; ETH_Sender: |
; ETH_sender: |
; |
; This function sends an ethernet packet to the correct driver. |
; |
280,35 → 294,66 |
;----------------------------------------------------------------- |
align 4 |
ETH_Sender: |
DEBUGF 1,"ETH_Sender \n" |
ETH_sender: |
DEBUGF 1,"ETH_Sender: queuing for device: %x, %u bytes\n", [esp], [esp + 4] |
add_to_queue ETH_OUT_QUEUE, ETH_QUEUE_SIZE, .gohome |
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 |
.gohome: |
.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, .gohome |
get_from_queue ETH_OUT_QUEUE, ETH_QUEUE_SIZE, eth_queue_entry.size, .gohome |
push ETH_send_queued |
lodsd |
mov ebx, eax |
sub esp, 8 |
mov edi, esp |
movsd |
movsd |
DEBUGF 1,"dequeued packet for device %x\n", ebx |
call ETH_struc2dev ; convert struct ptr to device num (this way we know if driver is still mounted) |
cmp edi, -1 |
je .fail |
DEBUGF 1,"ETH_Sender - device: %u\n", edi |
jmp [ebx+ETH_DEVICE.transmit] ; we will return to get_from_queue macro after transmitting packet |
jmp [ebx+ETH_DEVICE.transmit] |
.fail: |
call kernel_free |
add esp, 4 ; pop (balance stack) |
DEBUGF 1,"ETH_Sender - fail\n" |
.gohome: |
ret |
;--------------------------------------------------------------------------- |
; |
; ETH_struc2dev |
362,7 → 407,7 |
;--------------------------------------------------------------------------- |
align 4 |
ETH_create_Packet: |
ETH_create_packet: |
DEBUGF 1,"Creating Ethernet Packet (size=%u): \n", ecx |
394,7 → 439,7 |
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 |
mov esi, ETH_sender |
xor ebx, ebx ;;;; TODO: Fixme |
mov ebx, [ETH_DRV_LIST + ebx] |
/kernel/branches/net/network/icmp.inc |
---|
133,8 → 133,8 |
; |
; ICMP_Handler: |
; |
; Called by IP_handler, |
; this procedure will send reply's to ICMP echo's etc ;;; 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 ;;; TODO: update this to work with fragmented packets too! |
; |
; IN: Pointer to buffer in [esp] |
; size of buffer in [esp+4] |
181,33 → 181,31 |
mov dword [esi + ETH_FRAME.Data + IPv4_Packet.SourceAddress], ecx |
; Recalculate ip header checksum |
; mov esi, [esp] |
add esi, ETH_FRAME.Data ; Point esi to start of IP Packet |
movzx eax, byte [esi + IPv4_Packet.VersionAndIHL] ; Calculate IP Header length by using IHL field |
and eax, 0x0000000F ; |
shl eax, 2 ; |
push ebx edx esi |
stdcall checksum_jb, esi, eax ; calculate the checksum |
pop esi edx ebx |
xchg al, ah ; convert to intel byte order |
; mov esi, [esp] |
mov word [esi + IPv4_Packet.HeaderChecksum], ax ; Store it in the IP Packet header |
movzx ecx, byte [esi + IPv4_Packet.VersionAndIHL] ; Calculate IP Header length by using IHL field |
and ecx, 0x0000000F ; |
shl cx , 2 |
push ebx edx ecx esi |
xor edx, edx |
call checksum_1 |
call checksum_2 |
pop esi |
mov word [esi + IPv4_Packet.HeaderChecksum], dx ; Store it in the IP Packet header |
; Recalculate ICMP CheckSum |
; mov esi, [esp] ; Find length of IP Packet |
movzx eax, word[esi + IPv4_Packet.TotalLength] ; |
movzx eax, word[esi + IPv4_Packet.TotalLength] ; Find length of IP Packet |
xchg ah , al ; |
movzx edi, byte [esi + IPv4_Packet.VersionAndIHL] ; Calculate IP Header length by using IHL field |
and edi, 0x0000000F ; |
shl edi, 2 ; |
sub ax , di ; Now we know the length of ICMP data in eax |
push ebx edx |
stdcall checksum_jb,edx,eax ; Calculate the checksum of icmp data |
pop edx ebx |
xchg al, ah ; Convert to intel byte order |
sub eax, [esp] ; Now we know the length of ICMP data in eax |
mov ecx, eax |
mov esi, [esp + 4] |
xor edx, edx |
call checksum_1 |
call checksum_2 |
mov ax , dx |
pop ecx edx ebx |
mov word [edx + ICMP_Packet.Checksum], ax |
jmp ETH_Sender ; Send the reply |
jmp ETH_sender ; Send the reply |
222,20 → 220,14 |
.try_more: |
mov ax , [edx + ICMP_Packet.Identifier] |
.next_socket: |
mov esi, [esi + SOCKET.NextPtr] |
mov esi, [esi + SOCKET_head.NextPtr] |
or esi, esi |
jz .dump |
cmp [esi + SOCKET.Type], IP_PROTO_ICMP |
cmp [esi + SOCKET_head.Type], IP_PROTO_ICMP |
jne .next_socket |
cmp [esi + SOCKET.LocalPort], ax |
cmp [esi + SOCKET_head.end + IPv4_SOCKET.end + ICMP_SOCKET.Identifier], ax |
jne .next_socket |
cmp [esi + SOCKET.rxDataCount],0 ; get # of bytes already in buffer |
jnz .dump ; only one packet at a time may be in the buffer! |
cmp ecx, SOCKETBUFFSIZE - SOCKETHEADERSIZE; TODO: fix this problem ! |
jg .dump |
call IPv4_dest_to_dev |
cmp edi,-1 |
je .dump |
243,60 → 235,25 |
DEBUGF 1,"Found valid ICMP packet for socket %x\n", esi |
lea ebx, [esi + SOCKET.lock] |
lea ebx, [esi + SOCKET_head.lock] |
call wait_mutex |
; Now, copy data to socket. We have socket address in esi. |
; Now, assign data to socket. We have socket address in esi. |
; We have ICMP Packet in edx |
; number of bytes in ecx |
; note: we do not strip the header! |
DEBUGF 1,"bytes: %u\n", ecx |
mov [esi + SOCKET.rxDataCount], ecx |
lea edi, [esi + SOCKETHEADERSIZE] |
push esi |
push ecx |
mov esi, edx |
shr ecx, 2 |
rep movsd ; copy the data across |
pop ecx |
and ecx, 3 |
rep movsb |
mov eax, esi |
pop esi |
add esp, 4 |
sub edx, esi |
mov edi, edx |
jmp socket_internal_receiver |
DEBUGF 1,"ICMP socket updated\n" |
mov [esi + SOCKET.lock], 0 |
; flag an event to the application |
mov eax, [esi + SOCKET.PID] ; get socket owner PID |
mov ecx, 1 |
mov esi, TASK_DATA + TASKDATA.pid |
.next_pid: |
cmp [esi], eax |
je .found_pid |
inc ecx |
add esi, 0x20 |
cmp ecx, [TASK_COUNT] |
jbe .next_pid |
jmp .dump |
.found_pid: |
shl ecx, 8 |
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK ; stack event |
mov [check_idle_semaphore], 200 |
.dump: |
DEBUGF 1,"ICMP_Handler - dumping\n" |
call kernel_free |
add esp, 8 ; pop (balance stack) |
add esp, 4 ; pop (balance stack) |
ret |
351,7 → 308,7 |
DEBUGF 1,"ICMP_Handler_fragments - end\n" |
call kernel_free |
add esp, 8 ; pop (balance stack) |
add esp, 4 ; pop (balance stack) |
ret |
;----------------------------------------------------------------- |
397,11 → 354,14 |
mov [edi + ICMP_Packet.Identifier], ax |
mov [edi + ICMP_Packet.Checksum], 0 |
stdcall checksum_jb, edi , ecx |
xchg al, ah |
mov [edi + ICMP_Packet.Checksum], ax |
push eax ebx ecx edx |
mov esi, edi |
xor edx, edx |
call checksum_1 |
call checksum_2 |
mov [edi + ICMP_Packet.Checksum], dx |
pop edx ecx ebx eax esi |
pop esi |
sub ecx, ICMP_Packet.Data |
add edi, ICMP_Packet.Data |
push cx |
/kernel/branches/net/network/queue.inc |
---|
15,13 → 15,13 |
$Revision$ |
struct queue |
.size dd ? |
.w_ptr dd ? |
.r_ptr dd ? |
.size dd ? ; number of queued packets in thsi queue |
.w_ptr dd ? ; current writing pointer in queue |
.r_ptr dd ? ; current reading pointer |
.data: |
ends |
struct queue_entry |
struct eth_queue_entry |
.owner dd ? |
.data_ptr dd ? |
.data_size dd ? |
28,75 → 28,84 |
.size: |
ends |
struct tcp_in_queue_entry |
.data_ptr dd ? |
.data_size dd ? |
.offset dd ? |
.size: |
ends |
macro add_to_queue ptr, size, returnaddr { |
struct tcp_out_queue_entry |
.data_ptr dd ? |
.data_size dd ? |
.ttl dd ? |
.retries dd ? |
.owner dd ? |
.sendproc dd ? |
.ack_num dd ? |
.size: |
ends |
cmp dword [ptr + queue.size], size ; Check if queue isnt full |
jge .fail |
struct socket_queue_entry |
.data_ptr dd ? |
.data_size dd ? |
.offset dd ? |
.size: |
ends |
DEBUGF 1,"Queuing packet for device %x\n",ebx |
macro add_to_queue ptr, size, entry_size, failaddr { |
inc dword [ptr + queue.size] |
cmp [ptr + queue.size], size ; Check if queue isnt full |
jge failaddr |
mov edi, dword [ptr + queue.w_ptr] ; Current write pointer (FIFO!) |
inc [ptr + queue.size] |
mov eax, ebx |
stosd |
pop eax |
stosd |
pop eax |
stosd |
mov edi, [ptr + queue.w_ptr] ; Current write pointer (FIFO!) |
mov ecx, entry_size/4 ; Write the queue entry |
rep movsd ; |
cmp edi, size*queue_entry.size+ptr+queue.data ; entry size |
lea ecx, [size*entry_size+ptr+queue.data] |
cmp edi, ecx ; entry size |
jl .no_wrap |
sub edi, size*queue_entry.size |
sub edi, size*entry_size |
.no_wrap: |
mov dword [ptr + queue.w_ptr], edi |
jmp returnaddr |
mov [ptr + queue.w_ptr], edi |
.fail: |
DEBUGF 1,"queuing failed\n" |
call kernel_free |
add esp, 4 |
ret |
} |
macro get_from_queue ptr, size, returnaddr { |
.start_of_code: |
cmp dword [ptr + queue.size], 0 ; any packets queued? |
je returnaddr |
macro get_from_queue ptr, size, entry_size, failaddr { |
DEBUGF 1,"Dequeuing packet" |
cmp [ptr + queue.size], 0 ; any packets queued? |
je failaddr |
dec dword [ptr + queue.size] |
dec [ptr + queue.size] |
push dword .start_of_code ; return address for call's |
mov esi, [ptr + queue.r_ptr] |
lodsd |
mov ebx, eax |
lodsd |
mov ecx, eax |
lodsd |
push eax |
push ecx |
xchg eax, ecx |
push esi |
DEBUGF 1," for device %x\n", ebx |
add esi, entry_size |
cmp esi, size*queue_entry.size+ptr+queue.data ; entry size |
lea ecx, [size*entry_size+ptr+queue.data] |
cmp esi, ecx ; entry size |
jl .no_wrap |
sub esi, size*queue_entry.size |
sub esi, size*entry_size |
.no_wrap: |
mov dword [ptr + queue.r_ptr], esi |
pop esi |
} |
macro init_queue queue_ptr { |
mov [queue_ptr + queue.size] , 0 |
lea esi, [queue_ptr + queue.data] |
mov [queue_ptr + queue.w_ptr], esi |
mov [queue_ptr + queue.r_ptr], esi |
} |
/kernel/branches/net/network/socket.inc |
---|
5,7 → 5,6 |
;; ;; |
;; SOCKET.INC ;; |
;; ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; based on code by mike.dld ;; |
;; ;; |
16,8 → 15,7 |
$Revision$ |
align 4 |
struct SOCKET |
struct SOCKET_head |
.PrevPtr dd ? ; pointer to previous socket in list |
.NextPtr dd ? ; pointer to next socket in list |
.Number dd ? ; socket number (unique within single process) |
25,19 → 23,34 |
.Domain dd ? ; INET/UNIX/.. |
.Type dd ? ; RAW/UDP/TCP/... |
.Protocol dd ? ; ICMP/IPv4/ARP/ |
.LocalIP dd ? ; local IP address |
.RemoteIP dd ? ; remote IP address |
.LocalPort dw ? ; local port (In INET byte order) |
.RemotePort dw ? ; remote port (IN INET byte order |
.lock dd ? ; lock mutex |
.end: |
ends |
struct IPv4_SOCKET |
.LocalIP dd ? |
.RemoteIP dd ? |
.SequenceNumber dd ? |
; todo: add options (for func 8 and 9) |
.end: |
ends |
struct TCP_SOCKET |
.LocalPort dw ? ; In INET byte order |
.RemotePort dw ? ; In INET byte order |
.backlog dw ? ; Backlog |
.OrigRemoteIP dd ? ; original remote IP address (used to reset to LISTEN state) |
.OrigRemotePort dw ? ; original remote port (used to reset to LISTEN state) |
.rxDataCount dd ? ; rx data count |
.TCBState dd ? ; TCB state |
.TCBTimer dd ? ; TCB timer (seconds) |
.ISS dd ? ; initial send sequence |
.IRS dd ? ; initial receive sequence |
.SND_UNA dd ? ; sequence number of unack'ed sent Packets |
.SND_NXT dd ? ; bext send sequence number to use |
.SND_NXT dd ? ; next send sequence number to use |
.SND_WND dd ? ; send window |
.RCV_NXT dd ? ; next receive sequence number to use |
.RCV_WND dd ? ; receive window |
44,17 → 57,40 |
.SEG_LEN dd ? ; segment length |
.SEG_WND dd ? ; segment window |
.wndsizeTimer dd ? ; window size timer |
.lock dd ? ; lock mutex |
.backlog dw ? ; Backlog |
.rxData: ; receive data buffer here |
.flags db ? ; packet flags |
.end: |
ends |
MAX_backlog equ 20 |
struct UDP_SOCKET |
; socket buffers |
SOCKETBUFFSIZE equ 4096 ; state + config + buffer. |
SOCKETHEADERSIZE equ SOCKET.rxData ; thus 4096 - SOCKETHEADERSIZE bytes data |
.LocalPort dw ? ; In INET byte order |
.RemotePort dw ? ; In INET byte order |
.end: |
ends |
struct ICMP_SOCKET |
.Identifier dw ? ; |
.end: |
ends |
struct IPC_SOCKET |
.ConnectedTo dd ? ; Socket number of other socket this one is connected to |
.end: |
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 |
uglobal |
net_sockets rd 2 |
last_UDP_port dw ? ; These values give the number of the last used ephemeral port |
110,6 → 146,10 |
jz socket_send ; 6 |
dec bl |
jz socket_recv ; 7 |
dec bl |
; jz socket_get_opt ; 8 |
dec bl |
; jz socket_set_opt ; 9 |
s_error: |
mov dword [esp+32],-1 |
139,9 → 179,9 |
or eax, eax |
jz s_error |
mov [eax + SOCKET.Domain], ecx |
mov [eax + SOCKET.Type], edx |
mov [eax + SOCKET.Protocol], esi |
mov [eax + SOCKET_head.Domain], ecx |
mov [eax + SOCKET_head.Type], edx |
mov [eax + SOCKET_head.Protocol], esi |
stdcall net_socket_addr_to_num, eax |
DEBUGF 1,", socketnumber: %u\n", eax |
153,6 → 193,10 |
;----------------------------------------------- |
; |
; SOCKET_bind |
176,14 → 220,28 |
jl s_error |
cmp word [edx], AF_INET4 |
jne s_error |
je .af_inet4 |
cmp word [edx], AF_UNIX |
je .af_unix |
jmp s_error |
.af_unix: |
; TODO: write code here |
mov dword [esp+32],0 |
ret |
.af_inet4: |
cmp esi, 6 |
jl s_error |
mov ecx, [eax + SOCKET.Type] |
mov ecx, [eax + SOCKET_head.Type] |
mov bx, word [edx + 2] |
DEBUGF 1,"local port: %x ",bx |
test bx, bx |
202,13 → 260,14 |
.got_port: |
DEBUGF 1,"using port: %x ",bx |
mov word [eax + SOCKET.LocalPort], bx |
mov word [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.LocalPort], bx |
mov ebx, dword [edx + 4] |
mov dword [eax + SOCKET.LocalIP], ebx |
mov dword [eax + SOCKET_head.end + IPv4_SOCKET.LocalIP], ebx |
DEBUGF 1,"local ip: %u.%u.%u.%u\n",\ |
[eax + SOCKET.LocalIP]:1,[eax + SOCKET.LocalIP + 1]:1,[eax + SOCKET.LocalIP + 2]:1,[eax + SOCKET.LocalIP + 3]:1 |
[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 |
mov dword [esp+32],0 |
ret |
249,13 → 308,13 |
cmp esi, 8 |
jl s_error |
cmp [eax + SOCKET.Type], IP_PROTO_UDP |
cmp [eax + SOCKET_head.Type], IP_PROTO_UDP |
je .udp |
cmp [eax + SOCKET.Type], IP_PROTO_ICMP |
cmp [eax + SOCKET_head.Type], IP_PROTO_ICMP |
je .icmp |
cmp [eax + SOCKET.Type], IP_PROTO_TCP |
cmp [eax + SOCKET_head.Type], IP_PROTO_TCP |
je .tcp |
jmp s_error |
263,11 → 322,11 |
.udp: |
mov bx , word [edx + 2] |
mov word [eax + SOCKET.RemotePort], bx |
mov word [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.RemotePort], bx |
DEBUGF 1,"remote port: %x ",bx |
mov ebx, dword [edx + 4] |
mov dword [eax + SOCKET.RemoteIP], ebx |
mov dword [eax + SOCKET_head.end + IPv4_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 |
275,6 → 334,7 |
.icmp: |
; TODO: write code here |
ret |
387,7 → 447,7 |
mov dx , 20 |
.ok: |
mov [eax + SOCKET.backlog], dx |
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog], dx |
; TODO: insert code for active connections like TCP |
420,7 → 480,21 |
jz s_error |
mov esi, eax |
cmp [esi + SOCKET.backlog], 0 |
cmp word [esi + SOCKET_head.Domain], AF_INET4 |
je .af_inet4 |
jmp s_error |
.af_inet4: |
cmp [esi + SOCKET_head.Type], IP_PROTO_TCP |
je .tcp |
jmp s_error |
.tcp: |
cmp [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog], 0 |
jz s_error |
call net_socket_alloc |
428,19 → 502,21 |
jz s_error |
mov edi, eax |
dec [esi + SOCKET.backlog] |
dec [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog] |
mov ecx, (SOCKET.rxData+3)/4 |
mov ecx, (SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.end+3)/4 |
push esi edi |
rep movsd |
pop edi esi |
mov [edi + SOCKET.backlog], 0 |
mov [edi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.backlog], 0 |
; TODO: fill in structure in ecx |
mov [esi + SOCKET.RemoteIP], 0 |
mov [esi + SOCKET.RemotePort], 0 |
mov [esi + SOCKET_head.end + IPv4_SOCKET.RemoteIP], 0 |
mov [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RemotePort], 0 |
stdcall net_socket_addr_to_num, eax |
stdcall net_socket_addr_to_num, edi |
mov [esp+32], eax |
ret |
465,14 → 541,16 |
or eax, eax |
jz s_error |
cmp [eax + SOCKET_head.Domain], AF_INET4 |
jne s_error |
cmp [eax + SOCKET.Type], IP_PROTO_UDP |
cmp [eax + SOCKET_head.Type], IP_PROTO_UDP |
je .udp |
cmp [eax + SOCKET.Type], IP_PROTO_ICMP |
cmp [eax + SOCKET_head.Type], IP_PROTO_ICMP |
je .icmp |
cmp [eax + SOCKET.Type], IP_PROTO_TCP |
cmp [eax + SOCKET_head.Type], IP_PROTO_TCP |
je .tcp |
jmp s_error |
479,12 → 557,7 |
.udp: |
lea ebx, [eax + SOCKET.lock] |
call wait_mutex |
; TODO: mark the socket for deletion, using the mutex |
stdcall net_socket_free, eax |
mov dword [esp+32],0 |
ret |
600,8 → 673,8 |
; |
; |
; IN: socket number in ecx |
; addr in edx |
; addrlen in esi |
; addr to buffer in edx |
; length of buffer in esi |
; flags in edi |
; OUT: eax is number of bytes copied, -1 on error |
; |
609,71 → 682,47 |
align 4 |
socket_recv: |
DEBUGF 1,"Socket_receive: socknum: %u sockaddr: %x, length: %u, flags: %x\n",ecx,edx,esi,edi |
DEBUGF 1,"Socket_receive: socknum: %u bufferaddress: %x, length: %u, flags: %x\n",ecx,edx,esi,edi |
stdcall net_socket_num_to_addr, ecx ; get real socket address |
or eax, eax |
jz s_error |
DEBUGF 1,"real socket address:%x\n", eax |
DEBUGF 1,"Socket pointer: %x\n", eax |
mov dword[esp+32], -1 |
get_from_queue (eax + 2048), SOCKET_QUEUE_SIZE, 4*3, s_error |
mov edi, edx |
mov ecx, [esi + socket_queue_entry.data_size] |
lea ebx, [eax + SOCKET.lock] |
call wait_mutex |
DEBUGF 1,"Got %u bytes of data\n", ecx |
mov ecx, [eax + SOCKET.rxDataCount] ; get count of bytes |
DEBUGF 1,"bytes in socket:%u\n", ecx |
test ecx, ecx ; if count of bytes is zero.. |
jz .exit ; exit function (eax will be zero) |
cmp ecx, edx |
jle .large_enough |
DEBUGF 1,"Buffer too small...\n" |
jmp s_error |
.large_enough: |
cmp ecx, esi ; if buffer size is larger then the bytes of data, copy all data |
jle .copy_all_bytes |
push [esi + socket_queue_entry.data_ptr] |
mov esi, [esi + socket_queue_entry.offset] |
add esi, [esp] |
DEBUGF 1,"Source buffer: %x, real addr: %x\n", [esp], esi |
sub ecx, esi ; store new count (data bytes in buffer - bytes we're about to copy) |
mov [eax + SOCKET.rxDataCount], ecx ; |
push ecx |
mov edx, esi |
mov dword[esp+32+4], ecx ; return number of bytes copied |
call .start_copy ; copy to the application |
shr ecx, 1 |
jnc .nb |
movsb |
.nb: shr ecx, 1 |
jnc .nw |
movsw |
.nw: rep movsd |
mov dword[esp+32], edx |
call kernel_free |
lea edi, [eax + SOCKET.rxData] ; Now shift the remaining bytes to start of buffer |
lea esi, [edi + edx] |
mov ecx, [esp] |
shr ecx, 2 ; divide eax by 4 |
rep movsd ; copy all full dwords |
pop ecx |
and ecx, 3 |
rep movsb ; copy remaining bytes |
.exit: |
mov [eax + SOCKET.lock], 0 |
ret |
.copy_all_bytes: |
mov dword[esp+32], ecx |
mov [eax + SOCKET.rxDataCount], 0 ; store new count (zero) |
push dword .exit ; this code results in same as commented out code |
.start_copy: |
DEBUGF 1,"copying %u bytes\n",ecx |
lea esi, [eax + SOCKET.rxData] |
push ecx |
shr ecx, 2 ; divide eax by 4 |
rep movsd |
pop ecx |
and ecx, 3 |
rep movsb ; copy the rest bytes |
ret ; exit, or go back to shift remaining bytes if any |
;----------------------------------------------- |
; |
; SOCKET_send |
695,87 → 744,100 |
or eax, eax |
jz s_error |
cmp word [eax + SOCKET.Domain], AF_INET4 |
cmp word [eax + SOCKET_head.Domain], AF_INET4 |
je .af_inet4 |
jmp s_error |
;--------- |
.af_inet4: |
DEBUGF 1,"Socket type:%u\n", [eax + SOCKET_head.Type]:4 |
DEBUGF 1,"Socket type:%u\n", [eax + SOCKET.Type]:4 |
cmp [eax + SOCKET_head.Type], IP_PROTO_TCP |
je .tcp |
cmp [eax + SOCKET.Type], IP_PROTO_UDP |
cmp [eax + SOCKET_head.Type], IP_PROTO_UDP |
je .udp |
cmp [eax + SOCKET.Type], IP_PROTO_ICMP |
je .icmp |
cmp [eax + SOCKET_head.Type], SOCK_RAW |
je .raw |
cmp [eax + SOCKET.Type], IP_PROTO_TCP |
je .tcp |
jmp s_error |
;-------- |
.udp: |
DEBUGF 1,"type: UDP, " |
cmp [eax + SOCKET.LocalPort],0 |
jne .port_ok |
cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.LocalPort],0 |
jne @f |
push esi |
mov ecx, [eax + SOCKET.Type] |
mov ecx, [eax + SOCKET_head.Type] |
call socket_find_port |
test bx, bx |
pop esi |
je s_error |
mov [eax + SOCKET.LocalPort], bx |
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.LocalPort], bx |
.port_ok: |
@@: |
mov ecx, esi |
mov esi, edx |
mov edx, dword [eax + SOCKET.LocalPort] ; load local port and remote port at once |
DEBUGF 1,"local port: %x, remote port: %x\n",[eax + SOCKET.LocalPort]:4, [eax + SOCKET.RemotePort]:4 |
mov ebx, [eax + SOCKET.LocalIP] |
mov eax, [eax + SOCKET.RemoteIP] |
call UDP_create_packet |
call UDP_socket_send |
mov [esp+32], eax |
ret |
.icmp: |
; note: for ICMP sockets the SOCKET.LocalPort is used as the 'Identifier' value for ICMP packets |
; the application must add the header to the data, the kernel will fill in 'identifier' and 'checksum' |
.tcp: |
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 |
;-------- |
.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 |
.tcp: |
.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_find_free_port (local port) |
812,15 → 874,15 |
mov esi, net_sockets |
.next_socket: |
mov esi, [esi + SOCKET.NextPtr] |
mov esi, [esi + SOCKET_head.NextPtr] |
or esi, esi |
jz .port_ok |
cmp [esi + SOCKET.Type], ecx |
cmp [esi + SOCKET_head.Type], ecx |
jne .next_socket |
rol bx, 8 |
cmp [esi + SOCKET.LocalPort], bx |
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 |
852,14 → 914,14 |
mov esi, net_sockets |
.next_socket: |
mov esi, [esi + SOCKET.NextPtr] |
mov esi, [esi + SOCKET_head.NextPtr] |
or esi, esi |
jz .port_ok |
cmp [esi + SOCKET.Type], ecx |
cmp [esi + SOCKET_head.Type], ecx |
jne .next_socket |
cmp [esi + SOCKET.LocalPort], bx |
cmp [esi + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.LocalPort], bx |
jne .next_socket |
xor ebx, ebx |
868,20 → 930,19 |
ret |
;----------------------------------------------- |
; |
; SOCKET_internal_receiver |
; |
; Checks if any socket wants the received data |
; If so, update the socket |
; Updates a socket with received data |
; |
; IN: eax = socket number |
; ecx = number of bytes |
; esi = pointer to beginning of data |
; dx = Remote port (in INET byte order) |
; edi = IP address of sender |
; Note: the mutex must already be set ! |
; |
; IN: eax = socket ptr |
; ecx = size |
; esi = pointer to buffer |
; edi = offset |
; |
; OUT: xxx |
; |
;----------------------------------------------- |
888,39 → 949,20 |
align 4 |
socket_internal_receiver: |
DEBUGF 1,"internal socket receiver\n" |
DEBUGF 1,"Internal socket receiver: buffer %x, offset: %x\n", esi, edi |
lea ebx, [eax + SOCKET.lock] |
call wait_mutex |
push edi ; offset |
push ecx ; size |
push esi ; data_ptr |
mov esi, esp |
add_to_queue (eax + 2048), SOCKET_QUEUE_SIZE, 3*4, .full |
DEBUGF 1,"Queued packet successfully\n" |
add esp, 4*3 |
mov [eax + SOCKET.RemotePort], dx ; update remote port number |
mov [eax + SOCKET.RemoteIP], edi |
mov [eax + SOCKET_head.lock], 0 |
mov edx, [eax + SOCKET.rxDataCount] ; get # of bytes already in buffer |
DEBUGF 1,"bytes already in socket: %u ", edx |
lea edi, [ecx + edx] ; check for buffer overflow |
cmp edi, SOCKETBUFFSIZE - SOCKETHEADERSIZE ; |
jg .dump ; |
lea edi, [eax + SOCKET.rxData + edx] |
add [eax + SOCKET.rxDataCount], ecx ; increment the count of bytes in buffer |
DEBUGF 1,"adding %u bytes\n", ecx |
; copy the data across |
push cx |
shr ecx, 2 |
rep movsd |
pop cx |
and cx, 3 |
rep movsb |
DEBUGF 1,"socket updated\n" |
mov [eax + SOCKET.lock], 0 |
; flag an event to the application |
mov edx, [eax + SOCKET.PID] ; get socket owner PID |
mov edx, [eax + SOCKET_head.PID] ; get socket owner PID |
mov ecx, 1 |
mov esi, TASK_DATA + TASKDATA.pid |
939,14 → 981,16 |
mov [check_idle_semaphore], 200 |
ret |
.dump: |
mov [eax + SOCKET.lock], 0 |
.full: |
DEBUGF 1,"Socket %x is full!\n",eax |
mov [eax + SOCKET_head.lock], 0 |
call kernel_free |
add esp, 8 |
ret |
; 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). |
963,6 → 1007,7 |
; zero-initialize allocated memory |
push eax |
mov edi, eax |
mov ecx, SOCKETBUFFSIZE / 4 |
; cld |
xor eax, eax |
969,21 → 1014,23 |
rep stosd |
pop eax |
init_queue (eax + 2048) |
; add socket to the list by changing pointers |
mov ebx, net_sockets |
push [ebx + SOCKET.NextPtr] |
mov [ebx + SOCKET.NextPtr], eax |
mov [eax + SOCKET.PrevPtr], ebx |
push [ebx + SOCKET_head.NextPtr] |
mov [ebx + SOCKET_head.NextPtr], eax |
mov [eax + SOCKET_head.PrevPtr], ebx |
pop ebx |
mov [eax + SOCKET.NextPtr], ebx |
mov [eax + SOCKET_head.NextPtr], ebx |
or ebx, ebx |
jz @f |
mov [ebx + SOCKET.PrevPtr], eax |
mov [ebx + SOCKET_head.PrevPtr], eax |
@@: ; set socket owner PID to the one of calling process |
mov ebx, [TASK_BASE] |
mov ebx, [ebx + TASKDATA.pid] |
mov [eax + SOCKET.PID], ebx |
mov [eax + SOCKET_head.PID], ebx |
; find first free socket number and use it |
;mov edx, ebx |
992,10 → 1039,10 |
.next_socket_number: |
inc ecx |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
mov ebx, [ebx + SOCKET_head.NextPtr] |
or ebx, ebx |
jz .last_socket_number |
cmp [ebx + SOCKET.Number], ecx |
cmp [ebx + SOCKET_head.Number], ecx |
jne .next_socket |
;cmp [ebx + SOCKET.PID], edx |
;jne .next_socket |
1003,7 → 1050,7 |
jmp .next_socket_number |
.last_socket_number: |
mov [eax + SOCKET.Number], ecx |
mov [eax + SOCKET_head.Number], ecx |
.exit: |
ret |
1025,7 → 1072,7 |
;mov ecx, [TASK_BASE] |
;mov ecx, [ecx + TASKDATA.pid] |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
mov ebx, [ebx + SOCKET_head.NextPtr] |
or ebx, ebx |
jz .error |
cmp ebx, eax |
1035,13 → 1082,16 |
; okay, we found the correct one |
; remove it from the list first, changing pointers |
mov ebx, [eax + SOCKET.NextPtr] |
mov eax, [eax + SOCKET.PrevPtr] |
mov [eax + SOCKET.NextPtr], ebx |
mov ebx, [eax + SOCKET_head.NextPtr] |
mov eax, [eax + SOCKET_head.PrevPtr] |
mov [eax + SOCKET_head.NextPtr], ebx |
or ebx, ebx |
jz @f |
mov [ebx + SOCKET.PrevPtr], eax |
mov [ebx + SOCKET_head.PrevPtr], eax |
lea ebx, [eax + SOCKET_head.lock] |
call wait_mutex |
@@: ; and finally free the memory structure used |
stdcall kernel_free, [sockAddr] |
ret |
1070,10 → 1120,10 |
;mov ecx, [TASK_BASE] |
;mov ecx, [ecx + TASKDATA.pid] |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
mov ebx, [ebx + SOCKET_head.NextPtr] |
or ebx, ebx |
jz .error |
cmp [ebx + SOCKET.Number], eax |
cmp [ebx + SOCKET_head.Number], eax |
jne .next_socket |
;cmp [ebx + SOCKET.PID], ecx |
;jne .next_socket |
1106,7 → 1156,7 |
;mov ecx, [TASK_BASE] |
;mov ecx, [ecx + TASKDATA.pid] |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
mov ebx, [ebx + SOCKET_head.NextPtr] |
or ebx, ebx |
jz .error |
cmp ebx, eax |
1115,7 → 1165,7 |
;jne .next_socket |
; okay, we found the correct one |
mov eax, [ebx + SOCKET.Number] |
mov eax, [ebx + SOCKET_head.Number] |
ret |
.error: |
/kernel/branches/net/network/stack.inc |
---|
33,7 → 33,7 |
ETHER_ARP equ 0x0608 |
;AF_UNSPEC equ 0 |
;AF_UNIX equ 1 |
AF_UNIX equ 1 |
AF_INET4 equ 2 |
;AF_AX25 equ 3 |
;AF_IPX equ 4 |
64,7 → 64,7 |
include "IPv4.inc" |
include "ethernet.inc" |
include "socket.inc" |
;include "tcp.inc" |
include "tcp.inc" |
include "udp.inc" |
include "icmp.inc" |
86,6 → 86,7 |
call IPv4_init |
call ARP_init |
call UDP_init |
call TCP_init |
call ICMP_init |
call socket_init |
115,20 → 116,20 |
cmp [ETH_RUNNING], 0 |
je .exit |
call ETH_handler ; handle all queued ethernet packets |
call ETH_send_queued |
; Test for 10ms tick, call tcp timer |
; Test for 10ms tick |
mov eax, [timer_ticks] |
cmp eax, [last_1hsTick] |
je .exit |
mov [last_1hsTick], eax |
; call tcp_tx_handler |
call ETH_handler ; handle all queued ethernet packets |
call ETH_send_queued |
call TCP_send_queued |
.sec_tick: |
; Test for 1 second event, call 1s timer functions |
; Test for 1 second event |
mov al, 0x0 ;second |
out 0x70, al |
in al, 0x71 |
139,7 → 140,7 |
call ARP_decrease_entry_ttls |
call IPv4_decrease_fragment_ttls |
; call tcp_tcb_handler |
call TCP_decrease_socket_ttls |
.exit: |
ret |
146,41 → 147,77 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; Checksum [by Johnny_B] |
;; IN: |
;; buf_ptr=POINTER to buffer |
;; buf_size=SIZE of buffer |
;; OUT: |
;; AX=16-bit checksum |
;; Saves all used registers |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
proc checksum_jb stdcall uses ebx esi ecx,\ |
buf_ptr:DWORD, buf_size:DWORD |
;----------------------------------------------------------------- |
; |
; checksum_1 |
; |
; This is the first of two functions needed to calculate the TCP checksum. |
; |
; IN: edx = start offeset for semi-checksum |
; esi = pointer to data |
; ecx = data size |
; OUT: edx = semi-checksum |
; |
;----------------------------------------------------------------- |
align 4 |
checksum_1: |
xor eax, eax |
xor ebx, ebx ;accumulator |
mov esi, dword[buf_ptr] |
mov ecx, dword[buf_size] |
shr ecx, 1 ; ecx=ecx/2 |
jnc @f ; if CF==0 then size is even number |
mov bh, byte[esi + ecx*2] |
@@: |
cld |
shr ecx, 1 |
pushf |
.loop: |
lodsw ;eax=word[esi],esi=esi+2 |
xchg ah,al ;cause must be a net byte-order |
add ebx, eax |
lodsw |
xchg al, ah |
add edx, eax |
loop .loop |
mov eax, ebx |
popf |
jnc .end |
lodsb |
shl ax, 8 |
add edx, eax |
.end: |
ret |
;----------------------------------------------------------------- |
; |
; checksum_2 |
; |
; This function calculates the final ip/tcp/udp checksum for you |
; |
; IN: edx = semi-checksum |
; OUT: dx = checksum (in INET byte order) |
; |
;----------------------------------------------------------------- |
align 4 |
checksum_2: |
mov ecx, edx |
shr ecx, 16 |
and edx, 0xffff |
add edx, ecx |
mov eax, edx |
shr eax, 16 |
add ax, bx |
not ax |
add edx, eax |
not dx |
jnz .not_zero |
dec dx |
.not_zero: |
xchg dl, dh |
DEBUGF 1,"Checksum: %x\n",dx |
ret |
endp |
250,7 → 287,15 |
jmp .return |
@@: |
dec bl ; 4 = Get driver pointer |
jnz @f |
; ..; |
@@: |
; ... ; 5 Get driver name |
.doesnt_exist: |
DEBUGF 1,"sys_network: invalid device/function specified!\n" |
mov eax, -1 |
/kernel/branches/net/network/tcp.inc |
---|
18,7 → 18,6 |
$Revision$ |
; TCP TCB states |
TCB_LISTEN equ 1 |
TCB_SYN_SENT equ 2 |
TCB_SYN_RECEIVED equ 3 |
31,20 → 30,21 |
TCB_TIMED_WAIT equ 10 |
TCB_CLOSED equ 11 |
TH_FIN equ 0x01 |
TH_SYN equ 0x02 |
TH_RST equ 0x04 |
TH_PUSH equ 0x08 |
TH_ACK equ 0x10 |
TH_URG equ 0x20 |
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 |
TWOMSL equ 10 ; # of secs to wait before closing socket |
TCP_RETRIES equ 5 ; Number of times to resend a Packet |
TCP_TIMEOUT equ 10 ; resend if not replied to in x hs |
TCP_TIMEOUT equ 10 ; resend if not replied to in 1/100 s |
TCP_QUEUE_SIZE equ 16 |
struct TCP_Packet |
.SourcePort dw ? |
.DestinationPort dw ? |
65,14 → 65,55 |
TCP_PACKETS_TX rd MAX_IP |
TCP_PACKETS_RX rd MAX_IP |
TCP_IN_QUEUE rd 3*TCP_QUEUE_SIZE+3 |
TCP_OUT_QUEUE rd 3*TCP_QUEUE_SIZE+3 |
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 |
endg |
align 4 |
iglobal |
TCBStateHandler: |
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 |
macro inc_INET reg { |
inc byte [reg + 0] |
adc byte [reg + 1], 0 |
adc byte [reg + 2], 0 |
adc byte [reg + 3], 0 |
} |
macro add_INET reg { |
rol ecx, 16 |
adc byte [reg + 0], ch |
adc byte [reg + 1], cl |
rol ecx, 16 |
adc byte [reg + 2], ch |
adc byte [reg + 3], cl |
} |
;----------------------------------------------------------------- |
; |
; TCP_init |
92,58 → 133,55 |
mov ecx, 2*MAX_IP |
rep stosd |
mov dword [TCP_IN_QUEUE], TCP_QUEUE_SIZE |
mov dword [TCP_IN_QUEUE+4], TCP_IN_QUEUE + queue.data |
mov dword [TCP_IN_QUEUE+8], TCP_IN_QUEUE + queue.data |
init_queue TCP_IN_QUEUE |
init_queue TCP_OUT_QUEUE |
mov dword [TCP_OUT_QUEUE], TCP_QUEUE_SIZE |
mov dword [TCP_OUT_QUEUE+4], TCP_OUT_QUEUE + queue.data |
mov dword [TCP_OUT_QUEUE+8], TCP_OUT_QUEUE + queue.data |
ret |
;----------------------------------------------------------------- |
; |
; tcp_tcb_handler |
; TCP_decrease_socket_ttls |
; |
; Handles sockets in the timewait state, closing them |
; when the TCB timer expires |
; IN: / |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
tcp_tcb_handler: |
TCP_decrease_socket_ttls: |
; scan through all the sockets, decrementing active timers |
mov ebx, net_sockets |
cmp [ebx + SOCKET.NextPtr], 0 |
cmp [ebx + SOCKET_head.NextPtr], 0 |
je .exit |
DEBUGF 1, "K : sockets:\n" |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
mov ebx, [ebx + SOCKET_head.NextPtr] |
or ebx, ebx |
jz .exit |
cmp [ebx + SOCKET_head.Type], IP_PROTO_TCP |
jne .next_socket |
; 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.TCBState] |
cmp [ebx + SOCKET.TCBTimer], 0 |
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBTimer], 0 |
jne .decrement_tcb |
cmp [ebx + SOCKET.wndsizeTimer], 0 |
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer], 0 |
jne .decrement_wnd |
jmp .next_socket |
.decrement_tcb: |
; decrement it, delete socket if TCB timer = 0 & socket in timewait state |
dec [ebx + SOCKET.TCBTimer] |
dec [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBTimer] |
jnz .next_socket |
cmp [ebx + SOCKET.TCBState], TCB_TIMED_WAIT |
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_TIMED_WAIT |
jne .next_socket |
push [ebx + SOCKET.PrevPtr] |
push [ebx + SOCKET_head.PrevPtr] |
stdcall net_socket_free, ebx |
pop ebx |
jmp .next_socket |
150,7 → 188,7 |
.decrement_wnd: |
; TODO - prove it works! |
dec [ebx + SOCKET.wndsizeTimer] |
dec [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer] |
jmp .next_socket |
.exit: |
157,120 → 195,124 |
ret |
;*************************************************************************** |
; Function |
; tcp_tx_handler |
;----------------------------------------------------------------- |
; |
; Description |
; Handles queued TCP data |
; This is a kernel function, called by stack_handler |
; TCP_send_queued: |
; |
;*************************************************************************** |
; 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_tx_handler: |
; decrement all resend buffers timers. If they |
; expire, queue them for sending, and restart the timer. |
; If the retries counter reach 0, delete the entry |
TCP_send_queued: |
mov esi, resendQ |
mov ecx, 0 |
cmp [TCP_OUT_QUEUE], 0 |
je .exit |
.next_resendq: |
; cmp ecx, NUMRESENDENTRIES |
je .exit ; None left |
cmp dword[esi + 4], 0 |
jne @f ; found one |
inc ecx |
add esi, 8 |
jmp .next_resendq |
mov eax, TCP_QUEUE_SIZE |
mov ecx, [TCP_OUT_QUEUE] |
mov esi, TCP_OUT_QUEUE+4 |
@@: ; we have one. decrement it's timer by 1 |
dec word[esi + 2] |
jz @f |
inc ecx |
add esi, 8 |
jmp .next_resendq ; Timer not zero, so move on |
.loop: |
cmp [esi + tcp_out_queue_entry.data_ptr], 0 |
jnz .found_one |
add esi, tcp_out_queue_entry.size |
loop .loop |
.exit: |
ret |
@@: |
xor ebx, ebx |
; restart timer, and decrement retries |
; After the first resend, back of on next, by a factor of 5 |
mov [esi + 2], word TCP_TIMEOUT * 5 |
dec byte[esi + 1] |
jnz @f |
.found_one: |
dec [esi + tcp_out_queue_entry.ttl] |
jz .send_it |
.find_next: |
dec eax |
jz .exit |
jmp .loop |
; retries now 0, so delete from queue |
xchg [esi + 4], ebx |
.send_it: |
push eax ecx esi |
@@: ; resend Packet |
pushad |
push [esi + tcp_out_queue_entry.data_size] |
push [esi + tcp_out_queue_entry.data_ptr] |
mov ebx, [esi + tcp_out_queue_entry.owner] |
; mov eax, EMPTY_QUEUE |
; call dequeue |
; cmp ax, NO_BUFFER |
jne .tth004z |
call [esi + tcp_out_queue_entry.sendproc] |
; TODO - try again in 10ms. |
test ebx, ebx |
jnz @f |
mov [esi + 4], ebx |
pop esi ecx eax |
@@: ; Mark it to expire in 10ms - 1 tick |
mov byte[esi + 1], 1 |
mov word[esi + 2], 1 |
jmp .tth005 |
dec [esi + tcp_out_queue_entry.retries] |
jz .remove_it |
mov [esi + tcp_out_queue_entry.ttl], TCP_TIMEOUT |
jmp .find_next |
.tth004z: |
; we have a buffer # in ax |
; push eax ecx |
; mov ecx, IPBUFFSIZE |
; mul ecx |
; add eax, IPbuffs |
.remove_it: |
push [esi + tcp_out_queue_entry.data_ptr] |
mov [esi + tcp_out_queue_entry.data_ptr], 0 |
dec [TCP_OUT_QUEUE] |
call kernel_free |
jmp .find_next |
; we have the buffer address in eax |
mov edi, eax |
pop ecx |
; Now get buffer location, and copy buffer across. argh! more copying,, |
; mov esi, resendBuffer |
; @@: add esi, IPBUFFSIZE |
loop @b |
; we have resend buffer location in esi |
; mov ecx, IPBUFFSIZE |
; copy data across |
push edi |
cld |
rep movsb |
pop edi |
;----------------------------------------------------------------- |
; |
; TCP_add_to_queue: |
; |
; Queue a TCP packet for sending |
; |
; IN: [esp] pointer to buffer |
; [esp + 4] size of buffer |
; ebx = driver struct |
; esi = sender proc |
; edx = acknum |
; OUT: / |
; |
;----------------------------------------------------------------- |
; queue Packet |
; mov eax, NET1OUT_QUEUE |
; mov edx, [IP_LIST] |
; cmp edx, [edi + IP_Packet.DestinationAddress] |
; jne .not_local |
; mov eax, IPIN_QUEUE |
align 4 |
TCP_add_to_queue: |
.not_local: |
pop ebx |
; call queue |
cmp [TCP_OUT_QUEUE], TCP_QUEUE_SIZE |
jge .full |
.tth005: |
popad |
mov ecx, TCP_QUEUE_SIZE |
mov eax, TCP_OUT_QUEUE+4 |
inc ecx |
add esi, 8 |
jmp .next_resendq |
.loop: |
cmp [eax + tcp_out_queue_entry.data_ptr], 0 |
je .found_it |
add eax, tcp_out_queue_entry.size |
loop .loop |
.exit: |
.full: ; silently discard the packet |
call kernel_free |
add esp, 4 |
ret |
.found_it: ; eax point to empty queue entry |
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.retries], TCP_RETRIES |
mov [eax + tcp_out_queue_entry.owner], ebx |
mov [eax + tcp_out_queue_entry.sendproc], esi |
mov [eax + tcp_out_queue_entry.ack_num], edx |
ret |
;----------------------------------------------------------------- |
; |
; TCP_Handler: |
; TCP_handler: |
; |
; Called by IPv4_handler, |
; this procedure will inject the tcp data diagrams in the application sockets. |
285,136 → 327,104 |
; |
;----------------------------------------------------------------- |
TCP_Handler : |
align 4 |
TCP_handler : |
DEBUGF 1,"TCP_Handler\n" |
jmp .exit ;;;; |
; Look for a socket where |
; IP Packet TCP Destination Port = local Port |
; IP Packet SA = Remote IP |
; IP Packet TCP Source Port = remote Port |
; IP Packet SA = Remote IP OR = 0 |
; IP Packet TCP Source Port = remote Port OR = 0 |
mov ebx, net_sockets |
.next_socket.1: |
mov ebx, [ebx + SOCKET.NextPtr] |
.socket_loop: |
mov ebx, [ebx + SOCKET_head.NextPtr] |
or ebx, ebx |
jz .next_socket.1.exit |
jz .dump |
; DEBUGF 1, "K : tcp_rx - 1.dport: %x - %x\n", [edx + 20 + TCP_Packet.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4 |
mov ax, [edx + TCP_Packet.DestinationPort] |
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort], ax |
jne .socket_loop |
mov ax, [edx + TCP_Packet.DestinationPort] ; get the dest. port from the TCP hdr |
cmp [ebx + SOCKET.LocalPort], ax ; get the dest. port from the TCP hdr |
jne .next_socket.1 ; different - try next socket |
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.RemoteIP] |
cmp eax, esi |
je @f |
test eax, eax |
jne .socket_loop |
@@: |
; DEBUGF 1, "K : tcp_rx - 1.addr: %x - %x\n", [edx + IP_Packet.SourceAddress], [ebx + SOCKET.RemoteIP] |
mov ax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RemotePort] |
cmp [edx + TCP_Packet.SourcePort] , ax |
je .change_state |
test ax, ax |
jne .socket_loop |
mov eax, esi ;[edx + IP_Packet.SourceAddress] ; get the source IP Addr from the IP hdr |
cmp [ebx + SOCKET.RemoteIP], eax ; compare with socket's remote IP |
jne .next_socket.1 ; different - try next socket |
.change_state: |
; DEBUGF 1, "K : tcp_rx - 1.sport: %x - %x\n", [edx + 20 + TCP_Packet.SourcePort]:4, [ebx + SOCKET.RemotePort]:4 |
push ebx |
lea ebx, [ebx + SOCKET_head.lock] |
call wait_mutex |
pop ebx |
mov ax, [edx + TCP_Packet.SourcePort] ; get the source port from the TCP hdr |
cmp [ebx + SOCKET.RemotePort], ax ; compare with socket's remote port |
jne .next_socket.1 ; different - try next socket |
;---------------------------------- |
; ebx is pointer to socket |
; ecx is size of tcp packet |
; edx is pointer to tcp packet |
; We have a complete match - use this socket |
jmp .change_state |
; as a Packet has been received, update the TCB timer |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBTimer], TWOMSL |
.next_socket.1.exit: |
; 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 .call_handler ; No ACK, so no data yet |
; If we got here, there was no match |
; Look for a socket where |
; IP Packet TCP Destination Port = local Port |
; IP Packet SA = Remote IP |
; socket remote Port = 0 |
mov eax, [edx + TCP_Packet.SequenceNumber] ; Calculate sequencenumber in eax |
bswap eax ; |
add eax, ecx ; |
mov ebx, net_sockets |
cmp [TCP_OUT_QUEUE], 0 |
je .call_handler |
.next_socket.2: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .next_socket.2.exit |
push ecx |
mov ecx, TCP_QUEUE_SIZE |
mov esi, TCP_OUT_QUEUE+4 |
; DEBUGF 1, "K : tcp_rx - 2.dport: %x - %x\n", [edx + 20 + TCP_Packet.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4 |
.loop: |
cmp [esi + tcp_out_queue_entry.data_ptr], 0 |
jne .maybe_next |
cmp [esi + tcp_out_queue_entry.ack_num], eax |
jg .maybe_next |
mov ax, [edx + TCP_Packet.DestinationPort] ; get the dest. port from the TCP hdr |
cmp [ebx + SOCKET.LocalPort], ax ; compare with socket's local port |
jne .next_socket.2 ; different - try next socket |
push [esi + tcp_out_queue_entry.data_ptr] |
mov [esi + tcp_out_queue_entry.data_ptr], 0 |
dec [TCP_OUT_QUEUE] |
call kernel_free |
; DEBUGF 1, "K : tcp_rx - 2.addr: %x - %x\n", [edx + IP_Packet.SourceAddress], [ebx + SOCKET.RemoteIP] |
.maybe_next: |
add esi, tcp_out_queue_entry.size |
loop .loop |
pop ecx |
; mov eax, esi ;[edx + IP_Packet.SourceAddress] ; get the source IP Addr from the IP hdr |
cmp [ebx + SOCKET.RemoteIP], esi ; compare with socket's remote IP |
jne .next_socket.2 ; different - try next socket |
.call_handler: |
; Call handler for given TCB state |
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState] |
cmp eax, TCB_LISTEN |
jb .exit |
cmp eax, TCB_CLOSED |
ja .exit |
; DEBUGF 1, "K : tcp_rx - 2.sport: 0000 - %x\n", [ebx + SOCKET.RemotePort]:4 |
shl eax, 2 |
add eax, TCBStateHandler - 4 |
cmp [ebx + SOCKET.RemotePort], 0 ; only match a remote socket of 0 |
jne .next_socket.2 ; different - try next socket |
push .exit |
jmp eax |
; We have a complete match - use this socket |
jmp .change_state |
.next_socket.2.exit: |
; If we got here, there was no match |
; Look for a socket where |
; IP Packet TCP Destination Port = local Port |
; socket Remote IP = 0 |
; socket remote Port = 0 |
mov ebx, net_sockets |
.next_socket.3: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
jz .next_socket.3.exit |
; DEBUGF 1, "K : tcp_rx - 3.dport: %x - %x\n", [edx + 20 + TCP_Packet.DestinationPort]:4, [ebx + SOCKET.LocalPort]:4 |
mov ax, [edx + TCP_Packet.DestinationPort] ; get destination port from the TCP hdr |
cmp [ebx + SOCKET.LocalPort], ax ; compare with socket's local port |
jne .next_socket.3 ; different - try next socket |
; DEBUGF 1, "K : tcp_rx - 3.addr: 00000000 - %x\n", [ebx + SOCKET.RemoteIP] |
cmp [ebx + SOCKET.RemoteIP], 0 ; only match a socket remote IP of 0 |
jne .next_socket.3 ; different - try next socket |
; DEBUGF 1, "K : tcp_rx - 3.sport: 0000 - %x\n", [ebx + SOCKET.RemotePort]:4 |
cmp [ebx + SOCKET.RemotePort], 0 ; only match a remote socket of 0 |
jne .next_socket.3 ; different - try next socket |
; We have a complete match - use this socket |
jmp .change_state |
.next_socket.3.exit: |
; If we got here, we need to reject the Packet |
DEBUGF 1, "K : tcp_rx - dumped\n" |
; DEBUGF 1, "K : --------: %x-%x-%x (flags: %x)\n", [edx + 20 + TCP_Packet.DestinationPort]:4, [edx + IP_Packet.SourceAddress], [edx + 20 + TCP_Packet.SourcePort]:4, [edx + 20 + TCP_Packet.Flags]:2 |
; inc [dumped_rx_count] |
jmp .exit |
.change_state: |
; We have a valid socket/TCB, so call the TCB State Machine for that skt. |
; socket is pointed to by ebx |
; IP Packet is pointed to by edx |
; IP buffer number is on stack ( it will be popped at the end) |
stdcall tcpStateMachine, ebx |
.exit: |
mov [ebx + SOCKET_head.lock], 0 |
.dump: |
DEBUGF 1,"Dumping TCP packet\n" |
call kernel_free |
add esp, 4 ; pop (balance stack) |
424,370 → 434,223 |
;----------------------------------------------------------------- |
; |
; IN: eax = dest ip |
; ebx = source ip |
; ecx = data length |
; edx = remote port shl 16 + local port |
; esi = data offset |
; TCP_socket_send |
; |
; IN: eax = socket pointer |
; ecx = number of bytes to send |
; esi = pointer to data |
; |
;----------------------------------------------------------------- |
TCP_create_Packet: |
align 4 |
TCP_socket_send: |
DEBUGF 1,"Create TCP Packet\n" |
;*************************************************************************** |
; Function |
; buildTCPPacket |
; |
; Description |
; builds an IP Packet with TCP data fully populated for transmission |
; You may destroy any and all registers |
; TCP control flags specified in bl |
; This TCB is in [sktAddr] |
; User data pointed to by esi |
; Data length in ecx |
; Transmit buffer number in eax |
; |
;*************************************************************************** |
DEBUGF 1,"Creating TCP Packet\n" |
push ecx ; Save data length |
mov di , IP_PROTO_TCP |
add ecx, UDP_Packet.Data |
mov di , IP_PROTO_UDP |
; Create an IPv4 Packet of the correct size |
push eax |
mov ebx, [eax + SOCKET_head.end + IPv4_SOCKET.LocalIP] |
mov eax, [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP] |
; dx = fragment id |
call IPv4_create_Packet ; TODO: figure out a way to choose between IPv4 and IPv6 |
cmp edi, -1 |
je .exit |
mov [edi + TCP_Packet.Flags], bl ; TCP flags |
; mov ebx, [sockAddr];---------------------------------------------------------- eof |
; So, ebx holds the socket ptr, edx holds the IPbuffer ptr |
; Fill in the IP header ( some data is in the socket descriptor) |
mov eax, [ebx + SOCKET.LocalIP] |
; mov [edx + IP_Packet.SourceAddress], eax |
mov eax, [ebx + SOCKET.RemoteIP] |
; mov [edx + IP_Packet.DestinationAddress], eax |
; mov [edx + IP_Packet.VersionAndIHL], 0x45 |
; mov [edx + IP_Packet.TypeOfService], 0 |
pop eax ; Get the TCP data length |
; meanwhile, create the pseudoheader in stack, |
; (now that we still have all the variables that are needed.) |
push cx |
push di |
push eax |
push ebx |
add eax, 20 + 20 ; add IP header and TCP header lengths |
rol ax, 8 |
; mov [edx + IP_Packet.TotalLength], ax |
; mov [edx + IP_Packet.Identification], 0 |
; mov [edx + IP_Packet.FlagsAndFragmentOffset], 0x0040 |
; mov [edx + IP_Packet.TimeToLive], 0x20 |
; mov [edx + IP_Packet.Protocol], PROTOCOL_TCP |
; Checksum left unfilled |
; mov [edx + IP_Packet.HeaderChecksum], 0 |
push ecx esi eax ; save some variables for later |
add ecx, TCP_Packet.Data |
call IPv4_create_packet |
cmp edi, -1 |
je .fail |
; Fill in the TCP header (some data is in the socket descriptor) |
mov ax, [ebx + SOCKET.LocalPort] |
mov [edx + 20 + TCP_Packet.SourcePort], ax ; Local Port |
pop esi |
mov ax, [ebx + SOCKET.RemotePort] |
mov [edx + 20 + TCP_Packet.DestinationPort], ax ; desitination Port |
; Now add the TCP header to the IPv4 packet |
; Checksum left unfilled |
mov [edx + 20 + TCP_Packet.Checksum], 0 |
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT] |
pop [edi + TCP_Packet.SequenceNumber] |
; sequence number |
mov eax, [ebx + SOCKET.SND_NXT] |
mov [edx + 20 + TCP_Packet.SequenceNumber], eax |
push dword [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort] |
pop dword [edi + TCP_Packet.SourcePort] |
; ack number |
mov eax, [ebx + SOCKET.RCV_NXT] |
mov [edx + 20 + TCP_Packet.AckNumber], eax |
; window ( 0x2000 is default ).I could accept 4KB, fa0, ( skt buffer size) |
; 768 bytes seems better |
mov [edx + 20 + TCP_Packet.Window], 0x0003 |
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT] |
pop [edi + TCP_Packet.AckNumber] |
; Urgent pointer (0) |
mov [edx + 20 + TCP_Packet.UrgentPointer], 0 |
mov al, [eax + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.flags] |
mov [edi + TCP_Packet.Flags], al |
; data offset ( 0x50 ) |
mov [edx + 20 + TCP_Packet.DataOffset], 0x50 |
mov [edi + TCP_Packet.Window], 0x0005 ; 1280 bytes ;;; TODO: read RFC ! |
mov [edi + TCP_Packet.UrgentPointer], 0 |
mov [edi + TCP_Packet.DataOffset], 0x50 |
mov [edi + TCP_Packet.Checksum], 0 |
pop ecx ; count of bytes to send |
mov ebx, ecx ; need the length later |
; Copy the data |
mov esi, [esp] |
mov ecx, [esp+4] |
add edi, TCP_Packet.Data |
cmp ebx, 0 |
jz @f |
shr ecx, 1 |
jnc .nb |
movsb |
.nb: shr ecx, 1 |
jnc .nw |
movsw |
.nw: rep movsd |
mov edi, edx |
add edi, 40 |
cld |
rep movsb ; copy the data across |
; Now, calculate the checksum for pseudoheader |
xor edx, edx |
mov ecx, 12 |
mov esi, esp |
call checksum_1 |
add esp, 12 ; remove the pseudoheader from stack |
; And that of the data |
pop esi |
pop ecx |
call checksum_1 |
; Now create the final checksum and store it in TCP header |
call checksum_2 |
mov [edi + TCP_Packet.Checksum], dx |
@@: ; we have edx as IPbuffer ptr. |
; Fill in the TCP checksum |
; First, fill in pseudoheader |
; mov eax, [edx + IP_Packet.SourceAddress] |
; mov [pseudoHeader], eax |
; mov eax, [edx + IP_Packet.DestinationAddress] |
; mov [pseudoHeader + 4], eax |
; mov word[pseudoHeader + 8], PROTOCOL_TCP shl 8 + 0 |
; add ebx, 20 |
; mov [pseudoHeader + 10], bh |
; mov [pseudoHeader + 11], bl |
; |
; mov eax, pseudoHeader |
; mov [checkAdd1], eax |
; mov word[checkSize1], 12 |
; mov eax, edx |
; add eax, 20 |
; mov [checkAdd2], eax |
; mov eax, ebx |
; mov [checkSize2], ax |
; |
; call checksum |
; And now, send it! |
DEBUGF 1,"Sending TCP Packet to device %x\n", ebx |
mov esi, ETH_sender |
mov edx, [edi + TCP_Packet.AckNumber] |
jmp TCP_add_to_queue |
; store it in the TCP checksum ( in the correct order! ) |
; mov ax, [checkResult] |
; rol ax, 8 |
; mov [edx + 20 + TCP_Packet.Checksum], ax |
; Fill in the IP header checksum |
; movzx eax, byte [edx + IP_Packet.VersionAndIHL] ; Calculate Header length by using IHL field |
; and eax, 0x0000000F ; |
; shl eax, 2 ; |
; |
stdcall checksum_jb, edx, eax ; buf_ptr, buf_size |
rol ax, 8 |
; mov [edx + IP_Packet.HeaderChecksum], ax |
.exit: |
call kernel_free |
add esp, 4 ; pop (balance stack) |
.fail: |
add esp, 12+4 |
ret |
;endp |
; Increments the 32 bit value pointed to by esi in internet order |
proc inc_inet_esi stdcall |
; push eax |
; mov eax, [esi] |
; bswap eax |
; inc eax |
; bswap eax |
; mov [esi], eax |
; pop eax |
; ret |
inc byte[esi+0] |
adc byte[esi+1],0 |
adc byte[esi+2],0 |
adc byte[esi+3],0 |
endp |
; Increments the 32 bit value pointed to by esi in internet order |
; by the value in ecx |
proc add_inet_esi stdcall |
push eax |
mov eax, [esi] |
bswap eax |
add eax, ecx |
bswap eax |
mov [esi], eax |
pop eax |
ret |
endp |
iglobal |
TCBStateHandler dd \ |
stateTCB_LISTEN, \ |
stateTCB_SYN_SENT, \ |
stateTCB_SYN_RECEIVED, \ |
stateTCB_ESTABLISHED, \ |
stateTCB_FIN_WAIT_1, \ |
stateTCB_FIN_WAIT_2, \ |
stateTCB_CLOSE_WAIT, \ |
stateTCB_CLOSING, \ |
stateTCB_LAST_ACK, \ |
stateTCB_TIME_WAIT, \ |
stateTCB_CLOSED |
endg |
;*************************************************************************** |
; Function |
; tcpStateMachine |
;----------------------------------------------------------------- |
; |
; Description |
; TCP state machine |
; This is a kernel function, called by tcp_rx |
; TCP_send_ack |
; |
; IP buffer address given in edx |
; Socket/TCB address in ebx |
; IN: eax = socket pointer |
; bl = flags |
; |
; The IP buffer will be released by the caller |
;*************************************************************************** |
;----------------------------------------------------------------- |
proc tcpStateMachine stdcall, sockAddr:DWORD |
; as a Packet has been received, update the TCB timer |
mov [ebx + SOCKET.TCBTimer], TWOMSL |
align 4 |
TCP_send_ack: |
; If the received Packet has an ACK bit set, |
; remove any Packets in the resend queue that this |
; received Packet acknowledges |
pushad |
test [edx + 20 + TCP_Packet.Flags], TH_ACK |
jz .call_handler ; No ACK, so no data yet |
DEBUGF 1,"Creating TCP ACK\n" |
; get skt number in eax |
stdcall net_socket_addr_to_num, ebx |
mov di , IP_PROTO_TCP |
mov cx , TCP_Packet.Data |
; The ack number is in [edx + 28], inet format |
; skt in eax |
push bx eax |
mov esi, resendQ |
xor ecx, ecx |
; 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] |
.next_resendq: |
; cmp ecx, NUMRESENDENTRIES |
je .call_handler ; None left |
cmp [esi + 4], eax |
je @f ; found one |
inc ecx |
add esi, 8 |
jmp .next_resendq |
call IPv4_create_packet |
cmp edi, -1 |
je .fail |
@@: ; Can we delete this buffer? |
; Fill in the TCP header |
pop esi |
; If yes, goto @@. No, goto .next_resendq |
; Get Packet data address |
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT] |
pop [edi + TCP_Packet.SequenceNumber] |
push ecx |
; Now get buffer location, and copy buffer across. argh! more copying,, |
; imul edi, ecx, IPBUFFSIZE |
; add edi, resendBuffer |
push dword [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.LocalPort] |
pop dword [edi + TCP_Packet.SourcePort] |
; we have dest buffer location in edi. incoming Packet in edx. |
; Get this Packets sequence number |
; preserve al, ecx, esi, edx |
mov ecx, [edi + 20 + TCP_Packet.SequenceNumber] |
bswap ecx |
movzx ebx, word[edi + 2] |
xchg bl, bh |
sub ebx, 40 |
add ecx, ebx ; ecx is now seq# of last byte +1, intel format |
push [esi + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT] |
pop [edi + TCP_Packet.AckNumber] |
; get recievd ack #, in intel format |
mov ebx, [edx + 20 + TCP_Packet.AckNumber] |
bswap ebx |
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 |
cmp ebx, ecx ; Finally. ecx = rx'ed ack. ebx = last byte in que |
; DANGER! need to handle case that we have just |
; passed the 2**32, and wrapped round! |
pop ecx |
jae @f ; if rx > old, delete old |
push eax edx |
inc ecx |
add esi, 8 |
jmp .next_resendq |
push word TCP_Packet.Data shl 8 |
push IP_PROTO_TCP |
push [esi + SOCKET_head.end + SOCKET_head.end + IPv4_SOCKET.RemoteIP] |
push [esi + SOCKET_head.end + SOCKET_head.end + IPv4_SOCKET.LocalIP] |
@@: mov dword[esi + 4], 0 |
inc ecx |
add esi, 8 |
jmp .next_resendq |
; Now, calculate the checksum for pseudoheader |
xor edx, edx |
mov ecx, 12 |
mov esi, esp |
call checksum_1 |
add esp, 12 ; remove the pseudoheader from stack |
; Now create the final checksum and store it in TCP header |
call checksum_2 |
mov [edi + TCP_Packet.Checksum], dx |
.call_handler: |
popad |
; And now, send it! |
DEBUGF 1,"Sending TCP Packet to device %x\n", ebx |
mov esi, ETH_sender |
mov edx, [edi + TCP_Packet.AckNumber] |
jmp TCP_add_to_queue |
; Call handler for given TCB state |
.fail: |
add esp, 12+4 |
ret |
mov eax, [ebx + SOCKET.TCBState] |
cmp eax, TCB_LISTEN |
jb .exit |
cmp eax, TCB_CLOSED |
ja .exit |
stdcall [TCBStateHandler + (eax - 1) * 4], [sockAddr] |
.exit: |
ret |
endp |
proc stateTCB_LISTEN stdcall, sockAddr:DWORD |
align 4 |
stateTCB_LISTEN: |
; In this case, we are expecting a SYN Packet |
; For now, if the Packet is a SYN, process it, and send a response |
; If not, ignore it |
; Look at control flags |
test [edx + 20 + TCP_Packet.Flags], TH_SYN |
test [edx + TCP_Packet.Flags], TH_SYN |
jz .exit |
; We have a SYN. update the socket with this IP Packets details, |
; And send a response |
; mov eax, [edx + IP_Packet.SourceAddress] |
; mov [ebx + SOCKET.RemoteIP], eax |
; mov ax, [edx + 20 + TCP_Packet.SourcePort] |
; mov [ebx + SOCKET.RemotePort], ax |
; mov eax, [edx + 20 + TCP_Packet.SequenceNumber] |
; mov [ebx + SOCKET.IRS], eax |
; mov [ebx + SOCKET.RCV_NXT], eax |
; lea esi, [ebx + SOCKET.RCV_NXT] |
; call inc_inet_esi ; RCV.NXT |
; mov eax, [ebx + SOCKET.ISS] |
; mov [ebx + SOCKET.SND_NXT], eax |
; |
; Now construct the response, and queue for sending by IP |
; mov eax, EMPTY_QUEUE |
; call dequeue |
; cmp ax, NO_BUFFER |
; je .exit |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.RemoteIP], esi ; IP source address |
mov ax, [edx + TCP_Packet.SourcePort] |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RemotePort], ax |
mov eax, [edx + TCP_Packet.SequenceNumber] |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.IRS], eax |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT], eax |
lea esi, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT] |
inc_INET esi ; RCV.NXT |
mov eax, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.ISS] |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT], eax |
push eax |
; Now construct the response |
mov bl, TH_SYN + TH_ACK |
xor ecx, ecx |
xor esi, esi |
; stdcall build_tcp_Packet, [sockAddr] |
call TCP_send_ack |
; mov eax, NET1OUT_QUEUE |
;;; mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
; mov eax, IPIN_QUEUE |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_SYN_RECEIVED |
.not_local: |
; Send it. |
pop ebx |
;;; call queue |
mov esi, [sockAddr] |
mov [esi + SOCKET.TCBState], TCB_SYN_RECEIVED |
; increment SND.NXT in socket |
add esi, SOCKET.SND_NXT |
call inc_inet_esi |
lea esi, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.SND_NXT] |
inc_INET esi |
.exit: |
ret |
endp |
proc stateTCB_SYN_SENT stdcall, sockAddr:DWORD |
align 4 |
stateTCB_SYN_SENT: |
; We are awaiting an ACK to our SYN, with a SYM |
; Look at control flags - expecting an ACK |
mov al, [edx + 20 + TCP_Packet.Flags] |
mov al, [edx + TCP_Packet.Flags] |
and al, TH_SYN + TH_ACK |
cmp al, TH_SYN + TH_ACK |
je .syn_ack |
795,139 → 658,89 |
test al, TH_SYN |
jz .exit |
mov [ebx + SOCKET.TCBState], TCB_SYN_RECEIVED |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_SYN_RECEIVED |
push TH_SYN + TH_ACK |
jmp .send |
.syn_ack: |
mov [ebx + SOCKET.TCBState], TCB_ESTABLISHED |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_ESTABLISHED |
push TH_ACK |
.send: |
; Store the recv.nxt field |
mov eax, [edx + 20 + TCP_Packet.SequenceNumber] |
mov eax, [edx + TCP_Packet.SequenceNumber] |
; Update our recv.nxt field |
mov [ebx + SOCKET.RCV_NXT], eax |
lea esi, [ebx + SOCKET.RCV_NXT] |
call inc_inet_esi |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT], eax |
lea esi, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT] |
inc_INET esi |
; Send an ACK |
; Now construct the response, and queue for sending by IP |
; mov eax, EMPTY_QUEUE |
; call dequeue |
; cmp ax, NO_BUFFER |
pop ebx |
je .exit |
call TCP_send_ack |
push eax |
xor ecx, ecx |
xor esi, esi |
; stdcall build_tcp_Packet, [sockAddr] |
; mov eax, NET1OUT_QUEUE |
;;; mov edx, [stack_ip] |
; mov ecx, [sockAddr] |
; cmp edx, [ecx + SOCKET.RemoteIP] |
; jne .not_local |
; mov eax, IPIN_QUEUE |
.not_local: |
; Send it. |
pop ebx |
;;; call queue |
.exit: |
ret |
endp |
proc stateTCB_SYN_RECEIVED stdcall, sockAddr:DWORD |
align 4 |
stateTCB_SYN_RECEIVED: |
; In this case, we are expecting an ACK Packet |
; For now, if the Packet is an ACK, process it, |
; If not, ignore it |
test [edx + 20 + TCP_Packet.Flags], TH_RST |
test [edx + TCP_Packet.Flags], TH_RST |
jz .check_ack |
push [ebx + SOCKET.OrigRemotePort] [ebx + SOCKET.OrigRemoteIP] |
pop [ebx + SOCKET.RemoteIP] [ebx + SOCKET.RemotePort] |
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] |
mov [ebx + SOCKET.TCBState], TCB_LISTEN |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_LISTEN |
jmp .exit |
.check_ack: |
; Look at control flags - expecting an ACK |
test [edx + 20 + TCP_Packet.Flags], TH_ACK |
test [edx + TCP_Packet.Flags], TH_ACK |
jz .exit |
mov [ebx + SOCKET.TCBState], TCB_ESTABLISHED |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_ESTABLISHED |
.exit: |
ret |
endp |
proc stateTCB_ESTABLISHED stdcall, sockAddr:DWORD |
align 4 |
stateTCB_ESTABLISHED: |
; Here we are expecting data, or a request to close |
; OR both... |
; Did we receive a FIN or RST? |
test [edx + 20 + TCP_Packet.Flags], TH_FIN |
test [edx + TCP_Packet.Flags], TH_FIN |
jz .check_ack |
; It was a fin or reset. |
; Remove resend entries from the queue - I dont want to send any more data |
pushad |
; Send an ACK to that fin, and enter closewait state |
; get skt # |
stdcall net_socket_addr_to_num, ebx |
mov esi, resendQ |
mov ecx, 0 |
.next_resendq: |
; cmp ecx, NUMRESENDENTRIES |
; je .last_resendq ; None left |
; cmp [esi + 4], eax |
; je @f ; found one |
; inc ecx |
; add esi, 8 |
; jmp .next_resendq |
@@: mov dword[esi + 4], 0 |
inc ecx |
add esi, 8 |
jmp .next_resendq |
.last_resendq: |
popad |
@@: ; Send an ACK to that fin, and enter closewait state |
mov [ebx + SOCKET.TCBState], TCB_CLOSE_WAIT |
lea esi, [ebx + SOCKET.RCV_NXT] |
mov eax, [esi] ; save original |
call inc_inet_esi |
;; jmp ste_ack - NO, there may be data |
.check_ack: |
; Check that we received an ACK |
test [edx + 20 + TCP_Packet.Flags], TH_ACK |
test [edx + TCP_Packet.Flags], TH_ACK |
jz .exit |
; TODO - done, I think! |
; 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 |
mov cx, [edx + 20 + TCP_Packet.Window] |
mov cx, [edx + TCP_Packet.Window] |
xchg cl, ch |
cmp cx, 1024 |
ja @f |
mov [ebx + SOCKET.wndsizeTimer], 1 |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.wndsizeTimer], 1 |
@@: ; OK, here is the deal |
; My recv.nct field holds the seq of the expected next rec byte |
938,117 → 751,41 |
; recv.nxt is in dword [edx+24], in inet format |
; recv seq is in [sktAddr]+56, in inet format |
; just do a comparision |
mov ecx, [ebx + SOCKET.RCV_NXT] |
cmp [ebx + SOCKET.TCBState], TCB_CLOSE_WAIT |
mov ecx, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT] |
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_CLOSE_WAIT |
jne @f |
mov ecx, eax |
@@: cmp ecx, [edx + 20 + TCP_Packet.SequenceNumber] |
@@: cmp ecx, [edx + TCP_Packet.SequenceNumber] |
jne .ack |
test ecx, ecx |
jnz .data |
; Read the data bytes, store in socket buffer |
; movzx ecx, [edx + IP_Packet.TotalLength] |
xchg cl, ch |
sub ecx, 40 ; Discard 40 bytes of header |
ja .data ; Read data, if any |
; If we had received a fin, we need to ACK it. |
cmp [ebx + SOCKET.TCBState], TCB_CLOSE_WAIT |
cmp [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_CLOSE_WAIT |
je .ack |
jmp .exit |
.data: |
push ebx |
add ebx, SOCKET.lock |
call wait_mutex |
pop ebx |
mov esi, [esp + 4] |
sub edx, esi |
mov edi, edx |
call socket_internal_receiver |
push ecx |
push [ebx + SOCKET.PID] ; get socket owner PID |
mov eax, [ebx + SOCKET.rxDataCount] |
add eax, ecx |
cmp eax, SOCKETBUFFSIZE - SOCKETHEADERSIZE |
ja .overflow |
mov [ebx + SOCKET.rxDataCount], eax ; increment the count of bytes in buffer |
; point to the location to store the data |
lea edi, [ebx + eax + SOCKETHEADERSIZE] |
sub edi, ecx |
add edx, 40 ; edx now points to the data |
mov esi, edx |
cld |
rep movsb ; copy the data across |
mov [ebx + SOCKET.lock], 0 ; release mutex |
; flag an event to the application |
pop eax |
mov ecx, 1 |
mov esi, TASK_DATA + TASKDATA.pid |
.next_pid: |
cmp [esi], eax |
je .found_pid |
inc ecx |
add esi, 0x20 |
cmp ecx, [TASK_COUNT] |
jbe .next_pid |
.found_pid: |
shl ecx, 8 |
or [ecx + SLOT_BASE + APPDATA.event_mask], EVENT_NETWORK ; stack event |
pop ecx |
; Update our recv.nxt field |
lea esi, [ebx + SOCKET.RCV_NXT] |
call add_inet_esi |
.ack: |
; Send an ACK |
; Now construct the response, and queue for sending by IP |
; mov eax, EMPTY_QUEUE |
; call dequeue |
; cmp ax, NO_BUFFER |
je .exit |
push eax |
mov bl, TH_ACK |
xor ecx, ecx |
xor esi, esi |
; stdcall build_tcp_Packet, [sockAddr] |
; mov eax, NET1OUT_QUEUE |
;;; mov edx, [stack_ip] |
; mov ecx, [sockAddr] |
; cmp edx, [ecx + SOCKET.RemoteIP] |
; jne .not_local |
; mov eax, IPIN_QUEUE |
.not_local: |
; Send it. |
pop ebx |
;;; call queue |
call TCP_send_ack |
.exit: |
ret |
.overflow: |
; no place in buffer |
; so simply restore stack and exit |
pop eax ecx |
mov [ebx + SOCKET.lock], 0 |
ret |
endp |
proc stateTCB_FIN_WAIT_1 stdcall, sockAddr:DWORD |
align 4 |
stateTCB_FIN_WAIT_1: |
; We can either receive an ACK of a fin, or a fin |
mov al, [edx + 20 + TCP_Packet.Flags] |
mov al, [edx + TCP_Packet.Flags] |
and al, TH_FIN + TH_ACK |
cmp al, TH_ACK |
1055,109 → 792,69 |
jne @f |
; It was an ACK |
mov [ebx + SOCKET.TCBState], TCB_FIN_WAIT_2 |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_FIN_WAIT_2 |
jmp .exit |
@@: mov [ebx + SOCKET.TCBState], TCB_CLOSING |
@@: mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_CLOSING |
cmp al, TH_FIN |
je @f |
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_TIMED_WAIT |
@@: lea esi, [ebx + SOCKET.RCV_NXT] |
call inc_inet_esi |
@@: lea esi, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT] |
inc_INET esi |
; Send an ACK |
; mov eax, EMPTY_QUEUE |
; call dequeue |
; cmp ax, NO_BUFFER |
je .exit |
push eax |
mov bl, TH_ACK |
xor ecx, ecx |
xor esi, esi |
; stdcall build_tcp_Packet, [sockAddr] |
call TCP_send_ack |
; mov eax, NET1OUT_QUEUE |
;;; mov edx, [stack_ip] |
; mov ecx, [sockAddr] |
; cmp edx, [ecx + SOCKET.RemoteIP] |
; jne .not_local |
; mov eax, IPIN_QUEUE |
.not_local: |
; Send it. |
pop ebx |
;;; call queue |
.exit: |
ret |
endp |
proc stateTCB_FIN_WAIT_2 stdcall, sockAddr:DWORD |
test [edx + 20 + TCP_Packet.Flags], TH_FIN |
align 4 |
stateTCB_FIN_WAIT_2: |
test [edx + TCP_Packet.Flags], TH_FIN |
jz .exit |
; Change state, as we have a fin |
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_TIMED_WAIT |
lea esi, [ebx + SOCKET.RCV_NXT] |
call inc_inet_esi |
lea esi, [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.RCV_NXT] |
inc_INET esi |
; Send an ACK |
; mov eax, EMPTY_QUEUE |
; call dequeue |
;; cmp ax, NO_BUFFER |
; je .exit |
push eax |
mov bl, TH_ACK |
xor ecx, ecx |
xor esi, esi |
; stdcall build_tcp_Packet, [sockAddr] |
call TCP_send_ack |
; mov eax, NET1OUT_QUEUE |
;;; mov edx, [stack_ip] |
mov ecx, [sockAddr] |
cmp edx, [ecx + SOCKET.RemoteIP] |
jne .not_local |
; mov eax, IPIN_QUEUE |
.not_local: |
; Send it. |
pop ebx |
;;; call queue |
.exit: |
ret |
endp |
proc stateTCB_CLOSE_WAIT stdcall, sockAddr:DWORD |
align 4 |
stateTCB_CLOSE_WAIT: |
; Intentionally left empty |
; socket_close_tcp handles this |
ret |
endp |
proc stateTCB_CLOSING stdcall, sockAddr:DWORD |
align 4 |
stateTCB_CLOSING: |
; We can either receive an ACK of a fin, or a fin |
test [edx + 20 + TCP_Packet.Flags], TH_ACK |
test [edx + TCP_Packet.Flags], TH_ACK |
jz .exit |
mov [ebx + SOCKET.TCBState], TCB_TIMED_WAIT |
mov [ebx + SOCKET_head.end + IPv4_SOCKET.end + TCP_SOCKET.TCBState], TCB_TIMED_WAIT |
.exit: |
ret |
endp |
proc stateTCB_LAST_ACK stdcall, sockAddr:DWORD |
align 4 |
stateTCB_LAST_ACK: |
; Look at control flags - expecting an ACK |
test [edx + 20 + TCP_Packet.Flags], TH_ACK |
test [edx + TCP_Packet.Flags], TH_ACK |
jz .exit |
; delete the socket |
1165,235 → 862,17 |
.exit: |
ret |
endp |
proc stateTCB_TIME_WAIT stdcall, sockAddr:DWORD |
align 4 |
stateTCB_TIME_WAIT: |
ret |
endp |
proc stateTCB_CLOSED stdcall, sockAddr:DWORD |
align 4 |
stateTCB_CLOSED: |
ret |
endp |
;; [53.7] Send data through STREAM socket |
; |
; @param EBX is socket number |
; @param ECX is application data size (number of bytes to send) |
; @param EDX is pointer to application data buffer |
; @return 0 (sent successfully) or -1 (error) in EAX |
;; |
;proc socket_write_tcp stdcall |
;local sockAddr dd ? |
; DEBUGF 1, "socket_write_tcp(0x%x)\n", ebx |
stdcall net_socket_num_to_addr, ebx |
or eax, eax |
jz .error |
mov ebx, eax |
; mov [sockAddr], ebx |
; If the sockets window timer is nonzero, do not queue Packet |
cmp [ebx + SOCKET.wndsizeTimer], 0 |
jne .error |
; mov eax, EMPTY_QUEUE |
; call dequeue |
; cmp ax, NO_BUFFER |
; je .error |
push eax |
; Get the address of the callers data |
mov edi, [TASK_BASE] |
add edi, TASKDATA.mem_start |
add edx, [edi] |
mov esi, edx |
pop eax |
push eax |
push ecx |
mov bl, TH_ACK |
; stdcall build_tcp_Packet, [sockAddr] |
pop ecx |
; Check destination IP address. |
; If it is the local host IP, route it back to IP_RX |
pop ebx |
push ecx |
; mov eax, NET1OUT_QUEUE |
;;; TODO: get device id in edx |
xor edx, edx |
shl edx, 2 |
mov edx, [IP_LIST+edx] |
; mov ecx, [sockAddr] |
; cmp edx, [ecx + SOCKET.RemoteIP] |
; jne .not_local |
; mov eax, IPIN_QUEUE |
.not_local: |
pop ecx |
push ebx ; save ipbuffer number |
;;;; call queue |
; mov esi, [sockAddr] |
; increament SND.NXT in socket |
; Amount to increment by is in ecx |
add esi, SOCKET.SND_NXT |
call add_inet_esi |
pop ebx |
; Copy the IP buffer to a resend queue |
; If there isn't one, dont worry about it for now |
mov esi, resendQ |
mov ecx, 0 |
.next_resendq: |
; cmp ecx, NUMRESENDENTRIES |
je .exit ; None found |
cmp dword[esi + 4], 0 |
je @f ; found one |
inc ecx |
add esi, 8 |
jmp .next_resendq |
@@: push ebx |
; OK, we have a buffer descriptor ptr in esi. |
; resend entry # in ecx |
; Populate it |
; socket # |
; retries count |
; retry time |
; fill IP buffer associated with this descriptor |
; stdcall net_socket_addr_to_num, [sockAddr] |
mov [esi + 4], eax |
mov byte[esi + 1], TCP_RETRIES |
mov word[esi + 2], TCP_TIMEOUT |
inc ecx |
; Now get buffer location, and copy buffer across. argh! more copying,, |
; mov edi, resendBuffer - IPBUFFSIZE |
; @@: add edi, IPBUFFSIZE |
loop @b |
; we have dest buffer location in edi |
pop eax |
; convert source buffer pointer eax to the absolute address |
; mov ecx, IPBUFFSIZE |
; mul ecx |
; add eax, IPbuffs |
; mov esi, eax |
; do copy |
; mov ecx, IPBUFFSIZE |
; cld |
rep movsb |
.exit: |
xor eax, eax |
ret |
.error: |
or eax, -1 |
ret |
;endp |
;*************************************************************************** |
; Function |
; checksum |
; |
; Description |
; checkAdd1,checkAdd2, checkSize1, checkSize2, checkResult |
; Dont break anything; Most registers are used by the caller |
; This code is derived from the 'C' source, cksum.c, in the book |
; Internetworking with TCP/IP Volume II by D.E. Comer |
; |
;*************************************************************************** |
checksum: |
pusha |
; mov eax, [checkAdd1] |
xor edx, edx ; edx is the accumulative checksum |
xor ebx, ebx |
; mov cx, [checkSize1] |
shr cx, 1 |
jz cs1_1 |
cs1: |
mov bh, [eax] |
mov bl, [eax + 1] |
add eax, 2 |
add edx, ebx |
loopw cs1 |
cs1_1: |
; and word [checkSize1], 0x01 |
jz cs_test2 |
mov bh, [eax] |
xor bl, bl |
add edx, ebx |
cs_test2: |
; mov cx, [checkSize2] |
cmp cx, 0 |
jz cs_exit ; Finished if no 2nd buffer |
; mov eax, [checkAdd2] |
shr cx, 1 |
jz cs2_1 |
cs2: |
mov bh, [eax] |
mov bl, [eax + 1] |
add eax, 2 |
add edx, ebx |
loopw cs2 |
cs2_1: |
; and word [checkSize2], 0x01 |
jz cs_exit |
mov bh, [eax] |
xor bl, bl |
add edx, ebx |
cs_exit: |
mov ebx, edx |
shr ebx, 16 |
and edx, 0xffff |
add edx, ebx |
mov eax, edx |
shr eax, 16 |
add edx, eax |
not dx |
; mov [checkResult], dx |
popa |
ret |
/kernel/branches/net/network/udp.inc |
---|
75,8 → 75,34 |
UDP_handler: |
DEBUGF 1,"UDP_Handler\n" |
; TODO: First validate the header & checksum! |
; First validate, checksum: |
DEBUGF 1,"Real UDP checksum: %x\n", [edx + UDP_Packet.Checksum]:4 |
mov [edx + UDP_Packet.Checksum], 0 |
pusha |
rol cx, 8 |
push cx |
rol cx, 8 |
push word IP_PROTO_UDP shl 8 |
push edi |
push esi |
mov esi, edx |
xor edx, edx |
call checksum_1 |
; Checksum for pseudoheader |
mov ecx, 12 |
mov esi, esp |
call checksum_1 |
add esp, 12 |
call checksum_2 |
popa |
; Look for a socket where |
; IP Packet UDP Destination Port = local Port |
; IP Packet SA = Remote IP |
85,14 → 111,14 |
.try_more: |
mov bx , [edx + UDP_Packet.DestinationPort] ; get the local port from the IP Packet's UDP header |
.next_socket: |
mov eax, [eax + SOCKET.NextPtr] |
mov eax, [eax + SOCKET_head.NextPtr] |
or eax, eax |
jz .dump |
cmp [eax + SOCKET.Domain], AF_INET4 |
cmp [eax + SOCKET_head.Domain], AF_INET4 |
jne .next_socket |
cmp [eax + SOCKET.Type], IP_PROTO_UDP |
cmp [eax + SOCKET_head.Type], IP_PROTO_UDP |
jne .next_socket |
cmp [eax + SOCKET.LocalPort], bx |
cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.LocalPort], bx |
jne .next_socket |
DEBUGF 1,"found socket with matching domain, type and localport\n" |
101,12 → 127,12 |
; 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.RemoteIP], 0xffffffff |
cmp [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP], 0xffffffff |
je .ok1 |
mov ebx, [esp] |
mov ebx, [ebx + ETH_FRAME.Data + IPv4_Packet.SourceAddress] ; get the Source address from the IP Packet FIXME |
cmp [eax + SOCKET.RemoteIP], ebx |
cmp [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP], ebx |
jne .try_more ; Quit if the source IP is not valid, check for more sockets with this IP/PORT combination |
115,10 → 141,10 |
mov bx, [edx + UDP_Packet.SourcePort] ; Remote port must be 0, or equal to sourceport of packet |
cmp [eax + SOCKET.RemotePort], 0 |
cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.RemotePort], 0 |
je .ok2 |
cmp [eax + SOCKET.RemotePort], bx |
cmp [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.RemotePort], bx |
jne .dump |
.ok2: |
130,10 → 156,21 |
sub cx , UDP_Packet.Data |
mov dx , bx |
call socket_internal_receiver |
lea ebx, [eax + SOCKET_head.lock] |
call wait_mutex |
mov [eax + SOCKET_head.end + IPv4_SOCKET.end + UDP_SOCKET.RemotePort], dx ; update remote port number |
mov [eax + SOCKET_head.end + IPv4_SOCKET.RemoteIP], edi |
inc [UDP_PACKETS_RX] |
pop edi |
add esp, 4 |
sub esi, edi |
xchg esi, edi |
jmp socket_internal_receiver |
.dump: |
DEBUGF 1,"Dumping UDP packet\n" |
call kernel_free |
146,69 → 183,93 |
;----------------------------------------------------------------- |
; |
; Note: UDP works only on top of IP protocol :) |
; UDP_socket_send |
; |
; IN: eax = dest ip |
; ebx = source ip |
; ecx = data length |
; edx = remote port shl 16 + local port (both in INET order) |
; esi = data offset |
; IN: eax = socket pointer |
; ecx = number of bytes to send |
; esi = pointer to data |
; |
;----------------------------------------------------------------- |
UDP_create_packet: |
align 4 |
UDP_socket_send: |
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,"Create UDP Packet (size=%u)\n",ecx |
push edx esi |
mov di , IP_PROTO_UDP |
sub esp, 8 ; reserve some place in stack for later |
; Create the pseudoheader in stack, |
; (now that we still have all the variables that are needed.) |
push dword IP_PROTO_UDP shl 8 |
push eax |
push ebx |
add ecx, UDP_Packet.Data |
mov di , IP_PROTO_UDP |
; dx = fragment id |
; TODO: fill in: dx = 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 |
mov byte[edi + UDP_Packet.Length], ch |
mov byte[edi + UDP_Packet.Length+1], cl |
sub ecx , UDP_Packet.Data |
mov [esp + 8 + 12], eax ; pointer to buffer start |
mov [esp + 8 + 12 + 4], edx ; buffer size |
rol cx, 8 |
mov [edi + UDP_Packet.Length], cx |
mov [esp + 8 + 10], cx |
ror cx, 8 |
pop esi |
push edi |
push edi ecx |
sub ecx, UDP_Packet.Data |
add edi, UDP_Packet.Data |
push cx |
shr ecx, 2 |
rep movsd |
pop cx |
mov ecx, [esp] |
and cx , 3 |
rep movsb |
pop edi |
pop ecx edi |
pop ecx |
mov dword [edi + UDP_Packet.SourcePort], ecx ; notice: we write both port's at once |
pop dword [edi + UDP_Packet.SourcePort] ; fill in both portnumbers |
mov [edi + UDP_Packet.Checksum], 0 ; set it to zero, to calculate checksum |
mov [edi + UDP_Packet.Checksum], 0 |
; Checksum for UDP header + data |
xor edx, edx |
mov esi, edi |
call checksum_1 |
; Checksum for pseudoheader |
mov ecx, 12 |
mov esi, esp |
call checksum_1 |
add esp, 12 ; remove the pseudoheader from stack |
; Now create the final checksum and store it in UDP header |
call checksum_2 |
mov [edi + UDP_Packet.Checksum], dx |
; TODO: calculate checksum using Pseudo-header (However, using a 0 as checksum shouldnt generate any errors :) |
inc [UDP_PACKETS_TX] |
push edx eax ; TODO: make this work on other protocols besides ethernet |
DEBUGF 1,"Sending UDP Packet to device %x\n", ebx ; |
jmp ETH_Sender ; |
jmp ETH_sender ; |
.exit: |
ret |
.fail: |
; todo: queue the packet |
add esp, 8 |
add esp, 8+12+8 |
ret |
;--------------------------------------------------------------------------- |
; |
; UDP_API |