;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                                 ;;
;; Copyright (C) KolibriOS team 2004-2009. All rights reserved.    ;;
;; Distributed under terms of the GNU General Public License       ;;
;;                                                                 ;;
;;  IPv4.INC                                                       ;;
;;                                                                 ;;
;;  Part of the tcp/ip network stack for KolibriOS                 ;;
;;                                                                 ;;
;;  Based on the work of [Johnny_B] and [smb]                      ;;
;;                                                                 ;;
;;    Written by hidnplayr@kolibrios.org                           ;;
;;                                                                 ;;
;;          GNU GENERAL PUBLIC LICENSE                             ;;
;;             Version 2, June 1991                                ;;
;;                                                                 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$Revision$

; IP underlying protocols numbers

ETHER_IPv4	equ 0x0008	; Reversed from 0800 for intel

MAX_FRAGMENTS	equ 16
MAX_IP		equ MAX_NET_DEVICES

struct	IPv4_Packet
	.VersionAndIHL		db  ?  ; Version[0-3 bits] and IHL(header length)[4-7 bits]
	.TypeOfService		db  ?
	.TotalLength		dw  ?
	.Identification 	dw  ?
	.FlagsAndFragmentOffset dw  ?  ; Flags[0-2] and FragmentOffset[3-15]
	.TimeToLive		db  ?  ;
	.Protocol		db  ?
	.HeaderChecksum 	dw  ?
	.SourceAddress		dd  ?
	.DestinationAddress	dd  ?
	.DataOrOptional:
ends

struct	FRAGMENT_slot
	.ttl			dw  ?  ; Time to live for this entry, 0 for empty slot's
	.id			dw  ?  ; Identification field from IP header
	.SrcIP			dd  ?  ; .. from IP header
	.DstIP			dd  ?  ; .. from IP header
	.ptr			dd  ?  ; Pointer to first packet
	.size:
ends

struct	FRAGMENT_entry		       ; This structure will replace the ethernet header in fragmented ip packets
	.PrevPtr		dd  ?  ; Pointer to previous fragment entry  (-1 for first packet)
	.NextPtr		dd  ?  ; Pointer to next fragment entry (-1 for last packet)
	.Owner			dd  ?  ; Pointer to structure of driver
				rb  2  ; to match ethernet header size          ; TODO: fix this hack
	.Data:			       ; Ip header begins here (we will need the IP header to re-construct the complete packet)
ends

align 4
uglobal
	BROADCAST	dd  ?
	IP_LIST 	rd  MAX_IP
	SUBNET_LIST	rd  MAX_IP
	DNS_LIST	rd  MAX_IP
	GATEWAY_LIST	rd  MAX_IP
	IP_PACKETS_TX	rd  MAX_IP
	IP_PACKETS_RX	rd  MAX_IP
	FRAGMENT_LIST	rb  MAX_FRAGMENTS*FRAGMENT_slot.size
endg


;-----------------------------------------------------------------
;
; IPv4_init
;
;  This function resets all IP variables
;
;  IN:  /
;  OUT: /
;
;-----------------------------------------------------------------
align 4
IPv4_init:

	or	eax, -1
	mov	edi, BROADCAST
	mov	ecx, 4*MAX_IP+1
	rep	stosd

	xor	eax, eax
	mov	edi, FRAGMENT_LIST
	mov	ecx, FRAGMENT_slot.size*MAX_FRAGMENTS/4 + 2*MAX_IP
	rep	stosd

	ret



