forked from KolibriOS/kolibrios
08596896d2
-Updates in TCP code: accepting connections now works. (TCPserv is able to send 'hello world!' to telnet client) git-svn-id: svn://kolibrios.org@1838 a494cfbc-eb01-0410-851d-a64ba20cac60
1019 lines
25 KiB
PHP
1019 lines
25 KiB
PHP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; Copyright (C) KolibriOS team 2004-2010. 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$
|
|
|
|
MAX_FRAGMENTS equ 64
|
|
MAX_IP equ MAX_NET_DEVICES
|
|
IP_MAX_INTERFACES equ MAX_IP
|
|
|
|
struct IPv4_Packet
|
|
.VersionAndIHL db ? ; Version[0-3 bits] and IHL(header length)[4-7 bits]
|
|
.TypeOfService db ? ; precedence [7-5] minimize delay [4], maximize throughput [3], maximize riliability [2] minimize momentary cost [1] and zero [0]
|
|
.TotalLength dw ?
|
|
.Identification dw ?
|
|
.FlagsAndFragmentOffset dw ? ; Flags[0-2] and FragmentOffset[3-15]
|
|
.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
|
|
|
|
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
|
|
;
|
|
;-----------------------------------------------------------------
|
|
macro IPv4_init {
|
|
|
|
xor eax, eax
|
|
mov edi, IP_LIST
|
|
mov ecx, 4*MAX_IP
|
|
rep stosd
|
|
|
|
mov edi, FRAGMENT_LIST
|
|
mov ecx, FRAGMENT_slot.size*MAX_FRAGMENTS/4 + 2*MAX_IP
|
|
rep stosd
|
|
|
|
}
|
|
|
|
|
|
;-----------------------------------------------------------------
|
|
;
|
|
; 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
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------
|
|
;
|
|
; IPv4_input:
|
|
;
|
|
; 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_input: ; TODO: implement handler for IP options
|
|
; TODO2: add code for raw sockets
|
|
|
|
DEBUGF 1,"IPv4_Handler, packet from: %u.%u.%u.%u ",\
|
|
[edx + IPv4_Packet.SourceAddress]:1,[edx + IPv4_Packet.SourceAddress + 1]:1,[edx + IPv4_Packet.SourceAddress + 2]:1,[edx + IPv4_Packet.SourceAddress + 3]:1
|
|
DEBUGF 1,"to: %u.%u.%u.%u\n",\
|
|
[edx + IPv4_Packet.DestinationAddress]:1,[edx + IPv4_Packet.DestinationAddress + 1]:1,[edx + IPv4_Packet.DestinationAddress + 2]:1,[edx + IPv4_Packet.DestinationAddress + 3]:1
|
|
|
|
;-------------------------------------------
|
|
; Check if the packet still has time to live
|
|
|
|
cmp byte [edx + IPv4_Packet.TimeToLive], 0
|
|
je .dump
|
|
|
|
;--------------------------------------
|
|
; First, check if IP packet has options
|
|
|
|
movzx eax, [edx + IPv4_Packet.VersionAndIHL]
|
|
and al , 0x0f ; get IHL(header length)
|
|
cmp al , 0x05 ; IHL!= 5*4(20 bytes)
|
|
jnz .has_options
|
|
|
|
;-------------------------------
|
|
; Now, re-calculate the checksum
|
|
|
|
IPv4_checksum edx
|
|
jnz .dump ; if checksum isn't valid then dump packet
|
|
|
|
DEBUGF 1,"IPv4 Checksum is correct\n"
|
|
|
|
;-----------------------------------
|
|
; Check if destination IP is correct
|
|
|
|
call NET_ptr_to_num
|
|
shl edi, 2
|
|
|
|
; check if it matches local ip
|
|
|
|
mov eax, dword[IP_LIST+edi]
|
|
cmp [edx + IPv4_Packet.DestinationAddress], eax
|
|
je .ip_ok
|
|
|
|
; check for broadcast
|
|
|
|
mov eax, dword[SUBNET_LIST+edi]
|
|
not eax
|
|
or eax, dword[IP_LIST+edi]
|
|
cmp [edx + IPv4_Packet.DestinationAddress], eax
|
|
je .ip_ok
|
|
|
|
; or a special broadcast
|
|
|
|
cmp [edx + IPv4_Packet.DestinationAddress], -1
|
|
je .ip_ok
|
|
|
|
; maybe it's a multicast then
|
|
|
|
mov eax, [edx + IPv4_Packet.DestinationAddress]
|
|
and eax, 0xff000000
|
|
; cmp eax, 224 shl 24
|
|
; je .ip_ok
|
|
|
|
; or a loopback address
|
|
|
|
cmp eax, 127 shl 24
|
|
je .ip_ok
|
|
|
|
; or it's not meant for us..
|
|
|
|
DEBUGF 2,"Destination address does not match!\n"
|
|
jmp .dump
|
|
|
|
;------------------------
|
|
; Now we can update stats
|
|
|
|
.ip_ok:
|
|
inc [IP_PACKETS_RX+edi]
|
|
|
|
;----------------------------------
|
|
; Check if the packet is fragmented
|
|
|
|
test [edx + IPv4_Packet.FlagsAndFragmentOffset], 1 shl 5 ; Is 'more fragments' flag set ?
|
|
jnz .has_fragments ; If so, we definately have a fragmented packet
|
|
|
|
test [edx + IPv4_Packet.FlagsAndFragmentOffset], 0xff1f ; If flag is not set, but there is a fragment offset, the packet is last in series of fragmented packets
|
|
jnz .is_last_fragment
|
|
|
|
;-------------------------------------------------------------------
|
|
; No, it's just a regular IP packet, pass it to the higher protocols
|
|
|
|
.handle_it: ; We reach here if packet hasnt been fragmented, or when it already has been re-constructed
|
|
movzx eax, byte [edx + IPv4_Packet.VersionAndIHL] ; Calculate Header length by using IHL field
|
|
and eax, 0x0000000f ;
|
|
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 esi, [edx + IPv4_Packet.SourceAddress] ; These values might be of interest to the higher protocols
|
|
mov edi, [edx + IPv4_Packet.DestinationAddress] ;
|
|
mov al , [edx + IPv4_Packet.Protocol]
|
|
pop edx ; Offset to data (tcp/udp/icmp/.. Packet)
|
|
|
|
cmp al , IP_PROTO_TCP
|
|
je TCP_input
|
|
|
|
cmp al , IP_PROTO_UDP
|
|
je UDP_input
|
|
|
|
cmp al , IP_PROTO_ICMP
|
|
je ICMP_input
|
|
|
|
DEBUGF 2,"unknown Internet protocol: %u\n", al
|
|
|
|
.dump:
|
|
DEBUGF 2,"IP_Handler - dumping\n"
|
|
; inc [dumped_rx_count]
|
|
call kernel_free
|
|
add esp, 4 ; pop (balance stack)
|
|
ret
|
|
|
|
|
|
;---------------------------
|
|
; Fragmented packet handler
|
|
|
|
|
|
.has_fragments:
|
|
movzx eax, [edx + IPv4_Packet.FlagsAndFragmentOffset]
|
|
xchg al , ah
|
|
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?
|
|
jz .is_first_fragment
|
|
|
|
|
|
;-------------------------------------------------------
|
|
; We have a fragmented IP packet, but it's not the first
|
|
|
|
DEBUGF 1,"Middle fragmented packet received!\n"
|
|
|
|
call IPv4_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 now 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
|
|
|
|
|
|
;------------------------------------
|
|
; We have received the first fragment
|
|
|
|
.is_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
|
|
|
|
|
|
;-----------------------------------
|
|
; We have received the last fragment
|
|
|
|
.is_last_fragment:
|
|
DEBUGF 1,"Last fragmented packet received!\n"
|
|
|
|
call IPv4_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 ebx, [edx + FRAGMENT_entry.Owner] ; we need to remeber the owner, in case this is the last packet
|
|
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 ;
|
|
|
|
push ecx ;;;;
|
|
push eax ;;;;
|
|
|
|
; mov esi, edx ; This prints the IP packet to the debug board (usefull when using serial output debug..)
|
|
; ;
|
|
; packet_to_debug
|
|
|
|
jmp .handle_it ; edx = buf ptr, ecx = size, [esp] buf ptr, [esp+4], total size, ebx=device ptr
|
|
|
|
.destroy_slot_pop:
|
|
add esp, 4
|
|
.destroy_slot:
|
|
DEBUGF 1,"Destroy fragment slot!\n"
|
|
; TODO!
|
|
jmp .dump
|
|
|
|
|
|
|
|
|
|
;-----------------------------------
|
|
; The IP packet has some options
|
|
|
|
.has_options:
|
|
jmp .dump
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------
|
|
;
|
|
; find fragment slot
|
|
;
|
|
; IN: pointer to fragmented packet in edx
|
|
; OUT: pointer to slot in edi, -1 on error
|
|
;
|
|
;-----------------------------------------------------------------
|
|
align 4
|
|
IPv4_find_fragment_slot:
|
|
|
|
;;; TODO: the RFC says we should check protocol number too
|
|
|
|
push eax ebx ecx edx
|
|
mov ax , word [edx + IPv4_Packet.Identification]
|
|
mov ecx, MAX_FRAGMENTS
|
|
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
|
|
|
|
|
|
;------------------------------------------------------------------
|
|
;
|
|
; IPv4_output
|
|
;
|
|
; IN: eax = dest ip
|
|
; ebx = source ip
|
|
; ecx = data length
|
|
; dx = fragment id
|
|
; di = TTL shl 8 + 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
|
|
; edi = pointer to start of data (0 on error)
|
|
;
|
|
;------------------------------------------------------------------
|
|
align 4
|
|
IPv4_output:
|
|
|
|
DEBUGF 1,"IPv4_create_packet: size=%u\n", ecx
|
|
|
|
cmp ecx, 65500 ; Max IPv4 packet size
|
|
jg .too_large
|
|
|
|
push ecx eax ebx dx di
|
|
|
|
call ARP_IP_to_MAC
|
|
|
|
test eax, 0xffff0000 ; error bits
|
|
jnz .arp_error
|
|
|
|
.continue:
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
;------------------------------------------------------------------
|
|
;
|
|
; IPv4_output_raw
|
|
;
|
|
; IN: eax = socket ptr
|
|
; ecx = data length
|
|
; esi = data ptr
|
|
;
|
|
; OUT: /
|
|
;
|
|
;------------------------------------------------------------------
|
|
align 4
|
|
IPv4_output_raw:
|
|
|
|
DEBUGF 1,"IPv4_output_raw: size=%u ptr=%x socket=%x\n", ecx, esi, eax
|
|
|
|
cmp ecx, 1480 ;;;;;
|
|
jg .too_large
|
|
|
|
sub esp, 8
|
|
push esi eax
|
|
|
|
call ARP_IP_to_MAC
|
|
|
|
test eax, 0xffff0000 ; error bits
|
|
jnz .arp_error
|
|
|
|
.continue:
|
|
|
|
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 + 6+4]
|
|
add ecx, IPv4_Packet.DataOrOptional
|
|
mov di, ETHER_IPv4
|
|
call ETH_output
|
|
jz .error
|
|
|
|
add esp, 6 ; pop the mac
|
|
|
|
mov dword[esp+4+4], edx
|
|
mov dword[esp+4+4+4], eax
|
|
|
|
pop eax esi
|
|
;; todo: check socket options if we should add header, or just compute checksum
|
|
|
|
push edi ecx
|
|
rep movsb
|
|
pop ecx edi
|
|
|
|
; [edi + IPv4_Packet.VersionAndIHL] ; IPv4, normal length (no Optional header)
|
|
; [edi + IPv4_Packet.TypeOfService] ; nothing special, just plain ip packet
|
|
; [edi + IPv4_Packet.TotalLength]
|
|
; [edi + IPv4_Packet.TotalLength] ; internet byte order
|
|
; [edi + IPv4_Packet.FlagsAndFragmentOffset]
|
|
|
|
mov [edi + IPv4_Packet.HeaderChecksum], 0
|
|
|
|
; [edi + IPv4_Packet.TimeToLive] ; ttl shl 8 + protocol
|
|
; [edi + IPv4_Packet.Protocol]
|
|
; [edi + IPv4_Packet.Identification] ; fragment id
|
|
; [edi + IPv4_Packet.SourceAddress]
|
|
; [edi + IPv4_Packet.DestinationAddress]
|
|
|
|
IPv4_checksum edi ;;;; todo: checksum for IP packet with options!
|
|
add edi, IPv4_Packet.DataOrOptional
|
|
DEBUGF 1,"IPv4 Packet for device %x created successfully\n", ebx
|
|
call [ebx + NET_DEVICE.transmit]
|
|
ret
|
|
|
|
.error:
|
|
add esp, 6
|
|
.arp_error:
|
|
add esp, 8+4+4
|
|
.too_large:
|
|
DEBUGF 1,"IPv4_output_raw: 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
|
|
jz .err2
|
|
|
|
push ebx
|
|
push ax
|
|
|
|
mov ebx, [NET_DRV_LIST]
|
|
lea eax, [ebx + ETH_DEVICE.mac]
|
|
push eax
|
|
|
|
|
|
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
|
|
call ETH_output
|
|
|
|
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 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
|
|
|
|
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<< send the packet
|
|
mov ecx, [esp + 1*4]
|
|
|
|
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
|
|
|
|
|
|
|
|
;---------------------------------------------------------------------------
|
|
;
|
|
; IPv4_dest_to_dev
|
|
;
|
|
; IN: eax = Destination IP
|
|
; OUT: edi = device id * 4
|
|
;
|
|
;---------------------------------------------------------------------------
|
|
align 4
|
|
IPv4_dest_to_dev:
|
|
|
|
cmp eax, 0xffffffff
|
|
je .invalid
|
|
|
|
xor edi, edi
|
|
mov ecx, MAX_IP
|
|
|
|
.loop:
|
|
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
|
|
|
|
.invalid:
|
|
xor edi, edi ; if none found, use device 0 as default device
|
|
|
|
.found_it:
|
|
DEBUGF 1,"IPv4_dest_to_dev: %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 |