/kernel/branches/net/core/exports.inc |
---|
82,7 → 82,7 |
szNetUnRegDev db 'NetUnRegDev',0 |
szNetPtrToNum db 'NetPtrToNum',0 |
szEthReceiver db 'EthReceiver',0 |
szIPv4Handler db 'IPv4Handler',0 |
szIPv4_input db 'IPv4_input',0 |
align 16 |
155,8 → 155,8 |
dd szNetRegDev , NET_add_device |
dd szNetUnRegDev , NET_remove_device |
dd szNetPtrToNum , NET_ptr_to_num |
dd szEthReceiver , ETH_receiver |
dd szIPv4Handler , IPv4_handler |
dd szEthReceiver , ETH_input |
dd szIPv4_input , IPv4_input |
exp_lfb: |
dd szLFBAddress , 0 |
/kernel/branches/net/drivers/netdrv.inc |
---|
35,22 → 35,15 |
macro set_io addr { |
if addr = 0 |
mov edx, [device.io_addr] |
else if addr = LAST_IO |
else |
add edx, addr - LAST_IO |
end if |
LAST_IO = addr |
} |
macro allocate_and_clear dest, size, err { |
; We need to allocate at least 8 pages, if we want a continuous memory in ram |
83,8 → 76,6 |
} |
macro find_io bus, dev, io { |
local .check, .inc, .got |
126,7 → 117,6 |
} |
macro find_rev bus, dev, rev { |
push eax edx ecx |
138,8 → 128,6 |
} |
macro make_bus_master bus, dev { |
movzx ecx, bus |
168,10 → 156,7 |
end virtual |
if used null_op |
align 4 |
null_op: |
or eax, -1 |
180,11 → 165,11 |
end if |
macro virt_to_dma { ; input is eax |
macro GetRealAddr { ; input is eax |
push ax |
call GetPgAddr |
and word[esp], PAGESIZE - 1 |
call GetPgAddr |
or ax, word[esp] |
inc esp |
inc esp |
209,7 → 194,7 |
.end: |
} |
;struc ETH_DEVICE { |
macro ETH_DEVICE { |
NET_DEVICE |
235,16 → 220,4 |
.mode dd ? |
} |
macro GetRealAddr { |
push eax |
call GetPgAddr |
and dword [esp], (PAGESIZE - 1) |
or eax, dword [esp] |
add esp, 4 |
} |
} |
/kernel/branches/net/network/ARP.inc |
---|
16,20 → 16,16 |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision$ |
ARP_NO_ENTRY equ 0 |
ARP_VALID_MAPPING equ 1 |
ARP_AWAITING_RESPONSE equ 2 |
ARP_RESPONSE_TIMEOUT equ 3 |
ARP_REQUEST_TTL = 20 ; in seconds |
ARP_ENTRY_TTL = 600 ; in seconds |
ARP_REQUEST_TTL equ 31 ; 20 s |
ARP_ENTRY_TTL equ 937 ; 600 s |
ETHER_ARP equ 0x0608 |
ARP_REQ_OPCODE equ 0x0100 ; request |
ARP_REP_OPCODE equ 0x0200 ; reply |
39,7 → 35,7 |
.IP dd ? |
.MAC dp ? |
.Status dw ? |
.TTL dw ? ; in seconds |
.TTL dw ? |
.size: |
ends |
53,26 → 49,16 |
.SenderIP dd ? |
.TargetMAC dp ? |
.TargetIP dd ? |
.size: |
ends |
; The TTL field is decremented every second, and is deleted when it |
; reaches 0. It is refreshed every time a packet is received |
; If the TTL field is 0xFFFF it is a static entry and is never deleted |
; The status field can be the following values: |
; 0x0000 entry not used |
; 0x0001 entry holds a valid mapping |
; 0x0002 entry contains an IP address, awaiting ARP response |
; 0x0003 No response received to ARP request. |
; The last status value is provided to allow the network layer to delete |
; a packet that is queued awaiting an ARP response |
align 4 |
uglobal |
NumARP dd ? |
ARPTable rb ARP_ENTRY.size * ARP_TABLE_SIZE |
ARP_table rb ARP_ENTRY.size * ARP_TABLE_SIZE |
ARP_PACKETS_TX rd MAX_NET_DEVICES |
ARP_PACKETS_RX rd MAX_NET_DEVICES |
88,15 → 74,10 |
; |
; This function resets all ARP variables |
; |
; IN: / |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
ARP_init: |
macro ARP_init { |
xor eax, eax |
mov [NumARP], eax |
mov edi, ARP_PACKETS_TX |
103,152 → 84,217 |
mov ecx, 2*MAX_NET_DEVICES |
rep stosd |
ret |
} |
;--------------------------------------------------------------------------- |
; |
; ARP_decrease_entry_ttls |
; |
;--------------------------------------------------------------------------- |
macro ARP_decrease_entry_ttls { |
local .loop |
local .exit |
; The TTL field is decremented every second, and is deleted when it reaches 0. |
; It is refreshed every time a packet is received. |
; If the TTL field is 0xFFFF it is a static entry and is never deleted. |
; The status field can be the following values: |
; 0x0000 entry not used |
; 0x0001 entry holds a valid mapping |
; 0x0002 entry contains an IP address, awaiting ARP response |
; 0x0003 No response received to ARP request. |
; The last status value is provided to allow the network layer to delete |
; a packet that is queued awaiting an ARP response |
mov ecx, [NumARP] |
test ecx, ecx |
jz .exit |
mov esi, ARP_table |
.loop: |
cmp [esi + ARP_ENTRY.TTL], 0xffff ; 0xffff = static entry |
je .next |
dec [esi + ARP_ENTRY.TTL] |
jz .time_out |
.next: |
add esi, ARP_ENTRY.size |
loop .loop |
jmp .exit |
.time_out: |
cmp [esi + ARP_ENTRY.Status], ARP_AWAITING_RESPONSE |
jz .response_timeout |
push esi ecx |
call ARP_del_entry |
pop ecx esi |
jmp .next |
.response_timeout: |
mov [esi + ARP_ENTRY.Status], ARP_RESPONSE_TIMEOUT |
mov [esi + ARP_ENTRY.TTL], 10 |
jmp .next |
.exit: |
} |
;----------------------------------------------------------------- |
; |
; ARP_IP_to_MAC |
; ARP_input |
; |
; This function resets all ARP variables |
; IN: Pointer to buffer in [esp] |
; size of buffer in [esp+4] |
; packet size (without ethernet header) in ecx |
; OUT: / |
; |
; IN: eax = IPv4 address |
; OUT: eax = -1 on error, else eax = first two bytes of mac |
; ( high 16 bits are zero) |
; ebx = last four bytes of mac ; TODO: special eax value for 'request send' |
; |
;----------------------------------------------------------------- |
align 4 |
ARP_IP_to_MAC: |
ARP_input: |
DEBUGF 1,"ARP_IP_to_MAC\n" |
DEBUGF 1,"ARP_Handler - start\n" |
cmp ecx, 28 |
jl .exit |
; first, check destination IP to see if it is on 'this' network. |
; The test is: |
; if ( destIP & subnet_mask == stack_ip & subnet_mask ) |
; destination is local |
; else |
; destination is remote, so pass to gateway |
;--------------------- |
; Handle Reply packets |
xor edx, edx ;;; TODO: find device num in edx |
cmp word [edx + ARP_Packet.Opcode], ARP_REP_OPCODE |
jne .maybe_request |
mov ebx, [IP_LIST + edx] |
and ebx, [SUBNET_LIST + edx] |
DEBUGF 1,"ARP_Handler - it's a reply packet from %u.%u.%u.%u\n",\ |
[edx + ARP_Packet.SenderIP]:1,[edx + ARP_Packet.SenderIP+1]:1,[edx + ARP_Packet.SenderIP+2]:1,[edx + ARP_Packet.SenderIP+3]:1, |
mov ecx, eax |
and ecx, [SUBNET_LIST + edx] |
mov ecx, [NumARP] |
test ecx, ecx |
jz .exit |
cmp ecx, ebx |
je .local |
mov eax, [edx + ARP_Packet.SenderIP] |
mov esi, ARP_table |
mov eax, [GATEWAY_LIST + edx] |
DEBUGF 1,"requested IP is not on subnet, using gateway\n" |
.local: |
; try to find it on the list |
mov ecx, [NumARP] |
test ecx, ecx |
jz .not_in_list |
mov esi, ARPTable + ARP_ENTRY.IP |
.scan_loop: |
cmp [esi], eax |
je .found_it |
.loop: |
cmp [esi + ARP_ENTRY.IP], eax |
je .gotit |
add esi, ARP_ENTRY.size |
loop .scan_loop |
.not_in_list: |
loop .loop |
DEBUGF 1,"IP not found on list, preparing for ARP request\n" |
jmp .exit |
; if not, reserve an entry in list and send an ARP request packet |
.gotit: |
DEBUGF 1,"ARP_Handler - found matching entry\n" |
cmp [esi+ARP_ENTRY.TTL], 0xffff ; if it is a static entry, dont touch it |
je .exit |
DEBUGF 1,"ARP_Handler - updating entry\n" |
mov [esi+ARP_ENTRY.Status], ARP_VALID_MAPPING |
mov [esi+ARP_ENTRY.TTL], ARP_ENTRY_TTL |
mov eax, dword [edx + ARP_Packet.SenderMAC] |
mov dword [esi+ARP_ENTRY.MAC], eax |
mov ax , word [edx + ARP_Packet.SenderMAC + 4] |
mov word [esi+ARP_ENTRY.MAC+4], ax |
jmp .exit |
;----------------------- |
; Handle Request packets |
.maybe_request: |
cmp word [edx + ARP_Packet.Opcode], ARP_REQ_OPCODE |
jne .exit |
call NET_ptr_to_num |
cmp edi, -1 |
jz .exit |
DEBUGF 1,"ARP Request packet through device: %u\n", edi |
inc [ARP_PACKETS_RX+4*edi] |
mov eax, [IP_LIST+4*edi] |
cmp eax, [edx + ARP_Packet.TargetIP] ; Is it looking for my IP address? |
jne .exit ; TODO: instead of quitting, update local entrys with matching IP's ? |
push eax |
push edi |
pushw ARP_REQUEST_TTL |
pushw ARP_AWAITING_RESPONSE |
pushd 0 |
pushw 0 |
pushd eax |
call ARP_add_entry |
cmp eax, -1 |
je .full |
; OK, it is a request for one of our MAC addresses. |
; Build the frame and send it. We can reuse the buffer. (faster then using ARP_create_packet) |
; <Some dirty test code> |
lea esi, [edx + ARP_Packet.SenderMAC] |
lea edi, [edx + ARP_Packet.TargetMAC] |
movsd ; Move Sender Mac to Dest MAC |
movsw ; |
movsd ; Move sender IP to Dest IP |
; This piece of code waits for an ARP reply |
mov ebx, eax |
pop esi |
mov esi, [NET_DRV_LIST + 4*esi] |
lea esi, [esi + ETH_DEVICE.mac] |
lea edi, [edx + ARP_Packet.SenderMAC] |
movsd ; Copy MAC address from in MAC_LIST |
movsw ; |
pop eax |
push ebx |
call ARP_create_request |
stosd ; Write our IP |
push [timer_ticks] |
add dword[esp], 100*ARP_REQUEST_TTL |
DEBUGF 1,"Waiting for ARP reply, time: %x, entry:%u\n",[timer_ticks], [esp + 4] |
.dirty_loop: |
mov word [edx + ARP_Packet.Opcode], ARP_REP_OPCODE |
call change_task ; The ARP reply hasnt been received yet, tell the processor to do some other stuff first |
; Now, Fill in ETHERNET header |
mov eax, [esp + 4] |
imul eax, ARP_ENTRY.size |
add eax, ARPTable |
cmp [eax + ARP_ENTRY.Status], ARP_VALID_MAPPING |
je .gogogo |
mov edi, [esp] |
lea esi, [edx + ARP_Packet.TargetMAC] |
movsd |
movsw |
lea esi, [edx + ARP_Packet.SenderMAC] |
movsd |
movsw |
; mov ax , ETHER_ARP |
; stosw |
mov eax, [esp] ; Check if the reply hasnt timed-out yet |
cmp [timer_ticks], eax |
jl .dirty_loop |
DEBUGF 1,"ARP_Handler - Sending reply \n" |
; </Some dirty test code> |
or eax, -1 |
add esp, 8 |
call [ebx + NET_DEVICE.transmit] |
ret |
.found_it: |
DEBUGF 1,"found MAC in ARPTable\n" |
movzx eax, word [esi+ARP_ENTRY.MAC] |
mov ebx, dword[esi+ARP_ENTRY.MAC+2] |
ret |
.exit: |
call kernel_free |
add esp, 4 ; pop (balance stack) |
.full: |
add esp, 4 |
mov eax, -1 |
DEBUGF 1,"ARP_Handler - exiting\n" |
ret |
.gogogo: |
DEBUGF 1,"got ARP reply, time: %x\n",[timer_ticks] |
mov ebx, dword[eax+ARP_ENTRY.MAC+2] |
movzx eax, word [eax+ARP_ENTRY.MAC] |
add esp, 8 |
ret |
;--------------------------------------------------------------------------- |
; |
; ARP_create_request |
; ARP_output_request |
; |
; IN: ip in eax |
; |
; OUT: / |
; |
;--------------------------------------------------------------------------- |
align 4 |
ARP_create_request: |
ARP_output_request: |
DEBUGF 1,"Create ARP Packet\n" |
call IPv4_dest_to_dev |
push eax ; DestIP |
mov eax, [IP_LIST+4*edi] ; senderIP |
push eax |
pushd [IP_LIST+edi] ; SenderIP |
mov edi, [NET_DRV_LIST + 4*edi] |
lea eax, [edi + ETH_DEVICE.mac] |
mov ebx, ETH_BROADCAST |
mov ecx, 60 ; minimum packet size |
mov edx, edi ;;; |
mov ebx, [NET_DRV_LIST+edi] ; device ptr |
lea eax, [ebx + ETH_DEVICE.mac] ; local device mac |
mov edx, ETH_BROADCAST ; broadcast mac |
mov ecx, ARP_Packet.size |
mov di , ETHER_ARP |
call ETH_create_packet |
call ETH_output |
jz .exit |
mov ecx, eax |
259,16 → 305,18 |
mov [edi + ARP_Packet.ProtocolSize], 4 ;IP-addr length |
mov [edi + ARP_Packet.Opcode], ARP_REQ_OPCODE ;Request |
add edi, ARP_Packet.SenderMAC ; sendermac |
lea esi, [ebx + ETH_DEVICE.mac] ; |
add edi, ARP_Packet.SenderMAC |
lea esi, [ebx + ETH_DEVICE.mac] ; SenderMac |
movsw ; |
movsd ; |
pop eax ; |
pop eax ; SenderIP |
stosd ; |
mov eax, -1 ; destmac |
mov eax, -1 ; DestMac |
stosd ; |
stosw ; |
pop eax |
pop eax ; DestIP |
stosd ; |
DEBUGF 1,"ARP Packet for device %x created successfully\n", ebx |
278,84 → 326,17 |
ret |
.exit: |
add esp, 8 |
add esp, 4+4 |
DEBUGF 1,"Create ARP Packet - failed\n" |
mov eax, -1 |
sub eax, eax |
ret |
;--------------------------------------------------------------------------- |
; |
; ARP_decrease_entry_ttls |
; |
; IN: / |
; OUT: / |
; |
;--------------------------------------------------------------------------- |
align 4 |
ARP_decrease_entry_ttls: |
mov ecx, [NumARP] |
test ecx, ecx |
jz .exit |
mov ebx, ARPTable |
.timer_loop: |
cmp [ebx + ARP_ENTRY.TTL], 0xFFFF |
je .timer_loop_end ;if TTL==0xFFFF then it's static entry |
cmp [ebx + ARP_ENTRY.TTL], 0 |
jnz .timer_loop_end_with_dec ;if TTL!=0 |
; Ok, TTL is 0 |
;if Status==AWAITING_RESPONSE and TTL==0 |
;then we have to change it to ARP_RESPONSE_TIMEOUT |
cmp [ebx + ARP_ENTRY.Status], ARP_AWAITING_RESPONSE |
jne @f |
mov [ebx + ARP_ENTRY.Status], ARP_RESPONSE_TIMEOUT |
mov [ebx + ARP_ENTRY.TTL], word 0x000A ;10 sec |
jmp .timer_loop_end |
@@: |
;if TTL==0 and Status==VALID_MAPPING, we have to delete it |
;if TTL==0 and Status==RESPONSE_TIMEOUT, delete too |
mov esi, [NumARP] |
sub esi, ecx ;esi=index of entry, will be deleted |
push ebx ecx |
call ARP_del_entry |
pop ecx ebx |
jmp .timer_loop_end |
.timer_loop_end_with_dec: |
dec [ebx + ARP_ENTRY.TTL] ;decrease TTL |
.timer_loop_end: |
add ebx, ARP_ENTRY.size |
loop .timer_loop |
.exit: |
ret |
;----------------------------------------------------------------- |
; |
; ARP_add_entry (or update) |
; |
; IN: arp entry in stack: esp .IP |
; esp+4 .MAC |
; esp+10 .Status |
; esp+12 .TTL |
; esp+14 |
; |
; IN: esi = ptr to entry (can easily be made on the stack) |
; OUT: eax = entry #, -1 on error |
; |
;----------------------------------------------------------------- ; TODO: use a mutex |
365,28 → 346,29 |
DEBUGF 1,"ARP add entry: " |
mov ecx, [NumARP] |
test ecx, ecx |
test ecx, ecx ; first entry? |
jz .add |
cmp ecx, ARP_TABLE_SIZE ; list full ? |
jge .error |
mov eax, dword[esp + 4 + ARP_ENTRY.MAC] |
mov bx , word[esp + 4 + ARP_ENTRY.MAC + 4] |
mov esi, ARPTable |
mov eax, dword[esi + ARP_ENTRY.MAC] |
mov bx , word[esi + ARP_ENTRY.MAC + 4] |
mov edi, ARP_table |
.loop: |
cmp dword [esi + ARP_ENTRY.MAC], eax |
jne .maybe_next |
cmp word [esi + ARP_ENTRY.MAC + 4], bx |
jne .maybe_next |
cmp dword [edi + ARP_ENTRY.MAC], eax ; Check for duplicate MAC's |
jne .maybe_next ; |
cmp word [edi + ARP_ENTRY.MAC + 4], bx ; |
jne .maybe_next ; |
cmp dword[esi + ARP_ENTRY.TTL], 0xFFFF ; static entry |
cmp dword[edi + ARP_ENTRY.TTL], 0xFFFF ; static entry |
jne .notstatic |
cmp dword[esp + 4 + ARP_ENTRY.TTL], 0xFFFF |
cmp dword[esi + ARP_ENTRY.TTL], 0xFFFF |
jne .error |
.notstatic: |
mov ebx, [NumARP] |
xchg ebx, ecx |
sub ecx, ebx |
neg ecx |
add ecx, [NumARP] |
jmp .add |
.maybe_next: |
394,30 → 376,26 |
loop .loop |
mov ecx, [NumARP] |
cmp ecx, ARP_TABLE_SIZE |
jge .error |
.add: |
push ecx |
imul ecx, ARP_ENTRY.size |
lea edi, [ecx + ARPTable] |
lea esi, [esp + 8] |
lea edi, [ecx + ARP_table] |
mov ecx, ARP_ENTRY.size/2 |
repz movsw |
rep movsw |
lea esi, [edi - ARP_ENTRY.size] |
inc [NumARP] |
pop eax |
DEBUGF 1,"New entry created: %u\n", eax |
.exit: |
DEBUGF 1,"Exiting\n" |
ret ARP_ENTRY.size |
ret |
.error: |
DEBUGF 1,"error! \n" |
mov eax, -1 |
jmp .exit |
ret |
;----------------------------------------------------------------- |
424,7 → 402,7 |
; |
; ARP_del_entry |
; |
; IN: entry # in esi |
; IN: esi = ptr to arp entry |
; OUT: / |
; |
;----------------------------------------------------------------- |
431,161 → 409,129 |
align 4 |
ARP_del_entry: |
DEBUGF 1,"ARP del entry %u, total entrys: %u\n", esi, [NumARP] |
DEBUGF 1,"ARP del entry %x, total entrys: %u\n", esi, [NumARP] |
cmp esi, [NumARP] |
jge .error |
imul esi, ARP_ENTRY.size |
mov ecx, (ARP_TABLE_SIZE - 1) * ARP_ENTRY.size |
mov ecx, ARP_table + (ARP_TABLE_SIZE - 1) * ARP_ENTRY.size |
sub ecx, esi |
shr ecx, 1 |
lea edi, [ARPTable + esi] ;edi=ptr to entry that should be deleted |
lea esi, [edi + ARP_ENTRY.size] ;esi=ptr to next entry |
shr ecx,1 ;ecx/2 => ARP_ENTRY_SIZE MUST BE EVEN NUMBER! |
mov edi, esi |
lea esi, [edi + ARP_ENTRY.size] |
rep movsw |
dec [NumARP] ;decrease arp-entries counter |
dec [NumARP] |
DEBUGF 1,"ARP entry deleted\n" |
.error: |
ret |
;----------------------------------------------------------------- |
; |
; ARP_Handler: |
; ARP_IP_to_MAC |
; |
; This function handles ARP protocol over ethernet |
; (other protocols may follow in the future) |
; This function translates an IP address to a MAC address |
; |
; IN: Pointer to buffer in [esp] |
; size of buffer in [esp+4] |
; packet size (without ethernet header) in ecx |
; OUT: / |
; IN: eax = IPv4 address |
; OUT: eax = -1 on error, -2 means request send |
; else, ax = first two bytes of mac (high 16 bits of eax will be 0) |
; ebx = last four bytes of mac |
; |
;----------------------------------------------------------------- |
align 4 |
ARP_handler: |
ARP_IP_to_MAC: |
DEBUGF 1,"ARP_Handler - start\n" |
cmp ecx, 28 |
jl .exit |
DEBUGF 1,"ARP_IP_to_MAC\n" |
cmp word [edx + ARP_Packet.Opcode], ARP_REP_OPCODE ; Is this a reply packet? |
jne .maybe_request |
cmp eax, 0xffffffff |
je .broadcast |
DEBUGF 1,"ARP_Handler - it's a reply packet from %u.%u.%u.%u\n",\ |
[edx + ARP_Packet.SenderIP]:1,[edx + ARP_Packet.SenderIP+1]:1,[edx + ARP_Packet.SenderIP+2]:1,[edx + ARP_Packet.SenderIP+3]:1, |
; if ((Remote IP & subnet_mask) == (local IP & subnet_mask )) |
; destination is on same subnet |
; else, destination is remote and must use a gateway |
mov ecx, [NumARP] |
test ecx, ecx |
jz .exit |
call IPv4_dest_to_dev |
mov ebx, [IP_LIST + edi] |
and ebx, [SUBNET_LIST + edi] |
mov eax, [edx + ARP_Packet.SenderIP] |
mov esi, ARPTable+ARP_ENTRY.IP |
mov ecx, eax |
and ecx, [SUBNET_LIST + edi] |
.loop: |
cmp [esi], eax |
je .gotit |
add esi, ARP_ENTRY.size |
loop .loop |
cmp ecx, ebx |
je .local |
jmp .exit |
mov eax, [GATEWAY_LIST + edi] |
DEBUGF 1,"requested IP is not on subnet, using default gateway\n" |
.gotit: |
;-------------------------------- |
; Try to find the IP in ARP_table |
DEBUGF 1,"ARP_Handler - found matching entry\n" |
.local: |
mov ecx, [NumARP] |
test ecx, ecx |
jz .not_in_list |
mov esi, ARP_table + ARP_ENTRY.IP |
.scan_loop: |
cmp [esi], eax |
je .found_it |
add esi, ARP_ENTRY.size |
loop .scan_loop |
cmp [esi+ARP_ENTRY.Status], 0x0300 ;if it is a static entry, dont touch it |
je .exit |
.not_in_list: |
DEBUGF 1,"IP not found on list, preparing for ARP request\n" |
DEBUGF 1,"ARP_Handler - updating entry\n" |
;-------------------- |
; Send an ARP request |
mov [esi+ARP_ENTRY.Status], ARP_VALID_MAPPING |
mov [esi+ARP_ENTRY.TTL], ARP_ENTRY_TTL |
mov eax, dword [edx + ARP_Packet.SenderMAC] |
mov dword [esi+ARP_ENTRY.MAC], eax |
mov ax , word [edx + ARP_Packet.SenderMAC + 4] |
mov word [esi+ARP_ENTRY.MAC+4], ax |
jmp .exit |
;------ |
.maybe_request: |
cmp word [edx + ARP_Packet.Opcode], ARP_REQ_OPCODE ; Is this a request packet? |
jne .exit |
call NET_ptr_to_num |
DEBUGF 1,"ARP Request packet through device: %u\n", edi |
inc [ARP_PACKETS_RX+4*edi] |
cmp edi, -1 |
jz .exit |
mov eax, edi |
shl eax, 2 |
add eax, IP_LIST |
mov eax, [eax] |
cmp eax, [edx + ARP_Packet.TargetIP] ; Is it looking for my IP address? |
jnz .exit |
push eax |
push edi |
; OK, it is a request for one of our MAC addresses. Build the frame and send it |
; We can reuse the buffer. (faster then using ARP_create_packet) |
pushw ARP_REQUEST_TTL |
pushw ARP_AWAITING_RESPONSE |
pushd 0 |
pushw 0 |
pushd eax |
mov esi, esp |
call ARP_add_entry |
add esp, ARP_ENTRY.size |
cld |
lea esi, [edx + ARP_Packet.SenderMAC] |
lea edi, [edx + ARP_Packet.TargetMAC] |
movsd ; Move Sender Mac to Dest MAC |
movsw ; |
movsd ; Move sender IP to Dest IP |
cmp eax, -1 |
je .full |
pop esi |
mov esi, [NET_DRV_LIST + 4*esi] |
lea esi, [esi + ETH_DEVICE.mac] |
lea edi, [edx + ARP_Packet.SenderMAC] |
movsd ; Copy MAC address from in MAC_LIST |
movsw ; |
pop eax |
stosd ; Write our IP |
mov word [edx + ARP_Packet.Opcode], ARP_REP_OPCODE |
call ARP_output_request |
; Now, Fill in ETHERNET header |
mov eax, -2 ; request send |
ret |
mov edi, [esp] |
lea esi, [edx + ARP_Packet.TargetMAC] |
movsd |
movsw |
lea esi, [edx + ARP_Packet.SenderMAC] |
movsd |
movsw |
; mov ax , ETHER_ARP |
; stosw |
.found_it: |
DEBUGF 1,"found IP in ARPTable\n" |
cmp [esi + ARP_ENTRY.Status], 1 |
jne .invalid |
DEBUGF 1,"ARP_Handler - Sending reply \n" |
movzx eax, word [esi+ARP_ENTRY.MAC] |
mov ebx, dword[esi+ARP_ENTRY.MAC+2] |
ret |
call [ebx + NET_DEVICE.transmit] |
.invalid: |
mov eax, -1 |
ret |
.exit: |
call kernel_free |
add esp, 4 ; pop (balance stack) |
.full: |
DEBUGF 1,"ARP table is full!\n" |
pop eax |
mov eax, -1 |
ret |
DEBUGF 1,"ARP_Handler - exiting\n" |
.broadcast: |
mov eax, 0x0000ffff |
mov ebx, 0xffffffff |
ret |
;----------------------------------------------------------------- |
; |
; ARP_API |
643,7 → 589,7 |
; edi = pointer to buffer |
; ecx = # entry |
imul ecx, ARP_ENTRY.size |
add ecx, ARPTable |
add ecx, ARP_table |
mov esi, ecx |
mov ecx, ARP_ENTRY.size/2 |
rep movsw |
653,16 → 599,15 |
.write: |
; esi = pointer to buffer |
sub esp, ARP_ENTRY.size |
mov edi, esp |
mov ecx, ARP_ENTRY.size/2 |
rep movsw |
call ARP_add_entry ;out: eax = entry number, -1 on error |
ret |
.remove: |
; ecx = # entry |
mov esi, ecx |
cmp ecx, [NumARP] |
jge .error |
imul ecx, ARP_ENTRY.size |
lea esi, [ARP_table + ecx] |
call ARP_del_entry |
ret |
/kernel/branches/net/network/IPv4.inc |
---|
53,6 → 53,73 |
.Data: ; Ip header begins here (we will need the IP header to re-construct the complete packet) |
ends |
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 |
} |
align 4 |
uglobal |
74,30 → 141,49 |
; |
; This function resets all IP variables |
; |
; IN: / |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
IPv4_init: |
macro IPv4_init { |
or eax, -1 |
xor eax, eax |
mov edi, IP_LIST |
mov ecx, 4*MAX_IP |
rep stosd |
inc eax |
mov edi, FRAGMENT_LIST |
mov ecx, FRAGMENT_slot.size*MAX_FRAGMENTS/4 + 2*MAX_IP |
rep stosd |
ret |
} |
;----------------------------------------------------------------- |
; |
; 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 |
} |
;----------------------------------------------------------------- |
; |
; IPv4_Handler: |
; IPv4_input: |
; |
; Will check if IP Packet isnt damaged |
; and call appropriate handler. (TCP/UDP/ICMP/..) |
112,7 → 198,7 |
; |
;----------------------------------------------------------------- |
align 4 |
IPv4_handler: ; TODO: implement handler for IP options |
IPv4_input: ; TODO: implement handler for IP options |
; TODO2: add code for raw sockets |
DEBUGF 1,"IPv4_Handler, packet from: %u.%u.%u.%u ",\ |
137,14 → 223,9 |
;------------------------------- |
; Now, re-calculate the checksum |
push edx ebx |
mov esi, edx |
call IPv4_checksum |
pop ebx edx |
IPv4_checksum edx |
jnz .dump ; if checksum isn't valid then dump packet |
cmp [edx + IPv4_Packet.HeaderChecksum], 0 |
jne .dump ; if checksum isn't valid then dump packet |
DEBUGF 1,"IPv4 Checksum is correct\n" |
;----------------------------------- |
435,10 → 516,7 |
; mov esi, edx ; This prints the IP packet to the debug board (usefull when using serial output debug..) |
; ; |
; @@: ; |
; lodsb ; |
; DEBUGF 1,"%x ", eax:2 ; |
; loop @r ; |
; packet_to_debug |
jmp .handle_it ; edx = buf ptr, ecx = size, [esp] buf ptr, [esp+4], total size, ebx=device ptr |
460,9 → 538,6 |
;----------------------------------------------------------------- |
; |
; find fragment slot |
501,70 → 576,15 |
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 |
;------------------------------------------------------------------ |
; |
; IPv4_output |
; |
; IN: dword [esp] = pointer to packet to be fragmented |
; dword [esp+4] = buffer size |
; edx = pointer to IPv4 header in that packet |
; ecx = data length |
; ebx = device structure |
; |
; OUT: / |
; |
;------------------------------------------------------------------ |
align 4 |
IPv4_fragment: |
;;; TODO: write code here |
call kernel_free |
add esp, 4 |
ret |
;------------------------------------------------------------------ |
; |
; Create_IPv4_Packet |
; |
; IN: eax = dest ip |
; ebx = source ip |
; ecx = data length |
; dx = fragment id ;;;; |
; di = protocol |
; dx = fragment id |
; di = TTL shl 8 + protocol |
; |
; OUT: eax = pointer to buffer start |
; ebx = pointer to device struct (needed for sending procedure) |
574,185 → 594,225 |
; |
;------------------------------------------------------------------ |
align 4 |
IPv4_create_packet: |
IPv4_output: |
DEBUGF 1,"Create IPv4 Packet (size=%u)\n", ecx |
DEBUGF 1,"IPv4_create_packet: size=%u\n", ecx |
cmp ecx, 65500 ; Max IPv4 packet size |
jg .exit_ |
jg .too_large |
test ebx, ebx ; if source 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 |
test eax, 0xffff0000 ; error bits |
jnz .arp_error |
push ebx |
push ebx ; push the mac |
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, [NET_DRV_LIST + 4*edi] |
lea eax, [edx + ETH_DEVICE.mac] |
mov ebx, esp |
mov ecx, [esp+18] ;; 18 or 22 ?? |
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 |
;;; TODO: detect if packet is too large for ethernet, if so, call IPv4_fragment |
call ETH_create_packet ;;; TODO: figure out a way to make this work with other protocols too |
add esp, 6 |
test edi, edi |
jz .exit |
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 |
xchg ch, cl |
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.TimeToLive], 128 |
mov [edi + IPv4_Packet.HeaderChecksum], 0 |
pop cx |
mov [edi + IPv4_Packet.Protocol], cl |
pop cx |
mov [edi + IPv4_Packet.Identification], cx |
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 |
mov [edi + IPv4_Packet.SourceAddress], ecx |
pop ecx |
mov [edi + IPv4_Packet.DestinationAddress], ecx |
push eax edx esi |
mov esi, edi |
call IPv4_checksum |
pop esi edx eax ecx |
IPv4_checksum edi |
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" |
;;;;;; |
.exit: |
add esp, 16 |
.exit_: |
DEBUGF 1,"Create IPv4 Packet - failed\n" |
and edi, 0 |
.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 |
;-------------------------------------------------------- |
; |
; |
; 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_checksum: |
IPv4_fragment: |
; 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 |
DEBUGF 1,"IPv4_fragment\n" |
xor edx, edx |
and ecx, not 111b ; align 4 |
add dl, [esi+1] |
adc dh, [esi+0] |
cmp ecx, IPv4_Packet.DataOrOptional + 8 ; must be able to put at least 8 bytes |
jl .err2 |
adc dl, [esi+3] |
adc dh, [esi+2] |
push esi ecx |
mov eax, [esi + IPv4_Packet.DestinationAddress] |
call ARP_IP_to_MAC |
pop ecx esi |
cmp eax, -1 |
jz .err2 |
adc dl, [esi+5] |
adc dh, [esi+4] |
push ebx |
push ax |
adc dl, [esi+7] |
adc dh, [esi+6] |
mov ebx, [NET_DRV_LIST] |
lea eax, [ebx + ETH_DEVICE.mac] |
push eax |
adc dl, [esi+9] |
adc dh, [esi+8] |
; we skip 11th and 12th byte, they are the checksum bytes and should be 0 for re-calculation |
push esi ; ptr to ip header |
sub ecx, 20 ; substract header size |
push ecx ; max data size |
push dword 0 ; offset |
adc dl, [esi+13] |
adc dh, [esi+12] |
.new_fragment: |
DEBUGF 1,"Ipv4_fragment - new_fragmentn" |
adc dl, [esi+15] |
adc dh, [esi+14] |
adc dl, [esi+17] |
adc dh, [esi+16] |
mov eax, [esp + 3*4] |
lea ebx, [esp + 4*4] |
mov di , ETHER_IPv4 |
call ETH_output |
adc dl, [esi+19] |
adc dh, [esi+18] |
cmp edi, -1 |
jz .err |
adc edx, 0 |
; copy header |
mov esi, [esp + 2*4] |
mov ecx, 5 ; 5 dwords: TODO: use IHL field of the header! |
rep movsd |
call checksum_2 |
; copy data |
mov esi, [esp + 2*4] |
add esi, 20 |
add esi, [esp] ; offset |
neg word [esi+10] ; zero will stay zero so we just get the checksum |
add word [esi+10], dx ; , else we will get (new checksum - old checksum) in the end, wich should be 0 :) |
mov ecx, [esp + 1*4] |
DEBUGF 1,"IPv4_fragment - copying data (%u bytes)\n", ecx |
rep movsb |
ret |
; 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: Destination IP in eax |
; OUT: device id in edi |
; IN: eax = Destination IP |
; OUT: edi = device id * 4 |
; |
;--------------------------------------------------------------------------- |
align 4 |
IPv4_dest_to_dev: |
DEBUGF 1,"IPv4 destination to device: " |
cmp eax, 0xffffffff |
je .invalid |
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 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: |
shr edi, 2 |
DEBUGF 1,"IPv4_dest_to_dev: %u\n", edi |
DEBUGF 1,"%u\n",edi |
ret |
868,8 → 928,4 |
add eax, GATEWAY_LIST |
mov [eax], ecx |
xor eax, eax |
ret |
ret |
/kernel/branches/net/network/ethernet.inc |
---|
56,37 → 56,33 |
; |
; This function resets all ethernet variables |
; |
; IN: / |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
ETH_init: |
macro ETH_init { |
mov [ETH_RUNNING], 0 |
ret |
} |
;----------------------------------------------------------------- |
; |
; ETH_Receiver: |
; ETH_input |
; |
; This function is called by ethernet drivers, |
; It pushes the received ethernet packets onto the eth_in_queue |
; |
; IN: [esp] = Pointer to buffer |
; [esp-4] = size of buffer |
; [esp+4] = size of buffer |
; ebx = pointer to eth_device |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
ETH_receiver: |
ETH_input: |
mov eax, [esp] |
mov ecx, [esp+4] |
DEBUGF 1,"ETH_Handler - size: %u\n", ecx |
DEBUGF 1,"ETH_input - size: %u\n", ecx |
cmp ecx, 60 ; check packet length |
jl .dump |
sub ecx, ETH_FRAME.Data |
95,10 → 91,10 |
mov ax , [eax + ETH_FRAME.Type] |
cmp ax, ETHER_IPv4 |
je IPv4_handler |
je IPv4_input |
cmp ax, ETHER_ARP |
je ARP_handler |
je ARP_input |
; cmp ax, ETHER_PPP_DISCOVERY |
; je PPPOE_discovery |
106,7 → 102,7 |
DEBUGF 2,"Unknown ethernet packet type %x\n", ax |
.dump: |
DEBUGF 2,"ETH_Handler - dumping\n" |
DEBUGF 2,"ETH_input - dumping\n" |
call kernel_free |
add esp, 4 |
ret |
113,12 → 109,12 |
;----------------------------------------------------------------- |
; |
; ETH_create_packet |
; ETH_output |
; |
; IN: eax = pointer to source mac |
; ebx = pointer to destination mac |
; ebx = device ptr |
; ecx = packet size |
; edx = device number |
; edx = pointer to destination mac |
; di = protocol |
; |
; OUT: edi = 0 on error, pointer to buffer otherwise |
129,62 → 125,60 |
; |
;----------------------------------------------------------------- |
align 4 |
ETH_create_packet: |
ETH_output: |
DEBUGF 1,"Creating Ethernet Packet (size=%u): \n", ecx |
DEBUGF 1,"ETH_output: size=%u device:%x\n", ecx, ebx |
push esi |
mov esi, [NET_DRV_LIST] ;;; TODO: FIXME |
cmp ecx, [esi + NET_DEVICE.mtu] |
pop esi |
cmp ecx, [ebx + NET_DEVICE.mtu] |
jg .exit |
push ecx di eax ebx edx |
push ecx ; << 1 |
push di eax edx ; << 2 |
add ecx, ETH_FRAME.Data |
add ecx, ETH_FRAME.Data |
push ecx |
push ecx |
call kernel_alloc |
push ecx ; << 3 |
push ecx ; << 4 |
call kernel_alloc ; >> 4 |
test eax, eax |
jz .out_of_ram |
mov edi, eax |
test edi, edi |
jz .pop_exit |
pop ecx |
pop edx |
pop ecx ; >> 3 |
pop esi |
pop esi ; >> 2 |
movsd |
movsw |
pop esi |
pop esi ; >> 2 |
movsd |
movsw |
pop ax |
pop ax ; >> 2 |
stosw |
lea eax, [edi - ETH_FRAME.Data] ; Set eax to buffer start |
mov edx, ecx ; Set ebx to complete buffer size |
pop ecx |
mov edx, ecx ; Set edx to complete buffer size |
xor ebx, ebx ;;;; TODO: Fixme |
mov ebx, [NET_DRV_LIST + ebx] |
pop ecx ; >> 1 |
cmp edx, 46 + ETH_FRAME.Data ; If data size is less then 46, add padding bytes |
jge .continue |
mov edx, 46 + ETH_FRAME.Data |
.continue: |
cmp edx, 60-1 ; minimum ethernet packet size |
jle .adjust_size |
DEBUGF 1,"ETH_output: done: %x total size: %u\n", eax, edx |
ret |
DEBUGF 1,"done: %x size:%u device:%x\n", eax, edx, ebx |
.adjust_size: |
mov edx, 60 |
ret |
.pop_exit: |
DEBUGF 2,"Out of ram space!!\n" |
add esp, 18 |
and edi, 0 |
.out_of_ram: |
DEBUGF 2,"ETH_output: Out of ram space!!\n" |
add esp, 3*4+2+4 |
sub edi, edi |
ret |
.exit: |
DEBUGF 2,"Packet too large!\n" |
and edi, 0 |
DEBUGF 2,"ETH_output: Packet too large!\n" |
sub edi, edi |
;;; dec edi |
ret |
/kernel/branches/net/network/icmp.inc |
---|
109,21 → 109,16 |
; |
; ICMP_init |
; |
; This function resets all ICMP variables |
; |
; IN: / |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
ICMP_init: |
macro ICMP_init { |
xor eax, eax |
mov edi, ICMP_PACKETS_TX |
mov ecx, 2*MAX_IP |
rep stosd |
ret |
} |
235,7 → 230,7 |
call IPv4_dest_to_dev |
cmp edi,-1 |
je .dump |
inc [ICMP_PACKETS_RX+4*edi] |
inc [ICMP_PACKETS_RX+edi] |
DEBUGF 1,"Found valid ICMP packet for socket %x\n", esi |
284,10 → 279,10 |
push esi edi edx |
add ecx, ICMP_Packet.Data |
mov di , IP_PROTO_ICMP |
mov di , IP_PROTO_ICMP SHL 8 + 128 ; TTL |
shr edx, 16 |
call IPv4_create_packet |
call IPv4_output |
jz .exit |
DEBUGF 1,"full icmp packet size: %u\n", edx |
/kernel/branches/net/network/socket.inc |
---|
5,9 → 5,11 |
;; ;; |
;; SOCKET.INC ;; |
;; ;; |
;; Written by hidnplayr@kolibrios.org ;; |
;; based on code by mike.dld ;; |
;; Written by hidnplayr@kolibrios.org, ;; |
;; and Clevermouse. ;; |
;; ;; |
;; Based on code by mike.dld ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
31,9 → 33,7 |
.errorcode dd ? |
.options dd ? |
.SO_SND.SB_CC dd ? ;;;;; socket options: number of bytes in socket |
.SO_RCV.SB_CC dd ? |
.state dd ? ;;;;;;;;; |
.state dd ? |
.end: |
end virtual |
51,15 → 51,6 |
.end: |
end virtual |
virtual at SOCKET.end |
SOCKET_virtual: |
.ConnectedTo dd ? ; Socket number of other socket this one is connected to |
.end: |
end virtual |
virtual at IP_SOCKET.end |
TCP_SOCKET: |
114,7 → 105,6 |
;---------------------- |
; Transmit timing stuff |
.t_idle dd ? |
.t_rtt dd ? |
.t_rtseq dd ? |
125,7 → 115,6 |
;----------------- |
; Out-of-band data |
.t_oobflags dd ? |
.t_iobc dd ? |
.t_softerror dd ? |
133,7 → 122,6 |
;--------- |
; RFC 1323 |
.SND_SCALE db ? ; Scale factor |
.RCV_SCALE db ? |
.request_r_scale db ? |
146,7 → 134,6 |
;------- |
; Timers |
.timer_retransmission dw ? ; rexmt |
.timer_ack dw ? |
.timer_persist dw ? |
176,9 → 163,32 |
.end: |
end virtual |
struc RING_BUFFER { |
.start_ptr dd ? ; Pointer to start of buffer |
.end_ptr dd ? ; pointer to end of buffer |
.read_ptr dd ? ; Read pointer |
.write_ptr dd ? ; Write pointer |
.size dd ? ; Number of bytes buffered |
} |
virtual at 0 |
RING_BUFFER RING_BUFFER |
end virtual |
virtual at TCP_SOCKET.end |
rcv RING_BUFFER |
snd RING_BUFFER |
STREAM_SOCKET: |
.end: |
end virtual |
struct socket_queue_entry |
; .owner dd ? |
.data_ptr dd ? |
.buf_ptr dd ? |
.data_size dd ? |
186,7 → 196,6 |
ends |
MAX_backlog equ 20 ; backlog for stream sockets |
SOCKETBUFFSIZE equ 4096 ; in bytes |
SOCKET_QUEUE_SIZE equ 10 ; maximum number ofincoming packets queued for 1 socket |
204,14 → 213,8 |
; |
; SOCKET_init |
; |
; - |
; |
; IN: / |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
socket_init: |
macro SOCKET_init { |
xor eax, eax |
mov edi, net_sockets |
218,12 → 221,35 |
mov ecx, 4 |
rep stosd |
mov [last_UDP_port], MIN_EPHEMERAL_PORT |
mov [last_TCP_port], MIN_EPHEMERAL_PORT |
;--- for random port -- |
ret |
mov al, 0x0 ; set up 1s timer |
out 0x70, al |
in al, 0x71 |
;---------------------- |
@@: |
pseudo_random eax |
cmp ax, MIN_EPHEMERAL_PORT |
jl @r |
cmp ax, MAX_EPHEMERAL_PORT |
jg @r |
mov [last_UDP_port], ax |
@@: |
pseudo_random eax |
cmp ax, MIN_EPHEMERAL_PORT |
jl @r |
cmp ax, MAX_EPHEMERAL_PORT |
jg @r |
mov [last_TCP_port], ax |
} |
;----------------------------------------------------------------- |
; |
; Socket API (function 74) |
232,11 → 258,20 |
align 4 |
sys_socket: |
cmp ebx, 8 ; highest possible number |
jg s_error |
lea ebx, [.table + 4*ebx] |
jg @f |
lea ebx, [sock_sysfn_table + 4*ebx] |
jmp dword [ebx] |
@@: |
cmp ebx, 255 |
jz SOCKET_debug |
.table: |
s_error: |
DEBUGF 1,"socket error\n" |
mov dword [esp+32], -1 |
ret |
sock_sysfn_table: |
dd SOCKET_open ; 0 |
dd SOCKET_close ; 1 |
dd SOCKET_bind ; 2 |
249,13 → 284,7 |
; dd SOCKET_set_opt ; 9 |
s_error: |
DEBUGF 1,"socket error\n" |
mov dword [esp+32], -1 |
ret |
;----------------------------------------------------------------- |
; |
; SOCKET_open |
280,10 → 309,44 |
mov [esp+32], edi |
cmp ecx, AF_INET4 |
jnz .no_stream |
push [IP_LIST] ;;;; |
pop [eax + IP_SOCKET.LocalIP] ;;;; |
cmp edx, IP_PROTO_TCP |
jnz .no_stream |
mov esi, eax |
stdcall kernel_alloc, SOCKET_MAXDATA |
mov [esi + rcv.start_ptr], eax |
mov [esi + rcv.write_ptr], eax |
mov [esi + rcv.read_ptr], eax |
mov [esi + rcv.size], 0 |
add eax, SOCKET_MAXDATA |
mov [esi + rcv.end_ptr], eax |
stdcall kernel_alloc, SOCKET_MAXDATA |
mov [esi + snd.start_ptr], eax |
mov [esi + snd.write_ptr], eax |
mov [esi + snd.read_ptr], eax |
mov [esi + snd.size], 0 |
add eax, SOCKET_MAXDATA |
mov [esi + snd.end_ptr], eax |
ret |
.no_stream: |
push edi |
init_queue (eax + SOCKET_QUEUE_LOCATION) |
pop edi |
ret |
;----------------------------------------------------------------- |
; |
; SOCKET_bind |
347,9 → 410,6 |
DEBUGF 1,"using local port: %u\n", bx |
mov word [eax + UDP_SOCKET.LocalPort], bx |
mov ebx, dword [edx + 4] |
mov dword [eax + IP_SOCKET.LocalIP], ebx |
DEBUGF 1,"local ip: %u.%u.%u.%u\n",\ |
[eax + IP_SOCKET.LocalIP + 0]:1,[eax + IP_SOCKET.LocalIP + 1]:1,\ |
[eax + IP_SOCKET.LocalIP + 2]:1,[eax + IP_SOCKET.LocalIP + 3]:1 |
411,21 → 471,11 |
.tcp: |
; set sequence number |
mov ebx, [TCP_sequence_num] |
add [TCP_sequence_num], 6400 |
mov [eax + TCP_SOCKET.ISS], ebx |
mov [eax + TCP_SOCKET.timer_keepalive], 120 ; 120*630ms => 75,6 seconds |
lea ebx, [eax + SOCKET.lock] |
call wait_mutex |
; fill in remote port and IP |
;;;;;; mov [eax + TCP_SOCKET.wndsizeTimer], 0 ; Reset the window timer. |
mov bx , word [edx + 2] |
mov [eax + TCP_SOCKET.RemotePort], bx |
DEBUGF 1,"remote port: %u\n", bx |
435,21 → 485,25 |
; check if local port and IP is ok |
cmp [eax + IP_SOCKET.LocalIP], 0 |
jne @f |
push [IP_LIST] ;;;;; device zero = default |
pop [eax + IP_SOCKET.LocalIP] |
@@: |
cmp [eax + TCP_SOCKET.LocalPort], 0 |
jne @f |
call SOCKET_find_port |
@@: |
DEBUGF 1,"local port: %u\n", [eax + TCP_SOCKET.LocalPort]:2 |
DEBUGF 1,"remote port: %u\n", [eax + TCP_SOCKET.LocalPort]:2 |
;;;;; |
mov [eax + TCP_SOCKET.timer_persist], 0 |
mov [eax + TCP_SOCKET.t_state], TCB_SYN_SENT |
mov ebx, [TCP_sequence_num] |
add [TCP_sequence_num], 6400 |
mov [eax + TCP_SOCKET.ISS], ebx |
mov [eax + TCP_SOCKET.timer_keepalive], 120 ; 120*640ms => 75,6 seconds |
; mov [eax + TCP_SOCKET.t_state], TCB_SYN_SENT |
;;;; mov [ebx + TCP_SOCKET.timer_retransmission], |
push eax |
call TCP_output |
pop eax |
mov [eax + SOCKET.lock], 0 |
583,11 → 637,11 |
test [eax + TCP_SOCKET.t_state], TCB_SYN_RECEIVED ;;;;;; |
jz .free |
call TCP_output |
;;; call TCP_output |
mov dword [esp+32], 0 |
;;; mov dword [esp+32], 0 |
ret |
;;; ret |
; state must be LISTEN, SYN_SENT, CLOSED or maybe even invalid |
; so, we may destroy the socket |
688,6 → 742,12 |
.af_inet4: |
DEBUGF 1,"af_inet4\n" |
cmp [eax + IP_SOCKET.LocalIP], 0 |
jne @f |
mov ebx, [IP_LIST] ;;;; |
mov dword [eax + IP_SOCKET.LocalIP], ebx |
@@: |
cmp [eax + SOCKET.Type], IP_PROTO_TCP |
je .tcp |
790,8 → 850,37 |
ret |
;----------------------------------------------------------------- |
; |
; SOCKET_debug |
; |
; Copies socket variables to application buffer |
; |
; IN: ecx = socket number |
; edx = pointer to buffer |
; |
; OUT: -1 on error |
;----------------------------------------------------------------- |
align 4 |
SOCKET_debug: |
DEBUGF 1,"socket_debug\n" |
call SOCKET_num_to_ptr |
jz s_error |
mov esi, eax |
mov edi, edx |
mov ecx, SOCKETBUFFSIZE/4 |
rep movsd |
mov dword [esp+32], 0 |
ret |
;----------------------------------------------------------------- |
; |
; SOCKET_find_port |
; |
; Fills in the local port number for TCP and UDP sockets |
941,6 → 1030,188 |
;----------------------------------------------------------------- |
; |
; SOCKET_ring_add |
; |
; Adds data to a stream socket |
; |
; IN: eax = ptr to ring struct |
; ecx = data size |
; esi = ptr to data |
; |
; OUT: eax = number of bytes stored |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_ring_add: |
DEBUGF 1,"SOCKET_ring_add: ringbuff=%x ptr=%x size=%u\n", eax, esi, ecx |
mov edi, [eax + RING_BUFFER.size] |
add edi, ecx |
cmp edi, SOCKET_MAXDATA |
jg .too_large |
mov [eax + RING_BUFFER.size], edi ; update size |
.copy: |
push ecx ;<<<< 1 |
mov edi, [eax + RING_BUFFER.write_ptr] ; set write ptr in edi |
add [eax + RING_BUFFER.write_ptr], ecx ; update write pointer |
mov edx, [eax + RING_BUFFER.end_ptr] |
cmp edx, [eax + RING_BUFFER.write_ptr] |
jg .copy_in_2 |
je .wrap_write_ptr |
.copy_more: |
push ecx |
and ecx, 3 |
rep movsb |
pop ecx |
shr ecx, 2 |
rep movsd |
pop ecx ; >>>> 1/2 |
DEBUGF 2,"Copied %u bytes\n", ecx |
ret |
.too_large: |
mov ecx, SOCKET_MAXDATA ; calculate number of bytes available in buffer |
sub ecx, [eax + RING_BUFFER.size] |
jz .full |
mov [eax + RING_BUFFER.size], SOCKET_MAXDATA ; update size, we will fill buffer completely |
jmp .copy |
.full: |
DEBUGF 2,"Ring buffer is full!\n" |
xor ecx, ecx |
ret |
.copy_in_2: |
DEBUGF 1,"Copying in 2 passes\n" |
mov edx, ecx |
mov ecx, [eax + RING_BUFFER.end_ptr] ; find number of bytes till end of buffer |
sub ecx, edi |
sub edx, ecx |
push edx ; <<<< 2 |
mov edi, [eax + RING_BUFFER.start_ptr] |
call .copy_more |
.wrap_write_ptr: |
sub [eax + RING_BUFFER.write_ptr], SOCKET_MAXDATA ; update write pointer |
jmp .copy_more |
;----------------------------------------------------------------- |
; |
; SOCKET_ring_read |
; |
; reads the data, but let the data remain in the buffer |
; |
; IN: eax = ptr to ring struct |
; ecx = buffer size |
; edi = ptr to buffer |
; |
; OUT: eax = number of bytes read |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_ring_read: |
DEBUGF 1,"SOCKET_ring_read: ringbuff=%x ptr=%x size=%u\n", eax, esi, ecx |
cmp [eax + RING_BUFFER.size], ecx ; update size |
jl .too_large |
mov esi, [eax + RING_BUFFER.read_ptr] ; update read ptr |
.copy: |
push ecx ;<<<< 1 |
mov edx, [eax + RING_BUFFER.read_ptr] |
add edx, ecx |
cmp edx, [eax + RING_BUFFER.end_ptr] |
jg .copy_in_2 |
.copy_more: |
push ecx |
and ecx, 3 |
rep movsb |
pop ecx |
shr ecx, 2 |
rep movsd |
pop ecx ; >>>> 1/2 |
DEBUGF 2,"Copied %u bytes\n", ecx |
ret |
.too_large: |
mov ecx, [eax + RING_BUFFER.size] |
jmp .copy |
.full: |
DEBUGF 2,"Ring buffer is full!\n" |
xor ecx, ecx |
ret |
.copy_in_2: |
DEBUGF 1,"Copying in 2 passes\n" |
mov edx, ecx |
mov ecx, [eax + RING_BUFFER.end_ptr] ; find number of bytes till end of buffer |
sub ecx, edi |
sub edx, ecx |
push edx ; <<<< 2 |
mov esi, [eax + RING_BUFFER.start_ptr] |
call .copy_more |
;----------------------------------------------------------------- |
; |
; SOCKET_ring_free |
; |
; Free's some bytes from the ringbuffer |
; |
; IN: eax = ptr to ring struct |
; ecx = data size |
; |
; OUT: ecx = number of bytes free-ed |
; |
;----------------------------------------------------------------- |
align 4 |
SOCKET_ring_free: |
DEBUGF 1,"Trying to free %u bytes of data from ring %x\n", ecx, eax |
cmp ecx, [eax + RING_BUFFER.size] |
jle .go_for_it |
cmp ecx, SOCKET_MAXDATA ;;;; |
jg .moron_input |
mov ecx, [eax + RING_BUFFER.size] |
.go_for_it: |
sub [eax + RING_BUFFER.size], ecx |
add [eax + RING_BUFFER.read_ptr], ecx |
mov edx, [eax + RING_BUFFER.end_ptr] |
cmp [eax + RING_BUFFER.read_ptr], edx |
jl @f |
sub [eax + RING_BUFFER.read_ptr], SOCKET_MAXDATA ;;;;; |
@@: |
ret |
.moron_input: |
xor ecx, ecx |
ret |
;----------------------------------------------------------------- |
; |
; SOCKET_notify_owner |
; |
; notify's the owner of a socket that something happened |
1011,7 → 1282,7 |
push ecx ebx |
stdcall kernel_alloc, SOCKETBUFFSIZE |
DEBUGF 1, "socket_alloc: %x ", eax |
DEBUGF 1, "SOCKET_alloc: ptr=%x\n", eax |
or eax, eax |
jz .exit |
1023,10 → 1294,7 |
rep stosd |
pop edi eax |
init_queue (eax + SOCKET_QUEUE_LOCATION) |
; find first free socket number and use it |
mov ebx, net_sockets |
xor ecx, ecx |
.next_socket_number: |
1033,7 → 1301,7 |
inc ecx |
.next_socket: |
mov ebx, [ebx + SOCKET.NextPtr] |
or ebx, ebx |
test ebx, ebx |
jz .last_socket |
cmp [ebx + SOCKET.Number], ecx |
jne .next_socket |
1042,23 → 1310,21 |
.last_socket: |
mov [eax + SOCKET.Number], ecx |
DEBUGF 1, "SOCKET_alloc: number=%u\n", ecx |
mov edi, ecx |
DEBUGF 1, "(number: %u)\n", ecx |
; Fill in PID |
mov ebx, [TASK_BASE] |
mov ebx, [ebx + TASKDATA.pid] |
mov [eax + SOCKET.PID], ebx |
; add socket to the list by changing pointers |
; add socket to the list by re-arranging some pointers |
mov ebx, [net_sockets + SOCKET.NextPtr] |
mov [eax + SOCKET.PrevPtr], net_sockets |
mov [eax + SOCKET.NextPtr], ebx |
or ebx, ebx |
test ebx, ebx |
jz @f |
add ebx, SOCKET.lock ; lock the next socket |
call wait_mutex |
1068,8 → 1334,6 |
@@: |
mov [net_sockets + SOCKET.NextPtr], eax |
mov edi, ecx |
or eax, eax ; used to clear zero flag |
.exit: |
pop ebx ecx |
1101,6 → 1365,16 |
DEBUGF 1, "freeing socket..\n" |
cmp [eax + SOCKET.Domain], AF_INET4 |
jnz .no_stream |
cmp [eax + SOCKET.Type], IP_PROTO_TCP |
jnz .no_stream |
stdcall kernel_free, [eax + rcv.start_ptr] |
stdcall kernel_free, [eax + snd.start_ptr] |
.no_stream: |
push eax ; this will be passed to kernel_free |
mov ebx, [eax + SOCKET.NextPtr] |
mov eax, [eax + SOCKET.PrevPtr] |
1126,6 → 1400,39 |
ret |
; socket nr in ebx |
; new socket nr in eax |
; preserver edx |
align 4 |
SOCKET_fork: |
;; Exit if backlog queue is full |
; mov ax, [ebx + TCP_SOCKET.backlog_cur] |
; cmp ax, [ebx + TCP_SOCKET.backlog] |
; jae .exit |
; Allocate new socket |
call SOCKET_alloc |
;;; jz .fail |
; Copy structure from current socket to new, (including lock!) |
; We start at PID to reserve the socket num, and the 2 pointers at beginning of socket |
lea esi, [edx + SOCKET.PID] |
lea edi, [eax + SOCKET.PID] |
mov ecx, (TCP_SOCKET.end - SOCKET.PID + 3)/4 |
rep movsd |
;; Push pointer to new socket to queue |
; movzx ecx, [ebx + TCP_SOCKET.backlog_cur] |
; inc [ebx + TCP_SOCKET.backlog_cur] |
; mov [ebx + TCP_SOCKET.end + ecx*4], eax |
;;;; mov [eax + IP_SOCKET.RemoteIP], esi ; IP source address |
ret |
;--------------------------------------------------- |
; |
; SOCKET_num_to_ptr |
/kernel/branches/net/network/stack.inc |
---|
68,13 → 68,15 |
; Socket options |
SO_ACCEPTCON equ 1 |
SOCKET_MAXDATA equ 4096 |
SOCKET_MAXDATA equ 4096*32 ; must be 4096*(power of 2) where 'power of 2' is at least 8 |
; Network driver types |
NET_TYPE_ETH equ 1 |
NET_TYPE_SLIP equ 2 |
MAX_backlog equ 20 ; backlog for stream sockets |
virtual at 0 |
NET_DEVICE: |
92,6 → 94,8 |
.packets_tx dd ? ; |
.packets_rx dd ? ; |
; .chksum dd ? ; bitmask stating available checksum routines on hardware |
.end: |
end virtual |
102,6 → 106,7 |
add reg, [esp] |
rol reg, 5 |
xor reg, [timer_ticks] |
add reg, [CPU_FREQ] |
imul reg, 214013 |
xor reg, 0xdeadbeef |
rol reg, 9 |
122,6 → 127,18 |
} |
macro packet_to_debug { ; set esi to packet you want to print, ecx to number of bytes |
local .loop |
.loop: |
lodsb |
DEBUGF 1,"%x ", eax:2 |
loop @r |
} |
include "queue.inc" |
include "ethernet.inc" |
161,26 → 178,24 |
stack_init: |
; Init the network drivers list |
xor eax, eax |
mov edi, NET_RUNNING |
mov ecx, MAX_NET_DEVICES + 1 |
rep stosd |
; Call other init procedures |
ETH_init |
; SLIP_init |
; PPPOE_init |
call ETH_init |
; call SLIP_init |
IPv4_init |
ICMP_init |
call IPv4_init |
call ICMP_init |
ARP_init |
UDP_init |
TCP_init |
call ARP_init |
call UDP_init |
call TCP_init |
SOCKET_init |
call socket_init |
mov [net_tmr_count], 0 |
ret |
211,14 → 226,14 |
test [net_10ms], 0x0f ; 160ms |
jnz .exit |
; call TCP_timer_160ms |
TCP_timer_160ms |
test [net_10ms], 0x3f ; 640ms |
jnz .exit |
; call TCP_timer_640ms |
call ARP_decrease_entry_ttls |
call IPv4_decrease_fragment_ttls |
TCP_timer_640ms |
ARP_decrease_entry_ttls |
IPv4_decrease_fragment_ttls |
.exit: |
ret |
275,13 → 290,13 |
jmp .error |
.ethernet: |
DEBUGF 1,"Trying to add an ethernet driver\n" |
DEBUGF 1,"Trying to add an ethernet device\n" |
inc [ETH_RUNNING] ; Indicate that one more ethernet device is up and running |
jmp .add_it |
.slip: |
DEBUGF 1,"Trying to add a slip driver\n" |
DEBUGF 1,"Trying to add a slip device\n" |
;;;; |
jmp .error |
490,8 → 505,8 |
mov ecx, edx |
shr ecx, 16 |
add edx, ecx |
add dx, cx |
test dx, dx ; it seems that ZF is not set when CF is set :( |
not dx |
jnz .not_zero |
dec dx |
/kernel/branches/net/network/tcp.inc |
---|
73,6 → 73,9 |
TCP_max_rxtshift equ 12 ; max retransmissions waiting for ACK |
TCP_max_keepcnt equ 8 ; max keepalive probes |
; |
TCP_max_winshift equ 14 |
TCP_max_win equ 65535 |
struct TCP_segment |
.SourcePort dw ? |
117,12 → 120,8 |
; |
; This function resets all TCP variables |
; |
; IN: / |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
TCP_init: |
macro TCP_init { |
xor eax, eax |
mov edi, TCP_segments_tx |
129,9 → 128,10 |
mov ecx, (6*IP_MAX_INTERFACES) |
rep stosd |
mov [TCP_sequence_num], 1 |
pseudo_random eax |
mov [TCP_sequence_num], eax |
ret |
} |
;---------------------- |
138,9 → 138,11 |
; |
; |
;---------------------- |
align 4 |
TCP_timer_160ms: |
macro TCP_timer_160ms { |
local .loop |
local .exit |
mov eax, net_sockets |
.loop: |
mov eax, [eax + SOCKET.NextPtr] |
156,7 → 158,7 |
DEBUGF 1,"TCP ack for socket %x expired, time to piggyback!\n", eax |
push eax |
call TCP_respond |
call TCP_respond_socket |
pop eax |
jmp .loop |
163,7 → 165,7 |
.exit: |
ret |
} |
;----------------------------------------------------------------- |
170,9 → 172,11 |
; |
; |
;----------------------------------------------------------------- |
align 4 |
TCP_timer_640ms: |
macro TCP_timer_640ms { |
local .loop |
local .exit |
; Update TCP sequence number |
add [TCP_sequence_num], 64000 |
190,6 → 194,7 |
cmp [eax + SOCKET.Type], IP_PROTO_TCP |
jne .loop |
inc [eax + TCP_SOCKET.t_idle] |
dec [eax + TCP_SOCKET.timer_retransmission] |
jnz .check_more2 |
221,9 → 226,47 |
jmp .loop |
.exit: |
ret |
} |
macro TCP_checksum IP1, IP2 { |
;------------- |
; Pseudoheader |
; protocol type |
mov edx, IP_PROTO_TCP |
; source address |
add dl, byte [IP1+1+4] |
adc dh, byte [IP1+0+4] |
adc dl, byte [IP1+3+4] |
adc dh, byte [IP1+2+4] |
; destination address |
adc dl, byte [IP2+1+8] |
adc dh, byte [IP2+0+8] |
adc dl, byte [IP2+3+8] |
adc dh, byte [IP2+2+8] |
; size |
adc dl, cl |
adc dh, ch |
;--------------------- |
; Real header and data |
push esi |
call checksum_1 |
call checksum_2 |
pop esi |
} ; returns in dx only |
;----------------------------------------------------------------- |
; |
; TCP_input: |
243,8 → 286,7 |
align 4 |
TCP_input: |
DEBUGF 1,"TCP_input\n" |
DEBUGF 1,"TCP_input size=%u\n", ecx |
; Offset must be greater than or equal to the size of the standard TCP header (20) and less than or equal to the TCP length. |
movzx eax, [edx + TCP_segment.DataOffset] |
251,39 → 293,39 |
and eax, 0xf0 |
shr al , 2 |
DEBUGF 1,"data offset: %u\n", eax |
DEBUGF 1,"headersize=%u\n", eax |
cmp eax, 20 |
jl .drop |
cmp eax, ecx |
jg .drop |
;------------------------------- |
; Now, re-calculate the checksum |
push eax edx ebx |
push edi |
push esi |
push eax ecx edx |
pushw [edx + TCP_segment.Checksum] |
mov [edx + TCP_segment.Checksum], 0 |
push esi edi |
mov esi, edx |
call TCP_checksum ; this destroys edx, ecx and esi (but not edi! :) |
pop ebx edx eax |
cmp [edx + TCP_segment.Checksum], 0 |
TCP_checksum |
pop esi edi ; yes, swap them (we dont need dest addr) |
pop cx ; previous checksum |
cmp cx, dx |
pop edx ecx esi |
jnz .drop |
DEBUGF 1,"Checksum is correct\n" |
sub ecx, esi ; update packet size |
jl .drop |
;----------------------------------------------------------------------------------------- |
; Check if this packet has a timestamp option (We do it here so we can process it quickly) |
cmp eax, 20 + 12 ; Timestamp option is 12 bytes |
cmp esi, 20 + 12 ; Timestamp option is 12 bytes |
jl .no_timestamp |
je .is_ok |
cmp byte [edx + TCP_segment.Data + 12], 0 ; end of option list |
cmp byte [edx + TCP_segment.Data + 12], TCP_OPT_EOL ; end of option list |
jne .no_timestamp |
.is_ok: |
295,11 → 337,9 |
DEBUGF 1,"timestamp ok\n" |
; TODO: Parse the options |
; TODO: Parse the option |
; TODO: Set a Bit in the TCP to tell all options are parsed |
ret |
.no_timestamp: |
;------------------------------------------- |
310,6 → 350,8 |
ntohlw [edx + TCP_segment.Window] |
ntohlw [edx + TCP_segment.UrgentPointer] |
ntohlw [edx + TCP_segment.SourcePort] |
ntohlw [edx + TCP_segment.DestinationPort] |
;------------------------------------------------------------ |
; Next thing to do is find the TCB (thus, the socket pointer) |
333,7 → 375,7 |
jne .socket_loop |
mov eax, [ebx + IP_SOCKET.RemoteIP] |
cmp eax, esi |
cmp eax, edi ; sender IP |
je @f |
test eax, eax |
jnz .socket_loop |
352,72 → 394,126 |
;---------------------------- |
; Check if socket isnt closed |
cmp [TCP_SOCKET.t_state], TCB_CLOSED |
cmp [ebx + TCP_SOCKET.t_state], TCB_CLOSED |
je .drop |
;---------------- |
; Lock the socket |
add ebx, SOCKET.lock ; TODO: figure out if we should lock now already |
call wait_mutex |
sub ebx, SOCKET.lock |
;; add ebx, SOCKET.lock ; TODO: figure out if we should lock now already |
;; call wait_mutex |
;; sub ebx, SOCKET.lock |
;--------------------------------------- |
; unscale the window into a 32 bit value ;;;;;; |
DEBUGF 1,"Socket locked\n" |
;---------------------------------------------------------------------------------------- |
; unscale the window into a 32 bit value (notice that SND_SCALE must be initialised to 0) |
movzx eax, [edx + TCP_segment.Window] |
xchg al, ah |
test [edx + TCP_segment.Flags], TH_SYN |
jnz .no_syn |
push cx |
mov cl , [ebx + TCP_SOCKET.SND_SCALE] |
shl eax, cl |
pop cx |
.no_syn: |
;;;; do something with eax |
;----------------------------------- |
; Is this socket a listening socket? |
; If so, create a new socket |
; test [ebx + SOCKET.options], SO_ACCEPTCON |
; jnz .listening_socket ;;;;; TODO |
test [ebx + SOCKET.options], SO_ACCEPTCON |
jz .no_accept_conn |
;------------------------------------- |
; Reset idle timer and keepalive timer |
mov [ebx + TCP_SOCKET.t_idle], 0 |
mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval |
; TODO: create a new socket |
;-------------------- |
; Process TCP options |
cmp esi, 20 ; esi is headersize |
je .no_options |
.no_accept_conn: |
DEBUGF 1,"Segment has options\n" |
;---------------------------- |
; Compute window scale factor |
test [ebx + TCP_SOCKET.t_state], TCB_LISTEN ; no options when in listen state |
jz .no_options |
lea edi, [edx + TCP_segment.Data] |
lea eax, [edx + esi] |
; TODO |
.opt_loop: |
cmp edi, eax |
jge .no_options |
cmp byte [edi], TCP_OPT_EOL ; end of option list? |
jz .no_options |
;------------------------------------- |
; Reset idle timer and keepalive timer |
cmp byte [edi], TCP_OPT_NOP ; nop ? |
jz .opt_nop |
;;;; TODO: idle timer? |
cmp byte [edi], TCP_OPT_MAXSEG |
je .opt_maxseg |
mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval |
cmp byte [edi], TCP_OPT_WINDOW |
je .opt_window |
;----------------------------------------- |
; Process TCP options if not in LISTEN state |
cmp byte [edi], TCP_OPT_TIMESTAMP |
je .opt_timestamp |
test [ebx + TCP_SOCKET.t_state], TCB_LISTEN |
jz .dont_do_options |
jmp .no_options ; If we reach here, some unknown options were received, skip them all! |
call TCP_do_options |
.opt_nop: |
inc edi |
jmp .opt_loop |
.dont_do_options: |
.opt_maxseg: |
cmp byte [edi+1], 4 |
jne .no_options ; error occured, ignore all options! |
test [edx + TCP_segment.Flags], TH_SYN |
jz @f |
DEBUGF 1,"Got maxseg option" |
;;;;; |
@@: |
add edi, 4 |
jmp .opt_loop |
.opt_window: |
cmp byte [edi+1], 3 |
jne .no_options |
test [edx + TCP_segment.Flags], TH_SYN |
jz @f |
DEBUGF 1,"Got window option" |
;;;;; |
@@: |
add edi, 3 |
jmp .opt_loop |
.opt_timestamp: |
cmp byte [edi+1], 10 |
jne .no_options |
DEBUGF 1,"Got timestamp option" |
;;;;; |
add edi, 10 |
jmp .opt_loop |
.no_options: |
;----------------------------------------------------------------------- |
; Time to do some header prediction (Original Principle by Van Jacobson) |
; There are two common cases for an uni-directional data transfer. |
; |
; General rule: the packets has no control flags, is in-sequence, |
430,13 → 526,13 |
; - If the length is not 0 and the ACK didn't move, we're the receiver side of the transfer. |
; If the packets are in order (data queue is empty), add the data to the socket buffer and request a delayed ACK |
cmp [TCP_SOCKET.t_state], TCB_ESTABLISHED |
cmp [ebx + TCP_SOCKET.t_state], TCB_ESTABLISHED |
jnz .not_uni_xfer |
test [TCP_segment.Flags], TH_SYN + TH_FIN + TH_RST + TH_URG |
test [edx + TCP_segment.Flags], TH_SYN + TH_FIN + TH_RST + TH_URG |
jnz .not_uni_xfer |
test [TCP_segment.Flags], TH_ACK |
test [edx + TCP_segment.Flags], TH_ACK |
jz .not_uni_xfer |
mov eax, [edx + TCP_segment.SequenceNumber] |
443,7 → 539,7 |
cmp eax, [ebx + TCP_SOCKET.RCV_NXT] |
jne .not_uni_xfer |
movzx eax, [edx + TCP_segment.Window] ;;;;; |
movzx eax, [edx + TCP_segment.Window] ;;;;; (should use pre-calculated value isntead: todo: figure out where to store it) |
cmp eax, [ebx + TCP_SOCKET.SND_WND] |
jne .not_uni_xfer |
451,47 → 547,30 |
cmp eax, [ebx + TCP_SOCKET.SND_MAX] |
jne .not_uni_xfer |
;------------------------------------------------------------------------------- |
; If last ACK falls within this segment's sequence number, record the timestamp. |
; TODO: check if it has a timestamp |
;--------------------------------------- |
; check if we are sender in the uni-xfer |
; If the following 4 conditions are all true, this segment is a pure ACK. |
; |
; - The segment contains no data (ti_len is 0). |
movzx eax, [edx + TCP_segment.DataOffset] |
and eax, 11110000b |
shr eax, 2 |
sub ecx, eax |
; - The segment contains no data. |
test ecx, ecx |
jnz .not_sender |
; - The acknowledgment field in the segment (ti_ack) is greater than the largest unacknowledged sequence number (snd_una). |
; Since this test is "greater than" and not "greater than or equal to," it is true only if some positive amount of data is acknowledged by the ACK. |
mov eax, [edx + TCP_segment.AckNumber] |
cmp eax, [ebx + TCP_SOCKET.SND_UNA] |
jle .not_uni_xfer |
; - The acknowledgment field in the segment (ti_ack) is less than or equal to the maximum sequence number sent (snd_max). |
; mov eax, [edx + TCP_segment.Ack] |
cmp eax, [ebx + TCP_SOCKET.SND_MAX] |
jg .not_uni_xfer |
; - The congestion window (snd_cwnd) is greater than or equal to the current send window (snd_wnd). |
; - The congestion window is greater than or equal to the current send window. |
; This test is true only if the window is fully open, that is, the connection is not in the middle of slow start or congestion avoidance. |
mov eax, [ebx + TCP_SOCKET.SND_CWND] |
cmp eax, [ebx + TCP_SOCKET.SND_WND] |
jl .not_uni_xfer |
; - The acknowledgment field in the segment is less than or equal to the maximum sequence number sent. |
mov ecx, [edx + TCP_segment.AckNumber] |
cmp ecx, [ebx + TCP_SOCKET.SND_MAX] |
jg .not_uni_xfer |
; - The acknowledgment field in the segment is greater than the largest unacknowledged sequence number. |
sub ecx, [ebx + TCP_SOCKET.SND_UNA] |
jle .not_uni_xfer |
DEBUGF 1,"Header prediction: we are sender\n" |
;--------------------------------- |
500,7 → 579,11 |
; Update RTT estimators |
; Delete acknowledged bytes from send buffer |
; notice how ecx already holds number of bytes ack-ed |
lea eax, [ebx + snd] |
call SOCKET_ring_free |
; Stop retransmit timer |
mov [ebx + TCP_SOCKET.timer_ack], 0 |
513,27 → 596,19 |
jmp .drop |
;------------------------------------------------- |
; maybe we are the receiver in the uni-xfer then.. |
.not_sender: |
; The amount of data in the segment (ti_len) is greater than 0 (data count is in ecx) |
; - The amount of data in the segment is greater than 0 (data count is in ecx) |
; The acknowledgment field (ti_ack) equals the largest unacknowledged sequence number. This means no data is acknowledged by this segment. |
; - The acknowledgment field equals the largest unacknowledged sequence number. This means no data is acknowledged by this segment. |
mov eax, [edx + TCP_segment.AckNumber] |
cmp eax, [ebx + TCP_SOCKET.SND_UNA] |
jne .not_uni_xfer |
; The reassembly list of out-of-order segments for the connection is empty (seg_next equals tp). |
;;;; |
jnz .not_uni_xfer |
; - The reassembly list of out-of-order segments for the connection is empty (seg_next equals tp). ;;;;;;; |
; There is room in the receive buffer for the data in the segment. |
;;;; |
jnz .not_uni_xfer |
;------------------------------------- |
541,63 → 616,41 |
DEBUGF 1,"header prediction: we are receiver\nreceiving %u bytes of data\n", ecx |
; The next expected receive sequence number (rcv_nxt) is incremented by the number of bytes of data. |
add esi, edx |
lea eax, [ebx + rcv] |
call SOCKET_ring_add ; Add the data to the socket buffer |
add [ebx + TCP_SOCKET.RCV_NXT], ecx |
add [ebx + TCP_SOCKET.RCV_NXT], ecx ; Update sequence number with number of bytes we have copied |
or [ebx + TCP_SOCKET.t_flags], TF_DELACK ; Set delayed ack flag |
; Add the data to the socket buffer |
mov eax, ebx |
;;; mov... |
call SOCKET_input |
; The delayed-ACK flag is set and the input processing is complete. |
jmp .drop |
;---------------------------------------------------- |
; Header prediction failed, doing it the slow way.. |
; Header prediction failed, doing it the slow way.. ;;;;; current implementation of header prediction destroys some regs (ecx) !! |
.not_uni_xfer: |
DEBUGF 1,"Header prediction failed\n" |
;------------------------ |
; calculate header length ;;;;; we already calculated this before! |
movzx eax, [edx + TCP_segment.DataOffset] |
and eax, 0xf0 |
shr eax, 2 |
; Update edx to point to data.. |
add edx, eax |
; ..and ecx to give data size |
sub ecx, eax |
;------------------------------ |
; Calculate receive window size |
;;;; |
;------------------------- |
; TCP slow input procedure |
DEBUGF 1,"TCP slow input procedure\n" |
cmp [eax + TCP_SOCKET.t_state], TCB_LISTEN |
cmp [ebx + TCP_SOCKET.t_state], TCB_LISTEN |
je .LISTEN |
cmp [eax + TCP_SOCKET.t_state], TCB_SYN_SENT |
cmp [ebx + TCP_SOCKET.t_state], TCB_SYN_SENT |
je .SYN_SENT |
;-------------------------------------------- |
; Protection Against Wrapped Sequence Numbers |
; First, check timestamp if present |
;;;; TODO |
608,6 → 661,9 |
jmp .trim_then_step6 |
;------------- |
; Passive Open |
align 4 |
.LISTEN: |
622,47 → 678,37 |
test [edx + TCP_segment.Flags], TH_SYN |
jz .drop |
; TODO: find sender ip address somewhere! |
; TODO: check if it's a broadcast or multicast, and drop if so |
;;; 28.6 |
call SOCKET_fork |
jz .drop ; if we could not open a new connection, drop segment (;;;; should we send RST too?) |
; create a new socket and fill in the nescessary variables |
;----------------------- |
; Fill in some variables |
;; Exit if backlog queue is full |
; mov ax, [ebx + TCP_SOCKET.backlog_cur] |
; cmp ax, [ebx + TCP_SOCKET.backlog] |
; jae .exit |
add [TCP_sequence_num], 64000 |
; Allocate new socket |
call SOCKET_alloc |
;;; jz .fail |
push [edx + TCP_segment.SourcePort] |
pop [eax + TCP_SOCKET.RemotePort] |
; Copy structure from current socket to new, (including lock!) |
; We start at PID to reserve the socket num, and the 2 pointers at beginning of socket |
lea esi, [edx + SOCKET.PID] |
lea edi, [eax + SOCKET.PID] |
mov ecx, (TCP_SOCKET.end - SOCKET.PID + 3)/4 |
rep movsd |
push [edx + TCP_segment.SequenceNumber] |
pop [eax + TCP_SOCKET.IRS] |
;; Push pointer to new socket to queue |
; movzx ecx, [ebx + TCP_SOCKET.backlog_cur] |
; inc [ebx + TCP_SOCKET.backlog_cur] |
; mov [ebx + TCP_SOCKET.end + ecx*4], eax |
push [eax + TCP_SOCKET.ISS] |
pop [eax + TCP_SOCKET.SND_NXT] |
mov [eax + IP_SOCKET.RemoteIP], esi ; IP source address |
mov [eax + TCP_SOCKET.t_state], TCB_SYN_RECEIVED |
mov [eax + TCP_SOCKET.t_flags], TF_ACKNOW |
mov [eax + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval |
mov cx, [edx + TCP_segment.SourcePort] |
mov [eax + TCP_SOCKET.RemotePort], cx |
mov ebx, eax |
mov ecx, [edx + TCP_segment.SequenceNumber] |
mov [eax + TCP_SOCKET.IRS], ecx |
mov ecx, [eax + TCP_SOCKET.ISS] |
mov [eax + TCP_SOCKET.SND_NXT], ecx |
jmp .trim_then_step6 |
;------------ |
; Active Open |
align 4 |
.SYN_SENT: |
676,12 → 722,13 |
cmp eax, [ebx + TCP_SOCKET.ISS] |
jle .drop_with_reset |
mov eax, [edx + TCP_segment.AckNumber] |
cmp eax, [ebx + TCP_SOCKET.SND_MAX] |
jg .drop_with_reset |
DEBUGF 1,"snd_max = %x\n", [ebx + TCP_SOCKET.SND_MAX] ;;; TODO: set this, but where? |
; mov eax, [edx + TCP_segment.AckNumber] |
;; cmp eax, [ebx + TCP_SOCKET.SND_MAX] |
;; jg .drop_with_reset |
@@: |
test [edx + TCP_segment.Flags], TH_RST |
jz @f |
696,24 → 743,41 |
test [edx + TCP_segment.Flags], TH_SYN |
jz .drop |
; now, process received SYN in response to an active open |
; at this point, segment seems to be valid |
test [edx + TCP_segment.Flags], TH_ACK |
jz @f |
jz .no_syn_ack |
; now, process received SYN in response to an active open |
mov eax, [edx + TCP_segment.AckNumber] |
mov [ebx + TCP_SOCKET.SND_UNA], eax |
mov eax, [ebx + TCP_SOCKET.SND_UNA] |
cmp eax, [ebx + TCP_SOCKET.SND_NXT] |
jle @f |
mov [ebx + TCP_SOCKET.SND_NXT], eax |
@@: |
mov [ebx + TCP_SOCKET.timer_keepalive], TCP_time_keep_interval |
mov [ebx + TCP_SOCKET.timer_retransmission], 0 |
.no_syn_ack: |
mov eax, [edx + TCP_segment.SequenceNumber] |
mov [ebx + TCP_SOCKET.IRS], eax |
mov [ebx + TCP_SOCKET.timer_retransmission], 0 ; disable retransmission |
push [edx + TCP_segment.SequenceNumber] |
pop [ebx + TCP_SOCKET.IRS] |
;;; TODO: tcp_rcvseqinit |
mov [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
mov eax, [ebx + TCP_SOCKET.SND_UNA] |
cmp eax, [ebx + TCP_SOCKET.ISS] |
jle .simultaneous_open |
test [edx + TCP_segment.Flags], TH_ACK |
jz .simultaneous_open |
DEBUGF 1,"TCP: active open\n" |
; TODO: update stats |
; TODO: set socket state to connected |
mov [ebx + TCP_SOCKET.t_state], TCB_ESTABLISHED |
721,9 → 785,11 |
; TODO: check if we should scale the connection (567-572) |
; TODO: update RTT estimators |
jmp .trimthenstep6 |
@@: |
.simultaneous_open: |
DEBUGF 1,"TCP: simultaneous open\n" |
; We have received a syn but no ACK, so we are having a simultaneous open.. |
mov [ebx + TCP_SOCKET.t_state], TCB_SYN_RECEIVED |
744,16 → 810,15 |
; TODO... |
@@: |
;;;;; |
;;; jmp .step6 |
jmp .step6 |
align 4 |
.trim_then_step6: |
DEBUGF 1,"Trim, then step 6\n" |
DEBUGF 1,"Trimming window\n" |
;---------------------------- |
; trim any data not in window |
783,9 → 848,10 |
.no_drop: |
DEBUGF 1,"Going to drop %u bytes of data", eax |
; eax holds number of bytes to drop |
;---------------------------------- |
; Check for entire duplicate packet |
794,11 → 860,6 |
;;; TODO: figure 28.30 |
;; inc [TCP_segments_rx] |
;; add dword [TCP_bytes_rx], ecx |
;; adc dword [TCP_bytes_rx+4], 0 |
;------------------------ |
; Check for duplicate FIN |
811,8 → 872,7 |
mov eax, ecx |
and [edx + TCP_segment.Flags], not TH_FIN |
;;; TODO: set ACKNOW flag |
or [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
jmp .no_duplicate |
@@: |
819,7 → 879,6 |
; Handle the case when a bound socket connects to itself |
; Allow packets with a SYN and an ACKto continue with the processing |
;------------------------------------- |
; Generate duplicate ACK if nescessary |
833,12 → 892,14 |
.duplicate: |
DEBUGF 1,"Duplicate received" |
;---------------------------------------- |
; Update statistics for duplicate packets |
;;; TODO |
;;; DROP the packet ?? |
jmp .drop ;;; DROP the packet ?? |
.no_duplicate: |
898,7 → 959,6 |
.no_excess_data: |
;----------------- |
; Record timestamp |
910,6 → 970,8 |
test [edx + TCP_segment.Flags], TH_RST |
jz .rst_skip |
DEBUGF 1,"Got an RST flag" |
mov eax, [ebx + TCP_SOCKET.t_state] |
shl eax, 2 |
jmp dword [eax + .rst_sw_list] |
929,6 → 991,8 |
.econnrefused: |
DEBUGF 1,"Connection refused" |
;;; TODO: debug info |
jmp .close |
935,13 → 999,19 |
.econnreset: |
DEBUGF 1,"Connection reset" |
;;; TODO: debug info |
.close: |
DEBUGF 1,"Closing connection" |
;;; update stats |
.rst_close: |
DEBUGF 1,"Closing with reset" |
;;; Close the socket |
jmp .drop |
1006,12 → 1076,15 |
;------------------------------------------ |
; Remove acknowledged data from send buffer |
;;;; 943 - 956 |
lea eax, [ebx + snd] |
mov ecx, ecx ;;;; 943 - 956 |
call SOCKET_ring_free |
;--------------------------------------- |
; Wake up process waiting on send buffer |
;;;;; |
mov eax, ebx |
call SOCKET_notify_owner |
mov eax, [ebx + TCP_SOCKET.t_state] |
shl eax, 2 |
1103,7 → 1176,6 |
.no_window_update: |
;----------------- |
; process URG flag |
1121,7 → 1193,7 |
;;; 1040-1050 |
movzx eax, [edx + TCP_segment.UrgentPointer] |
add eax, [ebx + SOCKET.SO_RCV.SB_CC] |
add eax, [ebx + rcv.size] |
cmp eax, SOCKET_MAXDATA |
jle .not_urgent |
1134,19 → 1206,19 |
;-------------------------------------- |
; processing of received urgent pointer |
;;; 1051-1093 |
;;; TODO (1051-1093) |
align 4 |
;-------------------------------- |
; process the data in the segment |
.do_data: |
DEBUGF 1,"Do data:\n" |
DEBUGF 1,"TCP: do data:\n" |
; process the data in the segment |
test [edx + TCP_segment.Flags], TH_FIN |
jz .process_fin |
jnz .process_fin |
test [ebx + TCP_SOCKET.t_state], TCB_FIN_WAIT_1 ;;;;; |
test [ebx + TCP_SOCKET.t_state], TCB_FIN_WAIT_1 |
jge .dont_do_data |
DEBUGF 1,"Processing data in segment\n" |
1158,7 → 1230,6 |
.dont_do_data: |
;--------------- |
; FIN processing |
1209,16 → 1280,26 |
;jnz .outputnow |
test [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
jz .ret |
jnz .ack_now |
.outputnow: |
mov [ebx + SOCKET.lock], 0 |
call kernel_free |
add esp, 4 |
ret |
.ack_now: |
DEBUGF 1,"ACK now!\n" |
push ebx |
mov eax, ebx |
call TCP_output |
pop ebx |
.ret: |
mov [ebx + SOCKET.lock], 0 |
call kernel_free |
ret 4 |
add esp, 4 |
ret |
;------------------------------------------ |
; Generate an ACK, droping incoming segment |
1233,12 → 1314,15 |
and [ebx + TCP_SOCKET.t_flags], TF_ACKNOW |
push ebx |
mov eax, ebx |
call TCP_output |
pop ebx |
mov [ebx + SOCKET.lock], 0 |
call kernel_free |
ret 4 |
add esp, 4 |
ret |
;------------------------------------------- |
1261,15 → 1345,15 |
jnz .respond_syn |
mov [ebx + SOCKET.lock], 0 |
call kernel_free |
ret 4 |
add esp, 4 |
ret |
.respond_ack: |
;;;; |
call TCP_respond |
call TCP_respond_segment |
jmp .destroy_new_socket |
1278,7 → 1362,7 |
;;;; |
call TCP_respond |
call TCP_respond_segment |
jmp .destroy_new_socket |
1297,128 → 1381,16 |
;;;; kill the newly created socket |
mov [ebx + SOCKET.lock], 0 |
call kernel_free |
ret 4 |
add esp, 4 |
ret |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;--------------------- |
; |
; TCP_do_options |
; |
;------------------- |
align 4 |
TCP_do_options: |
DEBUGF 1,"TCP_do_options\n" |
push eax |
sub eax, 20 |
jz .no_options |
lea esi, [edx + TCP_segment.Data] |
;------------------------------------------- |
; Begin the loop by checking for EOL and NOP |
.loop: |
cmp byte [esi], TCP_OPT_EOL ; end of option list? |
jz .no_options |
cmp byte [esi], TCP_OPT_NOP ; nop ? |
;;; cmove edi, 1 ; if so, set option size to 1 |
jz .continue ; and continue scanning |
;------------------ |
; We have an option |
movzx edi, byte [esi + 1] ; get the length of this option in edi |
;-------------------------------------- |
; Check for Maximum segment size option |
cmp byte [esi], TCP_OPT_MAXSEG |
jne .no_maxseg |
cmp edi, 4 ; option length |
jne .continue |
test [edx + TCP_segment.Flags], TH_SYN |
jz .continue |
; Now parse the option... |
jmp .continue |
.no_maxseg: |
;------------------------ |
; Check for Window option |
cmp byte [esi], TCP_OPT_WINDOW |
jne .no_window |
cmp edi, 3 ; option length |
jne .continue |
test [edx + TCP_segment.Flags], TH_SYN |
jz .continue |
; ... |
jmp .continue |
.no_window: |
;--------------------------- |
; Check for Timestamp option |
cmp byte [esi], TCP_OPT_TIMESTAMP |
jne .no_timestamp |
cmp edi, 10 ; option length |
jne .continue |
; ... |
jmp .continue |
.no_timestamp: |
;---------------------------------- |
; Future options may be placed here |
;------------------------------ |
; Continue scanning for options |
.continue: |
add esi, edi |
sub eax, edi |
jg .loop |
.no_options: |
pop eax |
ret |
;--------------------------- |
; |
; TCP_pull_out_of_band |
; |
1441,14 → 1413,6 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;----------------------------------------------------------------- |
; |
; TCP_output |
1494,7 → 1458,7 |
mov ecx, [eax + TCP_SOCKET.SND_CWND] ; |
@@: ; |
call TCP_outflags |
call TCP_outflags ; in dl |
; If in persist timeout with window of 0, send 1 byte. |
; Otherwise, if window is small but nonzero, and timer expired, |
1506,7 → 1470,7 |
test ecx, ecx |
jnz .no_zero_window |
cmp ebx, [eax + SOCKET.SO_SND.SB_CC] |
cmp ebx, [eax + snd.size] |
jge @f |
and dl, not (TH_FIN) ; clear the FIN flag ??? how can it be set before? |
1517,7 → 1481,7 |
.no_zero_window: |
;;; mov [eax + TCP_SOCKET.t_timer....TCPT_PERSIST], 0 |
mov [eax + TCP_SOCKET.timer_persist], 0 ;;;; |
mov [eax + TCP_SOCKET.t_rxtshift], 0 |
.no_persist_timeout: |
1524,7 → 1488,7 |
;;;106 |
mov esi, [eax + SOCKET.SO_SND.SB_CC] |
mov esi, [eax + snd.size] |
cmp esi, ecx |
jl @f |
mov esi, ecx |
1546,7 → 1510,7 |
test ecx, ecx |
jnz @f |
;;; mov [eax + TCP_SOCKET.t_timer..TCPT_REXMT], 0 |
mov [eax + TCP_SOCKET.timer_retransmission], 0 ; cancel retransmit |
push [eax + TCP_SOCKET.SND_UNA] |
pop [eax + TCP_SOCKET.SND_NXT] |
1569,7 → 1533,7 |
mov edi, [eax + TCP_SOCKET.SND_NXT] |
add edi, esi ; len |
sub edi, [eax + TCP_SOCKET.SND_UNA] |
add edi, [eax + SOCKET.SO_SND.SB_CC] |
add edi, [eax + snd.size] |
cmp edi, 0 |
jle @f |
1578,17 → 1542,16 |
@@: |
;;;; 130 TODO: set window (ecx) to space in send buffer |
; set ecx to space available in receive buffer |
; From now on, ecx will be the window we advertise to the other end |
mov ecx, SOCKET_MAXDATA |
sub ecx, [eax + rcv.size] |
;------------------------------ |
; Sender silly window avoidance |
test esi, esi |
jz .zero_length |
cmp esi, [eax + TCP_SOCKET.t_maxseg] |
cmp ecx, [eax + TCP_SOCKET.t_maxseg] |
je .send |
;;; TODO: 144-145 |
1596,16 → 1559,20 |
test [eax + TCP_SOCKET.t_force], -1 |
jnz .send |
;;; TODO: 149..152 |
mov ebx, [eax + TCP_SOCKET.max_sndwnd] |
shr ebx, 1 |
cmp ecx, ebx |
jge .send |
.zero_length: |
mov ebx, [eax + TCP_SOCKET.SND_NXT] |
cmp ebx, [eax + TCP_SOCKET.SND_MAX] |
jl .send |
;---------------------------------------- |
; Check if a window update should be sent |
cmp ecx, 0 ; window |
jle .no_window |
test ecx, ecx ; window |
jz .no_window |
;;; TODO 154-172 |
1641,8 → 1608,6 |
DEBUGF 1,"Entering persist state\n" |
;-------------------------------------- |
; No reason to send a segment, just ret |
1651,14 → 1616,11 |
ret |
;----------------------------------------------- |
; |
; Send a segment |
; |
; ebx = socket pointer |
; eax = socket pointer |
; dl = flags |
; |
;----------------------------------------------- |
1667,8 → 1629,11 |
DEBUGF 1,"Preparing to send a segment\n" |
xor edi, edi ; edi will contain the number of header option bytes |
mov edi, TCP_segment.Data ; edi will contain headersize |
sub esp, 8 ; create some space on stack |
push eax ; save this too.. |
;------------------------------------ |
; Send options with first SYN segment |
1675,35 → 1640,32 |
test dl, TH_SYN |
jz .no_options |
mov eax, [ebx + TCP_SOCKET.ISS] |
mov [ebx + TCP_SOCKET.SND_NXT], eax |
push [eax + TCP_SOCKET.ISS] |
pop [eax + TCP_SOCKET.SND_NXT] |
test [ebx + TCP_SOCKET.t_flags], TF_NOOPT |
test [eax + TCP_SOCKET.t_flags], TF_NOOPT |
jnz .no_options |
mov eax, TCP_OPT_MAXSEG shl 24 + 4 shl 16 |
mov ax, 1280 ;;;;;; |
bswap eax |
push eax |
mov ecx, 1460 |
or ecx, TCP_OPT_MAXSEG shl 24 + 4 shl 16 |
bswap ecx |
push ecx |
add di, 4 |
mov di, 4 |
test [ebx + TCP_SOCKET.t_flags], TF_REQ_SCALE |
test [eax + TCP_SOCKET.t_flags], TF_REQ_SCALE |
jz .no_syn |
test dl, TH_ACK |
jnz .scale_opt |
test [ebx + TCP_SOCKET.t_flags], TF_RCVD_SCALE |
test [eax + TCP_SOCKET.t_flags], TF_RCVD_SCALE |
jz .no_syn |
.scale_opt: |
mov eax, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP |
mov ah, byte [ebx + TCP_SOCKET.request_r_scale] |
bswap eax |
push eax |
movzx ecx, byte [eax + TCP_SOCKET.request_r_scale] |
or ecx, TCP_OPT_WINDOW shl 24 + 4 shl 16 + TCP_OPT_NOP shl 8 |
bswap ecx |
pushd ecx |
add di, 4 |
.no_syn: |
1711,7 → 1673,7 |
;------------------------------------ |
; Make the timestamp option if needed |
test [ebx + TCP_SOCKET.t_flags], TF_REQ_TSTMP |
test [eax + TCP_SOCKET.t_flags], TF_REQ_TSTMP |
jz .no_timestamp |
test dl, TH_RST |
1720,145 → 1682,129 |
test dl, TH_ACK |
jz .timestamp |
test [ebx + TCP_SOCKET.t_flags], TF_RCVD_TSTMP |
test [eax + TCP_SOCKET.t_flags], TF_RCVD_TSTMP |
jz .no_timestamp |
.timestamp: |
DEBUGF 1,"Creating a timestamp\n" |
push dword (TCP_OPT_TIMESTAMP shl 8 + 10 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24) |
mov esi, [timer_ticks] |
bswap esi |
push esi |
pushw 0 |
mov eax, [timer_ticks] |
bswap eax |
push eax |
pushd TCP_OPT_TIMESTAMP + 10 shl 8 + TCP_OPT_NOP shl 16 + TCP_OPT_NOP shl 24 |
add di, 10 |
.no_timestamp: |
;; TODO: check if we dont exceed the max segment size |
.no_options: |
add edi, TCP_segment.Data |
; eax = socket ptr |
; edx = flags |
; ecx = data size |
; edi = header size |
; esi = snd ring buff ptr |
;----------------------------------- |
; Check if we have some data to send |
;;; mov ecx, [huppeldepup] |
test ecx, ecx |
jz .no_data |
;;; 278-316 |
jmp .header |
.no_data: |
;;; 317-338 |
;---------- |
push di dx ebx |
xor ecx, ecx ;;;;; |
add ecx, edi ; total TCP segment size |
mov eax, [ebx + IP_SOCKET.RemoteIP] |
mov ebx, [ebx + IP_SOCKET.LocalIP] |
mov di , IP_PROTO_TCP |
call IPv4_create_packet |
; Start by pushing all TCP header values in reverse order on stack |
; (essentially, creating the tcp header!) |
;;;; jz .fail |
pushw 0 ; .UrgentPointer dw ? |
pushw 0 ; .Checksum dw ? |
pushw 0x00a0 ; .Window dw ? ;;;;;;; |
shl edi, 2 ; .DataOffset db ? only 4 left-most bits |
shl dx, 8 |
or dx, di ; .Flags db ? |
pushw dx |
shr edi, 2 ; .DataOffset db ? ;;;; |
push edx eax |
call [ebx + NET_DEVICE.transmit] |
ret |
push [eax + TCP_SOCKET.RCV_NXT] ; .AckNumber dd ? |
ntohld [esp] |
;---------------- |
push [eax + TCP_SOCKET.SND_NXT] ; .SequenceNumber dd ? |
ntohld [esp] |
push [eax + TCP_SOCKET.RemotePort] ; .DestinationPort dw ? |
ntohlw [esp] |
;------------------------------- |
; Now, create the 20-byte header |
push [eax + TCP_SOCKET.LocalPort] ; .SourcePort dw ? |
ntohlw [esp] |
.header: |
push edi ; header size |
;----------------------- |
; Fill in the TCP header |
pop esi |
; Create the IP packet |
mov ebx, [eax + IP_SOCKET.LocalIP] ; source ip |
mov eax, [eax + IP_SOCKET.RemoteIP] ; dest ip |
; mov ecx, ; data length |
; mov dx, ; fragment id |
mov di, IP_PROTO_TCP shl 8 + 128 |
call IPv4_output |
jz .fail |
push [esi + TCP_SOCKET.SND_NXT] |
rol word [esp], 8 |
rol dword [esp], 16 |
pop [edi + TCP_segment.SequenceNumber] |
;----------------------------------------- |
; Move TCP header from stack to TCP packet |
push [esi + TCP_SOCKET.RCV_NXT] |
rol word [esp], 8 |
rol dword [esp], 16 |
pop [edi + TCP_segment.AckNumber] |
; pop ecx ; header size |
; mov esi, esp |
; add esp, ecx |
; shr ecx, 2 |
; rep movsd |
push [esi + TCP_SOCKET.LocalPort] |
rol word [esp], 8 |
pop [edi + TCP_segment.SourcePort] |
mov ecx, [esp] |
lea esi, [esp+4] |
shr ecx, 2 |
rep movsd |
push [esi + TCP_SOCKET.RemotePort] |
rol word [esp], 8 |
pop [edi + TCP_segment.DestinationPort] |
pop ecx |
add esp, ecx |
mov [esp + 3*4+4], edx ; packet size |
mov [esp + 3*4], eax ; packet ptr |
mov [edi + TCP_segment.Window], 0x0005 |
; 1280 bytes |
mov [edi + TCP_segment.UrgentPointer], 0 |
mov edx, edi |
sub edx, ecx |
mov [edi + TCP_segment.DataOffset], 0x50 |
mov [edi + TCP_segment.Flags], cl |
mov [edi + TCP_segment.Checksum], 0 |
;----- |
;-------------- |
; Copy the data |
pop esi |
push edi |
add edi, TCP_segment.Data ;; |
sub ecx, TCP_segment.Data ;;; |
; eax = ptr to ring struct |
; ecx = buffer size |
; edi = ptr to buffer |
shr ecx, 1 |
jnc .nb |
movsb |
.nb: |
shr ecx, 1 |
jnc .nw |
movsw |
.nw: |
test ecx, ecx |
jz .nd |
rep movsd |
.nd: |
pop edi |
mov eax, [esp] ; socket ptr |
push ecx edx |
add eax, snd |
call SOCKET_ring_read |
pop esi ecx |
pop eax |
;-------------------- |
; Create the checksum |
;------------------------------------------------------------- |
; Create the checksum (we have already pushed IPs onto stack) |
push [ebx + IP_SOCKET.LocalIP] |
push [ebx + IP_SOCKET.RemoteIP] |
call TCP_checksum |
DEBUGF 1,"checksum: ptr=%x size=%u\n", esi, ecx |
DEBUGF 1,"ip=%x\n", [esp]:8 |
DEBUGF 1,"ip=%x\n", [esp+4]:8 |
TCP_checksum (eax + IP_SOCKET.LocalIP), (eax + IP_SOCKET.RemoteIP) |
mov [esi+TCP_segment.Checksum], dx |
;---------------- |
; Send the packet |
;;;;; |
DEBUGF 1,"Sending TCP Packet to device %x\n", ebx |
call [ebx + NET_DEVICE.transmit] |
ret |
.fail: |
pop ecx |
add esp, ecx |
add esp, 4+4+8+4 |
DEBUGF 1,"TCP_output: failed\n" |
ret |
;------------------------- |
; |
; TCP_outflags |
1893,6 → 1839,8 |
db TH_ACK ; TCB_TIMED_WAIT |
;------------------------- |
; |
; TCP_drop |
1924,62 → 1872,68 |
;--------------------------------------- |
; |
; TCP_ack |
; |
; The easy way to send an ACK/RST/keepalive segment |
; |
; IN: eax = socket ptr |
; -or- |
; edx = packet ptr (eax must be 0) |
; TCP_respond_socket: |
; |
; IN: ebx = socket ptr |
; cl = flags |
; |
; OUT: / |
; |
;--------------------------------------- |
;-------------------------------------- |
align 4 |
TCP_respond: |
TCP_respond_socket: |
DEBUGF 1,"TCP_respond\n" |
DEBUGF 1,"TCP_respond_socket\n" |
;--------------------- |
; Create the IP packet |
push cx eax edx |
mov ebx, [eax + IP_SOCKET.LocalIP] |
mov eax, [eax + IP_SOCKET.RemoteIP] |
push cx ebx |
mov eax, [ebx + IP_SOCKET.RemoteIP] |
mov ebx, [ebx + IP_SOCKET.LocalIP] |
mov ecx, TCP_segment.Data |
mov di , IP_PROTO_TCP |
call IPv4_create_packet |
mov di , IP_PROTO_TCP shl 8 + 128 |
call IPv4_output |
test edi, edi |
jz .error |
pop esi cx |
push edx eax |
;--------------------------- |
; Now fill in the TCP header |
;----------------------------------------------- |
; Fill in the TCP header by using the socket ptr |
pop ecx |
pop esi |
mov ax, [esi + TCP_SOCKET.LocalPort] |
rol ax, 8 |
stosw |
mov ax, [esi + TCP_SOCKET.RemotePort] |
rol ax, 8 |
stosw |
mov eax, [esi + TCP_SOCKET.SND_NXT] |
bswap eax |
stosd |
mov eax, [esi + TCP_SOCKET.RCV_NXT] |
bswap eax |
stosd |
mov al, 0x50 ; Dataoffset: 20 bytes |
stosb |
mov al, cl |
stosb |
mov ax, [esi + TCP_SOCKET.RCV_WND] |
rol ax, 8 |
stosw ; window |
xor eax, eax |
stosd ; checksum + urgentpointer |
test esi, esi |
; jz |
push edx eax |
push dword .checksum |
je .use_segment |
jmp .use_socket |
;--------------------- |
; Fill in the checksum |
.checksum: |
sub edi, TCP_segment.Data |
mov ecx, TCP_segment.Data |
xchg esi, edi |
TCP_checksum (edi + IP_SOCKET.LocalIP), (esi + IP_SOCKET.RemoteIP) |
mov [esi+TCP_segment.Checksum], dx |
push [esi + IP_SOCKET.LocalIP] |
push [esi + IP_SOCKET.RemoteIP] |
lea esi, [edi - 20] |
xor ecx, ecx |
call TCP_checksum |
;-------------------- |
; And send the segment |
1987,16 → 1941,42 |
ret |
.error: |
DEBUGF 1,"TCP_ack failed\n" |
add esp, 4 |
DEBUGF 1,"TCP_respond failed\n" |
add esp, 2+4 |
ret |
;------------------------- |
; TCP_respond.segment: |
; |
; IN: edx = segment ptr (a previously received segment) |
; cl = flags |
align 4 |
TCP_respond_segment: |
DEBUGF 1,"TCP_respond_segment\n" |
;--------------------- |
; Create the IP packet |
push cx edx |
mov ebx, [edx - 20 + IPv4_Packet.SourceAddress] ;;;; and what if ip packet had options?! |
mov eax, [edx - 20 + IPv4_Packet.DestinationAddress] ;;; |
mov ecx, TCP_segment.Data |
mov di , IP_PROTO_TCP shl 8 + 128 |
call IPv4_output |
test edi, edi |
jz .error |
pop esi cx |
push edx eax |
;--------------------------------------------------- |
; Fill in the TCP header by using a received segment |
.use_segment: |
mov ax, [esi + TCP_segment.DestinationPort] |
rol ax, 8 |
stosw |
2018,98 → 1998,30 |
xor eax, eax |
stosd ; checksum + urgentpointer |
ret |
;--------------------- |
; Fill in the checksum |
.checksum: |
lea esi, [edi - TCP_segment.Data] |
mov ecx, TCP_segment.Data |
TCP_checksum (esi - 20 + IPv4_Packet.DestinationAddress), (esi - 20 + IPv4_Packet.DestinationAddress) |
mov [esi+TCP_segment.Checksum], dx |
;----------------------------------------------- |
; Fill in the TCP header by using the socket ptr |
;-------------------- |
; And send the segment |
.use_socket: |
call [ebx + NET_DEVICE.transmit] |
ret |
mov ax, [esi + TCP_SOCKET.LocalPort] |
rol ax, 8 |
stosw |
mov ax, [esi + TCP_SOCKET.RemotePort] |
rol ax, 8 |
stosw |
mov eax, [esi + TCP_SOCKET.SND_NXT] |
bswap eax |
stosd |
mov eax, [esi + TCP_SOCKET.RCV_NXT] |
bswap eax |
stosd |
mov al, 0x50 ; Dataoffset: 20 bytes |
stosb |
mov al, cl |
stosb |
mov ax, [esi + TCP_SOCKET.RCV_WND] |
rol ax, 8 |
stosw ; window |
xor eax, eax |
stosd ; checksum + urgentpointer |
.error: |
DEBUGF 1,"TCP_respond failed\n" |
add esp, 2+4 |
ret |
;----------------------------------------------------------------- |
; |
; TCP_checksum |
; |
; This is the fast procedure to create or check a UDP header |
; - 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 |
; |
; IN: push source ip |
; push dest ip |
; |
; esi = packet ptr |
; |
; OUT: checksum is filled in in packet! (but also in dx) |
; |
;----------------------------------------------------------------- |
align 4 |
TCP_checksum: |
;------------- |
; Pseudoheader |
; protocol type |
mov edx, IP_PROTO_TCP ; NO shl 8 here ! (it took me ages to figure this one out) |
; source address |
add dl, [esp+1+4] |
adc dh, [esp+0+4] |
adc dl, [esp+3+4] |
adc dh, [esp+2+4] |
; destination address |
adc dl, [esp+1+8] |
adc dh, [esp+0+8] |
adc dl, [esp+3+8] |
adc dh, [esp+2+8] |
; size |
adc dl, cl |
adc dh, ch |
;--------------------- |
; Real header and data |
push esi |
call checksum_1 |
call checksum_2 |
pop esi |
neg [esi+UDP_Packet.Checksum] ; zero will stay zero so we just get the checksum |
add [esi+UDP_Packet.Checksum], dx ; , else we will get (new checksum - old checksum) in the end, wich should be 0 :) |
ret 8 ; Remove the IPs from stack |
;--------------------------------------------------------------------------- |
; |
; TCP_API |
/kernel/branches/net/network/udp.inc |
---|
40,21 → 40,64 |
; |
; This function resets all UDP variables |
; |
; IN: / |
; OUT: / |
; |
;----------------------------------------------------------------- |
align 4 |
UDP_init: |
macro UDP_init { |
xor eax, eax |
mov edi, UDP_PACKETS_TX |
mov ecx, 2*MAX_IP |
rep stosd |
} |
ret |
macro UDP_checksum IP1, IP2 { ; esi = ptr to udp packet, ecx = packet size, destroys: ecx, edx |
; Pseudoheader |
mov edx, IP_PROTO_UDP |
add dl, [IP1+1+4] |
adc dh, [IP1+0+4] |
adc dl, [IP1+3+4] |
adc dh, [IP1+2+4] |
adc dl, [IP2+1+8] |
adc dh, [IP2+0+8] |
adc dl, [IP2+3+8] |
adc dh, [IP2+2+8] |
adc dl, cl ; byte[esi+UDP_Packet.Length+1] |
adc dh, ch ; byte[esi+UDP_Packet.Length+0] |
; Done with pseudoheader, now do real header |
adc dl, byte[esi+UDP_Packet.SourcePort+1] |
adc dh, byte[esi+UDP_Packet.SourcePort+0] |
adc dl, byte[esi+UDP_Packet.DestinationPort+1] |
adc dh, byte[esi+UDP_Packet.DestinationPort+0] |
adc dl, byte[esi+UDP_Packet.Length+1] |
adc dh, byte[esi+UDP_Packet.Length+0] |
adc edx, 0 |
; Done with header, now do data |
push esi |
movzx ecx, [esi+UDP_Packet.Length] |
rol cx , 8 |
sub cx , UDP_Packet.Data |
add esi, UDP_Packet.Data |
call checksum_1 |
call checksum_2 |
pop esi |
add [esi+UDP_Packet.Checksum], dx ; this final instruction will set or clear ZF :) |
} |
;----------------------------------------------------------------- |
; |
; UDP_input: |
62,11 → 105,11 |
; Called by IPv4_input, |
; this procedure will inject the udp data diagrams in the application sockets. |
; |
; IN: Pointer to buffer in [esp] |
; size of buffer in [esp+4] |
; pointer to device struct in ebx |
; UDP Packet size in ecx |
; pointer to UDP Packet in edx |
; IN: [esp] = Pointer to buffer |
; [esp+4] = size of buffer |
; ebx = ptr to device struct |
; ecx = UDP Packet size |
; edx = ptr to UDP header |
; |
; esi = ipv4 source address |
; edi = ipv4 dest address |
80,18 → 123,18 |
DEBUGF 1,"UDP_input, size:%u\n", ecx |
; First validate, checksum: |
cmp [edx + UDP_Packet.Checksum], 0 |
jz .no_checksum |
neg [esi+UDP_Packet.Checksum] ; substract chechksum from 0 |
jz .no_checksum ; if checksum is zero, it is considered valid and we continue processing |
; otherwise, we will re-calculate the checksum and add it to this value, thus creating 0 when it is correct |
xchg edi, esi ; save ipv4 source address to edi so we can use it later |
push edx |
push esi edi |
push edi |
push esi |
mov esi, edx |
call UDP_checksum ; this destroys edx, ecx and esi (but not edi...) |
UDP_checksum (esp), (esp+4) |
pop edi |
pop esi ; we dont need it, but it is smaller then add esp, 4 |
pop edx |
cmp [edx + UDP_Packet.Checksum], 0 |
jnz .checksum_mismatch |
.no_checksum: |
168,12 → 211,6 |
DEBUGF 2,"UDP_Handler - checksum mismatch\n" |
; mov esi, edx |
; @@: ; |
; lodsb ; |
; DEBUGF 2,"%x ", eax:2 ; |
; loop @r ; |
.dump: |
call kernel_free |
add esp, 4 ; pop (balance stack) |
207,26 → 244,23 |
DEBUGF 1,"local port: %u\n", dx |
rol dx, 8 |
mov ebx, [eax + IP_SOCKET.LocalIP] |
mov eax, [eax + IP_SOCKET.RemoteIP] |
mov di , IP_PROTO_UDP |
mov di, IP_PROTO_UDP shl 8 + 128 |
sub esp, 8 ; Data ptr and data size will be placed here |
add ecx, UDP_Packet.Data |
;;; TODO: fragment id |
push edx esi |
call IPv4_create_packet |
call IPv4_output |
jz .fail |
mov [esp + 8], eax ; pointer to buffer start |
mov [esp + 8 + 4], edx ; buffer size |
rol cx, 8 |
mov [edi + UDP_Packet.Length], cx |
ror cx, 8 |
rol [edi + UDP_Packet.Length], 8 |
pop esi |
push edi ecx |
240,13 → 274,11 |
pop ecx edi |
pop dword [edi + UDP_Packet.SourcePort] |
mov [edi + UDP_Packet.Checksum], 0 ; set it to zero, to calculate checksum |
; Checksum |
mov esi, edi |
pushd [edi-4] ; destination address ; TODO: fix this, IPv4 packet could have options.. |
pushd [edi-8] ; source address |
call UDP_checksum |
mov [edi + UDP_Packet.Checksum], 0 |
UDP_checksum (edi-4), (edi-8) ; TODO: fix this, IPv4 packet could have options.. |
inc [UDP_PACKETS_TX] |
254,81 → 286,15 |
call [ebx + NET_DEVICE.transmit] |
ret |
.fail: |
add esp, 8+8 |
DEBUGF 1,"UDP_output: failed\n" |
add esp, 4+4+8 |
xor eax, eax |
ret |
;----------------------------------------------------------------- |
; |
; UDP_checksum |
; |
; This is the fast procedure to create or check a UDP header |
; - 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 |
; |
; IN: push source ip |
; push dest ip |
; esi = packet ptr |
; |
; OUT: checksum is filled in in packet! (but also in dx) |
; |
;----------------------------------------------------------------- |
align 4 |
UDP_checksum: |
; Pseudoheader |
mov edx, IP_PROTO_UDP ; NO shl 8 here ! (it took me ages to figure this one out) |
add dl, [esp+1+4] |
adc dh, [esp+0+4] |
adc dl, [esp+3+4] |
adc dh, [esp+2+4] |
adc dl, [esp+1+8] |
adc dh, [esp+0+8] |
adc dl, [esp+3+8] |
adc dh, [esp+2+8] |
adc dl, cl ; byte[esi+UDP_Packet.Length+1] |
adc dh, ch ; byte[esi+UDP_Packet.Length+0] |
; Done with pseudoheader, now do real header |
adc dl, byte[esi+UDP_Packet.SourcePort+1] |
adc dh, byte[esi+UDP_Packet.SourcePort+0] |
adc dl, byte[esi+UDP_Packet.DestinationPort+1] |
adc dh, byte[esi+UDP_Packet.DestinationPort+0] |
adc dl, byte[esi+UDP_Packet.Length+1] |
adc dh, byte[esi+UDP_Packet.Length+0] |
adc edx, 0 |
; Done with header, now do data |
push esi |
movzx ecx, [esi+UDP_Packet.Length] |
rol cx , 8 |
sub cx , UDP_Packet.Data |
add esi, UDP_Packet.Data |
call checksum_1 |
call checksum_2 |
pop esi |
neg [esi+UDP_Packet.Checksum] ; zero will stay zero so we just get the checksum |
add [esi+UDP_Packet.Checksum], dx ; , else we will get (new checksum - old checksum) in the end, wich should be 0 :) |
ret 8 |
;--------------------------------------------------------------------------- |
; |
; UDP_API |