;-----------------------------------------------------------------
;
; IPv4_Handler:
;
;  Will check if IP Packet isnt damaged
;  and call appropriate handler. (TCP/UDP/ICMP/..)
;
;  It will also re-construct fragmented packets
;
;  IN:  Pointer to buffer in [esp]
;       size of buffer in [esp+4]
;       pointer to device struct in ebx
;       pointer to IP Packet data in edx
;  OUT: /
;
;-----------------------------------------------------------------
align 4
IPv4_handler:	 ; TODO: clean up this mess
		 ; for instance, there should be only one piece of code wich make the jump to an underlying protocol, and not two..
		 ; TODO2: add code for IPv4 sockets (raw sockets)

	DEBUGF	1,"IP_Handler - start\n"

	push	edx ebx

	; save checksum, and clear it in original packet
	mov	di , [edx + IPv4_Packet.HeaderChecksum]
	mov	word [edx + IPv4_Packet.HeaderChecksum], 0

	; Re-calculate checksum
	movzx	ecx, byte [edx + IPv4_Packet.VersionAndIHL]	; Calculate Header length by using IHL field
	and	ecx, 0x0000000F  ;
	shl	cx , 2		 ;
	mov	esi, edx
	xor	edx, edx
	call	checksum_1
	call	checksum_2

	; now compare the two..
	cmp	dx, di
	pop	ebx edx
	jne	.dump						; if checksum isn't valid then dump packet

	DEBUGF	1,"IPv4 Checksum is correct\n",di

	mov	eax, [edx + IPv4_Packet.DestinationAddress]
	mov	edi, BROADCAST
	mov	ecx, MAX_IP+1
	repnz	scasd
	jz	.ip_ok

	not	eax
	test	eax, 127 shl 24 ; 127.x.x.x
	jz	.ip_ok

;  TODO: we need to check for broadcasts (other then 255.255.255.255)

	jmp	.dump

  .ip_ok:
	call	ETH_struc2dev					; TODO: make this work on other protocols too!
	inc	[IP_PACKETS_RX+4*edi]
	DEBUGF	1,"packet comes from %u.%u.%u.%u\n",\
	[edx + IPv4_Packet.SourceAddress]:1,[edx + IPv4_Packet.SourceAddress + 1]:1,[edx + IPv4_Packet.SourceAddress + 2]:1,[edx + IPv4_Packet.SourceAddress + 3]:1

	mov	al , [edx + IPv4_Packet.VersionAndIHL]
	and	al , 0x0f					; get IHL(header length)
	cmp	al , 0x05					; IHL!= 5*4(20 bytes)
	jnz	.dump						; TODO: dont dump packets wich have optional fiels !!!                   /!\

	cmp	byte [edx + IPv4_Packet.TimeToLive], 0
	je	.dump

	movzx	eax, word [edx + IPv4_Packet.FlagsAndFragmentOffset]
	xchg	al , ah

	test	ax , 1 shl 13					; Is 'more fragments' flag set ?
	jnz	.yes_fragments					; If so, we definately have a fragmented packet

	test	ax , 0x1fff					; If flag is not set, but there is a fragment offset, the packet is last in series of fragmented packets
	jnz	.last_fragment

   .handle_it:							; We reach here if packet hasnt been fragmented, or when it already has been re-constructed
	movzx	eax, byte [edx + IPv4_Packet.VersionAndIHL]	; Calculate Header length by using IHL field
	and	eax, 0x0000000F 				;
	shl	eax, 2						;

	movzx	ecx, word [edx + IPv4_Packet.TotalLength]	; Calculate length of encapsulated Packet
	xchg	cl , ch 					;
	sub	ecx, eax					;

	add	eax, edx
	push	eax
	mov	al , [edx + IPv4_Packet.Protocol]
	mov	esi, [edx + IPv4_Packet.SourceAddress]
	mov	edi, [edx + IPv4_Packet.DestinationAddress]
	pop	edx						; Offset to data (tcp/udp/icmp/.. Packet)

	cmp	al , IP_PROTO_TCP
	je	TCP_handler

	cmp	al , IP_PROTO_UDP
	je	UDP_handler

	cmp	al , IP_PROTO_ICMP
	je	ICMP_handler

	DEBUGF	1,"unknown protocol: %u\n",al

  .dump:
	DEBUGF	1,"IP_Handler - done\n"
