8a7ebf6b32
added pci ids of dec21x4x cards to netcfg bugfixes in dex21x4x driver, pcnet32 driver, rtl8139 driver and sis900 driver new network program (ICMP) to ping computers, uses new RAW socket code (experimental) git-svn-id: svn://kolibrios.org@1541 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 |