kolibrios/kernel/trunk/network/IPv4.inc
hidnplayr 93a672ba27 network code cleanup
git-svn-id: svn://kolibrios.org@7679 a494cfbc-eb01-0410-851d-a64ba20cac60
2019-08-25 18:46:47 +00:00

1161 lines
42 KiB
PHP

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2019. 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$
IPv4_MAX_FRAGMENTS = 64
IPv4_MAX_ROUTES = 64
IPv4_ROUTE_FLAG_UP = 1 shl 0
IPv4_ROUTE_FLAG_GATEWAY = 1 shl 1
IPv4_ROUTE_FLAG_HOST = 1 shl 2
IPv4_ROUTE_FLAG_D = 1 shl 3 ; Route was created by a redirect
IPv4_ROUTE_FLAG_M = 1 shl 4 ; Route was modified by a redirect
struct IPv4_header
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 ?
ends
struct IPv4_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
ends
struct IPv4_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 ;;; FIXME
; Ip header begins here (we will need the IP header to re-construct the complete packet)
ends
;struct IPv4_ROUTE
;
; Destination dd ?
; Gateway dd ?
; Flags dd ?
; Use dd ?
; Interface dd ?
;
;ends
uglobal
align 4
IPv4_address rd NET_DEVICES_MAX
IPv4_subnet rd NET_DEVICES_MAX
IPv4_nameserver rd NET_DEVICES_MAX
IPv4_gateway rd NET_DEVICES_MAX
IPv4_broadcast rd NET_DEVICES_MAX
IPv4_packets_tx rd NET_DEVICES_MAX
IPv4_packets_rx rd NET_DEVICES_MAX
IPv4_packets_dumped rd NET_DEVICES_MAX
IPv4_fragments rb IPv4_MAX_FRAGMENTS * sizeof.IPv4_FRAGMENT_slot
; IPv4_routes rd IPv4_MAX_ROUTES * sizeof.IPv4_ROUTE
endg
;-----------------------------------------------------------------;
; ;
; ipv4_init: Resets all IPv4 variables ;
; ;
;-----------------------------------------------------------------;
macro ipv4_init {
xor eax, eax
mov edi, IPv4_address
mov ecx, 7*NET_DEVICES_MAX + (sizeof.IPv4_FRAGMENT_slot*IPv4_MAX_FRAGMENTS)/4
rep stosd
}
;-----------------------------------------------------------------;
; ;
; Decrease TimeToLive of all fragment slots ;
; ;
;-----------------------------------------------------------------;
macro ipv4_decrease_fragment_ttls {
local .loop, .next
mov esi, IPv4_fragments
mov ecx, IPv4_MAX_FRAGMENTS
.loop:
cmp [esi + IPv4_FRAGMENT_slot.ttl], 0
je .next
dec [esi + IPv4_FRAGMENT_slot.ttl]
jz .died
.next:
add esi, sizeof.IPv4_FRAGMENT_slot
dec ecx
jnz .loop
jmp .done
.died:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4 Fragment slot timed-out!\n"
;;; TODO: clear all entry's of timed-out slot
jmp .next
.done:
}
macro ipv4_checksum ptr {
; This is the fast procedure to create or check an 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: Check if IPv4 Packet isnt damaged and call ;
; appropriate handler. (TCP/UDP/ICMP/..) ;
; We will also re-construct fragmented packets. ;
; ;
; IN: Pointer to buffer in [esp] ;
; pointer to device struct in ebx ;
; pointer to IPv4 header in edx ;
; size of IPv4 packet in ecx ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
align 4
ipv4_input:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: packet from %u.%u.%u.%u ",\
[edx + IPv4_header.SourceAddress + 0]:1,[edx + IPv4_header.SourceAddress + 1]:1,\
[edx + IPv4_header.SourceAddress + 2]:1,[edx + IPv4_header.SourceAddress + 3]:1
DEBUGF DEBUG_NETWORK_VERBOSE, "to %u.%u.%u.%u\n",\
[edx + IPv4_header.DestinationAddress + 0]:1,[edx + IPv4_header.DestinationAddress + 1]:1,\
[edx + IPv4_header.DestinationAddress + 2]:1,[edx + IPv4_header.DestinationAddress + 3]:1
call net_ptr_to_num4
cmp edi, -1
je .invalid_device
;-------------------------------
; re-calculate the checksum
ipv4_checksum edx
jnz .dump ; if checksum isn't valid then dump packet
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Checksum ok\n"
;--------------------------------
; Check if destination IP matches
; local ip (Using RFC1122 strong end system model)
mov eax, [edx + IPv4_header.DestinationAddress]
cmp eax, [IPv4_address + edi]
je .ip_ok
; network layer broadcast
cmp eax, [IPv4_broadcast + edi]
je .ip_ok
; physical layer broadcast (255.255.255.255)
cmp eax, 0xffffffff
je .ip_ok
; multicast (224.0.0.0/4 = 224.0.0.0 to 239.255.255.255)
and eax, 0x0fffffff
cmp eax, 224
je .ip_ok
; maybe we just dont have an IP yet and should accept everything on the IP level
cmp [IPv4_address + edi], 0
je .ip_ok
; or it's just not meant for us.. :(
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Destination address does not match!\n"
jmp .dump
;------------------------
; Now we can update stats
.ip_ok:
inc [IPv4_packets_rx + edi]
;----------------------------------
; Check if the packet is fragmented
test [edx + IPv4_header.FlagsAndFragmentOffset], 1 shl 5 ; Is 'more fragments' flag set ?
jnz .has_fragments ; If so, we definately have a fragmented packet
test [edx + IPv4_header.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 esi, [edx + IPv4_header.VersionAndIHL] ; Calculate Header length by using IHL field
and esi, 0x0000000f ;
shl esi, 2 ;
movzx ecx, [edx + IPv4_header.TotalLength] ; Calculate length of encapsulated Packet
xchg cl, ch ;
sub ecx, esi ;
mov al, [edx + IPv4_header.Protocol]
add esi, edx ; make esi ptr to data
cmp al, IP_PROTO_TCP
je tcp_input
cmp al, IP_PROTO_UDP
je udp_input
cmp al, IP_PROTO_ICMP
je icmp_input
;-------------------------------
; Look for a matching RAW socket
pusha
mov ecx, socket_mutex
call mutex_lock
popa
add ecx, esi
sub ecx, edx
mov esi, edx
movzx edx, al
mov eax, net_sockets
.next_socket:
mov eax, [eax + SOCKET.NextPtr]
or eax, eax
jz .dump_unlock
cmp [eax + SOCKET.Domain], AF_INET4
jne .next_socket
cmp [eax + SOCKET.Protocol], edx
jne .next_socket
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: found matching RAW socket: 0x%x\n", eax
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
jmp socket_input
.dump_unlock:
pusha
mov ecx, socket_mutex
call mutex_unlock
popa
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: unknown protocol %u\n", al
.dump:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: dumping\n"
inc [IPv4_packets_dumped + edi]
call net_buff_free
ret
.invalid_device:
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_input: packet originated from invalid device\n"
call net_buff_free
ret
;---------------------------
; Fragmented packet handler
.has_fragments:
movzx eax, [edx + IPv4_header.FlagsAndFragmentOffset]
xchg al, ah
shl ax, 3
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: fragmented packet offset=%u id=%x ptr=0x%x\n", ax, [edx + IPv4_header.Identification]:4, edx
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 DEBUG_NETWORK_VERBOSE, "IPv4_input: Middle fragment packet received!\n"
call ipv4_find_fragment_slot
cmp esi, -1
je .dump
mov [esi + IPv4_FRAGMENT_slot.ttl], 15 ; Reset the ttl
mov esi, [esi + IPv4_FRAGMENT_slot.ptr]
or edi, -1
.find_last_entry: ; The following routine will try to find the last entry
cmp edi, [esi + IPv4_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 + IPv4_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 + IPv4_FRAGMENT_entry.NextPtr], eax ; update pointer of previous entry to the new entry
mov [eax + IPv4_FRAGMENT_entry.NextPtr], -1
mov [eax + IPv4_FRAGMENT_entry.PrevPtr], edi
mov [eax + IPv4_FRAGMENT_entry.Owner], ebx
ret
;------------------------------------
; We have received the first fragment
.is_first_fragment:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: First fragment packet received!\n"
; try to locate a free slot..
mov ecx, IPv4_MAX_FRAGMENTS
mov esi, IPv4_fragments
.find_free_slot:
cmp word [esi + IPv4_FRAGMENT_slot.ttl], 0
je .found_free_slot
add esi, sizeof.IPv4_FRAGMENT_slot
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 [esi + IPv4_FRAGMENT_slot.ttl], 15 ; RFC recommends 15 secs as ttl
mov ax, [edx + IPv4_header.Identification]
mov [esi + IPv4_FRAGMENT_slot.id], ax
mov eax, [edx + IPv4_header.SourceAddress]
mov [esi + IPv4_FRAGMENT_slot.SrcIP], eax
mov eax, [edx + IPv4_header.DestinationAddress]
mov [esi + IPv4_FRAGMENT_slot.DstIP], eax
pop eax
mov [esi + IPv4_FRAGMENT_slot.ptr], eax
; Now, replace ethernet header in original buffer with a FRAGMENT_entry structure
mov [eax + IPv4_FRAGMENT_entry.NextPtr], -1
mov [eax + IPv4_FRAGMENT_entry.PrevPtr], -1
mov [eax + IPv4_FRAGMENT_entry.Owner], ebx
ret
;-----------------------------------
; We have received the last fragment
.is_last_fragment:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Last fragment packet received!\n"
call ipv4_find_fragment_slot
cmp esi, -1
je .dump
mov esi, [esi + IPv4_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 + IPv4_FRAGMENT_entry.PrevPtr], edi
jne .destroy_slot_pop ; Damn, something screwed up, remove the whole slot (and free buffers too if possible!)
mov cx, [esi + sizeof.IPv4_FRAGMENT_entry + IPv4_header.TotalLength] ; Add total length
xchg cl, ch
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Packet size=%u\n", cx
add ax, cx
movzx cx, [esi + sizeof.IPv4_FRAGMENT_entry + IPv4_header.VersionAndIHL] ; Sub Header length
and cx, 0x000F
shl cx, 2
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Header size=%u\n", cx
sub ax, cx
mov edi, esi
mov esi, [esi + IPv4_FRAGMENT_entry.NextPtr]
cmp esi, -1
jne .count_bytes
mov esi, [esp+4]
mov [edi + IPv4_FRAGMENT_entry.NextPtr], esi ; Add this packet to the chain, this simplifies the following code
mov [esi + IPv4_FRAGMENT_entry.NextPtr], -1
mov [esi + IPv4_FRAGMENT_entry.PrevPtr], edi
mov [esi + IPv4_FRAGMENT_entry.Owner], ebx
mov cx, [edx + IPv4_header.TotalLength] ; Note: This time we dont substract Header length
xchg cl, ch
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Packet size=%u\n", cx
add ax, cx
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Total Received data size=%u\n", eax
push eax
mov ax, [edx + IPv4_header.FlagsAndFragmentOffset]
xchg al, ah
shl ax, 3
add cx, ax
pop eax
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: 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, [edx + sizeof.IPv4_FRAGMENT_entry + IPv4_header.FlagsAndFragmentOffset] ; Calculate the fragment offset
xchg cl, ch ; intel byte order
shl cx, 3 ; multiply by 8 and clear first 3 bits
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Fragment offset=%u\n", cx
lea edi, [eax + ecx] ; Notice that edi will be equal to eax for first fragment
movzx ebx, [edx + sizeof.IPv4_FRAGMENT_entry + IPv4_header.VersionAndIHL] ; Find header size (in ebx) of fragment
and bx, 0x000F ;
shl bx, 2 ;
lea esi, [edx + sizeof.IPv4_FRAGMENT_entry] ; Set esi to the correct begin of fragment
movzx ecx, [edx + sizeof.IPv4_FRAGMENT_entry + IPv4_header.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:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Copying %u bytes from 0x%x to 0x%x\n", ecx, esi, edi
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 + IPv4_FRAGMENT_entry.Owner] ; we need to remeber the owner, in case this is the last packet
push [edx + IPv4_FRAGMENT_entry.NextPtr] ; Set edx to the next pointer
push edx ; Push pointer to fragment onto stack
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_input: Next Fragment: 0x%x\n", edx
call net_buff_free ; free the previous fragment buffer (this uses the value from stack)
pop edx ebx 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 [edx + IPv4_header.TotalLength], cx
add esp, 12
xchg cl, ch
push ecx edx ; size and pointer
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 DEBUG_NETWORK_VERBOSE, "IPv4_input: Destroy fragment slot!\n"
; TODO!
jmp .dump
;-----------------------------------------------------------------;
; ;
; ipv4_find_fragment_slot ;
; ;
; IN: pointer to fragmented packet in edx ;
; ;
; OUT: pointer to slot in esi, -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, [edx + IPv4_header.Identification]
mov ecx, IPv4_MAX_FRAGMENTS
mov esi, IPv4_fragments
mov ebx, [edx + IPv4_header.SourceAddress]
mov edx, [edx + IPv4_header.DestinationAddress]
.find_slot:
cmp [esi + IPv4_FRAGMENT_slot.id], ax
jne .try_next
cmp [esi + IPv4_FRAGMENT_slot.SrcIP], ebx
jne .try_next
cmp [esi + IPv4_FRAGMENT_slot.DstIP], edx
je .found_slot
.try_next:
add esi, sizeof.IPv4_FRAGMENT_slot
loop .find_slot
or esi, -1
.found_slot:
pop edx ecx ebx eax
ret
;------------------------------------------------------------------;
; ;
; ipv4_output ;
; ;
; IN: al = protocol ;
; ah = TTL ;
; ebx = device ptr (or 0 to let IP layer decide) ;
; ecx = data length ;
; edx = Source IP ;
; edi = Destination IP ;
; ;
; OUT: eax = pointer to buffer start ;
; eax = 0 on error ;
; ebx = device ptr (send packet through this device) ;
; ecx = data length ;
; edx = size of complete frame ;
; edi = start of IPv4 payload ;
; ;
;------------------------------------------------------------------;
align 4
ipv4_output:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_output: size=%u ip=0x%x\n", ecx, edi
cmp ecx, 65500 ; Max IPv4 packet size
ja .too_large
push ecx ax edi
mov eax, edi
call ipv4_route ; outputs device number in edi, dest ip in eax, source IP in edx
test eax, eax
jz .no_route
push edx
test edi, edi
jz .loopback
call arp_ip_to_mac
test eax, 0xffff0000 ; error bits
jnz .arp_error
push ebx ; push the mac onto the stack
push ax
inc [IPv4_packets_tx + edi] ; update stats
mov ax, ETHER_PROTO_IPv4
mov ebx, [net_device_list + edi]
mov ecx, [esp + 6 + 8 + 2]
add ecx, sizeof.IPv4_header
mov edx, esp
call eth_output
jz .eth_error
add esp, 6 ; pop the mac out of the stack
.continue:
xchg cl, ch ; internet byte order
mov [edi + IPv4_header.VersionAndIHL], 0x45 ; IPv4, normal length (no Optional header)
mov [edi + IPv4_header.TypeOfService], 0 ; nothing special, just plain ip packet
mov [edi + IPv4_header.TotalLength], cx
mov [edi + IPv4_header.Identification], 0 ; fragment id: FIXME
mov [edi + IPv4_header.FlagsAndFragmentOffset], 0
mov [edi + IPv4_header.HeaderChecksum], 0
popd [edi + IPv4_header.SourceAddress]
popd [edi + IPv4_header.DestinationAddress]
pop word[edi + IPv4_header.TimeToLive] ; ttl shl 8 + protocol
; [edi + IPv4_header.Protocol]
pop ecx
ipv4_checksum edi
add edi, sizeof.IPv4_header
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_output: success!\n"
ret
.eth_error:
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_output: ethernet error\n"
add esp, 3*4+2+6
xor eax, eax
ret
.no_route:
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_output: No route to host!\n"
add esp, 2*4+2
xor eax, eax
ret
.arp_error:
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_output: ARP error=%x\n", eax
add esp, 4
pop eax
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_output: ip=0x%x\n", eax
add esp, 4+2
xor eax, eax
ret
.too_large:
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_output: Packet too large!\n"
xor eax, eax
ret
.loopback:
inc [IPv4_packets_tx + edi] ; update stats
mov dword [esp], eax ; set source IP to dest IP
mov ecx, [esp + 10]
add ecx, sizeof.IPv4_header
mov edi, AF_INET4
call loop_output
jmp .continue
;------------------------------------------------------------------;
; ;
; ipv4_output_raw ;
; ;
; IN: eax = socket ptr ;
; ecx = data length ;
; esi = data ptr ;
; ;
; OUT: eax = -1 on error ;
; ;
;------------------------------------------------------------------;
align 4
ipv4_output_raw:
DEBUGF 1,"IPv4_output_raw: size=%u ptr=%x socket=%x\n", ecx, esi, eax
sub esp, 8
push esi eax
call ipv4_route
call arp_ip_to_mac
test eax, 0xffff0000 ; error bits
jnz .arp_error
push ebx ; push the mac
push ax
inc [IPv4_packets_tx + 4*edi]
mov ax, ETHER_PROTO_IPv4
mov ebx, [net_device_list + 4*edi]
mov ecx, [esp + 6 + 4]
add ecx, sizeof.IPv4_header
mov edx, esp
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_header.VersionAndIHL] ; IPv4, normal length (no Optional header)
; [edi + IPv4_header.TypeOfService] ; nothing special, just plain ip packet
; [edi + IPv4_header.TotalLength]
; [edi + IPv4_header.TotalLength] ; internet byte order
; [edi + IPv4_header.FlagsAndFragmentOffset]
mov [edi + IPv4_header.HeaderChecksum], 0
; [edi + IPv4_header.TimeToLive] ; ttl shl 8 + protocol
; [edi + IPv4_header.Protocol]
; [edi + IPv4_header.Identification] ; fragment id
; [edi + IPv4_header.SourceAddress]
; [edi + IPv4_header.DestinationAddress]
ipv4_checksum edi ;;;; todo: checksum for IP packet with options!
add edi, sizeof.IPv4_header
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_output_raw: device=%x\n", ebx
call [ebx + NET_DEVICE.transmit]
ret
.error:
add esp, 6+8+4+4
mov ebx, ENOBUFS ; FIXME: NOBUFS or MSGSIZE error
or eax, -1
ret
.arp_error:
add esp, 8+4+4
mov ebx, ENOTCONN
or eax, -1
ret
;-----------------------------------------------------------------;
; ;
; ipv4_fragment ;
; ;
; IN: [esp] = ptr to packet buffer to fragment ;
; edi = ptrr to ip header in that buffer ;
; ebx = device ptr ;
; ;
; OUT: / ;
; ;
;-----------------------------------------------------------------;
proc ipv4_fragment stdcall buffer
locals
offset dd ?
headerlength dd ?
headerptr dd ?
dataptr dd ?
remaining dd ?
segmentsize dd ?
endl
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_fragment\n"
; We must be able to put at least 8 bytes per segment
movzx eax, byte[edi] ; IHL
and eax, 0xf
shl eax, 2
mov [headerlength], eax
add eax, 8
mov ecx, [ebx + NET_DEVICE.mtu]
and ecx, not 11b
cmp ecx, eax
jb .fail
mov [edi + IPv4_header.HeaderChecksum], 0
mov [segmentsize], ecx
mov [headerptr], edi
movzx ecx, [edi + IPv4_header.TotalLength]
xchg cl, ch
sub ecx, [headerlength]
mov [remaining], ecx
mov [offset], 0
add edi, [headerlength]
mov [dataptr], edi
.loop:
DEBUGF DEBUG_NETWORK_VERBOSE, "Ipv4_fragment: new fragment"
mov ecx, [segmentsize]
cmp ecx, [remaining]
jbe @f
mov ecx, [remaining]
@@:
mov ax, ETHER_PROTO_IPv4
mov edx, [esp]
add edx, [edx + NET_BUFF.offset]
; add edx, ETH_header.DstMAC ; = 0
call ETH_output
jz .fail
push edi
mov edx, ecx
; copy header
mov esi, [headerptr]
mov ecx, [headerlength]
shr ecx, 2
rep movsd
; copy data
mov esi, [dataptr]
add esi, [offset]
mov ecx, edx
sub ecx, [headerlength]
shr ecx, 2
rep movsd
pop edi
; now, correct header
; packet length
mov ax, dx
xchg al, ah
mov [edi + IPv4_header.TotalLength], ax
; offset
mov eax, [offset]
xchg al, ah
sub edx, [headerlength]
sub [remaining], edx
je @f
jb .fail
or ah, 1 shl 2 ; more fragments
add [offset], edx
@@:
mov [edi + IPv4_header.FlagsAndFragmentOffset], ax
; Send the fragment
IPv4_checksum edi
call [ebx + NET_DEVICE.transmit]
cmp [remaining], 0
jne .loop
call NET_BUFF_free
ret
.fail:
DEBUGF DEBUG_NETWORK_ERROR, "Ipv4_fragment: failed\n"
call NET_BUFF_free
ret
endp
;-----------------------------------------------------------------;
; ;
; ipv4_route ;
; ;
; IN: eax = Destination IP ;
; ebx = outgoing device / 0 ;
; edx = Source IP ;
; ;
; OUT: eax = Destination IP (may be gateway), 0 on error ;
; edx = Source IP ;
; edi = device number*4 ;
; ;
; DESTROYED: ;
; ecx ;
; ;
;-----------------------------------------------------------------;
align 4
ipv4_route:
test ebx, ebx
jnz .got_device
; Broadcast does not need gateway
cmp eax, 0xffffffff
je .broadcast
xor edi, edi
.loop:
mov ebx, [IPv4_address + edi]
and ebx, [IPv4_subnet + edi]
jz .next
mov ecx, eax
and ecx, [IPv4_subnet + edi]
cmp ebx, ecx
je .got_it
.next:
add edi, 4
cmp edi, 4*NET_DEVICES_MAX
jb .loop
mov eax, [IPv4_gateway + 4] ; TODO: let user (or a user space daemon) configure default route
.broadcast:
mov edi, 4 ; TODO: same as above
.got_it:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_route: %u\n", edi
test edx, edx
jnz @f
mov edx, [IPv4_address + edi]
@@:
ret
.got_device:
; Validate device ptr and convert to device number
call net_ptr_to_num4
cmp edi, -1
je .fail
mov edx, [IPv4_address + edi] ; Source IP
; Broadcast does not need gateway
cmp eax, 0xffffffff
je @f
; Check if we should route to gateway or not
mov ebx, [IPv4_address + edi]
and ebx, [IPv4_subnet + edi]
mov ecx, eax
and ecx, [IPv4_subnet + edi]
cmp ecx, ebx
je @f
mov eax, [IPv4_gateway + edi]
@@:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_route: %u\n", edi
ret
.fail:
DEBUGF DEBUG_NETWORK_ERROR, "IPv4_route failed\n"
xor eax, eax
ret
;-----------------------------------------------------------------;
; ;
; ipv4_get_frgmnt_num ;
; ;
; IN: / ;
; ;
; OUT: ax = fragment number ;
; ;
;-----------------------------------------------------------------;
align 4
ipv4_get_frgmnt_num:
xor ax, ax ;;; TODO: replace this with real code
ret
;-----------------------------------------------------------------;
; ;
; ipv4_connect ;
; ;
; IN: eax = socket pointer ;
; ;
; OUT: eax = 0 on success ;
; eax = -1 on error ;
; ebx = error code on error ;
; ;
;-----------------------------------------------------------------;
align 4
ipv4_connect:
push eax edx
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
pop edx eax
; Fill in local IP
cmp [eax + IP_SOCKET.LocalIP], 0
jne @f
push [IPv4_address + 4] ; FIXME: use correct local IP
pop [eax + IP_SOCKET.LocalIP]
; Fill in remote IP
pushd [edx + 4]
pop [eax + IP_SOCKET.RemoteIP]
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
xor eax, eax
ret
;-----------------------------------------------------------------;
; ;
; ipv4_API: Part of system function 76. ;
; ;
; IN: bl = subfunction number ;
; bh = device number ;
; ecx, edx, .. depends on subfunction ;
; ;
; OUT: depends on subfunction ;
; ;
;-----------------------------------------------------------------;
align 4
ipv4_api:
movzx eax, bh
shl eax, 2
and ebx, 0x000000ff
cmp ebx, .number
ja .error
jmp dword [.table + 4*ebx]
.table:
dd .packets_tx ; 0
dd .packets_rx ; 1
dd .read_ip ; 2
dd .write_ip ; 3
dd .read_dns ; 4
dd .write_dns ; 5
dd .read_subnet ; 6
dd .write_subnet ; 7
dd .read_gateway ; 8
dd .write_gateway ; 9
.number = ($ - .table) / 4 - 1
.error:
mov eax, -1
ret
.packets_tx:
mov eax, [IPv4_packets_tx + eax]
ret
.packets_rx:
mov eax, [IPv4_packets_rx + eax]
ret
.read_ip:
mov eax, [IPv4_address + eax]
ret
.write_ip:
mov [IPv4_address + eax], ecx
mov edi, eax ; device number, we'll need it for ARP
; pre-calculate the local broadcast address
mov ebx, [IPv4_subnet + eax]
not ebx
or ebx, ecx
mov [IPv4_broadcast + eax], ebx
mov ebx, [net_device_list + eax]
mov eax, [IPv4_address + eax]
call arp_output_request ; now send a gratuitous ARP
call net_send_event
xor eax, eax
ret
.read_dns:
mov eax, [IPv4_nameserver + eax]
ret
.write_dns:
mov [IPv4_nameserver + eax], ecx
call net_send_event
xor eax, eax
ret
.read_subnet:
mov eax, [IPv4_subnet + eax]
ret
.write_subnet:
mov [IPv4_subnet + eax], ecx
; pre-calculate the local broadcast address
mov ebx, [IPv4_address + eax]
not ecx
or ecx, ebx
mov [IPv4_broadcast + eax], ecx
call net_send_event
xor eax, eax
ret
.read_gateway:
mov eax, [IPv4_gateway + eax]
ret
.write_gateway:
mov [IPv4_gateway + eax], ecx
call net_send_event
xor eax, eax
ret