;        inc     [dumped_rx_count]
	call	kernel_free
	add	esp, 4						; pop (balance stack)
	ret


  .yes_fragments:
	shl	ax , 3
	DEBUGF	1,"Fragmented packet, offset:%u, id:%x\n", ax, [edx + IPv4_Packet.Identification]:4

	test	ax , ax 					; Is this the first packet of the fragment?
	jnz	.not_first_fragment

	DEBUGF	1,"First fragmented packet received!\n"
								; try to locate a free slot..
	mov	ecx, MAX_FRAGMENTS
	mov	esi, FRAGMENT_LIST
     .find_free_slot:
	cmp	word [esi + FRAGMENT_slot.ttl], 0
	je	.found_free_slot
	add	esi, FRAGMENT_slot.size
	loop	.find_free_slot
	jmp	.dump						; If no free slot was found, dump the packet

     .found_free_slot:						; We found a free slot, let's fill in the FRAGMENT_slot structure
	mov	word [esi + FRAGMENT_slot.ttl], 15		; RFC recommends 15 secs as ttl
	mov	ax , word [edx + IPv4_Packet.Identification]
	mov	word [esi + FRAGMENT_slot.id], ax
	mov	eax, dword [edx + IPv4_Packet.SourceAddress]
	mov	dword [esi + FRAGMENT_slot.SrcIP], eax
	mov	eax, dword [edx + IPv4_Packet.DestinationAddress]
	mov	dword [esi + FRAGMENT_slot.DstIP], eax
	pop	eax
	mov	dword [esi + FRAGMENT_slot.ptr], eax
								; Now, replace ethernet header in original buffer with a FRAGMENT_entry structure
	mov	[eax + FRAGMENT_entry.NextPtr], -1
	mov	[eax + FRAGMENT_entry.PrevPtr], -1
	mov	[eax + FRAGMENT_entry.Owner], ebx

	add	esp, 4						; balance stack and exit
	ret



  .not_first_fragment:
	DEBUGF	1,"Middle fragmented packet received!\n"

	call	.find_fragment_slot
	cmp	esi, -1
	je	.dump

	mov	word [esi + FRAGMENT_slot.ttl], 15		; Reset the ttl
	mov	esi, [esi + FRAGMENT_slot.ptr]
	or	edi, -1
     .find_last_entry:						; The following routine will try to find the last entry
	cmp	edi, [esi + FRAGMENT_entry.PrevPtr]
	jne	.destroy_slot					; Damn, something screwed up, remove the whole slot (and free buffers too if possible!)
	mov	edi, esi
	mov	esi, [esi + FRAGMENT_entry.NextPtr]
	cmp	esi, -1
	jne	.find_last_entry
								; We found the last entry (pointer is noww in edi)
								; We are going to overwrite the ethernet header in received packet with a FRAGMENT_entry structure

	pop	eax						; pointer to packet
	mov	[edi + FRAGMENT_entry.NextPtr], eax		; update pointer of previous entry to the new entry
	mov	[eax + FRAGMENT_entry.NextPtr], -1
	mov	[eax + FRAGMENT_entry.PrevPtr], edi
	mov	[eax + FRAGMENT_entry.Owner], ebx

	add	esp, 4
	ret



  .last_fragment:
	DEBUGF	1,"Last fragmented packet received!\n"
	call	.find_fragment_slot
	cmp	esi, -1
	je	.dump

	mov	esi, [esi + FRAGMENT_slot.ptr]			; We found the first entry, let's calculate total size of the packet in eax, so we can allocate a buffer
	push	esi
	xor	eax, eax					;
	or	edi, -1
     .count_bytes:
	cmp	[esi + FRAGMENT_entry.PrevPtr], edi
	jne	.destroy_slot_pop						; Damn, something screwed up, remove the whole slot (and free buffers too if possible!)
	mov	cx, word [esi + FRAGMENT_entry.Data + IPv4_Packet.TotalLength]	  ; Add total length
	xchg	cl, ch
	DEBUGF	1,"Packet size: %u\n", cx
	add	ax, cx
	movzx	cx, byte [esi + FRAGMENT_entry.Data + IPv4_Packet.VersionAndIHL]  ; Sub Header length
	and	cx, 0x000F
	shl	cx, 2
	DEBUGF	1,"Header size: %u\n", cx
	sub	ax, cx
	mov	edi, esi
	mov	esi, [esi + FRAGMENT_entry.NextPtr]
	cmp	esi, -1
	jne	.count_bytes

	mov	esi, [esp+4]  ;;;
	mov	[edi + FRAGMENT_entry.NextPtr], esi			       ; Add this packet to the chain, this simplifies the following code
	mov	[esi + FRAGMENT_entry.NextPtr], -1
	mov	[esi + FRAGMENT_entry.PrevPtr], edi
	mov	[esi + FRAGMENT_entry.Owner], ebx

	mov	cx, [edx + IPv4_Packet.TotalLength]			       ; Note: This time we dont substract Header length
	xchg	cl , ch
	DEBUGF	1,"Packet size: %u\n", cx
	add	ax , cx
	DEBUGF	1,"Total Received data size: %u\n", eax

	push	eax
	mov	ax , [edx + IPv4_Packet.FlagsAndFragmentOffset]
	xchg	al , ah
	shl	ax , 3
	add	cx , ax
	pop	eax
	DEBUGF	1,"Total Fragment size: %u\n", ecx

	cmp	ax, cx
	jne	.destroy_slot_pop

	push	eax
	push	eax
	call	kernel_alloc
	test	eax, eax
	je	.destroy_slot_pop							; If we dont have enough space to allocate the buffer, discard all packets in slot
	mov	edx, [esp+4]								; Get pointer to first fragment entry back in edx

     .rebuild_packet_loop:
	movzx	ecx, word [edx + FRAGMENT_entry.Data + IPv4_Packet.FlagsAndFragmentOffset]	; Calculate the fragment offset
	xchg	cl , ch 									;   intel byte order
	shl	cx , 3										;   multiply by 8 and clear first 3 bits
	DEBUGF	1,"Fragment offset: %u\n", cx

	lea	edi, [eax + ecx]								; Notice that edi will be equal to eax for first fragment
	movzx	ebx, byte [edx + FRAGMENT_entry.Data + IPv4_Packet.VersionAndIHL]		; Find header size (in ebx) of fragment
	and	bx , 0x000F									;
	shl	bx , 2										;

	lea	esi, [edx + FRAGMENT_entry.Data]						; Set esi to the correct begin of fragment
	movzx	ecx, word [edx + FRAGMENT_entry.Data + IPv4_Packet.TotalLength] 		; Calculate total length of fragment
	xchg	cl, ch										;  intel byte order

	cmp	edi, eax									; Is this packet the first fragment ?
	je	.first_fragment
	sub	cx, bx										; If not, dont copy the header
	add	esi, ebx									;
     .first_fragment:

	push	cx										; First copy dword-wise, then byte-wise
	shr	cx, 2										;
	rep	movsd										;
	pop	cx										;
	and	cx, 3										;
	rep	movsb										;

	push	eax
	push	edx										; Push pointer to fragment onto stack
	mov	edx, [edx + FRAGMENT_entry.NextPtr]						; Set edx to the next pointer
	call	kernel_free									; free the previous fragment buffer (this uses the value from stack)
	pop	eax
	cmp	edx, -1 									; Check if it is last fragment in chain
	jne	.rebuild_packet_loop

	pop	ecx										;
	xchg	cl, ch
	mov	edx, eax
	mov	word [edx + IPv4_Packet.TotalLength], cx
	add	esp, 8

	xchg	cl, ch		   ;  This prints the IP packet to the debug board (usefull when using serial output debug..)
	push	ecx  ;;;;
	push	eax	;;;;
