From fab920c5c8573a0e5a83a4155e55cd449c3e9cc9 Mon Sep 17 00:00:00 2001 From: hidnplayr Date: Tue, 27 Jul 2010 18:53:38 +0000 Subject: [PATCH] Deleted double macro in netdrv.inc Refactoring of ARP code. Used universal names for protocol handlers. Moved some pieces of code from procs to macros. Added IPv4_fragment (currently disabled) Added ring-buffer functions for sockets Updates in TCP code git-svn-id: svn://kolibrios.org@1529 a494cfbc-eb01-0410-851d-a64ba20cac60 --- kernel/branches/net/core/exports.inc | 6 +- kernel/branches/net/drivers/netdrv.inc | 35 +- kernel/branches/net/network/ARP.inc | 729 +++++++------- kernel/branches/net/network/IPv4.inc | 488 +++++---- kernel/branches/net/network/ethernet.inc | 94 +- kernel/branches/net/network/icmp.inc | 17 +- kernel/branches/net/network/socket.inc | 471 +++++++-- kernel/branches/net/network/stack.inc | 61 +- kernel/branches/net/network/tcp.inc | 1168 ++++++++++------------ kernel/branches/net/network/udp.inc | 176 ++-- 10 files changed, 1704 insertions(+), 1541 deletions(-) diff --git a/kernel/branches/net/core/exports.inc b/kernel/branches/net/core/exports.inc index c284136350..43a942a363 100644 --- a/kernel/branches/net/core/exports.inc +++ b/kernel/branches/net/core/exports.inc @@ -82,7 +82,7 @@ iglobal szNetUnRegDev db 'NetUnRegDev',0 szNetPtrToNum db 'NetPtrToNum',0 szEthReceiver db 'EthReceiver',0 - szIPv4Handler db 'IPv4Handler',0 + szIPv4_input db 'IPv4_input',0 align 16 @@ -155,8 +155,8 @@ kernel_export: dd szNetRegDev , NET_add_device dd szNetUnRegDev , NET_remove_device dd szNetPtrToNum , NET_ptr_to_num - dd szEthReceiver , ETH_receiver - dd szIPv4Handler , IPv4_handler + dd szEthReceiver , ETH_input + dd szIPv4_input , IPv4_input exp_lfb: dd szLFBAddress , 0 diff --git a/kernel/branches/net/drivers/netdrv.inc b/kernel/branches/net/drivers/netdrv.inc index 09d2d1aa6d..2ba7a46f81 100644 --- a/kernel/branches/net/drivers/netdrv.inc +++ b/kernel/branches/net/drivers/netdrv.inc @@ -35,22 +35,15 @@ macro set_io addr { if addr = 0 - mov edx, [device.io_addr] - else if addr = LAST_IO - else - add edx, addr - LAST_IO - end if LAST_IO = addr } - - macro allocate_and_clear dest, size, err { ; We need to allocate at least 8 pages, if we want a continuous memory in ram @@ -83,8 +76,6 @@ macro allocate_and_clear dest, size, err { } - - macro find_io bus, dev, io { local .check, .inc, .got @@ -126,7 +117,6 @@ macro find_irq bus, dev, irq { } - macro find_rev bus, dev, rev { push eax edx ecx @@ -138,8 +128,6 @@ macro find_rev bus, dev, rev { } - - macro make_bus_master bus, dev { movzx ecx, bus @@ -168,10 +156,7 @@ virtual at edx end virtual - - if used null_op - align 4 null_op: or eax, -1 @@ -180,11 +165,11 @@ null_op: end if -macro virt_to_dma { ; input is eax +macro GetRealAddr { ; input is eax push ax - and word[esp], PAGESIZE - 1 call GetPgAddr + and word[esp], PAGESIZE - 1 or ax, word[esp] inc esp inc esp @@ -209,7 +194,7 @@ macro NET_DEVICE { .end: } -;struc ETH_DEVICE { + macro ETH_DEVICE { NET_DEVICE @@ -235,16 +220,4 @@ macro SLIP_DEVICE { .mode dd ? -} - -macro GetRealAddr { - - push eax - call GetPgAddr - and dword [esp], (PAGESIZE - 1) - or eax, dword [esp] - add esp, 4 - -} - - +} \ No newline at end of file diff --git a/kernel/branches/net/network/ARP.inc b/kernel/branches/net/network/ARP.inc index e7d6a204dc..c7665ea98d 100644 --- a/kernel/branches/net/network/ARP.inc +++ b/kernel/branches/net/network/ARP.inc @@ -12,34 +12,30 @@ ;; Written by hidnplayr@kolibrios.org ;; ;; ;; ;; GNU GENERAL PUBLIC LICENSE ;; -;; Version 2, June- 1991 ;; +;; Version 2, June- 1991 ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - $Revision$ +ARP_NO_ENTRY equ 0 +ARP_VALID_MAPPING equ 1 +ARP_AWAITING_RESPONSE equ 2 +ARP_RESPONSE_TIMEOUT equ 3 -ARP_NO_ENTRY equ 0 -ARP_VALID_MAPPING equ 1 -ARP_AWAITING_RESPONSE equ 2 -ARP_RESPONSE_TIMEOUT equ 3 +ARP_REQUEST_TTL equ 31 ; 20 s +ARP_ENTRY_TTL equ 937 ; 600 s -ARP_REQUEST_TTL = 20 ; in seconds -ARP_ENTRY_TTL = 600 ; in seconds +ARP_REQ_OPCODE equ 0x0100 ; request +ARP_REP_OPCODE equ 0x0200 ; reply -ETHER_ARP equ 0x0608 - -ARP_REQ_OPCODE equ 0x0100 ; request -ARP_REP_OPCODE equ 0x0200 ; reply - -ARP_TABLE_SIZE equ 20 ; Size of table +ARP_TABLE_SIZE equ 20 ; Size of table struct ARP_ENTRY .IP dd ? .MAC dp ? .Status dw ? - .TTL dw ? ; in seconds + .TTL dw ? .size: ends @@ -53,29 +49,19 @@ struct ARP_Packet .SenderIP dd ? .TargetMAC dp ? .TargetIP dd ? + .size: ends -; The TTL field is decremented every second, and is deleted when it -; reaches 0. It is refreshed every time a packet is received -; If the TTL field is 0xFFFF it is a static entry and is never deleted -; The status field can be the following values: -; 0x0000 entry not used -; 0x0001 entry holds a valid mapping -; 0x0002 entry contains an IP address, awaiting ARP response -; 0x0003 No response received to ARP request. -; The last status value is provided to allow the network layer to delete -; a packet that is queued awaiting an ARP response - align 4 uglobal NumARP dd ? - ARPTable rb ARP_ENTRY.size * ARP_TABLE_SIZE + ARP_table rb ARP_ENTRY.size * ARP_TABLE_SIZE - ARP_PACKETS_TX rd MAX_NET_DEVICES - ARP_PACKETS_RX rd MAX_NET_DEVICES + ARP_PACKETS_TX rd MAX_NET_DEVICES + ARP_PACKETS_RX rd MAX_NET_DEVICES endg @@ -88,379 +74,81 @@ endg ; ; This function resets all ARP variables ; -; IN: / -; OUT: / -; ;----------------------------------------------------------------- -align 4 -ARP_init: +macro ARP_init { xor eax, eax - mov [NumARP], eax mov edi, ARP_PACKETS_TX mov ecx, 2*MAX_NET_DEVICES rep stosd - ret - - -;----------------------------------------------------------------- -; -; ARP_IP_to_MAC -; -; This function resets all ARP variables -; -; IN: eax = IPv4 address -; OUT: eax = -1 on error, else eax = first two bytes of mac -; ( high 16 bits are zero) -; ebx = last four bytes of mac ; TODO: special eax value for 'request send' -; -;----------------------------------------------------------------- -align 4 -ARP_IP_to_MAC: - - DEBUGF 1,"ARP_IP_to_MAC\n" - - ; first, check destination IP to see if it is on 'this' network. - ; The test is: - ; if ( destIP & subnet_mask == stack_ip & subnet_mask ) - ; destination is local - ; else - ; destination is remote, so pass to gateway - - xor edx, edx ;;; TODO: find device num in edx - - mov ebx, [IP_LIST + edx] - and ebx, [SUBNET_LIST + edx] - - mov ecx, eax - and ecx, [SUBNET_LIST + edx] - - cmp ecx, ebx - je .local - - mov eax, [GATEWAY_LIST + edx] - DEBUGF 1,"requested IP is not on subnet, using gateway\n" - - .local: - ; try to find it on the list - mov ecx, [NumARP] - test ecx, ecx - jz .not_in_list - mov esi, ARPTable + ARP_ENTRY.IP - .scan_loop: - cmp [esi], eax - je .found_it - add esi, ARP_ENTRY.size - loop .scan_loop - .not_in_list: - - DEBUGF 1,"IP not found on list, preparing for ARP request\n" - - ; if not, reserve an entry in list and send an ARP request packet - - push eax - - pushw ARP_REQUEST_TTL - pushw ARP_AWAITING_RESPONSE - pushd 0 - pushw 0 - pushd eax - call ARP_add_entry - cmp eax, -1 - je .full - - ; - - ; This piece of code waits for an ARP reply - - mov ebx, eax - pop eax - push ebx - call ARP_create_request - - push [timer_ticks] - add dword[esp], 100*ARP_REQUEST_TTL - DEBUGF 1,"Waiting for ARP reply, time: %x, entry:%u\n",[timer_ticks], [esp + 4] - .dirty_loop: - - call change_task ; The ARP reply hasnt been received yet, tell the processor to do some other stuff first - - mov eax, [esp + 4] - imul eax, ARP_ENTRY.size - add eax, ARPTable - cmp [eax + ARP_ENTRY.Status], ARP_VALID_MAPPING - je .gogogo - - mov eax, [esp] ; Check if the reply hasnt timed-out yet - cmp [timer_ticks], eax - jl .dirty_loop - - ; - or eax, -1 - add esp, 8 - ret - - .found_it: - DEBUGF 1,"found MAC in ARPTable\n" - movzx eax, word [esi+ARP_ENTRY.MAC] - mov ebx, dword[esi+ARP_ENTRY.MAC+2] - ret - - .full: - add esp, 4 - mov eax, -1 - ret - - .gogogo: - DEBUGF 1,"got ARP reply, time: %x\n",[timer_ticks] - mov ebx, dword[eax+ARP_ENTRY.MAC+2] - movzx eax, word [eax+ARP_ENTRY.MAC] - add esp, 8 - ret - - -;--------------------------------------------------------------------------- -; -; ARP_create_request -; -; IN: ip in eax -; -; OUT: / -; -;--------------------------------------------------------------------------- -align 4 -ARP_create_request: - - DEBUGF 1,"Create ARP Packet\n" - - call IPv4_dest_to_dev - - push eax ; DestIP - mov eax, [IP_LIST+4*edi] ; senderIP - push eax - - mov edi, [NET_DRV_LIST + 4*edi] - lea eax, [edi + ETH_DEVICE.mac] - mov ebx, ETH_BROADCAST - mov ecx, 60 ; minimum packet size - mov edx, edi ;;; - mov di , ETHER_ARP - call ETH_create_packet - jz .exit - - mov ecx, eax - - mov [edi + ARP_Packet.HardwareType], 0x0100 ;Ethernet - mov [edi + ARP_Packet.ProtocolType], 0x0008 ;IP - mov [edi + ARP_Packet.HardwareSize], 6 ;MAC-addr length - mov [edi + ARP_Packet.ProtocolSize], 4 ;IP-addr length - mov [edi + ARP_Packet.Opcode], ARP_REQ_OPCODE ;Request - - add edi, ARP_Packet.SenderMAC ; sendermac - lea esi, [ebx + ETH_DEVICE.mac] ; - movsw ; - movsd ; - pop eax ; - stosd ; - mov eax, -1 ; destmac - stosd ; - stosw ; - pop eax - stosd ; - - DEBUGF 1,"ARP Packet for device %x created successfully\n", ebx - - push edx ecx - call [ebx + NET_DEVICE.transmit] - ret - - .exit: - add esp, 8 - DEBUGF 1,"Create ARP Packet - failed\n" - mov eax, -1 - ret - - +} ;--------------------------------------------------------------------------- ; ; ARP_decrease_entry_ttls ; -; IN: / -; OUT: / -; ;--------------------------------------------------------------------------- -align 4 -ARP_decrease_entry_ttls: + +macro ARP_decrease_entry_ttls { + +local .loop +local .exit + +; The TTL field is decremented every second, and is deleted when it reaches 0. +; It is refreshed every time a packet is received. +; If the TTL field is 0xFFFF it is a static entry and is never deleted. +; The status field can be the following values: +; 0x0000 entry not used +; 0x0001 entry holds a valid mapping +; 0x0002 entry contains an IP address, awaiting ARP response +; 0x0003 No response received to ARP request. +; The last status value is provided to allow the network layer to delete +; a packet that is queued awaiting an ARP response mov ecx, [NumARP] test ecx, ecx jz .exit - mov ebx, ARPTable + mov esi, ARP_table + .loop: + cmp [esi + ARP_ENTRY.TTL], 0xffff ; 0xffff = static entry + je .next -.timer_loop: + dec [esi + ARP_ENTRY.TTL] + jz .time_out - cmp [ebx + ARP_ENTRY.TTL], 0xFFFF - je .timer_loop_end ;if TTL==0xFFFF then it's static entry - - cmp [ebx + ARP_ENTRY.TTL], 0 - jnz .timer_loop_end_with_dec ;if TTL!=0 - - ; Ok, TTL is 0 - ;if Status==AWAITING_RESPONSE and TTL==0 - ;then we have to change it to ARP_RESPONSE_TIMEOUT - cmp [ebx + ARP_ENTRY.Status], ARP_AWAITING_RESPONSE - jne @f - - mov [ebx + ARP_ENTRY.Status], ARP_RESPONSE_TIMEOUT - mov [ebx + ARP_ENTRY.TTL], word 0x000A ;10 sec - jmp .timer_loop_end - - @@: - ;if TTL==0 and Status==VALID_MAPPING, we have to delete it - ;if TTL==0 and Status==RESPONSE_TIMEOUT, delete too - mov esi, [NumARP] - sub esi, ecx ;esi=index of entry, will be deleted - - push ebx ecx - call ARP_del_entry - pop ecx ebx - - jmp .timer_loop_end - - -.timer_loop_end_with_dec: - - dec [ebx + ARP_ENTRY.TTL] ;decrease TTL - -.timer_loop_end: - - add ebx, ARP_ENTRY.size - loop .timer_loop - -.exit: - - ret - -;----------------------------------------------------------------- -; -; ARP_add_entry (or update) -; -; IN: arp entry in stack: esp .IP -; esp+4 .MAC -; esp+10 .Status -; esp+12 .TTL -; esp+14 -; -; OUT: eax = entry #, -1 on error -; -;----------------------------------------------------------------- ; TODO: use a mutex -align 4 -ARP_add_entry: - - DEBUGF 1,"ARP add entry: " - - mov ecx, [NumARP] - test ecx, ecx - jz .add - - mov eax, dword[esp + 4 + ARP_ENTRY.MAC] - mov bx , word[esp + 4 + ARP_ENTRY.MAC + 4] - mov esi, ARPTable - -.loop: - cmp dword [esi + ARP_ENTRY.MAC], eax - jne .maybe_next - cmp word [esi + ARP_ENTRY.MAC + 4], bx - jne .maybe_next - - cmp dword[esi + ARP_ENTRY.TTL], 0xFFFF ; static entry - jne .notstatic - cmp dword[esp + 4 + ARP_ENTRY.TTL], 0xFFFF - jne .error -.notstatic: - - mov ebx, [NumARP] - xchg ebx, ecx - sub ecx, ebx - jmp .add - -.maybe_next: + .next: add esi, ARP_ENTRY.size loop .loop - - mov ecx, [NumARP] - cmp ecx, ARP_TABLE_SIZE - jge .error - -.add: - push ecx - imul ecx, ARP_ENTRY.size - lea edi, [ecx + ARPTable] - lea esi, [esp + 8] - mov ecx, ARP_ENTRY.size/2 - repz movsw - - inc [NumARP] - pop eax - DEBUGF 1,"New entry created: %u\n", eax -.exit: - DEBUGF 1,"Exiting\n" - ret ARP_ENTRY.size - -.error: - - DEBUGF 1,"error! \n" - - mov eax, -1 jmp .exit + .time_out: + cmp [esi + ARP_ENTRY.Status], ARP_AWAITING_RESPONSE + jz .response_timeout -;----------------------------------------------------------------- -; -; ARP_del_entry -; -; IN: entry # in esi -; OUT: / -; -;----------------------------------------------------------------- -align 4 -ARP_del_entry: + push esi ecx + call ARP_del_entry + pop ecx esi - DEBUGF 1,"ARP del entry %u, total entrys: %u\n", esi, [NumARP] + jmp .next - cmp esi, [NumARP] - jge .error + .response_timeout: + mov [esi + ARP_ENTRY.Status], ARP_RESPONSE_TIMEOUT + mov [esi + ARP_ENTRY.TTL], 10 - imul esi, ARP_ENTRY.size - - mov ecx, (ARP_TABLE_SIZE - 1) * ARP_ENTRY.size - sub ecx, esi - - lea edi, [ARPTable + esi] ;edi=ptr to entry that should be deleted - lea esi, [edi + ARP_ENTRY.size] ;esi=ptr to next entry - - shr ecx,1 ;ecx/2 => ARP_ENTRY_SIZE MUST BE EVEN NUMBER! - rep movsw - - dec [NumARP] ;decrease arp-entries counter - DEBUGF 1,"ARP entry deleted\n" -.error: - ret + jmp .next + .exit: +} ;----------------------------------------------------------------- ; -; ARP_Handler: -; -; This function handles ARP protocol over ethernet -; (other protocols may follow in the future) +; ARP_input ; ; IN: Pointer to buffer in [esp] ; size of buffer in [esp+4] @@ -469,13 +157,16 @@ ARP_del_entry: ; ;----------------------------------------------------------------- align 4 -ARP_handler: +ARP_input: DEBUGF 1,"ARP_Handler - start\n" cmp ecx, 28 jl .exit - cmp word [edx + ARP_Packet.Opcode], ARP_REP_OPCODE ; Is this a reply packet? +;--------------------- +; Handle Reply packets + + cmp word [edx + ARP_Packet.Opcode], ARP_REP_OPCODE jne .maybe_request DEBUGF 1,"ARP_Handler - it's a reply packet from %u.%u.%u.%u\n",\ @@ -486,10 +177,10 @@ ARP_handler: jz .exit mov eax, [edx + ARP_Packet.SenderIP] - mov esi, ARPTable+ARP_ENTRY.IP + mov esi, ARP_table .loop: - cmp [esi], eax + cmp [esi + ARP_ENTRY.IP], eax je .gotit add esi, ARP_ENTRY.size loop .loop @@ -497,10 +188,9 @@ ARP_handler: jmp .exit .gotit: - DEBUGF 1,"ARP_Handler - found matching entry\n" - cmp [esi+ARP_ENTRY.Status], 0x0300 ;if it is a static entry, dont touch it + cmp [esi+ARP_ENTRY.TTL], 0xffff ; if it is a static entry, dont touch it je .exit DEBUGF 1,"ARP_Handler - updating entry\n" @@ -516,32 +206,29 @@ ARP_handler: jmp .exit -;------ - +;----------------------- +; Handle Request packets .maybe_request: - cmp word [edx + ARP_Packet.Opcode], ARP_REQ_OPCODE ; Is this a request packet? + cmp word [edx + ARP_Packet.Opcode], ARP_REQ_OPCODE jne .exit call NET_ptr_to_num - DEBUGF 1,"ARP Request packet through device: %u\n", edi - inc [ARP_PACKETS_RX+4*edi] cmp edi, -1 jz .exit + DEBUGF 1,"ARP Request packet through device: %u\n", edi + inc [ARP_PACKETS_RX+4*edi] - mov eax, edi - shl eax, 2 - add eax, IP_LIST - mov eax, [eax] + mov eax, [IP_LIST+4*edi] cmp eax, [edx + ARP_Packet.TargetIP] ; Is it looking for my IP address? - jnz .exit + jne .exit ; TODO: instead of quitting, update local entrys with matching IP's ? + push eax push edi -; OK, it is a request for one of our MAC addresses. Build the frame and send it -; We can reuse the buffer. (faster then using ARP_create_packet) +; OK, it is a request for one of our MAC addresses. +; Build the frame and send it. We can reuse the buffer. (faster then using ARP_create_packet) - cld lea esi, [edx + ARP_Packet.SenderMAC] lea edi, [edx + ARP_Packet.TargetMAC] movsd ; Move Sender Mac to Dest MAC @@ -580,10 +267,269 @@ ARP_handler: call kernel_free add esp, 4 ; pop (balance stack) - DEBUGF 1,"ARP_Handler - exiting\n" + DEBUGF 1,"ARP_Handler - exiting\n" ret +;--------------------------------------------------------------------------- +; +; ARP_output_request +; +; IN: ip in eax +; OUT: / +; +;--------------------------------------------------------------------------- +align 4 +ARP_output_request: + + DEBUGF 1,"Create ARP Packet\n" + + call IPv4_dest_to_dev + push eax ; DestIP + pushd [IP_LIST+edi] ; SenderIP + + mov ebx, [NET_DRV_LIST+edi] ; device ptr + + lea eax, [ebx + ETH_DEVICE.mac] ; local device mac + mov edx, ETH_BROADCAST ; broadcast mac + mov ecx, ARP_Packet.size + mov di, ETHER_ARP + call ETH_output + jz .exit + + mov ecx, eax + + mov [edi + ARP_Packet.HardwareType], 0x0100 ; Ethernet + mov [edi + ARP_Packet.ProtocolType], 0x0008 ; IP + mov [edi + ARP_Packet.HardwareSize], 6 ; MAC-addr length + mov [edi + ARP_Packet.ProtocolSize], 4 ; IP-addr length + mov [edi + ARP_Packet.Opcode], ARP_REQ_OPCODE ; Request + + add edi, ARP_Packet.SenderMAC + + lea esi, [ebx + ETH_DEVICE.mac] ; SenderMac + movsw ; + movsd ; + pop eax ; SenderIP + stosd ; + + mov eax, -1 ; DestMac + stosd ; + stosw ; + pop eax ; DestIP + stosd ; + + DEBUGF 1,"ARP Packet for device %x created successfully\n", ebx + + push edx ecx + call [ebx + NET_DEVICE.transmit] + ret + + .exit: + add esp, 4+4 + DEBUGF 1,"Create ARP Packet - failed\n" + sub eax, eax + ret + + +;----------------------------------------------------------------- +; +; ARP_add_entry (or update) +; +; IN: esi = ptr to entry (can easily be made on the stack) +; OUT: eax = entry #, -1 on error +; +;----------------------------------------------------------------- ; TODO: use a mutex +align 4 +ARP_add_entry: + + DEBUGF 1,"ARP add entry: " + + mov ecx, [NumARP] + test ecx, ecx ; first entry? + jz .add + cmp ecx, ARP_TABLE_SIZE ; list full ? + jge .error + + mov eax, dword[esi + ARP_ENTRY.MAC] + mov bx , word[esi + ARP_ENTRY.MAC + 4] + mov edi, ARP_table + + .loop: + cmp dword [edi + ARP_ENTRY.MAC], eax ; Check for duplicate MAC's + jne .maybe_next ; + cmp word [edi + ARP_ENTRY.MAC + 4], bx ; + jne .maybe_next ; + + cmp dword[edi + ARP_ENTRY.TTL], 0xFFFF ; static entry + jne .notstatic + cmp dword[esi + ARP_ENTRY.TTL], 0xFFFF + jne .error + .notstatic: + + neg ecx + add ecx, [NumARP] + jmp .add + + .maybe_next: + add esi, ARP_ENTRY.size + loop .loop + + mov ecx, [NumARP] + .add: + push ecx + imul ecx, ARP_ENTRY.size + lea edi, [ecx + ARP_table] + mov ecx, ARP_ENTRY.size/2 + rep movsw + + lea esi, [edi - ARP_ENTRY.size] + inc [NumARP] + pop eax + DEBUGF 1,"New entry created: %u\n", eax + + .exit: + DEBUGF 1,"Exiting\n" + ret + + .error: + DEBUGF 1,"error! \n" + mov eax, -1 + ret + + +;----------------------------------------------------------------- +; +; ARP_del_entry +; +; IN: esi = ptr to arp entry +; OUT: / +; +;----------------------------------------------------------------- +align 4 +ARP_del_entry: + + DEBUGF 1,"ARP del entry %x, total entrys: %u\n", esi, [NumARP] + + mov ecx, ARP_table + (ARP_TABLE_SIZE - 1) * ARP_ENTRY.size + sub ecx, esi + shr ecx, 1 + + mov edi, esi + lea esi, [edi + ARP_ENTRY.size] + rep movsw + + dec [NumARP] + DEBUGF 1,"ARP entry deleted\n" + + ret + + + + + +;----------------------------------------------------------------- +; +; ARP_IP_to_MAC +; +; This function translates an IP address to a MAC address +; +; IN: eax = IPv4 address +; OUT: eax = -1 on error, -2 means request send +; else, ax = first two bytes of mac (high 16 bits of eax will be 0) +; ebx = last four bytes of mac +; +;----------------------------------------------------------------- +align 4 +ARP_IP_to_MAC: + + DEBUGF 1,"ARP_IP_to_MAC\n" + + cmp eax, 0xffffffff + je .broadcast + +; if ((Remote IP & subnet_mask) == (local IP & subnet_mask )) +; destination is on same subnet +; else, destination is remote and must use a gateway + + call IPv4_dest_to_dev + mov ebx, [IP_LIST + edi] + and ebx, [SUBNET_LIST + edi] + + mov ecx, eax + and ecx, [SUBNET_LIST + edi] + + cmp ecx, ebx + je .local + + mov eax, [GATEWAY_LIST + edi] + DEBUGF 1,"requested IP is not on subnet, using default gateway\n" + +;-------------------------------- +; Try to find the IP in ARP_table + + .local: + mov ecx, [NumARP] + test ecx, ecx + jz .not_in_list + mov esi, ARP_table + ARP_ENTRY.IP + .scan_loop: + cmp [esi], eax + je .found_it + add esi, ARP_ENTRY.size + loop .scan_loop + + .not_in_list: + DEBUGF 1,"IP not found on list, preparing for ARP request\n" + +;-------------------- +; Send an ARP request + + push eax + + pushw ARP_REQUEST_TTL + pushw ARP_AWAITING_RESPONSE + pushd 0 + pushw 0 + pushd eax + mov esi, esp + call ARP_add_entry + add esp, ARP_ENTRY.size + + cmp eax, -1 + je .full + + pop eax + + call ARP_output_request + + mov eax, -2 ; request send + ret + + .found_it: + DEBUGF 1,"found IP in ARPTable\n" + cmp [esi + ARP_ENTRY.Status], 1 + jne .invalid + + movzx eax, word [esi+ARP_ENTRY.MAC] + mov ebx, dword[esi+ARP_ENTRY.MAC+2] + ret + + .invalid: + mov eax, -1 + ret + + .full: + DEBUGF 1,"ARP table is full!\n" + pop eax + mov eax, -1 + ret + + .broadcast: + mov eax, 0x0000ffff + mov ebx, 0xffffffff + ret + ;----------------------------------------------------------------- @@ -643,7 +589,7 @@ ARP_API: ; edi = pointer to buffer ; ecx = # entry imul ecx, ARP_ENTRY.size - add ecx, ARPTable + add ecx, ARP_table mov esi, ecx mov ecx, ARP_ENTRY.size/2 rep movsw @@ -653,16 +599,15 @@ ARP_API: .write: ; esi = pointer to buffer - sub esp, ARP_ENTRY.size - mov edi, esp - mov ecx, ARP_ENTRY.size/2 - rep movsw call ARP_add_entry ;out: eax = entry number, -1 on error ret .remove: ; ecx = # entry - mov esi, ecx + cmp ecx, [NumARP] + jge .error + imul ecx, ARP_ENTRY.size + lea esi, [ARP_table + ecx] call ARP_del_entry ret diff --git a/kernel/branches/net/network/IPv4.inc b/kernel/branches/net/network/IPv4.inc index 59e288046e..b49d2c66ac 100644 --- a/kernel/branches/net/network/IPv4.inc +++ b/kernel/branches/net/network/IPv4.inc @@ -53,6 +53,73 @@ struct FRAGMENT_entry ; This structure will replace the ethernet header .Data: ; Ip header begins here (we will need the IP header to re-construct the complete packet) ends + + + +macro IPv4_checksum ptr { + +; This is the fast procedure to create or check a IP header without options +; To create a new checksum, the checksum field must be set to 0 before computation +; To check an existing checksum, leave the checksum as is, and it will be 0 after this procedure, if it was correct + + push ebx + xor ebx, ebx + add bl, [ptr+1] + adc bh, [ptr+0] + + adc bl, [ptr+3] + adc bh, [ptr+2] + + adc bl, [ptr+5] + adc bh, [ptr+4] + + adc bl, [ptr+7] + adc bh, [ptr+6] + + adc bl, [ptr+9] + adc bh, [ptr+8] + +; we skip 11th and 12th byte, they are the checksum bytes and should be 0 for re-calculation + + adc bl, [ptr+13] + adc bh, [ptr+12] + + adc bl, [ptr+15] + adc bh, [ptr+14] + + adc bl, [ptr+17] + adc bh, [ptr+16] + + adc bl, [ptr+19] + adc bh, [ptr+18] + + adc ebx, 0 + + push ecx + mov ecx, ebx + shr ecx, 16 + and ebx, 0xffff + add ebx, ecx + + mov ecx, ebx + shr ecx, 16 + add ebx, ecx + + not bx + jnz .not_zero + dec bx + .not_zero: + xchg bl, bh + pop ecx + + neg word [ptr+10] ; zero will stay zero so we just get the checksum + add word [ptr+10], bx ; , else we will get (new checksum - old checksum) in the end, wich should be 0 :) + pop ebx + +} + + + align 4 uglobal @@ -74,30 +141,49 @@ endg ; ; This function resets all IP variables ; -; IN: / -; OUT: / -; ;----------------------------------------------------------------- -align 4 -IPv4_init: +macro IPv4_init { - or eax, -1 + xor eax, eax mov edi, IP_LIST mov ecx, 4*MAX_IP rep stosd - inc eax mov edi, FRAGMENT_LIST mov ecx, FRAGMENT_slot.size*MAX_FRAGMENTS/4 + 2*MAX_IP rep stosd - ret +} + + +;----------------------------------------------------------------- +; +; Decrease TimeToLive of all fragment slots +; +;----------------------------------------------------------------- +macro IPv4_decrease_fragment_ttls { + +local .loop + + mov esi, FRAGMENT_LIST + mov ecx, MAX_FRAGMENTS + .loop: + cmp [esi + FRAGMENT_slot.ttl], 0 + je .try_next + dec [esi + FRAGMENT_slot.ttl] + jnz .try_next + DEBUGF 1,"Fragment slot timed-out!\n" +;;; TODO: clear all entry's of timed-out slot + .try_next: + add esi, 4 + loop .loop +} ;----------------------------------------------------------------- ; -; IPv4_Handler: +; IPv4_input: ; ; Will check if IP Packet isnt damaged ; and call appropriate handler. (TCP/UDP/ICMP/..) @@ -112,7 +198,7 @@ IPv4_init: ; ;----------------------------------------------------------------- align 4 -IPv4_handler: ; TODO: implement handler for IP options +IPv4_input: ; TODO: implement handler for IP options ; TODO2: add code for raw sockets DEBUGF 1,"IPv4_Handler, packet from: %u.%u.%u.%u ",\ @@ -137,13 +223,8 @@ IPv4_handler: ; TODO: implement handler for IP options ;------------------------------- ; Now, re-calculate the checksum - push edx ebx - mov esi, edx - call IPv4_checksum - pop ebx edx - - cmp [edx + IPv4_Packet.HeaderChecksum], 0 - jne .dump ; if checksum isn't valid then dump packet + IPv4_checksum edx + jnz .dump ; if checksum isn't valid then dump packet DEBUGF 1,"IPv4 Checksum is correct\n" @@ -435,10 +516,7 @@ IPv4_handler: ; TODO: implement handler for IP options ; mov esi, edx ; This prints the IP packet to the debug board (usefull when using serial output debug..) ; ; -; @@: ; -; lodsb ; -; DEBUGF 1,"%x ", eax:2 ; -; loop @r ; +; packet_to_debug jmp .handle_it ; edx = buf ptr, ecx = size, [esp] buf ptr, [esp+4], total size, ebx=device ptr @@ -460,9 +538,6 @@ IPv4_handler: ; TODO: implement handler for IP options - - - ;----------------------------------------------------------------- ; ; find fragment slot @@ -501,70 +576,15 @@ IPv4_find_fragment_slot: ret -;----------------------------------------------------------------- -; -; Decrease TimeToLive of all fragment slots -; -; IN: / -; OUT: / -; -;----------------------------------------------------------------- -align 4 -IPv4_decrease_fragment_ttls: - - mov esi, FRAGMENT_LIST - mov ecx, MAX_FRAGMENTS - .loop: - cmp [esi + FRAGMENT_slot.ttl], 0 - je .try_next - dec [esi + FRAGMENT_slot.ttl] - jnz .try_next - DEBUGF 1,"Fragment slot timed-out!\n" -;;; TODO: clear all entry's of timed-out slot - .try_next: - add esi, 4 - loop .loop - ret - - - - - ;------------------------------------------------------------------ ; -; -; 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 +; IPv4_output ; ; IN: eax = dest ip ; ebx = source ip ; ecx = data length -; dx = fragment id ;;;; -; di = protocol +; dx = fragment id +; di = TTL shl 8 + protocol ; ; OUT: eax = pointer to buffer start ; ebx = pointer to device struct (needed for sending procedure) @@ -574,184 +594,224 @@ IPv4_fragment: ; ;------------------------------------------------------------------ align 4 -IPv4_create_packet: +IPv4_output: - DEBUGF 1,"Create IPv4 Packet (size=%u)\n", ecx + DEBUGF 1,"IPv4_create_packet: size=%u\n", ecx - cmp ecx, 65500 ; Max IPv4 packet size - jg .exit_ - - test ebx, ebx ; if source ip = 0 - jnz .ip_ok ; and local ip is valid - ; use local ip instead - cmp [IP_LIST],0xffffffff ; - je .ip_ok ; TODO: find solution to send broadcast - ; on device other then device 0 - mov ebx, [IP_LIST] ; - ; - .ip_ok: ; + cmp ecx, 65500 ; Max IPv4 packet size + jg .too_large push ecx eax ebx dx di - cmp eax, -1 - je .broadcast ; If it is broadcast, just send - call ARP_IP_to_MAC + test eax, 0xffff0000 ; error bits + jnz .arp_error + + push ebx ; push the mac + push ax + + call IPv4_dest_to_dev + inc [IP_PACKETS_TX+edi] + mov ebx, [NET_DRV_LIST+edi] + lea eax, [ebx + ETH_DEVICE.mac] + mov edx, esp + mov ecx, [esp + 18] + add ecx, IPv4_Packet.DataOrOptional + mov di , ETHER_IPv4 + call ETH_output + jz .error + + add esp, 6 ; pop the mac + + mov [edi + IPv4_Packet.VersionAndIHL], 0x45 ; IPv4, normal length (no Optional header) + mov [edi + IPv4_Packet.TypeOfService], 0 ; nothing special, just plain ip packet + mov [edi + IPv4_Packet.TotalLength], cx + rol [edi + IPv4_Packet.TotalLength], 8 ; internet byte order + mov [edi + IPv4_Packet.FlagsAndFragmentOffset], 0x0000 + mov [edi + IPv4_Packet.HeaderChecksum], 0 + popw word [edi + IPv4_Packet.TimeToLive] ; ttl shl 8 + protocol +; [edi + IPv4_Packet.Protocol] + popw [edi + IPv4_Packet.Identification] ; fragment id + popd [edi + IPv4_Packet.SourceAddress] + popd [edi + IPv4_Packet.DestinationAddress] + + pop ecx + + IPv4_checksum edi + add edi, IPv4_Packet.DataOrOptional + DEBUGF 1,"IPv4 Packet for device %x created successfully\n", ebx + ret + + .error: + add esp, 6 + .arp_error: + add esp, 4+4+4+2+2 + .too_large: + DEBUGF 1,"IPv4_create_packet: Failed\n" + sub edi, edi + ret + + +;-------------------------------------------------------- +; +; +; IN: dword [esp] = pointer to buffer containing ipv4 packet to be fragmented +; dword [esp+4] = buffer size +; esi = pointer to ip header in that buffer +; ecx = max size of fragments +; +; OUT: / +; +;-------------------------------------------------------- + +align 4 +IPv4_fragment: + + DEBUGF 1,"IPv4_fragment\n" + + and ecx, not 111b ; align 4 + + cmp ecx, IPv4_Packet.DataOrOptional + 8 ; must be able to put at least 8 bytes + jl .err2 + + push esi ecx + mov eax, [esi + IPv4_Packet.DestinationAddress] + call ARP_IP_to_MAC + pop ecx esi cmp eax, -1 - je .not_found + jz .err2 push ebx push ax - jmp .send + mov ebx, [NET_DRV_LIST] + lea eax, [ebx + ETH_DEVICE.mac] + push eax - .broadcast: - push word -1 - push dword -1 - .send: - call IPv4_dest_to_dev - inc [IP_PACKETS_TX+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 + push esi ; ptr to ip header + sub ecx, 20 ; substract header size + push ecx ; max data size + push dword 0 ; offset + + .new_fragment: + DEBUGF 1,"Ipv4_fragment - new_fragmentn" + + + mov eax, [esp + 3*4] + lea ebx, [esp + 4*4] mov di , ETHER_IPv4 - ;;; 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 - test edi, edi - jz .exit + call ETH_output - mov [edi + IPv4_Packet.VersionAndIHL], 0x45 ; IPv4, normal length (no Optional header) - mov [edi + IPv4_Packet.TypeOfService], 0 - xchg ch, cl + cmp edi, -1 + jz .err + +; copy header + mov esi, [esp + 2*4] + mov ecx, 5 ; 5 dwords: TODO: use IHL field of the header! + rep movsd + +; copy data + mov esi, [esp + 2*4] + add esi, 20 + add esi, [esp] ; offset + + mov ecx, [esp + 1*4] + DEBUGF 1,"IPv4_fragment - copying data (%u bytes)\n", ecx + rep movsb + +; now, correct header + mov ecx, [esp + 1*4] + add ecx, 20 + xchg cl, ch mov [edi + IPv4_Packet.TotalLength], cx - mov [edi + IPv4_Packet.FlagsAndFragmentOffset], 0x0000 - mov [edi + IPv4_Packet.TimeToLive], 128 + + mov ecx, [esp] ; offset + xchg cl, ch + +; cmp dword[esp + 4*4], 0 ; last fragment?;<<<<<< +; je .last_fragment + or cx, 1 shl 2 ; more fragments +; .last_fragment: + mov [edi + IPv4_Packet.FlagsAndFragmentOffset], cx + mov [edi + IPv4_Packet.HeaderChecksum], 0 - pop cx - mov [edi + IPv4_Packet.Protocol], cl - pop cx - mov [edi + IPv4_Packet.Identification], cx - pop ecx - mov [edi + IPv4_Packet.SourceAddress], ecx - pop ecx - mov [edi + IPv4_Packet.DestinationAddress], ecx - push eax edx esi - mov esi, edi - call IPv4_checksum - pop esi edx eax ecx - add edi, IPv4_Packet.DataOrOptional + ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<< send the packet + mov ecx, [esp + 1*4] - DEBUGF 1,"IPv4 Packet for device %x created successfully\n", ebx + push edx eax + IPv4_checksum edi + + call [ebx + NET_DEVICE.transmit] + ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + mov ecx, [esp+4] + add [esp], ecx + + mov ecx, [esp+3*4+6+4] ; ptr to begin of buff + add ecx, [esp+3*4+6+4+4] ; buff size + sub ecx, [esp+2*4] ; ptr to ip header + add ecx, [esp] ; offset + + DEBUGF 1,"Ipv4_fragment - bytes remaining: %u\n", ecx + + cmp ecx, [esp+1*4] + jge .new_fragment + + mov [esp+4], ecx ; set fragment size to remaining packet size + jmp .new_fragment + + .err: + DEBUGF 1,"Ipv4_fragment - failed\n" + .done: + add esp, 12 + 4 + 6 + .err2: + DEBUGF 1,"Ipv4_fragment - dumping\n" + call kernel_free + add esp, 4 ret - .not_found: - DEBUGF 1,"Create IPv4 Packet - ARP entry not found!\n" - ;;;;;; - .exit: - add esp, 16 - .exit_: - DEBUGF 1,"Create IPv4 Packet - failed\n" - and edi, 0 - ret - - - -align 4 -IPv4_checksum: - -; This is the fast procedure to create or check a IP header without options -; -; To create a new checksum, the checksum field must be set to 0 before computation -; -; To check an existing checksum, leave the checksum as is, and it will be 0 after this procedure, if it was correct - - xor edx, edx - - add dl, [esi+1] - adc dh, [esi+0] - - adc dl, [esi+3] - adc dh, [esi+2] - - adc dl, [esi+5] - adc dh, [esi+4] - - adc dl, [esi+7] - adc dh, [esi+6] - - adc dl, [esi+9] - adc dh, [esi+8] - -; we skip 11th and 12th byte, they are the checksum bytes and should be 0 for re-calculation - - adc dl, [esi+13] - adc dh, [esi+12] - - adc dl, [esi+15] - adc dh, [esi+14] - - adc dl, [esi+17] - adc dh, [esi+16] - - adc dl, [esi+19] - adc dh, [esi+18] - - adc edx, 0 - - call checksum_2 - - neg word [esi+10] ; zero will stay zero so we just get the checksum - add word [esi+10], dx ; , else we will get (new checksum - old checksum) in the end, wich should be 0 :) - - ret - - - - ;--------------------------------------------------------------------------- ; ; IPv4_dest_to_dev ; -; IN: Destination IP in eax -; OUT: device id in edi +; IN: eax = Destination IP +; OUT: edi = device id * 4 ; ;--------------------------------------------------------------------------- align 4 IPv4_dest_to_dev: - DEBUGF 1,"IPv4 destination to device: " + cmp eax, 0xffffffff + je .invalid xor edi, edi mov ecx, MAX_IP .loop: - mov ebx, [IP_LIST+edi] ; we dont need to worry about non exisiting ip interfaces - and ebx, [SUBNET_LIST+edi] ; they have IP and SUBNET set to all one's, so they will have no match except 255.255.255.255 - ; (only a moron would insert that ip into this function..) + mov ebx, [IP_LIST+edi] + and ebx, [SUBNET_LIST+edi] + jz .next + mov edx, eax and edx, [SUBNET_LIST+edi] cmp ebx, edx je .found_it - + .next: add edi, 4 loop .loop - xor edi, edi ; if none found, use device 0 as default device + .invalid: + xor edi, edi ; if none found, use device 0 as default device .found_it: - shr edi, 2 - - DEBUGF 1,"%u\n",edi + DEBUGF 1,"IPv4_dest_to_dev: %u\n", edi ret @@ -868,8 +928,4 @@ IPv4_API: add eax, GATEWAY_LIST mov [eax], ecx xor eax, eax - ret - - - - + ret \ No newline at end of file diff --git a/kernel/branches/net/network/ethernet.inc b/kernel/branches/net/network/ethernet.inc index 6f3309e1ec..baf79a1cb8 100644 --- a/kernel/branches/net/network/ethernet.inc +++ b/kernel/branches/net/network/ethernet.inc @@ -56,37 +56,33 @@ endg ; ; This function resets all ethernet variables ; -; IN: / -; OUT: / -; ;----------------------------------------------------------------- -align 4 -ETH_init: +macro ETH_init { mov [ETH_RUNNING], 0 - ret +} ;----------------------------------------------------------------- ; -; ETH_Receiver: +; ETH_input ; ; This function is called by ethernet drivers, ; It pushes the received ethernet packets onto the eth_in_queue ; ; IN: [esp] = Pointer to buffer -; [esp-4] = size of buffer +; [esp+4] = size of buffer ; ebx = pointer to eth_device ; OUT: / ; ;----------------------------------------------------------------- align 4 -ETH_receiver: +ETH_input: mov eax, [esp] mov ecx, [esp+4] - DEBUGF 1,"ETH_Handler - size: %u\n", ecx + DEBUGF 1,"ETH_input - size: %u\n", ecx cmp ecx, 60 ; check packet length jl .dump sub ecx, ETH_FRAME.Data @@ -95,10 +91,10 @@ ETH_receiver: mov ax , [eax + ETH_FRAME.Type] cmp ax, ETHER_IPv4 - je IPv4_handler + je IPv4_input cmp ax, ETHER_ARP - je ARP_handler + je ARP_input ; cmp ax, ETHER_PPP_DISCOVERY ; je PPPOE_discovery @@ -106,19 +102,19 @@ ETH_receiver: DEBUGF 2,"Unknown ethernet packet type %x\n", ax .dump: - DEBUGF 2,"ETH_Handler - dumping\n" + DEBUGF 2,"ETH_input - dumping\n" call kernel_free add esp, 4 ret ;----------------------------------------------------------------- ; -; ETH_create_packet +; ETH_output ; ; IN: eax = pointer to source mac -; ebx = pointer to destination mac +; ebx = device ptr ; ecx = packet size -; edx = device number +; edx = pointer to destination mac ; di = protocol ; ; OUT: edi = 0 on error, pointer to buffer otherwise @@ -129,62 +125,60 @@ ETH_receiver: ; ;----------------------------------------------------------------- align 4 -ETH_create_packet: +ETH_output: - DEBUGF 1,"Creating Ethernet Packet (size=%u): \n", ecx + DEBUGF 1,"ETH_output: size=%u device:%x\n", ecx, ebx - push esi - mov esi, [NET_DRV_LIST] ;;; TODO: FIXME - cmp ecx, [esi + NET_DEVICE.mtu] - pop esi + cmp ecx, [ebx + NET_DEVICE.mtu] jg .exit - push ecx di eax ebx edx - + push ecx ; << 1 + push di eax edx ; << 2 add ecx, ETH_FRAME.Data - push ecx - push ecx - call kernel_alloc + + push ecx ; << 3 + + push ecx ; << 4 + call kernel_alloc ; >> 4 + test eax, eax + jz .out_of_ram mov edi, eax - test edi, edi - jz .pop_exit - pop ecx - pop edx + pop ecx ; >> 3 - pop esi + pop esi ; >> 2 movsd movsw - pop esi + pop esi ; >> 2 movsd movsw - pop ax + pop ax ; >> 2 stosw lea eax, [edi - ETH_FRAME.Data] ; Set eax to buffer start - mov edx, ecx ; Set ebx to complete buffer size - pop ecx + mov edx, ecx ; Set edx to complete buffer size - xor ebx, ebx ;;;; TODO: Fixme - mov ebx, [NET_DRV_LIST + ebx] + pop ecx ; >> 1 - cmp edx, 46 + ETH_FRAME.Data ; If data size is less then 46, add padding bytes - jge .continue - mov edx, 46 + ETH_FRAME.Data - .continue: - - DEBUGF 1,"done: %x size:%u device:%x\n", eax, edx, ebx + cmp edx, 60-1 ; minimum ethernet packet size + jle .adjust_size + DEBUGF 1,"ETH_output: done: %x total size: %u\n", eax, edx ret - .pop_exit: - DEBUGF 2,"Out of ram space!!\n" - add esp, 18 - and edi, 0 + .adjust_size: + mov edx, 60 + ret + + .out_of_ram: + DEBUGF 2,"ETH_output: Out of ram space!!\n" + add esp, 3*4+2+4 + sub edi, edi ret .exit: - DEBUGF 2,"Packet too large!\n" - and edi, 0 + DEBUGF 2,"ETH_output: Packet too large!\n" + sub edi, edi +;;; dec edi ret diff --git a/kernel/branches/net/network/icmp.inc b/kernel/branches/net/network/icmp.inc index ddcea7c90f..4b8ece9848 100644 --- a/kernel/branches/net/network/icmp.inc +++ b/kernel/branches/net/network/icmp.inc @@ -109,21 +109,16 @@ endg ; ; ICMP_init ; -; This function resets all ICMP variables -; -; IN: / -; OUT: / -; ;----------------------------------------------------------------- -align 4 -ICMP_init: + +macro ICMP_init { xor eax, eax mov edi, ICMP_PACKETS_TX mov ecx, 2*MAX_IP rep stosd - ret +} @@ -235,7 +230,7 @@ ICMP_input: call IPv4_dest_to_dev cmp edi,-1 je .dump - inc [ICMP_PACKETS_RX+4*edi] + inc [ICMP_PACKETS_RX+edi] DEBUGF 1,"Found valid ICMP packet for socket %x\n", esi @@ -284,10 +279,10 @@ ICMP_output: push esi edi edx add ecx, ICMP_Packet.Data - mov di , IP_PROTO_ICMP + mov di , IP_PROTO_ICMP SHL 8 + 128 ; TTL shr edx, 16 - call IPv4_create_packet + call IPv4_output jz .exit DEBUGF 1,"full icmp packet size: %u\n", edx diff --git a/kernel/branches/net/network/socket.inc b/kernel/branches/net/network/socket.inc index 281d515ba5..758f9b31ee 100644 --- a/kernel/branches/net/network/socket.inc +++ b/kernel/branches/net/network/socket.inc @@ -5,11 +5,13 @@ ;; ;; ;; SOCKET.INC ;; ;; ;; -;; Written by hidnplayr@kolibrios.org ;; -;; based on code by mike.dld ;; +;; Written by hidnplayr@kolibrios.org, ;; +;; and Clevermouse. ;; ;; ;; -;; GNU GENERAL PUBLIC LICENSE ;; -;; Version 2, June 1991 ;; +;; Based on code by mike.dld ;; +;; ;; +;; GNU GENERAL PUBLIC LICENSE ;; +;; Version 2, June 1991 ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -31,9 +33,7 @@ virtual at 0 .errorcode dd ? .options dd ? - .SO_SND.SB_CC dd ? ;;;;; socket options: number of bytes in socket - .SO_RCV.SB_CC dd ? - .state dd ? ;;;;;;;;; + .state dd ? .end: end virtual @@ -51,15 +51,6 @@ virtual at SOCKET.end .end: end virtual -virtual at SOCKET.end - - SOCKET_virtual: - - .ConnectedTo dd ? ; Socket number of other socket this one is connected to - - .end: -end virtual - virtual at IP_SOCKET.end TCP_SOCKET: @@ -114,7 +105,6 @@ virtual at IP_SOCKET.end ;---------------------- ; Transmit timing stuff - .t_idle dd ? .t_rtt dd ? .t_rtseq dd ? @@ -125,7 +115,6 @@ virtual at IP_SOCKET.end ;----------------- ; Out-of-band data - .t_oobflags dd ? .t_iobc dd ? .t_softerror dd ? @@ -133,7 +122,6 @@ virtual at IP_SOCKET.end ;--------- ; RFC 1323 - .SND_SCALE db ? ; Scale factor .RCV_SCALE db ? .request_r_scale db ? @@ -146,12 +134,11 @@ virtual at IP_SOCKET.end ;------- ; Timers - - .timer_retransmission dw ? ; rexmt + .timer_retransmission dw ? ; rexmt .timer_ack dw ? .timer_persist dw ? - .timer_keepalive dw ? ; keepalive/syn timeout - .timer_timed_wait dw ? ; also used as 2msl timer + .timer_keepalive dw ? ; keepalive/syn timeout + .timer_timed_wait dw ? ; also used as 2msl timer .end: end virtual @@ -176,9 +163,32 @@ virtual at IP_SOCKET.end .end: end virtual +struc RING_BUFFER { + .start_ptr dd ? ; Pointer to start of buffer + .end_ptr dd ? ; pointer to end of buffer + .read_ptr dd ? ; Read pointer + .write_ptr dd ? ; Write pointer + .size dd ? ; Number of bytes buffered +} + +virtual at 0 + + RING_BUFFER RING_BUFFER + +end virtual + +virtual at TCP_SOCKET.end + + rcv RING_BUFFER + snd RING_BUFFER + + STREAM_SOCKET: + .end: + +end virtual + struct socket_queue_entry -; .owner dd ? .data_ptr dd ? .buf_ptr dd ? .data_size dd ? @@ -186,7 +196,6 @@ struct socket_queue_entry 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 @@ -204,24 +213,41 @@ endg ; ; SOCKET_init ; -; - -; -; IN: / -; OUT: / -; ;----------------------------------------------------------------- -align 4 -socket_init: +macro SOCKET_init { 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 +;--- for random port -- - ret + mov al, 0x0 ; set up 1s timer + out 0x70, al + in al, 0x71 + +;---------------------- + + @@: + pseudo_random eax + cmp ax, MIN_EPHEMERAL_PORT + jl @r + cmp ax, MAX_EPHEMERAL_PORT + jg @r + + mov [last_UDP_port], ax + + @@: + pseudo_random eax + cmp ax, MIN_EPHEMERAL_PORT + jl @r + cmp ax, MAX_EPHEMERAL_PORT + jg @r + + mov [last_TCP_port], ax + +} ;----------------------------------------------------------------- @@ -232,11 +258,20 @@ socket_init: align 4 sys_socket: cmp ebx, 8 ; highest possible number - jg s_error - lea ebx, [.table + 4*ebx] + jg @f + lea ebx, [sock_sysfn_table + 4*ebx] jmp dword [ebx] + @@: + cmp ebx, 255 + jz SOCKET_debug -.table: +s_error: + DEBUGF 1,"socket error\n" + mov dword [esp+32], -1 + + ret + +sock_sysfn_table: dd SOCKET_open ; 0 dd SOCKET_close ; 1 dd SOCKET_bind ; 2 @@ -249,12 +284,6 @@ sys_socket: ; dd SOCKET_set_opt ; 9 -s_error: - DEBUGF 1,"socket error\n" - mov dword [esp+32], -1 - - ret - ;----------------------------------------------------------------- ; @@ -280,6 +309,40 @@ SOCKET_open: mov [esp+32], edi + cmp ecx, AF_INET4 + jnz .no_stream + + push [IP_LIST] ;;;; + pop [eax + IP_SOCKET.LocalIP] ;;;; + + cmp edx, IP_PROTO_TCP + jnz .no_stream + + mov esi, eax + stdcall kernel_alloc, SOCKET_MAXDATA + mov [esi + rcv.start_ptr], eax + mov [esi + rcv.write_ptr], eax + mov [esi + rcv.read_ptr], eax + mov [esi + rcv.size], 0 + add eax, SOCKET_MAXDATA + mov [esi + rcv.end_ptr], eax + + stdcall kernel_alloc, SOCKET_MAXDATA + mov [esi + snd.start_ptr], eax + mov [esi + snd.write_ptr], eax + mov [esi + snd.read_ptr], eax + mov [esi + snd.size], 0 + add eax, SOCKET_MAXDATA + mov [esi + snd.end_ptr], eax + + ret + + .no_stream: + + push edi + init_queue (eax + SOCKET_QUEUE_LOCATION) + pop edi + ret @@ -347,9 +410,6 @@ SOCKET_bind: DEBUGF 1,"using local port: %u\n", bx mov word [eax + UDP_SOCKET.LocalPort], bx - mov ebx, dword [edx + 4] - mov dword [eax + IP_SOCKET.LocalIP], ebx - DEBUGF 1,"local ip: %u.%u.%u.%u\n",\ [eax + IP_SOCKET.LocalIP + 0]:1,[eax + IP_SOCKET.LocalIP + 1]:1,\ [eax + IP_SOCKET.LocalIP + 2]:1,[eax + IP_SOCKET.LocalIP + 3]:1 @@ -411,21 +471,11 @@ SOCKET_connect: .tcp: - ; set sequence number - - mov ebx, [TCP_sequence_num] - add [TCP_sequence_num], 6400 - mov [eax + TCP_SOCKET.ISS], ebx - - mov [eax + TCP_SOCKET.timer_keepalive], 120 ; 120*630ms => 75,6 seconds - lea ebx, [eax + SOCKET.lock] call wait_mutex ; fill in remote port and IP -;;;;;; mov [eax + TCP_SOCKET.wndsizeTimer], 0 ; Reset the window timer. - mov bx , word [edx + 2] mov [eax + TCP_SOCKET.RemotePort], bx DEBUGF 1,"remote port: %u\n", bx @@ -435,21 +485,25 @@ SOCKET_connect: ; check if local port and IP is ok - cmp [eax + IP_SOCKET.LocalIP], 0 - jne @f - push [IP_LIST] ;;;;; device zero = default - pop [eax + IP_SOCKET.LocalIP] - @@: - cmp [eax + TCP_SOCKET.LocalPort], 0 jne @f call SOCKET_find_port @@: + DEBUGF 1,"local port: %u\n", [eax + TCP_SOCKET.LocalPort]:2 - DEBUGF 1,"remote port: %u\n", [eax + TCP_SOCKET.LocalPort]:2 +;;;;; + mov [eax + TCP_SOCKET.timer_persist], 0 + mov [eax + TCP_SOCKET.t_state], TCB_SYN_SENT + mov ebx, [TCP_sequence_num] + add [TCP_sequence_num], 6400 + mov [eax + TCP_SOCKET.ISS], ebx + mov [eax + TCP_SOCKET.timer_keepalive], 120 ; 120*640ms => 75,6 seconds -; mov [eax + TCP_SOCKET.t_state], TCB_SYN_SENT +;;;; mov [ebx + TCP_SOCKET.timer_retransmission], + + push eax call TCP_output + pop eax mov [eax + SOCKET.lock], 0 @@ -583,11 +637,11 @@ SOCKET_close: test [eax + TCP_SOCKET.t_state], TCB_SYN_RECEIVED ;;;;;; jz .free - call TCP_output + ;;; call TCP_output - mov dword [esp+32], 0 + ;;; mov dword [esp+32], 0 - ret + ;;; ret ; state must be LISTEN, SYN_SENT, CLOSED or maybe even invalid ; so, we may destroy the socket @@ -688,6 +742,12 @@ SOCKET_send: .af_inet4: DEBUGF 1,"af_inet4\n" + cmp [eax + IP_SOCKET.LocalIP], 0 + jne @f + mov ebx, [IP_LIST] ;;;; + mov dword [eax + IP_SOCKET.LocalIP], ebx + @@: + cmp [eax + SOCKET.Type], IP_PROTO_TCP je .tcp @@ -790,6 +850,35 @@ SOCKET_get_opt: ret + +;----------------------------------------------------------------- +; +; SOCKET_debug +; +; Copies socket variables to application buffer +; +; IN: ecx = socket number +; edx = pointer to buffer +; +; OUT: -1 on error +;----------------------------------------------------------------- +align 4 +SOCKET_debug: + + DEBUGF 1,"socket_debug\n" + + call SOCKET_num_to_ptr + jz s_error + + mov esi, eax + mov edi, edx + mov ecx, SOCKETBUFFSIZE/4 + rep movsd + + mov dword [esp+32], 0 + ret + + ;----------------------------------------------------------------- ; ; SOCKET_find_port @@ -939,6 +1028,188 @@ SOCKET_input: ret +;----------------------------------------------------------------- +; +; SOCKET_ring_add +; +; Adds data to a stream socket +; +; IN: eax = ptr to ring struct +; ecx = data size +; esi = ptr to data +; +; OUT: eax = number of bytes stored +; +;----------------------------------------------------------------- +align 4 +SOCKET_ring_add: + + DEBUGF 1,"SOCKET_ring_add: ringbuff=%x ptr=%x size=%u\n", eax, esi, ecx + + mov edi, [eax + RING_BUFFER.size] + add edi, ecx + cmp edi, SOCKET_MAXDATA + jg .too_large + + mov [eax + RING_BUFFER.size], edi ; update size + .copy: + push ecx ;<<<< 1 + mov edi, [eax + RING_BUFFER.write_ptr] ; set write ptr in edi + add [eax + RING_BUFFER.write_ptr], ecx ; update write pointer + mov edx, [eax + RING_BUFFER.end_ptr] + cmp edx, [eax + RING_BUFFER.write_ptr] + jg .copy_in_2 + je .wrap_write_ptr + + .copy_more: + push ecx + and ecx, 3 + rep movsb + pop ecx + shr ecx, 2 + rep movsd + pop ecx ; >>>> 1/2 + DEBUGF 2,"Copied %u bytes\n", ecx + + ret + + .too_large: + mov ecx, SOCKET_MAXDATA ; calculate number of bytes available in buffer + sub ecx, [eax + RING_BUFFER.size] + jz .full + + mov [eax + RING_BUFFER.size], SOCKET_MAXDATA ; update size, we will fill buffer completely + + jmp .copy + + .full: + DEBUGF 2,"Ring buffer is full!\n" + xor ecx, ecx + ret + + .copy_in_2: + DEBUGF 1,"Copying in 2 passes\n" + + mov edx, ecx + mov ecx, [eax + RING_BUFFER.end_ptr] ; find number of bytes till end of buffer + sub ecx, edi + sub edx, ecx + push edx ; <<<< 2 + + mov edi, [eax + RING_BUFFER.start_ptr] + call .copy_more + + .wrap_write_ptr: + sub [eax + RING_BUFFER.write_ptr], SOCKET_MAXDATA ; update write pointer + jmp .copy_more + + + + +;----------------------------------------------------------------- +; +; SOCKET_ring_read +; +; reads the data, but let the data remain in the buffer +; +; IN: eax = ptr to ring struct +; ecx = buffer size +; edi = ptr to buffer +; +; OUT: eax = number of bytes read +; +;----------------------------------------------------------------- +align 4 +SOCKET_ring_read: + + DEBUGF 1,"SOCKET_ring_read: ringbuff=%x ptr=%x size=%u\n", eax, esi, ecx + + cmp [eax + RING_BUFFER.size], ecx ; update size + jl .too_large + + mov esi, [eax + RING_BUFFER.read_ptr] ; update read ptr + .copy: + push ecx ;<<<< 1 + mov edx, [eax + RING_BUFFER.read_ptr] + add edx, ecx + cmp edx, [eax + RING_BUFFER.end_ptr] + jg .copy_in_2 + + .copy_more: + push ecx + and ecx, 3 + rep movsb + pop ecx + shr ecx, 2 + rep movsd + pop ecx ; >>>> 1/2 + DEBUGF 2,"Copied %u bytes\n", ecx + + ret + + .too_large: + mov ecx, [eax + RING_BUFFER.size] + jmp .copy + + .full: + DEBUGF 2,"Ring buffer is full!\n" + xor ecx, ecx + ret + + .copy_in_2: + DEBUGF 1,"Copying in 2 passes\n" + + mov edx, ecx + mov ecx, [eax + RING_BUFFER.end_ptr] ; find number of bytes till end of buffer + sub ecx, edi + sub edx, ecx + push edx ; <<<< 2 + + mov esi, [eax + RING_BUFFER.start_ptr] + call .copy_more + + +;----------------------------------------------------------------- +; +; SOCKET_ring_free +; +; Free's some bytes from the ringbuffer +; +; IN: eax = ptr to ring struct +; ecx = data size +; +; OUT: ecx = number of bytes free-ed +; +;----------------------------------------------------------------- +align 4 +SOCKET_ring_free: + + DEBUGF 1,"Trying to free %u bytes of data from ring %x\n", ecx, eax + + cmp ecx, [eax + RING_BUFFER.size] + jle .go_for_it + + cmp ecx, SOCKET_MAXDATA ;;;; + jg .moron_input + + mov ecx, [eax + RING_BUFFER.size] + + .go_for_it: + sub [eax + RING_BUFFER.size], ecx + add [eax + RING_BUFFER.read_ptr], ecx + + mov edx, [eax + RING_BUFFER.end_ptr] + cmp [eax + RING_BUFFER.read_ptr], edx + jl @f + sub [eax + RING_BUFFER.read_ptr], SOCKET_MAXDATA ;;;;; + @@: + ret + + .moron_input: + xor ecx, ecx + ret + + ;----------------------------------------------------------------- ; ; SOCKET_notify_owner @@ -1011,7 +1282,7 @@ SOCKET_alloc: push ecx ebx stdcall kernel_alloc, SOCKETBUFFSIZE - DEBUGF 1, "socket_alloc: %x ", eax + DEBUGF 1, "SOCKET_alloc: ptr=%x\n", eax or eax, eax jz .exit @@ -1023,17 +1294,14 @@ SOCKET_alloc: rep stosd pop edi eax - init_queue (eax + SOCKET_QUEUE_LOCATION) - ; find first free socket number and use it - mov ebx, net_sockets xor ecx, ecx .next_socket_number: inc ecx .next_socket: mov ebx, [ebx + SOCKET.NextPtr] - or ebx, ebx + test ebx, ebx jz .last_socket cmp [ebx + SOCKET.Number], ecx jne .next_socket @@ -1042,23 +1310,21 @@ SOCKET_alloc: .last_socket: mov [eax + SOCKET.Number], ecx - - DEBUGF 1, "(number: %u)\n", ecx + DEBUGF 1, "SOCKET_alloc: number=%u\n", ecx + mov edi, 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 - +; add socket to the list by re-arranging some pointers mov ebx, [net_sockets + SOCKET.NextPtr] mov [eax + SOCKET.PrevPtr], net_sockets mov [eax + SOCKET.NextPtr], ebx - or ebx, ebx + test ebx, ebx jz @f add ebx, SOCKET.lock ; lock the next socket call wait_mutex @@ -1068,9 +1334,7 @@ SOCKET_alloc: @@: mov [net_sockets + SOCKET.NextPtr], eax - - mov edi, ecx - or eax, eax ; used to clear zero flag + or eax, eax ; used to clear zero flag .exit: pop ebx ecx @@ -1101,6 +1365,16 @@ SOCKET_free: DEBUGF 1, "freeing socket..\n" + cmp [eax + SOCKET.Domain], AF_INET4 + jnz .no_stream + + cmp [eax + SOCKET.Type], IP_PROTO_TCP + jnz .no_stream + + stdcall kernel_free, [eax + rcv.start_ptr] + stdcall kernel_free, [eax + snd.start_ptr] + .no_stream: + push eax ; this will be passed to kernel_free mov ebx, [eax + SOCKET.NextPtr] mov eax, [eax + SOCKET.PrevPtr] @@ -1126,6 +1400,39 @@ SOCKET_free: ret +; socket nr in ebx +; new socket nr in eax +; preserver edx + +align 4 +SOCKET_fork: + +;; 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 + +;; 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 + +;;;; mov [eax + IP_SOCKET.RemoteIP], esi ; IP source address + + ret + + ;--------------------------------------------------- ; ; SOCKET_num_to_ptr diff --git a/kernel/branches/net/network/stack.inc b/kernel/branches/net/network/stack.inc index 856160ee8b..919715ad6a 100644 --- a/kernel/branches/net/network/stack.inc +++ b/kernel/branches/net/network/stack.inc @@ -66,14 +66,16 @@ SOCK_DGRAM equ 2 SOCK_RAW equ 3 ; Socket options -SO_ACCEPTCON equ 1 +SO_ACCEPTCON equ 1 -SOCKET_MAXDATA equ 4096 +SOCKET_MAXDATA equ 4096*32 ; must be 4096*(power of 2) where 'power of 2' is at least 8 ; Network driver types NET_TYPE_ETH equ 1 NET_TYPE_SLIP equ 2 +MAX_backlog equ 20 ; backlog for stream sockets + virtual at 0 @@ -92,6 +94,8 @@ virtual at 0 .packets_tx dd ? ; .packets_rx dd ? ; +; .chksum dd ? ; bitmask stating available checksum routines on hardware + .end: end virtual @@ -102,6 +106,7 @@ macro pseudo_random reg { add reg, [esp] rol reg, 5 xor reg, [timer_ticks] + add reg, [CPU_FREQ] imul reg, 214013 xor reg, 0xdeadbeef rol reg, 9 @@ -111,7 +116,7 @@ macro ntohld reg { rol word reg, 8 rol dword reg, 16 - rol word reg, 8 + rol word reg , 8 } @@ -122,6 +127,18 @@ macro ntohlw reg { } +macro packet_to_debug { ; set esi to packet you want to print, ecx to number of bytes + +local .loop + + .loop: + lodsb + DEBUGF 1,"%x ", eax:2 + loop @r + +} + + include "queue.inc" include "ethernet.inc" @@ -161,25 +178,23 @@ 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 + ETH_init +; SLIP_init +; PPPOE_init - call ETH_init -; call SLIP_init + IPv4_init + ICMP_init - call IPv4_init - call ICMP_init + ARP_init + UDP_init + TCP_init - call ARP_init - call UDP_init - call TCP_init - - call socket_init + SOCKET_init mov [net_tmr_count], 0 @@ -211,14 +226,14 @@ stack_handler: test [net_10ms], 0x0f ; 160ms jnz .exit -; call TCP_timer_160ms + TCP_timer_160ms test [net_10ms], 0x3f ; 640ms jnz .exit -; call TCP_timer_640ms - call ARP_decrease_entry_ttls - call IPv4_decrease_fragment_ttls + TCP_timer_640ms + ARP_decrease_entry_ttls + IPv4_decrease_fragment_ttls .exit: ret @@ -275,13 +290,13 @@ NET_add_device: jmp .error .ethernet: - DEBUGF 1,"Trying to add an ethernet driver\n" + DEBUGF 1,"Trying to add an ethernet device\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" + DEBUGF 1,"Trying to add a slip device\n" ;;;; jmp .error @@ -490,15 +505,15 @@ checksum_2: mov ecx, edx shr ecx, 16 - add edx, ecx - + add dx, cx + test dx, dx ; it seems that ZF is not set when CF is set :( not dx jnz .not_zero dec dx .not_zero: xchg dl, dh - DEBUGF 1,"Checksum: %x\n",dx + DEBUGF 1,"Checksum: %x\n", dx ret diff --git a/kernel/branches/net/network/tcp.inc b/kernel/branches/net/network/tcp.inc index 54830ed564..6f095f1dca 100644 --- a/kernel/branches/net/network/tcp.inc +++ b/kernel/branches/net/network/tcp.inc @@ -73,6 +73,9 @@ TCP_time_rtt_default equ 5 ; default Round Trip Time (3,2s) TCP_max_rxtshift equ 12 ; max retransmissions waiting for ACK TCP_max_keepcnt equ 8 ; max keepalive probes +; +TCP_max_winshift equ 14 +TCP_max_win equ 65535 struct TCP_segment .SourcePort dw ? @@ -117,29 +120,28 @@ endg ; ; This function resets all TCP variables ; -; IN: / -; OUT: / -; ;----------------------------------------------------------------- -align 4 -TCP_init: +macro TCP_init { xor eax, eax mov edi, TCP_segments_tx mov ecx, (6*IP_MAX_INTERFACES) rep stosd - mov [TCP_sequence_num], 1 + pseudo_random eax + mov [TCP_sequence_num], eax - ret +} ;---------------------- ; ; ;---------------------- -align 4 -TCP_timer_160ms: +macro TCP_timer_160ms { + +local .loop +local .exit mov eax, net_sockets .loop: @@ -156,22 +158,24 @@ TCP_timer_160ms: DEBUGF 1,"TCP ack for socket %x expired, time to piggyback!\n", eax push eax - call TCP_respond + call TCP_respond_socket pop eax jmp .loop .exit: - ret +} ;----------------------------------------------------------------- ; ; ;----------------------------------------------------------------- -align 4 -TCP_timer_640ms: +macro TCP_timer_640ms { + +local .loop +local .exit ; Update TCP sequence number @@ -190,7 +194,8 @@ TCP_timer_640ms: cmp [eax + SOCKET.Type], IP_PROTO_TCP jne .loop - dec [eax + TCP_SOCKET.timer_retransmission] + inc [eax + TCP_SOCKET.t_idle] + dec [eax + TCP_SOCKET.timer_retransmission] jnz .check_more2 DEBUGF 1,"socket %x: Retransmission timer expired\n", eax @@ -221,7 +226,45 @@ TCP_timer_640ms: jmp .loop .exit: - ret +} + + + + +macro TCP_checksum IP1, IP2 { + +;------------- +; Pseudoheader + + ; protocol type + mov edx, IP_PROTO_TCP + + ; source address + add dl, byte [IP1+1+4] + adc dh, byte [IP1+0+4] + adc dl, byte [IP1+3+4] + adc dh, byte [IP1+2+4] + + ; destination address + adc dl, byte [IP2+1+8] + adc dh, byte [IP2+0+8] + adc dl, byte [IP2+3+8] + adc dh, byte [IP2+2+8] + + ; size + adc dl, cl + adc dh, ch + +;--------------------- +; Real header and data + + push esi + call checksum_1 + call checksum_2 + pop esi + +} ; returns in dx only + ;----------------------------------------------------------------- @@ -243,47 +286,46 @@ TCP_timer_640ms: align 4 TCP_input: - DEBUGF 1,"TCP_input\n" - + DEBUGF 1,"TCP_input size=%u\n", ecx ; 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. movzx eax, [edx + TCP_segment.DataOffset] and eax, 0xf0 - shr al , 2 + shr al, 2 - DEBUGF 1,"data offset: %u\n", eax + DEBUGF 1,"headersize=%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 + push eax ecx edx + pushw [edx + TCP_segment.Checksum] + mov [edx + TCP_segment.Checksum], 0 + push esi edi 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 + TCP_checksum + pop esi edi ; yes, swap them (we dont need dest addr) + pop cx ; previous checksum + cmp cx, dx + pop edx ecx esi jnz .drop DEBUGF 1,"Checksum is correct\n" + sub ecx, esi ; update packet size + jl .drop + ;----------------------------------------------------------------------------------------- ; 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 + cmp esi, 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 + cmp byte [edx + TCP_segment.Data + 12], TCP_OPT_EOL ; end of option list jne .no_timestamp .is_ok: @@ -295,11 +337,9 @@ TCP_input: DEBUGF 1,"timestamp ok\n" - ; TODO: Parse the options + ; TODO: Parse the option ; TODO: Set a Bit in the TCP to tell all options are parsed - ret - .no_timestamp: ;------------------------------------------- @@ -310,6 +350,8 @@ TCP_input: ntohlw [edx + TCP_segment.Window] ntohlw [edx + TCP_segment.UrgentPointer] + ntohlw [edx + TCP_segment.SourcePort] + ntohlw [edx + TCP_segment.DestinationPort] ;------------------------------------------------------------ ; Next thing to do is find the TCB (thus, the socket pointer) @@ -333,7 +375,7 @@ TCP_input: jne .socket_loop mov eax, [ebx + IP_SOCKET.RemoteIP] - cmp eax, esi + cmp eax, edi ; sender IP je @f test eax, eax jnz .socket_loop @@ -345,79 +387,133 @@ TCP_input: test ax, ax jnz .socket_loop .found_socket: - DEBUGF 1,"Socket ptr: %x\n", ebx + DEBUGF 1,"Socket ptr: %x\n", ebx ; ebx now contains the pointer to the socket ;---------------------------- ; Check if socket isnt closed - cmp [TCP_SOCKET.t_state], TCB_CLOSED + cmp [ebx + 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.lock +;; add ebx, SOCKET.lock ; TODO: figure out if we should lock now already +;; call wait_mutex +;; sub ebx, SOCKET.lock -;--------------------------------------- -; unscale the window into a 32 bit value ;;;;;; + DEBUGF 1,"Socket locked\n" + +;---------------------------------------------------------------------------------------- +; unscale the window into a 32 bit value (notice that SND_SCALE must be initialised to 0) 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] + push cx + mov cl, [ebx + TCP_SOCKET.SND_SCALE] shl eax, cl + pop cx - .no_syn: + ;;;; do something with eax ;----------------------------------- ; 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 - +; test [ebx + SOCKET.options], SO_ACCEPTCON +; jnz .listening_socket ;;;;; TODO ;------------------------------------- ; Reset idle timer and keepalive timer - ;;;; TODO: idle timer? - + mov [ebx + TCP_SOCKET.t_idle], 0 mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval -;----------------------------------------- -; Process TCP options if not in LISTEN state +;-------------------- +; Process TCP options - test [ebx + TCP_SOCKET.t_state], TCB_LISTEN - jz .dont_do_options + cmp esi, 20 ; esi is headersize + je .no_options - call TCP_do_options + DEBUGF 1,"Segment has options\n" - .dont_do_options: + test [ebx + TCP_SOCKET.t_state], TCB_LISTEN ; no options when in listen state + jz .no_options + + lea edi, [edx + TCP_segment.Data] + lea eax, [edx + esi] + + .opt_loop: + cmp edi, eax + jge .no_options + + cmp byte [edi], TCP_OPT_EOL ; end of option list? + jz .no_options + + cmp byte [edi], TCP_OPT_NOP ; nop ? + jz .opt_nop + + cmp byte [edi], TCP_OPT_MAXSEG + je .opt_maxseg + + cmp byte [edi], TCP_OPT_WINDOW + je .opt_window + + cmp byte [edi], TCP_OPT_TIMESTAMP + je .opt_timestamp + + jmp .no_options ; If we reach here, some unknown options were received, skip them all! + + .opt_nop: + inc edi + jmp .opt_loop + + .opt_maxseg: + cmp byte [edi+1], 4 + jne .no_options ; error occured, ignore all options! + + test [edx + TCP_segment.Flags], TH_SYN + jz @f + + DEBUGF 1,"Got maxseg option" + + ;;;;; + @@: + add edi, 4 + jmp .opt_loop + + + .opt_window: + cmp byte [edi+1], 3 + jne .no_options + + test [edx + TCP_segment.Flags], TH_SYN + jz @f + + DEBUGF 1,"Got window option" + + ;;;;; + @@: + add edi, 3 + jmp .opt_loop + + + .opt_timestamp: + cmp byte [edi+1], 10 + jne .no_options + + DEBUGF 1,"Got timestamp option" + + ;;;;; + + add edi, 10 + jmp .opt_loop + + .no_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, @@ -430,20 +526,20 @@ TCP_input: ; - 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 + cmp [ebx + TCP_SOCKET.t_state], TCB_ESTABLISHED jnz .not_uni_xfer - test [TCP_segment.Flags], TH_SYN + TH_FIN + TH_RST + TH_URG + test [edx + TCP_segment.Flags], TH_SYN + TH_FIN + TH_RST + TH_URG jnz .not_uni_xfer - test [TCP_segment.Flags], TH_ACK + test [edx + 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] ;;;;; + movzx eax, [edx + TCP_segment.Window] ;;;;; (should use pre-calculated value isntead: todo: figure out where to store it) cmp eax, [ebx + TCP_SOCKET.SND_WND] jne .not_uni_xfer @@ -451,47 +547,30 @@ TCP_input: 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 - sub ecx, eax +; - The segment contains no data. + test ecx, ecx jnz .not_sender -; - 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. - - mov eax, [edx + TCP_segment.AckNumber] - cmp eax, [ebx + TCP_SOCKET.SND_UNA] - jle .not_uni_xfer - -; - The acknowledgment field in the segment (ti_ack) is less than or equal to the maximum sequence number sent (snd_max). - -; mov eax, [edx + TCP_segment.Ack] - cmp eax, [ebx + TCP_SOCKET.SND_MAX] - jg .not_uni_xfer - -; - The congestion window (snd_cwnd) is greater than or equal to the current send window (snd_wnd). +; - The congestion window is greater than or equal to the current send window. ; This test is true only if the window is fully open, that is, the connection is not in the middle of slow start or congestion avoidance. - mov eax, [ebx + TCP_SOCKET.SND_CWND] cmp eax, [ebx + TCP_SOCKET.SND_WND] jl .not_uni_xfer +; - The acknowledgment field in the segment is less than or equal to the maximum sequence number sent. + mov ecx, [edx + TCP_segment.AckNumber] + cmp ecx, [ebx + TCP_SOCKET.SND_MAX] + jg .not_uni_xfer + +; - The acknowledgment field in the segment is greater than the largest unacknowledged sequence number. + sub ecx, [ebx + TCP_SOCKET.SND_UNA] + jle .not_uni_xfer + DEBUGF 1,"Header prediction: we are sender\n" ;--------------------------------- @@ -500,6 +579,10 @@ TCP_input: ; Update RTT estimators ; Delete acknowledged bytes from send buffer +; notice how ecx already holds number of bytes ack-ed + + lea eax, [ebx + snd] + call SOCKET_ring_free ; Stop retransmit timer mov [ebx + TCP_SOCKET.timer_ack], 0 @@ -513,27 +596,19 @@ TCP_input: jmp .drop - - - ;------------------------------------------------- ; 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 amount of data in the segment 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. +; - The acknowledgment field 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 +; - The reassembly list of out-of-order segments for the connection is empty (seg_next equals tp). ;;;;;;; -; There is room in the receive buffer for the data in the segment. -;;;; jnz .not_uni_xfer ;------------------------------------- @@ -541,63 +616,41 @@ TCP_input: 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 esi, edx + lea eax, [ebx + rcv] + call SOCKET_ring_add ; Add the data to the socket buffer - add [ebx + TCP_SOCKET.RCV_NXT], ecx - -; Add the data to the socket buffer - mov eax, ebx - ;;; mov... - call SOCKET_input - -; The delayed-ACK flag is set and the input processing is complete. + add [ebx + TCP_SOCKET.RCV_NXT], ecx ; Update sequence number with number of bytes we have copied + or [ebx + TCP_SOCKET.t_flags], TF_DELACK ; Set delayed ack flag jmp .drop - - - - ;---------------------------------------------------- -; Header prediction failed, doing it the slow way.. +; Header prediction failed, doing it the slow way.. ;;;;; current implementation of header prediction destroys some regs (ecx) !! .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 + cmp [ebx + TCP_SOCKET.t_state], TCB_LISTEN je .LISTEN - cmp [eax + TCP_SOCKET.t_state], TCB_SYN_SENT + cmp [ebx + TCP_SOCKET.t_state], TCB_SYN_SENT je .SYN_SENT - ;-------------------------------------------- ; Protection Against Wrapped Sequence Numbers - ; First, check timestamp if present ;;;; TODO @@ -608,6 +661,9 @@ TCP_input: jmp .trim_then_step6 +;------------- +; Passive Open + align 4 .LISTEN: @@ -622,47 +678,37 @@ align 4 test [edx + TCP_segment.Flags], TH_SYN jz .drop + ; TODO: find sender ip address somewhere! ; TODO: check if it's a broadcast or multicast, and drop if so -;;; 28.6 + call SOCKET_fork + jz .drop ; if we could not open a new connection, drop segment (;;;; should we send RST too?) - ; create a new socket and fill in the nescessary variables +;----------------------- +; Fill in some variables -;; Exit if backlog queue is full -; mov ax, [ebx + TCP_SOCKET.backlog_cur] -; cmp ax, [ebx + TCP_SOCKET.backlog] -; jae .exit + add [TCP_sequence_num], 64000 -; Allocate new socket - call SOCKET_alloc - ;;; jz .fail + push [edx + TCP_segment.SourcePort] + pop [eax + TCP_SOCKET.RemotePort] -; 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 + push [edx + TCP_segment.SequenceNumber] + pop [eax + TCP_SOCKET.IRS] -;; 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 + push [eax + TCP_SOCKET.ISS] + pop [eax + TCP_SOCKET.SND_NXT] - mov [eax + IP_SOCKET.RemoteIP], esi ; IP source address + mov [eax + TCP_SOCKET.t_state], TCB_SYN_RECEIVED + mov [eax + TCP_SOCKET.t_flags], TF_ACKNOW + mov [eax + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval - mov cx, [edx + TCP_segment.SourcePort] - mov [eax + TCP_SOCKET.RemotePort], cx - - mov ecx, [edx + TCP_segment.SequenceNumber] - mov [eax + TCP_SOCKET.IRS], ecx - - mov ecx, [eax + TCP_SOCKET.ISS] - mov [eax + TCP_SOCKET.SND_NXT], ecx + mov ebx, eax jmp .trim_then_step6 +;------------ +; Active Open align 4 .SYN_SENT: @@ -676,11 +722,12 @@ align 4 cmp eax, [ebx + TCP_SOCKET.ISS] jle .drop_with_reset - mov eax, [edx + TCP_segment.AckNumber] - cmp eax, [ebx + TCP_SOCKET.SND_MAX] - jg .drop_with_reset - @@: + DEBUGF 1,"snd_max = %x\n", [ebx + TCP_SOCKET.SND_MAX] ;;; TODO: set this, but where? +; mov eax, [edx + TCP_segment.AckNumber] +;; cmp eax, [ebx + TCP_SOCKET.SND_MAX] +;; jg .drop_with_reset + @@: test [edx + TCP_segment.Flags], TH_RST jz @f @@ -696,24 +743,41 @@ align 4 test [edx + TCP_segment.Flags], TH_SYN jz .drop -; now, process received SYN in response to an active open +; at this point, segment seems to be valid + test [edx + TCP_segment.Flags], TH_ACK - jz @f + jz .no_syn_ack + +; now, process received SYN in response to an active open 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 + @@: - mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval - mov [ebx + TCP_SOCKET.timer_retransmission], 0 + .no_syn_ack: - mov eax, [edx + TCP_segment.SequenceNumber] - mov [ebx + TCP_SOCKET.IRS], eax + mov [ebx + TCP_SOCKET.timer_retransmission], 0 ; disable retransmission + push [edx + TCP_segment.SequenceNumber] + pop [ebx + TCP_SOCKET.IRS] + +;;; TODO: tcp_rcvseqinit + + mov [ebx + TCP_SOCKET.t_flags], TF_ACKNOW + + mov eax, [ebx + TCP_SOCKET.SND_UNA] + cmp eax, [ebx + TCP_SOCKET.ISS] + jle .simultaneous_open + + test [edx + TCP_segment.Flags], TH_ACK + jz .simultaneous_open + + DEBUGF 1,"TCP: active open\n" + +; TODO: update stats ; TODO: set socket state to connected mov [ebx + TCP_SOCKET.t_state], TCB_ESTABLISHED @@ -721,9 +785,11 @@ align 4 ; TODO: check if we should scale the connection (567-572) ; TODO: update RTT estimators + jmp .trimthenstep6 - @@: + .simultaneous_open: + DEBUGF 1,"TCP: simultaneous open\n" ; We have received a syn but no ACK, so we are having a simultaneous open.. mov [ebx + TCP_SOCKET.t_state], TCB_SYN_RECEIVED @@ -744,16 +810,15 @@ align 4 ; TODO... @@: ;;;;; - ;;; jmp .step6 + jmp .step6 -align 4 -.trim_then_step6: + .trim_then_step6: - DEBUGF 1,"Trim, then step 6\n" + DEBUGF 1,"Trimming window\n" ;---------------------------- ; trim any data not in window @@ -783,8 +848,9 @@ align 4 .no_drop: -; eax holds number of bytes to drop + DEBUGF 1,"Going to drop %u bytes of data", eax +; eax holds number of bytes to drop ;---------------------------------- ; Check for entire duplicate packet @@ -794,11 +860,6 @@ align 4 ;;; 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 @@ -811,15 +872,13 @@ align 4 mov eax, ecx and [edx + TCP_segment.Flags], not TH_FIN - ;;; TODO: set ACKNOW flag - + or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW 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 @@ -833,12 +892,14 @@ align 4 .duplicate: + DEBUGF 1,"Duplicate received" + ;---------------------------------------- ; Update statistics for duplicate packets ;;; TODO - ;;; DROP the packet ?? + jmp .drop ;;; DROP the packet ?? .no_duplicate: @@ -898,7 +959,6 @@ align 4 .no_excess_data: - ;----------------- ; Record timestamp @@ -910,6 +970,8 @@ align 4 test [edx + TCP_segment.Flags], TH_RST jz .rst_skip + DEBUGF 1,"Got an RST flag" + mov eax, [ebx + TCP_SOCKET.t_state] shl eax, 2 jmp dword [eax + .rst_sw_list] @@ -929,19 +991,27 @@ align 4 .econnrefused: + DEBUGF 1,"Connection refused" + ;;; TODO: debug info jmp .close .econnreset: + DEBUGF 1,"Connection reset" + ;;; TODO: debug info - .close: + .close: + + DEBUGF 1,"Closing connection" ;;; update stats .rst_close: + DEBUGF 1,"Closing with reset" + ;;; Close the socket jmp .drop @@ -1006,12 +1076,15 @@ align 4 ;------------------------------------------ ; Remove acknowledged data from send buffer - ;;;; 943 - 956 + lea eax, [ebx + snd] + mov ecx, ecx ;;;; 943 - 956 + call SOCKET_ring_free ;--------------------------------------- ; Wake up process waiting on send buffer - ;;;;; + mov eax, ebx + call SOCKET_notify_owner mov eax, [ebx + TCP_SOCKET.t_state] shl eax, 2 @@ -1103,7 +1176,6 @@ align 4 .no_window_update: - ;----------------- ; process URG flag @@ -1121,7 +1193,7 @@ align 4 ;;; 1040-1050 movzx eax, [edx + TCP_segment.UrgentPointer] - add eax, [ebx + SOCKET.SO_RCV.SB_CC] + add eax, [ebx + rcv.size] cmp eax, SOCKET_MAXDATA jle .not_urgent @@ -1134,19 +1206,19 @@ align 4 ;-------------------------------------- ; processing of received urgent pointer - ;;; 1051-1093 + ;;; TODO (1051-1093) -align 4 -.do_data: +;-------------------------------- +; process the data in the segment - DEBUGF 1,"Do data:\n" + .do_data: - ; process the data in the segment + DEBUGF 1,"TCP: do data:\n" test [edx + TCP_segment.Flags], TH_FIN - jz .process_fin + jnz .process_fin - test [ebx + TCP_SOCKET.t_state], TCB_FIN_WAIT_1 ;;;;; + test [ebx + TCP_SOCKET.t_state], TCB_FIN_WAIT_1 jge .dont_do_data DEBUGF 1,"Processing data in segment\n" @@ -1158,7 +1230,6 @@ align 4 .dont_do_data: - ;--------------- ; FIN processing @@ -1209,16 +1280,26 @@ align 4 ;jnz .outputnow test [ebx + TCP_SOCKET.t_flags], TF_ACKNOW - jz .ret + jnz .ack_now - .outputnow: - call TCP_output - - .ret: mov [ebx + SOCKET.lock], 0 - call kernel_free - ret 4 + add esp, 4 + ret + + .ack_now: + + DEBUGF 1,"ACK now!\n" + + push ebx + mov eax, ebx + call TCP_output + pop ebx + + mov [ebx + SOCKET.lock], 0 + call kernel_free + add esp, 4 + ret ;------------------------------------------ ; Generate an ACK, droping incoming segment @@ -1233,12 +1314,15 @@ align 4 and [ebx + TCP_SOCKET.t_flags], TF_ACKNOW + push ebx + mov eax, ebx call TCP_output + pop ebx mov [ebx + SOCKET.lock], 0 - call kernel_free - ret 4 + add esp, 4 + ret ;------------------------------------------- @@ -1261,15 +1345,15 @@ align 4 jnz .respond_syn mov [ebx + SOCKET.lock], 0 - call kernel_free - ret 4 + add esp, 4 + ret .respond_ack: ;;;; - call TCP_respond + call TCP_respond_segment jmp .destroy_new_socket @@ -1278,7 +1362,7 @@ align 4 ;;;; - call TCP_respond + call TCP_respond_segment jmp .destroy_new_socket @@ -1297,127 +1381,15 @@ align 4 ;;;; kill the newly created socket mov [ebx + SOCKET.lock], 0 - call kernel_free - ret 4 - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - -;--------------------- -; -; TCP_do_options -; -;------------------- - -align 4 -TCP_do_options: - - DEBUGF 1,"TCP_do_options\n" - - push eax - sub eax, 20 - jz .no_options - - lea esi, [edx + TCP_segment.Data] - - -;------------------------------------------- -; Begin the loop by checking for EOL and NOP - - .loop: - - cmp byte [esi], TCP_OPT_EOL ; end of option list? - jz .no_options - - cmp byte [esi], TCP_OPT_NOP ; nop ? - ;;; cmove edi, 1 ; if so, set option size to 1 - jz .continue ; and continue scanning - -;------------------ -; We have an option - - movzx edi, byte [esi + 1] ; get the length of this option in edi - - -;-------------------------------------- -; Check for Maximum segment size option - - cmp byte [esi], TCP_OPT_MAXSEG - jne .no_maxseg - - cmp edi, 4 ; option length - jne .continue - - test [edx + TCP_segment.Flags], TH_SYN - jz .continue - - ; Now parse the option... - - jmp .continue - - .no_maxseg: - -;------------------------ -; Check for Window option - - cmp byte [esi], TCP_OPT_WINDOW - jne .no_window - - cmp edi, 3 ; option length - jne .continue - - test [edx + TCP_segment.Flags], TH_SYN - jz .continue - - ; ... - - jmp .continue - - .no_window: - -;--------------------------- -; Check for Timestamp option - - cmp byte [esi], TCP_OPT_TIMESTAMP - jne .no_timestamp - - 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 - + add esp, 4 ret + + ;--------------------------- ; ; TCP_pull_out_of_band @@ -1441,14 +1413,6 @@ TCP_pull_out_of_band: - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - - - ;----------------------------------------------------------------- ; ; TCP_output @@ -1494,7 +1458,7 @@ TCP_output: mov ecx, [eax + TCP_SOCKET.SND_CWND] ; @@: ; - call TCP_outflags + call TCP_outflags ; in dl ; If in persist timeout with window of 0, send 1 byte. ; Otherwise, if window is small but nonzero, and timer expired, @@ -1506,7 +1470,7 @@ TCP_output: test ecx, ecx jnz .no_zero_window - cmp ebx, [eax + SOCKET.SO_SND.SB_CC] + cmp ebx, [eax + snd.size] jge @f and dl, not (TH_FIN) ; clear the FIN flag ??? how can it be set before? @@ -1517,14 +1481,14 @@ TCP_output: .no_zero_window: -;;; mov [eax + TCP_SOCKET.t_timer....TCPT_PERSIST], 0 + mov [eax + TCP_SOCKET.timer_persist], 0 ;;;; mov [eax + TCP_SOCKET.t_rxtshift], 0 .no_persist_timeout: ;;;106 - mov esi, [eax + SOCKET.SO_SND.SB_CC] + mov esi, [eax + snd.size] cmp esi, ecx jl @f mov esi, ecx @@ -1546,7 +1510,7 @@ TCP_output: test ecx, ecx jnz @f -;;; mov [eax + TCP_SOCKET.t_timer..TCPT_REXMT], 0 + mov [eax + TCP_SOCKET.timer_retransmission], 0 ; cancel retransmit push [eax + TCP_SOCKET.SND_UNA] pop [eax + TCP_SOCKET.SND_NXT] @@ -1569,7 +1533,7 @@ TCP_output: 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] + add edi, [eax + snd.size] cmp edi, 0 jle @f @@ -1578,17 +1542,16 @@ TCP_output: @@: -;;;; 130 TODO: set window (ecx) to space in send buffer +; set ecx to space available in receive buffer +; From now on, ecx will be the window we advertise to the other end + mov ecx, SOCKET_MAXDATA + sub ecx, [eax + rcv.size] ;------------------------------ ; Sender silly window avoidance - test esi, esi - jz .zero_length - - - cmp esi, [eax + TCP_SOCKET.t_maxseg] + cmp ecx, [eax + TCP_SOCKET.t_maxseg] je .send ;;; TODO: 144-145 @@ -1596,16 +1559,20 @@ TCP_output: test [eax + TCP_SOCKET.t_force], -1 jnz .send -;;; TODO: 149..152 - - .zero_length: + mov ebx, [eax + TCP_SOCKET.max_sndwnd] + shr ebx, 1 + cmp ecx, ebx + jge .send + mov ebx, [eax + TCP_SOCKET.SND_NXT] + cmp ebx, [eax + TCP_SOCKET.SND_MAX] + jl .send ;---------------------------------------- ; Check if a window update should be sent - cmp ecx, 0 ; window - jle .no_window + test ecx, ecx ; window + jz .no_window ;;; TODO 154-172 @@ -1641,8 +1608,6 @@ TCP_output: DEBUGF 1,"Entering persist state\n" - - ;-------------------------------------- ; No reason to send a segment, just ret @@ -1651,23 +1616,23 @@ TCP_output: ret - - - ;----------------------------------------------- ; ; Send a segment ; -; ebx = socket pointer +; eax = socket pointer ; dl = flags ; ;----------------------------------------------- -.send: + .send: DEBUGF 1,"Preparing to send a segment\n" - xor edi, edi ; edi will contain the number of header option bytes + mov edi, TCP_segment.Data ; edi will contain headersize + + sub esp, 8 ; create some space on stack + push eax ; save this too.. ;------------------------------------ ; Send options with first SYN segment @@ -1675,35 +1640,32 @@ TCP_output: test dl, TH_SYN jz .no_options - mov eax, [ebx + TCP_SOCKET.ISS] - mov [ebx + TCP_SOCKET.SND_NXT], eax + push [eax + TCP_SOCKET.ISS] + pop [eax + TCP_SOCKET.SND_NXT] - test [ebx + TCP_SOCKET.t_flags], TF_NOOPT + test [eax + TCP_SOCKET.t_flags], TF_NOOPT jnz .no_options - mov eax, TCP_OPT_MAXSEG shl 24 + 4 shl 16 - mov ax, 1280 ;;;;;; - bswap eax - push eax + mov ecx, 1460 + or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16 + bswap ecx + push ecx + add di, 4 - mov di, 4 - - test [ebx + TCP_SOCKET.t_flags], TF_REQ_SCALE + test [eax + 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 + test [eax + 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 - push eax - + movzx ecx, byte [eax + TCP_SOCKET.request_r_scale] + or ecx, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP shl 8 + bswap ecx + pushd ecx add di, 4 .no_syn: @@ -1711,7 +1673,7 @@ TCP_output: ;------------------------------------ ; Make the timestamp option if needed - test [ebx + TCP_SOCKET.t_flags], TF_REQ_TSTMP + test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP jz .no_timestamp test dl, TH_RST @@ -1720,145 +1682,129 @@ TCP_output: test dl, TH_ACK jz .timestamp - test [ebx + TCP_SOCKET.t_flags], TF_RCVD_TSTMP + test [eax + 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) + mov esi, [timer_ticks] + bswap esi + push esi pushw 0 - mov eax, [timer_ticks] - bswap eax - push eax - + pushd TCP_OPT_TIMESTAMP + 10 shl 8 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24 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 .no_data - - ;;; 278-316 - - jmp .header - - .no_data: - - ;;; 317-338 - - -;---------- - - push di dx ebx + ; eax = socket ptr + ; edx = flags + ; ecx = data size + ; edi = header size + ; esi = snd ring buff ptr + xor ecx, ecx ;;;;; 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 +; Start by pushing all TCP header values in reverse order on stack +; (essentially, creating the tcp header!) -;;;; jz .fail + pushw 0 ; .UrgentPointer dw ? + pushw 0 ; .Checksum dw ? + pushw 0x00a0 ; .Window dw ? ;;;;;;; + shl edi, 2 ; .DataOffset db ? only 4 left-most bits + shl dx, 8 + or dx, di ; .Flags db ? + pushw dx + shr edi, 2 ; .DataOffset db ? ;;;; - push edx eax - call [ebx + NET_DEVICE.transmit] - ret + push [eax + TCP_SOCKET.RCV_NXT] ; .AckNumber dd ? + ntohld [esp] -;---------------- + push [eax + TCP_SOCKET.SND_NXT] ; .SequenceNumber dd ? + ntohld [esp] + push [eax + TCP_SOCKET.RemotePort] ; .DestinationPort dw ? + ntohlw [esp] -;------------------------------- -; Now, create the 20-byte header + push [eax + TCP_SOCKET.LocalPort] ; .SourcePort dw ? + ntohlw [esp] - .header: + push edi ; header size -;----------------------- -; Fill in the TCP header - pop esi +; Create the IP packet + mov ebx, [eax + IP_SOCKET.LocalIP] ; source ip + mov eax, [eax + IP_SOCKET.RemoteIP] ; dest ip +; mov ecx, ; data length +; mov dx, ; fragment id + mov di, IP_PROTO_TCP shl 8 + 128 + call IPv4_output + jz .fail - push [esi + TCP_SOCKET.SND_NXT] - rol word [esp], 8 - rol dword [esp], 16 - pop [edi + TCP_segment.SequenceNumber] +;----------------------------------------- +; Move TCP header from stack to TCP packet - push [esi + TCP_SOCKET.RCV_NXT] - rol word [esp], 8 - rol dword [esp], 16 - pop [edi + TCP_segment.AckNumber] +; pop ecx ; header size +; mov esi, esp +; add esp, ecx +; shr ecx, 2 +; rep movsd - push [esi + TCP_SOCKET.LocalPort] - rol word [esp], 8 - pop [edi + TCP_segment.SourcePort] + mov ecx, [esp] + lea esi, [esp+4] + shr ecx, 2 + rep movsd - push [esi + TCP_SOCKET.RemotePort] - rol word [esp], 8 - pop [edi + TCP_segment.DestinationPort] + pop ecx + add esp, ecx + mov [esp + 3*4+4], edx ; packet size + mov [esp + 3*4], eax ; packet ptr - mov [edi + TCP_segment.Window], 0x0005 - ; 1280 bytes - mov [edi + TCP_segment.UrgentPointer], 0 - - mov [edi + TCP_segment.DataOffset], 0x50 - - mov [edi + TCP_segment.Flags], cl - - mov [edi + TCP_segment.Checksum], 0 - -;----- - + mov edx, edi + sub edx, ecx ;-------------- ; Copy the data - pop esi - push edi - add edi, TCP_segment.Data ;; - sub ecx, TCP_segment.Data ;;; +; eax = ptr to ring struct +; ecx = buffer size +; edi = ptr to buffer - shr ecx, 1 - jnc .nb - movsb - .nb: - shr ecx, 1 - jnc .nw - movsw - .nw: - test ecx, ecx - jz .nd - rep movsd - .nd: - pop edi + mov eax, [esp] ; socket ptr + push ecx edx + add eax, snd + call SOCKET_ring_read + pop esi ecx + pop eax -;-------------------- -; Create the checksum +;------------------------------------------------------------- +; Create the checksum (we have already pushed IPs onto stack) - push [ebx + IP_SOCKET.LocalIP] - push [ebx + IP_SOCKET.RemoteIP] - call TCP_checksum + DEBUGF 1,"checksum: ptr=%x size=%u\n", esi, ecx + DEBUGF 1,"ip=%x\n", [esp]:8 + DEBUGF 1,"ip=%x\n", [esp+4]:8 + + TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP) + mov [esi+TCP_segment.Checksum], dx ;---------------- ; Send the packet - ;;;;; - - - DEBUGF 1,"Sending TCP Packet to device %x\n", ebx + DEBUGF 1,"Sending TCP Packet to device %x\n", ebx call [ebx + NET_DEVICE.transmit] ret + + .fail: + pop ecx + add esp, ecx + add esp, 4+4+8+4 + DEBUGF 1,"TCP_output: failed\n" + ret + + + ;------------------------- ; ; TCP_outflags @@ -1893,6 +1839,8 @@ TCP_outflags: db TH_ACK ; TCB_TIMED_WAIT + + ;------------------------- ; ; TCP_drop @@ -1924,108 +1872,36 @@ TCP_drop: ;--------------------------------------- ; -; TCP_ack -; ; The easy way to send an ACK/RST/keepalive segment ; -; IN: eax = socket ptr -; -or- -; edx = packet ptr (eax must be 0) +; TCP_respond_socket: +; +; IN: ebx = socket ptr ; cl = flags ; -; OUT: / -; -;--------------------------------------- +;-------------------------------------- align 4 -TCP_respond: +TCP_respond_socket: - DEBUGF 1,"TCP_respond\n" + DEBUGF 1,"TCP_respond_socket\n" ;--------------------- ; Create the IP packet - push cx eax edx - mov ebx, [eax + IP_SOCKET.LocalIP] - mov eax, [eax + IP_SOCKET.RemoteIP] + push cx ebx + mov eax, [ebx + IP_SOCKET.RemoteIP] + mov ebx, [ebx + IP_SOCKET.LocalIP] mov ecx, TCP_segment.Data - mov di , IP_PROTO_TCP - call IPv4_create_packet + mov di , IP_PROTO_TCP shl 8 + 128 + call IPv4_output test edi, edi jz .error - -;--------------------------- -; Now fill in the TCP header - - pop ecx - pop esi - - test esi, esi -; jz - - + pop esi cx push edx eax - push dword .checksum - je .use_segment - jmp .use_socket - -;--------------------- -; Fill in the checksum - - .checksum: - - push [esi + IP_SOCKET.LocalIP] - push [esi + IP_SOCKET.RemoteIP] - lea esi, [edi - 20] - xor ecx, ecx - call TCP_checksum - -;-------------------- -; And send the segment - - call [ebx + NET_DEVICE.transmit] - ret - - .error: - DEBUGF 1,"TCP_ack failed\n" - add esp, 4 - - ret - -;--------------------------------------------------- -; Fill in the TCP header by using a received segment - - .use_segment: - - mov ax, [esi + TCP_segment.DestinationPort] - rol ax, 8 - stosw - mov ax, [esi + TCP_segment.SourcePort] - rol ax, 8 - stosw - mov eax, [esi + TCP_segment.AckNumber] - bswap eax - stosd - xor eax, eax - stosd - mov al, 0x50 ; Dataoffset: 20 bytes - stosb - mov al, cl - stosb - mov ax, 1280 - rol ax, 8 - stosw ; window - xor eax, eax - stosd ; checksum + urgentpointer - - ret - - ;----------------------------------------------- ; Fill in the TCP header by using the socket ptr - .use_socket: - mov ax, [esi + TCP_SOCKET.LocalPort] rol ax, 8 stosw @@ -2048,64 +1924,100 @@ TCP_respond: xor eax, eax stosd ; checksum + urgentpointer +;--------------------- +; Fill in the checksum + + .checksum: + sub edi, TCP_segment.Data + mov ecx, TCP_segment.Data + xchg esi, edi + TCP_checksum (edi + IP_SOCKET.LocalIP), (esi + IP_SOCKET.RemoteIP) + mov [esi+TCP_segment.Checksum], dx + +;-------------------- +; And send the segment + + call [ebx + NET_DEVICE.transmit] + ret + + .error: + DEBUGF 1,"TCP_respond failed\n" + add esp, 2+4 + ret -;----------------------------------------------------------------- +;------------------------- +; TCP_respond.segment: ; -; 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) -; -;----------------------------------------------------------------- +; IN: edx = segment ptr (a previously received segment) +; cl = flags + align 4 -TCP_checksum: +TCP_respond_segment: -;------------- -; Pseudoheader - - ; protocol type - mov edx, IP_PROTO_TCP ; NO shl 8 here ! (it took me ages to figure this one out) - - ; 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] - - ; size - adc dl, cl - adc dh, ch + DEBUGF 1,"TCP_respond_segment\n" ;--------------------- -; Real header and data +; Create the IP packet - push esi - call checksum_1 - call checksum_2 - pop esi + push cx edx + mov ebx, [edx - 20 + IPv4_Packet.SourceAddress] ;;;; and what if ip packet had options?! + mov eax, [edx - 20 + IPv4_Packet.DestinationAddress] ;;; + mov ecx, TCP_segment.Data + mov di , IP_PROTO_TCP shl 8 + 128 + call IPv4_output + test edi, edi + jz .error - 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 :) + pop esi cx + push edx eax - ret 8 ; Remove the IPs from stack +;--------------------------------------------------- +; Fill in the TCP header by using a received segment + + mov ax, [esi + TCP_segment.DestinationPort] + rol ax, 8 + stosw + mov ax, [esi + TCP_segment.SourcePort] + rol ax, 8 + stosw + mov eax, [esi + TCP_segment.AckNumber] + bswap eax + stosd + xor eax, eax + stosd + mov al, 0x50 ; Dataoffset: 20 bytes + stosb + mov al, cl + stosb + mov ax, 1280 + rol ax, 8 + stosw ; window + xor eax, eax + stosd ; checksum + urgentpointer + +;--------------------- +; Fill in the checksum + + .checksum: + lea esi, [edi - TCP_segment.Data] + mov ecx, TCP_segment.Data + TCP_checksum (esi - 20 + IPv4_Packet.DestinationAddress), (esi - 20 + IPv4_Packet.DestinationAddress) + mov [esi+TCP_segment.Checksum], dx + +;-------------------- +; And send the segment + + call [ebx + NET_DEVICE.transmit] + ret + + .error: + DEBUGF 1,"TCP_respond failed\n" + add esp, 2+4 + + ret diff --git a/kernel/branches/net/network/udp.inc b/kernel/branches/net/network/udp.inc index c258c2ca07..b6acfe8db6 100644 --- a/kernel/branches/net/network/udp.inc +++ b/kernel/branches/net/network/udp.inc @@ -40,19 +40,62 @@ endg ; ; This function resets all UDP variables ; -; IN: / -; OUT: / -; ;----------------------------------------------------------------- -align 4 -UDP_init: +macro UDP_init { xor eax, eax mov edi, UDP_PACKETS_TX mov ecx, 2*MAX_IP rep stosd +} - ret + + + +macro UDP_checksum IP1, IP2 { ; esi = ptr to udp packet, ecx = packet size, destroys: ecx, edx + +; Pseudoheader + mov edx, IP_PROTO_UDP + + add dl, [IP1+1+4] + adc dh, [IP1+0+4] + adc dl, [IP1+3+4] + adc dh, [IP1+2+4] + + adc dl, [IP2+1+8] + adc dh, [IP2+0+8] + adc dl, [IP2+3+8] + adc dh, [IP2+2+8] + + adc dl, cl ; byte[esi+UDP_Packet.Length+1] + adc dh, ch ; byte[esi+UDP_Packet.Length+0] + +; Done with pseudoheader, now do real header + adc dl, byte[esi+UDP_Packet.SourcePort+1] + adc dh, byte[esi+UDP_Packet.SourcePort+0] + + adc dl, byte[esi+UDP_Packet.DestinationPort+1] + adc dh, byte[esi+UDP_Packet.DestinationPort+0] + + adc dl, byte[esi+UDP_Packet.Length+1] + adc dh, byte[esi+UDP_Packet.Length+0] + + adc edx, 0 + +; Done with header, now do data + push esi + movzx ecx, [esi+UDP_Packet.Length] + rol cx , 8 + sub cx , UDP_Packet.Data + add esi, UDP_Packet.Data + + call checksum_1 + call checksum_2 + pop esi + + add [esi+UDP_Packet.Checksum], dx ; this final instruction will set or clear ZF :) + +} ;----------------------------------------------------------------- @@ -62,11 +105,11 @@ UDP_init: ; Called by IPv4_input, ; this procedure will inject the udp data diagrams in the application sockets. ; -; IN: Pointer to buffer in [esp] -; size of buffer in [esp+4] -; pointer to device struct in ebx -; UDP Packet size in ecx -; pointer to UDP Packet in edx +; IN: [esp] = Pointer to buffer +; [esp+4] = size of buffer +; ebx = ptr to device struct +; ecx = UDP Packet size +; edx = ptr to UDP header ; ; esi = ipv4 source address ; edi = ipv4 dest address @@ -80,18 +123,18 @@ UDP_input: 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 to edi so we can use it later + neg [esi+UDP_Packet.Checksum] ; substract chechksum from 0 + jz .no_checksum ; if checksum is zero, it is considered valid and we continue processing + ; otherwise, we will re-calculate the checksum and add it to this value, thus creating 0 when it is correct push edx - push esi edi + push edi + push esi mov esi, edx - call UDP_checksum ; this destroys edx, ecx and esi (but not edi...) + UDP_checksum (esp), (esp+4) + pop edi + pop esi ; we dont need it, but it is smaller then add esp, 4 pop edx - - cmp [edx + UDP_Packet.Checksum], 0 jnz .checksum_mismatch .no_checksum: @@ -168,12 +211,6 @@ UDP_input: DEBUGF 2,"UDP_Handler - checksum mismatch\n" -; mov esi, edx -; @@: ; -; lodsb ; -; DEBUGF 2,"%x ", eax:2 ; -; loop @r ; - .dump: call kernel_free add esp, 4 ; pop (balance stack) @@ -207,26 +244,23 @@ UDP_output: 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 + mov di, IP_PROTO_UDP shl 8 + 128 sub esp, 8 ; Data ptr and data size will be placed here add ecx, UDP_Packet.Data ;;; TODO: fragment id - push edx esi - call IPv4_create_packet + call IPv4_output jz .fail mov [esp + 8], eax ; pointer to buffer start mov [esp + 8 + 4], edx ; buffer size - rol cx, 8 mov [edi + UDP_Packet.Length], cx - ror cx, 8 + rol [edi + UDP_Packet.Length], 8 pop esi push edi ecx @@ -240,13 +274,11 @@ UDP_output: pop ecx edi pop dword [edi + UDP_Packet.SourcePort] - mov [edi + UDP_Packet.Checksum], 0 ; set it to zero, to calculate checksum ; Checksum mov esi, edi - pushd [edi-4] ; destination address ; TODO: fix this, IPv4 packet could have options.. - pushd [edi-8] ; source address - call UDP_checksum + mov [edi + UDP_Packet.Checksum], 0 + UDP_checksum (edi-4), (edi-8) ; TODO: fix this, IPv4 packet could have options.. inc [UDP_PACKETS_TX] @@ -254,81 +286,15 @@ UDP_output: call [ebx + NET_DEVICE.transmit] ret + .fail: - add esp, 8+8 + DEBUGF 1,"UDP_output: failed\n" + add esp, 4+4+8 + xor eax, eax ret - -;----------------------------------------------------------------- -; -; 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 -; - 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 -UDP_checksum: - -; Pseudoheader - mov edx, IP_PROTO_UDP ; NO shl 8 here ! (it took me ages to figure this one out) - - add dl, [esp+1+4] - adc dh, [esp+0+4] - adc dl, [esp+3+4] - adc dh, [esp+2+4] - - adc dl, [esp+1+8] - adc dh, [esp+0+8] - adc dl, [esp+3+8] - adc dh, [esp+2+8] - - - adc dl, cl ; byte[esi+UDP_Packet.Length+1] - adc dh, ch ; byte[esi+UDP_Packet.Length+0] - -; Done with pseudoheader, now do real header - adc dl, byte[esi+UDP_Packet.SourcePort+1] - adc dh, byte[esi+UDP_Packet.SourcePort+0] - - adc dl, byte[esi+UDP_Packet.DestinationPort+1] - adc dh, byte[esi+UDP_Packet.DestinationPort+0] - - adc dl, byte[esi+UDP_Packet.Length+1] - adc dh, byte[esi+UDP_Packet.Length+0] - - adc edx, 0 - -; Done with header, now do data - push esi - movzx ecx, [esi+UDP_Packet.Length] - rol cx , 8 - sub cx , UDP_Packet.Data - add esi, UDP_Packet.Data - - call checksum_1 - call checksum_2 - pop esi - - 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 - - - - ;--------------------------------------------------------------------------- ; ; UDP_API