From d5b1d9466aef225c551eced1d321c39e2ebe06e4 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Sat, 9 Jul 2022 15:54:08 +0200 Subject: [PATCH] nic_router: clear ARP cache when domain is down Whenever a domain looses all its interfaces or the link state of all attached interfaces is down at once, the domain potentially moves to another Ethernet segment and should therefore consider its ARP cache to be outdated. RFC 826 states that "... If a host moves, any connections initiated by that host will work, assuming its own address resolution table is cleared when it moves. ...". Therefore, this commit introduces clearing the ARP cache and the initially stated events. This commit was motivated by an issue with the PinePhone Modem and USB NIC. On the PinePhone, the Modem has its own OS and acts as direct gateway to the outer world for the USB NIC that is driven by Genode. However, whenever the Modem gets restarted, Modem and USB NIC receive a new MAC address. This used to conflict with the NIC routers ARP entry for the Modem that didn't cease to be valid. With this commit, the integrator of such a scenario at least has a convenient way of fixing this by ensuring that all interfaces at the USB NIC domain go down when resetting (e.g. by ensuring that the USB NIC is the only interface at that domain). Fixes #4558 --- repos/os/src/server/nic_router/arp_cache.cc | 14 ++++++++++++++ repos/os/src/server/nic_router/arp_cache.h | 2 ++ repos/os/src/server/nic_router/domain.cc | 7 +++++-- repos/os/src/server/nic_router/interface.cc | 1 + 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/repos/os/src/server/nic_router/arp_cache.cc b/repos/os/src/server/nic_router/arp_cache.cc index 500e858f6e..6f8e0b1b0e 100644 --- a/repos/os/src/server/nic_router/arp_cache.cc +++ b/repos/os/src/server/nic_router/arp_cache.cc @@ -105,3 +105,17 @@ void Arp_cache::destroy_entries_with_mac(Mac_address const &mac) } catch (Arp_cache_entry_slot::Deref_unconstructed_object) { } } } + + +void Arp_cache::destroy_all_entries() +{ + if (_domain.config().verbose()) { + log("[", _domain, "] destroy all ARP entries"); + } + while (Arp_cache_entry *entry = first()) { + remove(entry); + } + for (unsigned curr = 0; curr < NR_OF_ENTRIES; curr++) { + _entries[curr].destruct(); + } +} diff --git a/repos/os/src/server/nic_router/arp_cache.h b/repos/os/src/server/nic_router/arp_cache.h index 8844f576bf..e1fca9f484 100644 --- a/repos/os/src/server/nic_router/arp_cache.h +++ b/repos/os/src/server/nic_router/arp_cache.h @@ -92,6 +92,8 @@ class Net::Arp_cache : public Genode::Avl_tree void destroy_entries_with_mac(Mac_address const &mac); Arp_cache_entry const &find_by_ip(Ipv4_address const &ip) const; + + void destroy_all_entries(); }; #endif /* _ARP_CACHE_H_ */ diff --git a/repos/os/src/server/nic_router/domain.cc b/repos/os/src/server/nic_router/domain.cc index 11af946ab7..2cede36955 100644 --- a/repos/os/src/server/nic_router/domain.cc +++ b/repos/os/src/server/nic_router/domain.cc @@ -364,8 +364,11 @@ void Domain::detach_interface(Interface &interface) { _interfaces.remove(&interface); _interface_cnt--; - if (!_interface_cnt && _ip_config_dynamic) { - discard_ip_config(); + if (!_interface_cnt) { + _arp_cache.destroy_all_entries(); + if (_ip_config_dynamic) { + discard_ip_config(); + } } if (_config.verbose_domain_state()) { log("[", *this, "] NIC sessions: ", _interface_cnt); diff --git a/repos/os/src/server/nic_router/interface.cc b/repos/os/src/server/nic_router/interface.cc index f80ce1a97c..50491bcd5a 100644 --- a/repos/os/src/server/nic_router/interface.cc +++ b/repos/os/src/server/nic_router/interface.cc @@ -930,6 +930,7 @@ void Interface::handle_interface_link_state() throw Keep_ip_config(); } }); domain_.discard_ip_config(); + domain_.arp_cache().destroy_all_entries(); } } catch (Pointer::Invalid) { }