;        mov     esi, edx           ;
;                                   ;
;       @@:                         ;
;        lodsb                      ;
;        DEBUGF  1,"%x ", eax:2     ;
;        loop    @r                 ;

	movzx	eax, byte [edx + IPv4_Packet.VersionAndIHL]	; Calculate Header length by using IHL field
	and	ax, 0x000F					;
	shl	ax, 2						;

	sub	ecx, eax


	add	eax, edx
	push	eax
	mov	al , [edx + IPv4_Packet.Protocol]
	mov	esi, [edx + IPv4_Packet.SourceAddress]
	mov	edi, [edx + IPv4_Packet.DestinationAddress]
	pop	edx						; Offset to data (tcp/udp/icmp/.. Packet)

	cmp	al , IP_PROTO_TCP
	je	TCP_handler

	cmp	al , IP_PROTO_UDP
	je	UDP_handler

	cmp	al , IP_PROTO_ICMP
	je	ICMP_handler_fragments

	DEBUGF	1,"IP_Handler - unknown protocol:%u\n",al

	call	kernel_free
	add	esp, 8						; pop (balance stack)

	ret


  .destroy_slot_pop:
	add	esp, 4
  .destroy_slot:
	DEBUGF	1,"Destroy fragment slot!\n"
	; TODO!
	jmp	.dump



