;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; STACK.INC ;; ;; ;; ;; TCP/IP stack for Menuet OS ;; ;; ;; ;; Version 0.7 4th July 2004 ;; ;; ;; ;; Copyright 2002 Mike Hibbett, mikeh@oceanfree.net ;; ;; ;; ;; See file COPYING for details ;; ;; ;; ;; Version 0.7 ;; ;; Added a timer per socket to allow delays when rx window ;; ;; gets below 1KB ;; ;; ;; ;; 10.01.2007 Bugfix for checksum function from Paolo Franchetti ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;******************************************************************* ; Interface ; The interfaces defined in ETHERNET.INC plus: ; stack_init ; stack_handler ; app_stack_handler ; app_socket_handler ; checksum ; ;******************************************************************* uglobal StackCounters: dumped_rx_count: dd 0 arp_tx_count: dd 0 arp_rx_count: dd 0 ip_rx_count: dd 0 ip_tx_count: dd 0 endg ; socket buffers SOCKETBUFFSIZE equ 4096 ; state + config + buffer. SOCKETHEADERSIZE equ 76 ; thus 4096 - SOCKETHEADERSIZE bytes data NUM_SOCKETS equ 16 ; Number of open sockets supported. Was 20 ; IPBUFF status values BUFF_EMPTY equ 0 BUFF_RX_FULL equ 1 BUFF_ALLOCATED equ 2 BUFF_TX_FULL equ 3 NUM_IPBUFFERS equ 20 ; buffers allocated for TX/RX NUMQUEUES equ 4 EMPTY_QUEUE equ 0 IPIN_QUEUE equ 1 IPOUT_QUEUE equ 2 NET1OUT_QUEUE equ 3 NO_BUFFER equ 0xFFFF IPBUFFSIZE equ 1500 ; MTU of an ethernet packet NUMQUEUEENTRIES equ NUM_IPBUFFERS NUMRESENDENTRIES equ 18 ; Buffers for TCP resend packets ; These are the 0x40 function codes for application access to the stack STACK_DRIVER_STATUS equ 52 SOCKET_INTERFACE equ 53 ; 128KB allocated for the stack and network driver buffers and other ; data requirements stack_data_start equ 0x700000 eth_data_start equ 0x700000 stack_data equ 0x704000 stack_data_end equ 0x71ffff ; 32 bit word stack_config equ stack_data ; 32 bit word - IP Address in network format stack_ip equ stack_data + 4 ; 1 byte. 0 == inactive, 1 = active ethernet_active equ stack_data + 9 ; TODO :: empty memory area ; Address of selected socket sktAddr equ stack_data + 32 ; Parameter to checksum routine - data ptr checkAdd1 equ stack_data + 36 ; Parameter to checksum routine - 2nd data ptr checkAdd2 equ stack_data + 40 ; Parameter to checksum routine - data size checkSize1 equ stack_data + 44 ; Parameter to checksum routine - 2nd data size checkSize2 equ stack_data + 46 ; result of checksum routine checkResult equ stack_data + 48 ; holds the TCP/UDP pseudo header. SA|DA|0|prot|UDP len| pseudoHeader equ stack_data + 50 ; receive and transmit IP buffer allocation sockets equ stack_data + 62 Next_free2 equ sockets + (SOCKETBUFFSIZE * NUM_SOCKETS) ; 1560 byte buffer for rx / tx ethernet packets Ether_buffer equ Next_free2 Next_free3 equ Ether_buffer + 1518 last_1sTick equ Next_free3 IPbuffs equ Next_free3 + 1 queues equ IPbuffs + ( NUM_IPBUFFERS * IPBUFFSIZE ) queueList equ queues + (2 * NUMQUEUES) last_1hsTick equ queueList + ( 2 * NUMQUEUEENTRIES ) ;resendQ equ queueList + ( 2 * NUMQUEUEENTRIES ) ;resendBuffer equ resendQ + ( 4 * NUMRESENDENTRIES ) ; for TCP ; equ resendBuffer + ( IPBUFFSIZE * NUMRESENDENTRIES ) resendQ equ 0x770000 resendBuffer equ resendQ + ( 4 * NUMRESENDENTRIES ) ; for TCP ; simple macro for memory set operation macro _memset_dw adr,value,amount { mov edi, adr mov ecx, amount if value = 0 xor eax, eax else mov eax, value end if cld rep stosd } ; Below, the main network layer source code is included ; include "queue.inc" include "eth_drv/ethernet.inc" include "ip.inc" include "icmp.inc" include "tcp.inc" include "udp.inc" include "socket.inc" ;*************************************************************************** ; Function ; stack_init ; ; Description ; Clear all allocated memory to zero. This ensures that ; on startup, the stack is inactive, and consumes no resources ; This is a kernel function, called prior to the OS main loop ; in set_variables ; ;*************************************************************************** stack_init: ; Init two address spaces with default values _memset_dw stack_data_start, 0, 0x20000/4 _memset_dw resendQ, 0xFFFFFFFF, NUMRESENDENTRIES ; Queries initialization call queueInit ; The following block sets up the 1s timer mov al, 0x0 out 0x70, al in al, 0x71 mov [last_1sTick], al ret ;*************************************************************************** ; Function ; stack_handler ; ; Description ; The kernel loop routine for the stack ; This is a kernel function, called in the main loop ; ;*************************************************************************** stack_handler: call ethernet_driver call ip_rx ; Test for 10ms tick, call tcp timer mov eax, [timer_ticks] ;[0xfdf0] cmp eax, [last_1hsTick] je sh_001 mov [last_1hsTick], eax call tcp_tx_handler sh_001: ; Test for 1 second event, call 1s timer functions mov al, 0x0 ;second out 0x70, al in al, 0x71 cmp al, [last_1sTick] je sh_exit mov [last_1sTick], al stdcall arp_table_manager, ARP_TABLE_TIMER, 0, 0 call tcp_tcb_handler sh_exit: ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Checksum [by Johnny_B] ;; IN: ;; buf_ptr=POINTER to buffer ;; buf_size=SIZE of buffer ;; OUT: ;; AX=16-bit checksum ;; Saves all used registers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; proc checksum_jb stdcall uses ebx esi ecx,\ buf_ptr:DWORD, buf_size:DWORD xor eax, eax xor ebx, ebx ;accumulator mov esi, dword[buf_ptr] mov ecx, dword[buf_size] shr ecx, 1 ; ecx=ecx/2 jnc @f ; if CF==0 then size is even number mov bh, byte[esi + ecx*2] @@: cld .loop: lodsw ;eax=word[esi],esi=esi+2 xchg ah,al ;cause must be a net byte-order add ebx, eax loop .loop mov eax, ebx shr eax, 16 add ax, bx not ax ret endp ;*************************************************************************** ; Function ; checksum ; ; Description ; checkAdd1,checkAdd2, checkSize1, checkSize2, checkResult ; Dont break anything; Most registers are used by the caller ; This code is derived from the 'C' source, cksum.c, in the book ; Internetworking with TCP/IP Volume II by D.E. Comer ; ;*************************************************************************** checksum: pusha mov eax, [checkAdd1] xor edx, edx ; edx is the accumulative checksum xor ebx, ebx mov cx, [checkSize1] shr cx, 1 jz cs1_1 cs1: mov bh, [eax] mov bl, [eax + 1] add eax, 2 add edx, ebx loopw cs1 cs1_1: and word [checkSize1], 0x01 jz cs_test2 mov bh, [eax] xor bl, bl add edx, ebx cs_test2: mov cx, [checkSize2] cmp cx, 0 jz cs_exit ; Finished if no 2nd buffer mov eax, [checkAdd2] shr cx, 1 jz cs2_1 cs2: mov bh, [eax] mov bl, [eax + 1] add eax, 2 add edx, ebx loopw cs2 cs2_1: and word [checkSize2], 0x01 jz cs_exit mov bh, [eax] xor bl, bl add edx, ebx cs_exit: mov ebx, edx shr ebx, 16 and edx, 0xffff add edx, ebx mov eax, edx shr eax, 16 add edx, eax not dx mov [checkResult], dx popa ret ;*************************************************************************** ; Function ; app_stack_handler ; ; Description ; This is an application service, called by int 0x40, function 52 ; It provides application access to the network interface layer ; ;*************************************************************************** app_stack_handler: cmp eax, 0 jnz not0 ; Read the configuration word mov eax, [stack_config] ret not0: cmp eax, 1 jnz not1 ; read the IP address mov eax, [stack_ip] ret not1: cmp eax, 2 jnz not2 ; write the configuration word mov [stack_config], ebx ; ; If ethernet now enabled, probe for the card, reset it and empty ; the packet buffer ; If all successfull, enable the card. ; If ethernet now disabled, set it as disabled. Should really ; empty the tcpip data area too. ; ethernet interface is '3' in ls 7 bits and bl, 0x7f cmp bl, 3 je ash_eth_enable ; Ethernet isn't enabled, so make sure that the card is disabled mov [ethernet_active], byte 0 ret ash_eth_enable: ; Probe for the card. This will reset it and enable the interface ; if found call eth_probe cmp eax, 0 je ash_eth_done ; Abort if no hardware found mov [ethernet_active], byte 1 ash_eth_done: ret not2: cmp eax, 3 jnz not3 ; write the IP Address mov [stack_ip], ebx ret ;old functions was deleted not3: cmp eax, 6 jnz not6 ; Insert an IP packet into the stacks received packet queue call stack_insert_packet ret not6: cmp eax, 7 jnz not7 ; Test for any packets queued for transmission over the network not7: cmp eax, 8 jnz not8 call stack_get_packet ; Extract a packet queued for transmission by the network ret not8: cmp eax, 9 jnz not9 ; read the gateway IP address mov eax, [gateway_ip] ret not9: cmp eax, 10 jnz not10 ; read the subnet mask mov eax, [subnet_mask] ret not10: cmp eax, 11 jnz not11 ; write the gateway IP Address mov [gateway_ip], ebx ret not11: cmp eax, 12 jnz not12 ; write the subnet mask mov [subnet_mask], ebx not12: cmp eax, 13 jnz not13 ; read the dns mov eax, [dns_ip] ret not13: cmp eax, 14 jnz not14 ; write the dns IP Address mov [dns_ip], ebx ret ; not14: cmp eax, 15 jnz not15 ; in ebx we need 4 to read the last 2 bytes cmp ebx, dword 4 je read ; or we need 0 to read the first 4 bytes cmp ebx, dword 0 jnz param_error ; read MAC, returned (in mirrored byte order) in eax read: mov eax, [node_addr + ebx] jmp @f param_error: mov eax, -1 ; params not accepted @@: ret ; 0 -> arp_probe ; 1 -> arp_announce ; 2 -> arp_responce (not supported yet) not15: ; ARP stuff cmp eax, 16 jnz not16 cmp ebx, 0 je a_probe cmp ebx, 1 je a_ann ; arp announce ; cmp ebx,2 ; jne a_resp ; arp response jmp param15_error ; arp probe, sender IP must be set to 0.0.0.0, target IP is set to address being probed ; ecx: pointer to target MAC, MAC should set to 0 by application ; edx: target IP a_probe: push dword [stack_ip] mov edx, [stack_ip] mov [stack_ip], dword 0 mov esi, ecx ; pointer to target MAC address call arp_request pop dword [stack_ip] jmp @f ; arp announce, sender IP must be set to target IP ; ecx: pointer to target MAC a_ann: mov edx, [stack_ip] mov esi, ecx ; pointer to target MAC address call arp_request jmp @f param15_error: mov eax, -1 @@: ret ; ; modified by [smb] ; ; ARPTable manager interface not16: cmp eax, 17 jnz stack_driver_end ;see "proc arp_table_manager" for more details stdcall arp_table_manager,ebx,ecx,edx ;Opcode,Index,Extra ret ; stack_driver_end: ret ;*************************************************************************** ; Function ; app_socket_handler ; ; Description ; This is an application service, called by int 0x40, function 53 ; It provides application access to stack socket services ; such as opening sockets ; ;*************************************************************************** app_socket_handler: cmp eax, 0 jnz nots0 call socket_open ret nots0: cmp eax, 1 jnz nots1 call socket_close ret nots1: cmp eax, 2 jnz nots2 call socket_poll ret nots2: cmp eax, 3 jnz nots3 call socket_read ret nots3: cmp eax, 4 jnz nots4 call socket_write ret nots4: cmp eax, 5 jnz nots5 call socket_open_tcp ret nots5: cmp eax, 6 jnz nots6 call socket_status ret nots6: cmp eax, 7 jnz nots7 call socket_write_tcp ret nots7: cmp eax, 8 jnz nots8 call socket_close_tcp ret nots8: cmp eax, 9 jnz nots9 call is_localport_unused ret nots9: cmp eax, 10 jnz nots10 mov eax,dword[drvr_cable] test eax,eax jnz @f ; if function is not implented, return -1 mov al,-1 ret @@: call dword[drvr_cable] ret nots10: cmp eax, 11 jnz nots11 call socket_read_packet ret nots11: cmp eax, 254 jnz notdump ret notdump: cmp eax, 255 jnz notsdebug ; This sub function allows access to debugging information on the stack ; ebx holds the request: ; 100 : return length of empty queue ; 101 : return length of IPOUT QUEUE ; 102 : return length of IPIN QUEUE ; 103 : return length of NET1OUT QUEUE ; 200 : return # of ARP entries ; 201 : return size of ARP table ( max # entries ) ; 202 : select ARP table entry # ; 203 : return IP of selected table entry ; 204 : return High 4 bytes of MAC address of selected table entry ; 205 : return low 2 bytes of MAC address of selected table entry ; 206 : return status word of selected table entry ; 207 : return Time to live of selected table entry ; 2 : return number of IP packets received ; 3 : return number of packets transmitted ; 4 : return number of received packets dumped ; 5 : return number of arp packets received ; 6 : return status of packet driver ; ( 0 == not active, FFFFFFFF = successful ) call stack_internal_status ret notsdebug: ; Invalid Option ret uglobal ARPTmp: times 14 db 0 endg ;*************************************************************************** ; Function ; stack_internal_status ; ; Description ; Returns information about the internal status of the stack ; This is only useful for debugging ; It works with the ethernet driver ; sub function in ebx ; return requested data in eax ; ;*************************************************************************** stack_internal_status: cmp ebx, 100 jnz notsis100 ; 100 : return length of EMPTY QUEUE mov ebx, EMPTY_QUEUE call queueSize ret notsis100: cmp ebx, 101 jnz notsis101 ; 101 : return length of IPOUT QUEUE mov ebx, IPOUT_QUEUE call queueSize ret notsis101: cmp ebx, 102 jnz notsis102 ; 102 : return length of IPIN QUEUE mov ebx, IPIN_QUEUE call queueSize ret notsis102: cmp ebx, 103 jnz notsis103 ; 103 : return length of NET1OUT QUEUE mov ebx, NET1OUT_QUEUE call queueSize ret notsis103: cmp ebx, 200 jnz notsis200 ; 200 : return num entries in arp table movzx eax, byte [NumARP] ret notsis200: cmp ebx, 201 jnz notsis201 ; 201 : return arp table size mov eax, 20 ; ARP_TABLE_SIZE ret notsis201: cmp ebx, 202 jnz notsis202 ; 202 - read the requested table entry ; into a temporary buffer ; ecx holds the entry number mov eax, ecx mov ecx, 14 ; ARP_ENTRY_SIZE mul ecx mov ecx, [eax + ARPTable] mov [ARPTmp], ecx mov ecx, [eax + ARPTable+4] mov [ARPTmp+4], ecx mov ecx, [eax + ARPTable+8] mov [ARPTmp+8], ecx mov cx, [eax + ARPTable+12] mov [ARPTmp+12], cx ret notsis202: cmp ebx, 203 jnz notsis203 ; 203 - return IP address mov eax, [ARPTmp] ret notsis203: cmp ebx, 204 jnz notsis204 ; 204 - return MAC high dword mov eax, [ARPTmp+4] ret notsis204: cmp ebx, 205 jnz notsis205 ; 205 - return MAC ls word movzx eax, word [ARPTmp+8] ret notsis205: cmp ebx, 206 jnz notsis206 ; 206 - return status word movzx eax, word [ARPTmp+10] ret notsis206: cmp ebx, 207 jnz notsis207 ; 207 - return ttl word movzx eax, word [ARPTmp+12] ret notsis207: cmp ebx, 2 jnz notsis2 ; 2 : return number of IP packets received mov eax, [ip_rx_count] ret notsis2: cmp ebx, 3 jnz notsis3 ; 3 : return number of packets transmitted mov eax, [ip_tx_count] ret notsis3: cmp ebx, 4 jnz notsis4 ; 4 : return number of received packets dumped mov eax, [dumped_rx_count] ret notsis4: cmp ebx, 5 jnz notsis5 ; 5 : return number of arp packets received mov eax, [arp_rx_count] ret notsis5: cmp ebx, 6 jnz notsis6 ; 6 : return status of packet driver ; ( 0 == not active, FFFFFFFF = successful ) mov eax, [eth_status] ret notsis6: xor eax, eax ret ;*************************************************************************** ; Function ; stack_get_packet ; ; Description ; extracts an IP packet from the NET1 output queue ; and sends the data to the calling process ; pointer to data in edx ; returns number of bytes read in eax ; ;*************************************************************************** stack_get_packet: ; Look for a buffer to tx mov eax, NET1OUT_QUEUE call dequeue cmp ax, NO_BUFFER je sgp_non_exit ; Exit if no buffer available push eax ; Save buffer number for freeing at end push edx ; convert buffer pointer eax to the absolute address mov ecx, IPBUFFSIZE mul ecx add eax, IPbuffs pop edx push eax ; save address of IP data ; Get the address of the callers data mov edi,[0x3010] add edi,TASKDATA.mem_start add edx,[edi] mov edi, edx pop eax mov ecx, 1500 ; should get the actual number of bytes to write mov esi, eax cld rep movsb ; copy the data across ; And finally, return the buffer to the free queue pop eax call freeBuff mov eax, 1500 ret sgp_non_exit: xor eax, eax ret ;*************************************************************************** ; Function ; stack_insert_packet ; ; Description ; writes an IP packet into the stacks receive queue ; # of bytes to write in ecx ; pointer to data in edx ; returns 0 in eax ok, -1 == failed ; ;*************************************************************************** stack_insert_packet: mov eax, EMPTY_QUEUE call dequeue cmp ax, NO_BUFFER je sip_err_exit push eax ; save the pointers to the data buffer & size push edx push ecx ; convert buffer pointer eax to the absolute address mov ecx, IPBUFFSIZE mul ecx add eax, IPbuffs mov edx, eax ; So, edx holds the IPbuffer ptr pop ecx ; count of bytes to send mov ebx, ecx ; need the length later pop eax ; get callers ptr to data to send ; Get the address of the callers data mov edi,[0x3010] add edi,TASKDATA.mem_start add eax,[edi] mov esi, eax mov edi, edx cld rep movsb ; copy the data across pop ebx mov eax, IPIN_QUEUE call queue inc dword [ip_rx_count] mov eax, 0 ret sip_err_exit: mov eax, 0xFFFFFFFF ret