kernel/network: forget a network device's state when it is removed (#719)

- `net_remove_device`: clear the slot's IPv4 settings, ARP table and counters, and drop its frames from the ethernet input queue.
- `ipv4_route`:
  - skip slots without a device;
  - an interface without an address routes broadcasts only;
  - off-link without a gateway fails instead of resolving 0.0.0.0.
- `ipv4_output_raw`: check the route before resolving it.
- `eth_output`, `arp_output_request`: refuse a null device.
- `eth_output`: count a too-large frame on the device, not on `eax`.

Reviewed-on: KolibriOS/kolibrios#719
Reviewed-by: hidnplayr <hidnplayr@gmail.com>
Reviewed-by: Burer <burer@kolibrios.org>
Co-authored-by: leency <lipatov.kiril@gmail.com>
This commit is contained in:
Leency authored and Burer committed 2026-09-29 13:30:00 +00:00
1 parent 939505198f
commit b59e371c0e
4 files changed
+122 -2

No files matched your search

+28
View File
@@ -83,6 +83,31 @@ macro arp_init {
} }
;-----------------------------------------------------------------;
; ;
; arp_clear_device: Empty the ARP table and counters of a removed ;
; device. ;
; ;
; IN: edi = device number * 4 ;
; ;
;-----------------------------------------------------------------;
macro arp_clear_device {
xor eax, eax
mov [ARP_entries + edi], eax
mov [ARP_packets_tx + edi], eax
mov [ARP_packets_rx + edi], eax
mov [ARP_conflicts + edi], eax
push edi
imul edi, (ARP_TABLE_SIZE * sizeof.ARP_entry)/4
add edi, ARP_table
mov ecx, (ARP_TABLE_SIZE * sizeof.ARP_entry)/2
rep stosw
pop edi
}
;-----------------------------------------------------------------; ;-----------------------------------------------------------------;
; ; ; ;
; arp_decrease_entry_ttls ; ; arp_decrease_entry_ttls ;
@@ -313,6 +338,9 @@ arp_output_request:
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_output_request: ip=%u.%u.%u.%u device=0x%x\n",\ DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_output_request: ip=%u.%u.%u.%u device=0x%x\n",\
[esp]:1, [esp + 1]:1, [esp + 2]:1, [esp + 3]:1, ebx [esp]:1, [esp + 1]:1, [esp + 2]:1, [esp + 3]:1, ebx
test ebx, ebx ; device is gone
jz .exit
mov ax, ETHER_PROTO_ARP mov ax, ETHER_PROTO_ARP
mov ecx, sizeof.ARP_header mov ecx, sizeof.ARP_header
mov edx, ETH_BROADCAST ; broadcast mac mov edx, ETH_BROADCAST ; broadcast mac
+40 -1
View File
@@ -105,6 +105,29 @@ macro ipv4_init {
} }
;-----------------------------------------------------------------;
; ;
; ipv4_clear_device: Reset the IPv4 settings of a removed device, ;
; so no route points to it anymore. ;
; ;
; IN: edi = device number * 4 ;
; ;
;-----------------------------------------------------------------;
macro ipv4_clear_device {
xor eax, eax
mov [IPv4_address + edi], eax
mov [IPv4_subnet + edi], eax
mov [IPv4_nameserver + edi], eax
mov [IPv4_gateway + edi], eax
mov [IPv4_broadcast + edi], eax
mov [IPv4_packets_tx + edi], eax
mov [IPv4_packets_rx + edi], eax
mov [IPv4_packets_dumped + edi], eax
}
;-----------------------------------------------------------------; ;-----------------------------------------------------------------;
; ; ; ;
; Decrease TimeToLive of all fragment slots ; ; Decrease TimeToLive of all fragment slots ;
@@ -757,6 +780,8 @@ ipv4_output_raw:
push esi eax push esi eax
call ipv4_route call ipv4_route
test eax, eax
jz .arp_error
call arp_ip_to_mac call arp_ip_to_mac
test eax, 0xffff0000 ; error bits test eax, 0xffff0000 ; error bits
@@ -967,6 +992,8 @@ ipv4_route:
; Check for on-link ; Check for on-link
xor edi, edi xor edi, edi
.loop: .loop:
cmp [net_device_list + edi], 0 ; skip slots without a device
je .next
mov ebx, [IPv4_address + edi] mov ebx, [IPv4_address + edi]
and ebx, [IPv4_subnet + edi] and ebx, [IPv4_subnet + edi]
jz .next jz .next
@@ -983,7 +1010,10 @@ ipv4_route:
mov edi, 4 ; skip loopback device mov edi, 4 ; skip loopback device
.loop_gw: .loop_gw:
cmp [IPv4_gateway + edi], 0 cmp [IPv4_gateway + edi], 0
je .next_gw
cmp [net_device_list + edi], 0
jne .found_gw jne .found_gw
.next_gw:
add edi, 4 add edi, 4
cmp edi, 4*NET_DEVICES_MAX cmp edi, 4*NET_DEVICES_MAX
jb .loop_gw jb .loop_gw
@@ -1049,14 +1079,23 @@ ipv4_route:
cmp eax, 0xffffffff cmp eax, 0xffffffff
je @f je @f
; An interface without an address (DHCP not done yet, or the lease
; dropped when the link went down) can send nothing but broadcasts: with
; address and mask both zero every destination compared as on-link, and
; the stack went asking ARP for internet addresses on behalf of 0.0.0.0.
test edx, edx
jz .fail
; Check if we should route to gateway or not ; Check if we should route to gateway or not
mov ebx, [IPv4_address + edi] mov ebx, edx
and ebx, [IPv4_subnet + edi] and ebx, [IPv4_subnet + edi]
mov ecx, eax mov ecx, eax
and ecx, [IPv4_subnet + edi] and ecx, [IPv4_subnet + edi]
cmp ecx, ebx cmp ecx, ebx
je @f je @f
mov eax, [IPv4_gateway + edi] mov eax, [IPv4_gateway + edi]
test eax, eax
jz .fail ; off-link and no gateway
@@: @@:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_route: %u\n", edi DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_route: %u\n", edi
ret ret
+44 -1
View File
@@ -61,6 +61,41 @@ macro eth_init {
} }
;-----------------------------------------------------------------;
; ;
; eth_clear_device: Drop the frames of a removed device that are ;
; still waiting in the input queue, their ;
; NET_BUFF.device will not be valid anymore. ;
; ;
; IN: ebx = device ptr ;
; ;
;-----------------------------------------------------------------;
macro eth_clear_device {
local .loop, .done
spin_lock_irqsave
mov esi, [ETH_frame_head]
.loop:
cmp esi, ETH_frame_head
je .done
mov eax, esi
mov esi, [esi + NET_BUFF.NextPtr]
cmp [eax + NET_BUFF.device], ebx
jne .loop
; unlink it, ETH_frame_head/tail double as the NextPtr/PrevPtr of the list head
mov ecx, [eax + NET_BUFF.PrevPtr]
mov [ecx + NET_BUFF.NextPtr], esi
mov [esi + NET_BUFF.PrevPtr], ecx
dec [ETH_frame_queued]
stdcall net_buff_free, eax
jmp .loop
.done:
spin_unlock_irqrestore
}
align 4 align 4
; This function is called by ethernet drivers. ; This function is called by ethernet drivers.
; Push the received ethernet packet onto the ethernet input queue. ; Push the received ethernet packet onto the ethernet input queue.
@@ -245,6 +280,9 @@ eth_output:
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_output: size=%u device=%x\n", ecx, ebx DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_output: size=%u device=%x\n", ecx, ebx
test ebx, ebx
jz .no_device
cmp ecx, [ebx + ETH_DEVICE.mtu] cmp ecx, [ebx + ETH_DEVICE.mtu]
ja .too_large ja .too_large
@@ -295,11 +333,16 @@ eth_output:
ret ret
.too_large: .too_large:
inc [eax + NET_DEVICE.packets_tx_err] inc [ebx + NET_DEVICE.packets_tx_err]
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_output: Packet too large!\n" DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_output: Packet too large!\n"
xor eax, eax xor eax, eax
ret ret
.no_device:
DEBUGF DEBUG_NETWORK_ERROR, "ETH_output: no device!\n"
xor eax, eax
ret
;-----------------------------------------------------------------; ;-----------------------------------------------------------------;
+10
View File
@@ -597,6 +597,16 @@ net_remove_device:
mov dword [edi-4], eax mov dword [edi-4], eax
dec [net_device_count] dec [net_device_count]
;-----------------------------------------------------------------
; Forget everything the protocols still know about this interface.
; Otherwise its IP address keeps matching in ipv4_route, and output
; ends up calling through the now empty net_device_list slot.
sub edi, net_device_list + 4 ; device number * 4
ipv4_clear_device
arp_clear_device
eth_clear_device
call net_send_event call net_send_event
xor eax, eax xor eax, eax