;-----------------------------------------------------------------
;
; find fragment slot
;
; IN: pointer to fragmented packet in edx         ; TODO: the RFC says we should check protocol too
; OUT: pointer to slot in edi, -1 on error
;
;-----------------------------------------------------------------

  .find_fragment_slot:

	push	eax ebx ecx edx
	mov	ax , word [edx + IPv4_Packet.Identification]
	mov	ecx, MAX_FRAGMENTS
	mov	esi, FRAGMENT_LIST
	mov	ebx, dword [edx + IPv4_Packet.SourceAddress]
	mov	edx, dword [edx + IPv4_Packet.DestinationAddress]
  .find_slot:
	cmp	word [esi + FRAGMENT_slot.id], ax
	jne	.try_next
	cmp	dword [esi + FRAGMENT_slot.SrcIP], ebx
	jne	.try_next
	cmp	dword [esi + FRAGMENT_slot.DstIP], edx
	je	.found_slot
  .try_next:
	add	esi, FRAGMENT_slot.size
	loop	.find_slot
 ;       pop     edx ebx
	or	esi, -1
;        ret

  .found_slot:
	pop	edx ecx ebx eax
	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





;-----------------------------------------------------------------
;
; Create_IPv4_Packet
;
; IN: eax = dest ip
;     ebx = source ip
;     ecx = data length
;     dx  = fragment id
;     di  = protocol
;
; OUT: eax = pointer to buffer start
;      ebx = pointer to device struct (needed for sending procedure)
;      ecx = unchanged (packet size of embedded data)
;      edx = size of complete buffer
;      esi = pointer to sending procedure
;      edi = pointer to start of data (-1 on error)
;
;----------------------------------------------------------------- ;;; TODO: create fragmented packets
align 4
IPv4_create_packet:

	DEBUGF 1,"Create IPv4 Packet (size=%u)\n", ecx

	cmp	ecx, 1480
	jg	.exit_

	test	ebx, ebx	      ; if dest 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:			      ;

	push	ecx eax ebx dx di

	cmp	eax, -1
	je	.broadcast		  ; If it is broadcast, just send

	call	ARP_IP_to_MAC

	cmp	eax, -1
	je	.not_found

	push	ebx
	push	ax

	jmp	.send

  .broadcast:
	push	word -1
	push	dword -1

  .send:
	call	IPv4_dest_to_dev
	inc	[IP_PACKETS_TX+4*edi]
	mov	edx, [ETH_DRV_LIST + 4*edi]
	lea	eax, [edx + ETH_DEVICE.mac]
	mov	ebx, esp
	mov	ecx, [esp+18]	 ;; 18 or  22 ??
	add	ecx, IPv4_Packet.DataOrOptional
	mov	di , ETHER_IPv4
	call	ETH_create_packet		   ; TODO: figure out a way to make this work with other protocols too
	add	esp, 6
	cmp	edi, -1
	je	.exit

	mov	[edi + IPv4_Packet.VersionAndIHL], 0x45   ; IPv4, normal length (no Optional header)
	mov	[edi + IPv4_Packet.TypeOfService], 0
	xchg	ch, cl
	mov	[edi + IPv4_Packet.TotalLength], cx
	mov	[edi + IPv4_Packet.FlagsAndFragmentOffset], 0x0000
	mov	[edi + IPv4_Packet.TimeToLive], 128
	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 ebx edx
	; calculate checksum
	xor	edx, edx
	mov	esi, edi
	mov	ecx, IPv4_Packet.DataOrOptional
	call	checksum_1
	call	checksum_2
	mov	[edi + IPv4_Packet.HeaderChecksum], dx
	pop	edx ebx eax ecx
	add	edi, IPv4_Packet.DataOrOptional

	DEBUGF 1,"IPv4 Packet for device %x created successfully\n", ebx

	ret


  .not_found:
	DEBUGF 1,"Create IPv4 Packet - ARP entry not found!\n"
	; TODO: QUEUE the packet to resend later!
  .exit:
	add	esp, 16
  .exit_:
	DEBUGF 1,"Create IPv4 Packet - failed\n"
	or	edi, -1
	ret




