diff --git a/kernel/trunk/network/ARP.inc b/kernel/trunk/network/ARP.inc index 4d10ffcbe..c37d20288 100644 --- a/kernel/trunk/network/ARP.inc +++ b/kernel/trunk/network/ARP.inc @@ -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 ; @@ -313,6 +338,9 @@ arp_output_request: 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 + test ebx, ebx ; device is gone + jz .exit + mov ax, ETHER_PROTO_ARP mov ecx, sizeof.ARP_header mov edx, ETH_BROADCAST ; broadcast mac diff --git a/kernel/trunk/network/IPv4.inc b/kernel/trunk/network/IPv4.inc index ab06fc709..872964478 100644 --- a/kernel/trunk/network/IPv4.inc +++ b/kernel/trunk/network/IPv4.inc @@ -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 ; @@ -757,6 +780,8 @@ ipv4_output_raw: push esi eax call ipv4_route + test eax, eax + jz .arp_error call arp_ip_to_mac test eax, 0xffff0000 ; error bits @@ -967,6 +992,8 @@ ipv4_route: ; Check for on-link xor edi, edi .loop: + cmp [net_device_list + edi], 0 ; skip slots without a device + je .next mov ebx, [IPv4_address + edi] and ebx, [IPv4_subnet + edi] jz .next @@ -983,7 +1010,10 @@ ipv4_route: mov edi, 4 ; skip loopback device .loop_gw: cmp [IPv4_gateway + edi], 0 + je .next_gw + cmp [net_device_list + edi], 0 jne .found_gw + .next_gw: add edi, 4 cmp edi, 4*NET_DEVICES_MAX jb .loop_gw @@ -1049,14 +1079,23 @@ ipv4_route: cmp eax, 0xffffffff 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 - mov ebx, [IPv4_address + edi] + mov ebx, edx and ebx, [IPv4_subnet + edi] mov ecx, eax and ecx, [IPv4_subnet + edi] cmp ecx, ebx je @f mov eax, [IPv4_gateway + edi] + test eax, eax + jz .fail ; off-link and no gateway @@: DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_route: %u\n", edi ret diff --git a/kernel/trunk/network/ethernet.inc b/kernel/trunk/network/ethernet.inc index 068e3a4bb..1a44a7beb 100644 --- a/kernel/trunk/network/ethernet.inc +++ b/kernel/trunk/network/ethernet.inc @@ -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 ; This function is called by ethernet drivers. ; 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 + test ebx, ebx + jz .no_device + cmp ecx, [ebx + ETH_DEVICE.mtu] ja .too_large @@ -295,11 +333,16 @@ eth_output: ret .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" xor eax, eax ret + .no_device: + DEBUGF DEBUG_NETWORK_ERROR, "ETH_output: no device!\n" + xor eax, eax + ret + ;-----------------------------------------------------------------; diff --git a/kernel/trunk/network/stack.inc b/kernel/trunk/network/stack.inc index 5fc6f617a..6b5e6aa69 100644 --- a/kernel/trunk/network/stack.inc +++ b/kernel/trunk/network/stack.inc @@ -597,6 +597,16 @@ net_remove_device: mov dword [edi-4], eax 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 xor eax, eax