;---------------------------------------------------------------------------
;
; IPv4_dest_to_dev
;
; IN: Destination IP in eax
; OUT: device id in edi
;
;---------------------------------------------------------------------------
align 4
IPv4_dest_to_dev:

	DEBUGF 1,"IPv4 destination to device: "

	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	edx, eax
	and	edx, [SUBNET_LIST+edi]

	cmp	ebx, edx
	je	.found_it

	add	edi, 4
	loop	.loop

	xor	edi, edi	; if none found, use device 0 as default device

  .found_it:
	shr	edi, 2

	DEBUGF 1,"%u\n",edi

	ret



;---------------------------------------------------------------------------
;
; IPv4_get_frgmnt_num
;
; IN: /
; OUT: fragment number in ax
;
;---------------------------------------------------------------------------
align 4
IPv4_get_frgmnt_num:
	xor	ax, ax	;;; TODO: replace this with real code

	ret


;---------------------------------------------------------------------------
;
; IPv4_API
;
; This function is called by system function 75
;
; IN:  subfunction number in bl
;      device number in bh
;      ecx, edx, .. depends on subfunction
;
; OUT:
;
;---------------------------------------------------------------------------
align 4
IPv4_API:

	movzx	eax, bh
	shl	eax, 2

	test	bl, bl
	jz	.packets_tx	; 0
	dec	bl
	jz	.packets_rx	; 1
	dec	bl
	jz	.read_ip	; 2
	dec	bl
	jz	.write_ip	; 3
	dec	bl
	jz	.read_dns	; 4
	dec	bl
	jz	.write_dns	; 5
	dec	bl
	jz	.read_subnet	; 6
	dec	bl
	jz	.write_subnet	; 7
	dec	bl
	jz	.read_gateway	; 8
	dec	bl
	jz	.write_gateway	; 9

.error:
	mov	eax, -1
	ret

.packets_tx:
	add	eax, IP_PACKETS_TX
	mov	eax, [eax]
	ret

.packets_rx:
	add	eax, IP_PACKETS_RX
	mov	eax, [eax]
	ret

.read_ip:
	add	eax, IP_LIST
	mov	eax, [eax]
	ret

.write_ip:
	add	eax, IP_LIST
	mov	[eax], ecx
	xor	eax, eax
	ret

.read_dns:
	add	eax, DNS_LIST
	mov	eax, [eax]
	ret

.write_dns:
	add	eax, DNS_LIST
	mov	[eax], ecx
	xor	eax, eax
	ret

.read_subnet:
	add	eax, SUBNET_LIST
	mov	eax, [eax]
	ret

.write_subnet:
	add	eax, SUBNET_LIST
	mov	[eax], ecx
	xor	eax, eax
	ret

.read_gateway:
	add	eax, GATEWAY_LIST
	mov	eax, [eax]
	ret

.write_gateway:
	add	eax, GATEWAY_LIST
	mov	[eax], ecx
	xor	eax, eax
	ret




