From 92a30e09539e164a584102cd50b62aa583ed2075 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Mon, 19 Mar 2018 19:16:17 +0100 Subject: [PATCH] nic_router: handle configuration changes The router reacts as follows to a configuration change: 1) Construct new internal configuration representation (the old one stays in place to be able to do comparisons in the following steps) 2) Iterate through all user-dependent objects (interfaces, link states, ARP information, DHCP information) and re-check which remain valid with the new configuration and which must be dismissed. 3) Adapt the objects that remain valid to the new configuration (re-write references) and remove or detach the dismissed objects. 4) Do a link state DOWN at each interface and a link state UP at each interface that remains attached to a domain. 5) Replace the old internal configuration representation with the new one This way, the router keeps as much user dependent states as possible while going through a configuration change. Thus, overwriting the old configuration with an exact copy of itself is (almost) transparent to clients of the router. Almost, because there are things the router must do on every configuration handling, like re-scheduling the expiration timeouts of links. Ref #2670 --- repos/os/src/server/nic_router/arp_waiter.cc | 18 +- repos/os/src/server/nic_router/arp_waiter.h | 19 +- repos/os/src/server/nic_router/component.cc | 20 +- repos/os/src/server/nic_router/component.h | 45 +- .../os/src/server/nic_router/configuration.cc | 48 +- .../os/src/server/nic_router/configuration.h | 33 +- repos/os/src/server/nic_router/dhcp_client.h | 10 +- repos/os/src/server/nic_router/dhcp_server.cc | 12 +- repos/os/src/server/nic_router/dhcp_server.h | 54 ++- repos/os/src/server/nic_router/direct_rule.h | 11 +- repos/os/src/server/nic_router/domain.cc | 23 +- repos/os/src/server/nic_router/domain.h | 8 +- repos/os/src/server/nic_router/forward_rule.h | 3 +- repos/os/src/server/nic_router/interface.cc | 449 +++++++++++++++--- repos/os/src/server/nic_router/interface.h | 99 +++- repos/os/src/server/nic_router/link.cc | 87 ++-- repos/os/src/server/nic_router/link.h | 64 +-- repos/os/src/server/nic_router/main.cc | 75 ++- repos/os/src/server/nic_router/nat_rule.h | 3 +- repos/os/src/server/nic_router/permit_rule.h | 18 +- repos/os/src/server/nic_router/report.cc | 9 +- repos/os/src/server/nic_router/report.h | 8 +- .../src/server/nic_router/transport_rule.cc | 34 +- .../os/src/server/nic_router/transport_rule.h | 22 +- repos/os/src/server/nic_router/uplink.cc | 3 +- repos/os/src/server/nic_router/uplink.h | 2 + 26 files changed, 890 insertions(+), 287 deletions(-) diff --git a/repos/os/src/server/nic_router/arp_waiter.cc b/repos/os/src/server/nic_router/arp_waiter.cc index 17a241a02a..d790ef55a5 100644 --- a/repos/os/src/server/nic_router/arp_waiter.cc +++ b/repos/os/src/server/nic_router/arp_waiter.cc @@ -29,12 +29,26 @@ Arp_waiter::Arp_waiter(Interface &src, _packet(packet) { _src.own_arp_waiters().insert(&_src_le); - _dst.foreign_arp_waiters().insert(&_dst_le); + _dst().foreign_arp_waiters().insert(&_dst_le); } Arp_waiter::~Arp_waiter() { _src.own_arp_waiters().remove(&_src_le); - _dst.foreign_arp_waiters().remove(&_dst_le); + _dst().foreign_arp_waiters().remove(&_dst_le); +} + + +void Arp_waiter::handle_config(Domain &dst) +{ + _dst().foreign_arp_waiters().remove(&_dst_le); + _dst = dst; + _dst().foreign_arp_waiters().insert(&_dst_le); +} + + +void Arp_waiter::print(Output &output) const +{ + Genode::print(output, "IP ", _ip, " DST ", _dst()); } diff --git a/repos/os/src/server/nic_router/arp_waiter.h b/repos/os/src/server/nic_router/arp_waiter.h index e93ea6661b..2a505d373b 100644 --- a/repos/os/src/server/nic_router/arp_waiter.h +++ b/repos/os/src/server/nic_router/arp_waiter.h @@ -14,6 +14,10 @@ #ifndef _ARP_WAITER_H_ #define _ARP_WAITER_H_ +/* local includes */ +#include +#include + /* Genode includes */ #include #include @@ -24,9 +28,10 @@ namespace Net { using Packet_descriptor = ::Nic::Packet_descriptor; class Interface; class Domain; + class Configuration; class Arp_waiter; using Arp_waiter_list_element = Genode::List_element; - using Arp_waiter_list = Genode::List; + using Arp_waiter_list = List; } @@ -37,7 +42,7 @@ class Net::Arp_waiter Arp_waiter_list_element _src_le; Interface &_src; Arp_waiter_list_element _dst_le; - Domain &_dst; + Reference _dst; Ipv4_address const _ip; Packet_descriptor const _packet; @@ -50,6 +55,15 @@ class Net::Arp_waiter ~Arp_waiter(); + void handle_config(Domain &dst); + + + /********* + ** Log ** + *********/ + + void print(Genode::Output &output) const; + /*************** ** Accessors ** @@ -58,6 +72,7 @@ class Net::Arp_waiter Interface &src() const { return _src; } Ipv4_address const &ip() const { return _ip; } Packet_descriptor const &packet() const { return _packet; } + Domain &dst() { return _dst(); } }; #endif /* _ARP_WAITER_H_ */ diff --git a/repos/os/src/server/nic_router/component.cc b/repos/os/src/server/nic_router/component.cc index adcf88cf09..f50af37154 100644 --- a/repos/os/src/server/nic_router/component.cc +++ b/repos/os/src/server/nic_router/component.cc @@ -59,7 +59,7 @@ Session_component_base(Allocator &guarded_alloc_backing, Net::Session_component_base:: Interface_policy::Interface_policy(Genode::Session_label const &label, Configuration const &config) -: label(label), config(config) { } +: _label(label), _config(config) { } Domain_name @@ -67,11 +67,11 @@ Net::Session_component_base::Interface_policy::determine_domain_name() const { Domain_name domain_name; try { - Session_policy policy(label, config.node()); + Session_policy policy(_label, _config().node()); domain_name = policy.attribute_value("domain", Domain_name()); } - catch (Session_policy::No_policy_defined) { if (config.verbose()) { log("No matching policy"); } } - catch (Xml_node::Nonexistent_attribute) { if (config.verbose()) { log("No domain attribute in policy"); } } + catch (Session_policy::No_policy_defined) { if (_config().verbose()) { log("No matching policy"); } } + catch (Xml_node::Nonexistent_attribute) { if (_config().verbose()) { log("No domain attribute in policy"); } } return domain_name; } @@ -91,12 +91,15 @@ Net::Session_component::Session_component(Allocator &alloc, Entrypoint &ep, Mac_address const &router_mac, Session_label const &label, + Interface_list &interfaces, Configuration &config) : Session_component_base(alloc, amount, buf_ram, tx_buf_size, rx_buf_size, config, label), - Session_rpc_object(region_map, _tx_buf, _rx_buf, &_range_alloc, ep.rpc_ep()), - Interface(ep, timer, router_mac, _guarded_alloc, mac, config, _intf_policy) + Session_rpc_object(region_map, _tx_buf, _rx_buf, &_range_alloc, + ep.rpc_ep()), + Interface(ep, timer, router_mac, _guarded_alloc, mac, config, interfaces, + _intf_policy) { _tx.sigh_ready_to_ack(_sink_ack); _tx.sigh_packet_avail(_sink_submit); @@ -115,11 +118,12 @@ Net::Root::Root(Entrypoint &ep, Mac_address const &router_mac, Configuration &config, Ram_session &buf_ram, + Interface_list &interfaces, Region_map ®ion_map) : Root_component(&ep.rpc_ep(), &alloc), _timer(timer), _ep(ep), _router_mac(router_mac), _config(config), _buf_ram(buf_ram), - _region_map(region_map) + _region_map(region_map), _interfaces(interfaces) { } @@ -153,7 +157,7 @@ Session_component *Net::Root::_create_session(char const *args) Session_component(*md_alloc(), _timer, ram_quota - session_size, _buf_ram, tx_buf_size, rx_buf_size, _region_map, _mac_alloc.alloc(), _ep, _router_mac, label, - _config); + _interfaces, _config()); } catch (Mac_allocator::Alloc_failed) { error("failed to allocate MAC address"); diff --git a/repos/os/src/server/nic_router/component.h b/repos/os/src/server/nic_router/component.h index 76c7bc9f7c..b5ed338697 100644 --- a/repos/os/src/server/nic_router/component.h +++ b/repos/os/src/server/nic_router/component.h @@ -23,6 +23,7 @@ /* local includes */ #include +#include namespace Net { @@ -54,18 +55,23 @@ class Net::Session_component_base struct Interface_policy : Net::Interface_policy { - Genode::Session_label const label; - Configuration const &config; + private: - Interface_policy(Genode::Session_label const &label, - Configuration const &config); + Genode::Session_label const _label; + Const_reference _config; + + public: + + Interface_policy(Genode::Session_label const &label, + Configuration const &config); - /*************************** - ** Net::Interface_policy ** - ***************************/ + /*************************** + ** Net::Interface_policy ** + ***************************/ - Domain_name determine_domain_name() const override; + Domain_name determine_domain_name() const override; + void handle_config(Configuration const &config) override { _config = config; } }; Genode::Allocator_guard _guarded_alloc; @@ -112,6 +118,7 @@ class Net::Session_component : private Session_component_base, Genode::Entrypoint &ep, Mac_address const &router_mac, Genode::Session_label const &label, + Interface_list &interfaces, Configuration &config); @@ -120,8 +127,8 @@ class Net::Session_component : private Session_component_base, ******************/ Mac_address mac_address() { return _mac; } - bool link_state() { return true; } - void link_state_sigh(Genode::Signal_context_capability) { } + bool link_state() { return Interface::link_state(); } + void link_state_sigh(Genode::Signal_context_capability sigh) { Interface::link_state_sigh(sigh); } }; @@ -129,13 +136,14 @@ class Net::Root : public Genode::Root_component { private: - Timer::Connection &_timer; - Mac_allocator _mac_alloc { }; - Genode::Entrypoint &_ep; - Mac_address const _router_mac; - Configuration &_config; - Genode::Ram_session &_buf_ram; - Genode::Region_map &_region_map; + Timer::Connection &_timer; + Mac_allocator _mac_alloc { }; + Genode::Entrypoint &_ep; + Mac_address const _router_mac; + Reference _config; + Genode::Ram_session &_buf_ram; + Genode::Region_map &_region_map; + Interface_list &_interfaces; /******************** @@ -152,7 +160,10 @@ class Net::Root : public Genode::Root_component Mac_address const &router_mac, Configuration &config, Genode::Ram_session &buf_ram, + Interface_list &interfaces, Genode::Region_map ®ion_map); + + void handle_config(Configuration &config) { _config = Reference(config); } }; #endif /* _COMPONENT_H_ */ diff --git a/repos/os/src/server/nic_router/configuration.cc b/repos/os/src/server/nic_router/configuration.cc index 2b3a9e06aa..b7005f707e 100644 --- a/repos/os/src/server/nic_router/configuration.cc +++ b/repos/os/src/server/nic_router/configuration.cc @@ -27,10 +27,19 @@ using namespace Genode; ** Configuration ** *******************/ +Configuration::Configuration(Xml_node const node, + Allocator &alloc) +: + _alloc(alloc), + _node(node) +{ } + + Configuration::Configuration(Env &env, Xml_node const node, Allocator &alloc, - Timer::Connection &timer) + Timer::Connection &timer, + Configuration &legacy) : _alloc(alloc), _verbose (node.attribute_value("verbose", false)), @@ -44,7 +53,6 @@ Configuration::Configuration(Env &env, _tcp_max_segm_lifetime(read_sec_attr(node, "tcp_max_segm_lifetime_sec", DEFAULT_TCP_MAX_SEGM_LIFETIME_SEC)), _node(node) { - /* read domains */ node.for_each_sub_node("domain", [&] (Xml_node const node) { try { _domains.insert(*new (_alloc) Domain(*this, node, _alloc)); } @@ -57,9 +65,37 @@ Configuration::Configuration(Env &env, domain.create_rules(_domains); }); - /* if configured, create a report generator */ try { - _report.set(*new (_alloc) Report(env, node.sub_node("report"), timer, - _domains)); - } catch (Genode::Xml_node::Nonexistent_sub_node) { } + /* check whether we shall create a report generator */ + Xml_node const report_node = node.sub_node("report"); + try { + /* try to re-use existing reporter */ + _reporter.set(legacy._reporter.deref()); + legacy._reporter.unset(); + } + catch (Pointer::Invalid) { + + /* there is no reporter by now, create a new one */ + _reporter.set(*new (_alloc) Reporter(env, "state")); + } + /* create report generator */ + _report.set(*new (_alloc) + Report(report_node, timer, _domains, _reporter.deref())); + } + catch (Genode::Xml_node::Nonexistent_sub_node) { } +} + + +Configuration::~Configuration() +{ + /* destroy reporter */ + try { destroy(_alloc, &_reporter.deref()); } + catch (Pointer::Invalid) { } + + /* destroy report generator */ + try { destroy(_alloc, &_report.deref()); } + catch (Pointer::Invalid) { } + + /* destroy domains */ + _domains.destroy_each(_alloc); } diff --git a/repos/os/src/server/nic_router/configuration.h b/repos/os/src/server/nic_router/configuration.h index 035915c89f..89987b7f24 100644 --- a/repos/os/src/server/nic_router/configuration.h +++ b/repos/os/src/server/nic_router/configuration.h @@ -31,18 +31,18 @@ class Net::Configuration private: Genode::Allocator &_alloc; - bool const _verbose; - bool const _verbose_packets; - bool const _verbose_domain_state; - Genode::Microseconds const _dhcp_discover_timeout; - Genode::Microseconds const _dhcp_request_timeout; - Genode::Microseconds const _dhcp_offer_timeout; - Genode::Microseconds const _udp_idle_timeout; - Genode::Microseconds const _tcp_idle_timeout; - Genode::Microseconds const _tcp_max_segm_lifetime; - Pointer _report { }; - Domain_tree _domains { }; - List _detached_interfaces { }; + bool const _verbose { false }; + bool const _verbose_packets { false }; + bool const _verbose_domain_state { false }; + Genode::Microseconds const _dhcp_discover_timeout { DEFAULT_DHCP_DISCOVER_TIMEOUT_SEC }; + Genode::Microseconds const _dhcp_request_timeout { DEFAULT_DHCP_REQUEST_TIMEOUT_SEC }; + Genode::Microseconds const _dhcp_offer_timeout { DEFAULT_DHCP_OFFER_TIMEOUT_SEC }; + Genode::Microseconds const _udp_idle_timeout { DEFAULT_UDP_IDLE_TIMEOUT_SEC }; + Genode::Microseconds const _tcp_idle_timeout { DEFAULT_TCP_IDLE_TIMEOUT_SEC }; + Genode::Microseconds const _tcp_max_segm_lifetime { DEFAULT_TCP_MAX_SEGM_LIFETIME_SEC }; + Pointer _report { }; + Pointer _reporter { }; + Domain_tree _domains { }; Genode::Xml_node const _node; public: @@ -55,10 +55,16 @@ class Net::Configuration enum { DEFAULT_TCP_IDLE_TIMEOUT_SEC = 600 }; enum { DEFAULT_TCP_MAX_SEGM_LIFETIME_SEC = 30 }; + Configuration(Genode::Xml_node const node, + Genode::Allocator &alloc); + Configuration(Genode::Env &env, Genode::Xml_node const node, Genode::Allocator &alloc, - Timer::Connection &timer); + Timer::Connection &timer, + Configuration &legacy); + + ~Configuration(); /*************** @@ -75,7 +81,6 @@ class Net::Configuration Genode::Microseconds tcp_idle_timeout() const { return _tcp_idle_timeout; } Genode::Microseconds tcp_max_segm_lifetime() const { return _tcp_max_segm_lifetime; } Domain_tree &domains() { return _domains; } - List &detached_interfaces() { return _detached_interfaces; } Report &report() { return _report.deref(); } Genode::Xml_node node() const { return _node; } }; diff --git a/repos/os/src/server/nic_router/dhcp_client.h b/repos/os/src/server/nic_router/dhcp_client.h index f6101bfa77..956b70f81f 100644 --- a/repos/os/src/server/nic_router/dhcp_client.h +++ b/repos/os/src/server/nic_router/dhcp_client.h @@ -36,11 +36,11 @@ class Net::Dhcp_client INIT = 0, SELECT = 1, REQUEST = 2, BOUND = 3, RENEW = 4, REBIND = 5 }; - Genode::Allocator &_alloc; - Interface &_interface; - State _state { State::INIT }; - Timer::One_shot_timeout _timeout; - unsigned long _lease_time_sec = 0; + Genode::Allocator &_alloc; + Interface &_interface; + State _state { State::INIT }; + Timer::One_shot_timeout _timeout; + unsigned long _lease_time_sec = 0; void _handle_dhcp_reply(Dhcp_packet &dhcp); diff --git a/repos/os/src/server/nic_router/dhcp_server.cc b/repos/os/src/server/nic_router/dhcp_server.cc index 757cb92bb3..ed1bc1a169 100644 --- a/repos/os/src/server/nic_router/dhcp_server.cc +++ b/repos/os/src/server/nic_router/dhcp_server.cc @@ -80,6 +80,14 @@ Ipv4_address Dhcp_server::alloc_ip() } +void Dhcp_server::alloc_ip(Ipv4_address const &ip) +{ + try { _ip_alloc.alloc_addr(ip.to_uint32_little_endian() - _ip_first_raw); } + catch (Bit_allocator_dynamic::Range_conflict) { throw Alloc_ip_failed(); } + catch (Bit_array_dynamic::Invalid_index_access) { throw Alloc_ip_failed(); } +} + + void Dhcp_server::free_ip(Ipv4_address const &ip) { _ip_alloc.free(ip.to_uint32_little_endian() - _ip_first_raw); @@ -147,8 +155,8 @@ void Dhcp_allocation::_handle_timeout(Duration) Dhcp_allocation & Dhcp_allocation_tree::find_by_mac(Mac_address const &mac) const { - if (!first()) { + if (!_tree.first()) { throw No_match(); } - return first()->find_by_mac(mac); + return _tree.first()->find_by_mac(mac); } diff --git a/repos/os/src/server/nic_router/dhcp_server.h b/repos/os/src/server/nic_router/dhcp_server.h index 387a103c90..08835da314 100644 --- a/repos/os/src/server/nic_router/dhcp_server.h +++ b/repos/os/src/server/nic_router/dhcp_server.h @@ -17,6 +17,7 @@ /* local includes */ #include #include +#include /* Genode includes */ #include @@ -30,7 +31,7 @@ namespace Net { class Dhcp_allocation; class Dhcp_allocation; class Dhcp_allocation_tree; - using Dhcp_allocation_list = Genode::List; + using Dhcp_allocation_list = List; /* forward declarations */ class Interface; @@ -64,6 +65,8 @@ class Net::Dhcp_server : private Genode::Noncopyable Ipv4_address alloc_ip(); + void alloc_ip(Ipv4_address const &ip); + void free_ip(Ipv4_address const &ip); @@ -83,14 +86,6 @@ class Net::Dhcp_server : private Genode::Noncopyable }; -struct Net::Dhcp_allocation_tree : public Genode::Avl_tree -{ - struct No_match : Genode::Exception { }; - - Dhcp_allocation &find_by_mac(Mac_address const &mac) const; -}; - - class Net::Dhcp_allocation : public Genode::Avl_node, public Dhcp_allocation_list::Element { @@ -143,4 +138,45 @@ class Net::Dhcp_allocation : public Genode::Avl_node, void set_bound() { _bound = true; } }; + +struct Net::Dhcp_allocation_tree +{ + private: + + Genode::Avl_tree _tree { }; + Dhcp_allocation_list _list { }; + + public: + + struct No_match : Genode::Exception { }; + + Dhcp_allocation &find_by_mac(Mac_address const &mac) const; + + void insert(Dhcp_allocation &dhcp_alloc) + { + _tree.insert(&dhcp_alloc); + _list.insert(&dhcp_alloc); + } + + void remove(Dhcp_allocation &dhcp_alloc) + { + _tree.remove(&dhcp_alloc); + _list.remove(&dhcp_alloc); + } + + Dhcp_allocation *first() { return _tree.first(); } + + template + void for_each(FUNC && functor) + { + using List_item = Dhcp_allocation_list::Element; + for (Dhcp_allocation *item = _list.first(); item; ) + { + Dhcp_allocation *const next_item = item->List_item::next(); + functor(*item); + item = next_item; + } + } +}; + #endif /* _DHCP_SERVER_H_ */ diff --git a/repos/os/src/server/nic_router/direct_rule.h b/repos/os/src/server/nic_router/direct_rule.h index 44e2439a94..8eaf20478f 100644 --- a/repos/os/src/server/nic_router/direct_rule.h +++ b/repos/os/src/server/nic_router/direct_rule.h @@ -17,6 +17,7 @@ /* local includes */ #include #include +#include /* Genode includes */ #include @@ -67,16 +68,16 @@ struct Net::Direct_rule : Direct_rule_base, template -struct Net::Direct_rule_list : Genode::List +struct Net::Direct_rule_list : List { - using List = Genode::List; + using Base = List; struct No_match : Genode::Exception { }; T const &longest_prefix_match(Ipv4_address const &ip) const { /* first match is sufficient as the list is prefix-size-sorted */ - for (T const *curr = List::first(); curr; curr = curr->next()) { + for (T const *curr = Base::first(); curr; curr = curr->next()) { if (curr->dst().prefix_matches(ip)) { return *curr; } } @@ -87,13 +88,13 @@ struct Net::Direct_rule_list : Genode::List { /* ensure that the list stays prefix-size-sorted (descending) */ T *behind = nullptr; - for (T *curr = List::first(); curr; curr = curr->next()) { + for (T *curr = Base::first(); curr; curr = curr->next()) { if (rule.dst().prefix >= curr->dst().prefix) { break; } behind = curr; } - List::insert(&rule, behind); + Base::insert(&rule, behind); } }; diff --git a/repos/os/src/server/nic_router/domain.cc b/repos/os/src/server/nic_router/domain.cc index c2cd211216..1f0c19b1d6 100644 --- a/repos/os/src/server/nic_router/domain.cc +++ b/repos/os/src/server/nic_router/domain.cc @@ -140,6 +140,9 @@ Link_side_tree &Domain::links(L3_protocol const protocol) void Domain::_ip_config_changed() { + _interfaces.for_each([&] (Interface &interface) { + interface.detach_from_ip_config(); + }); if (!ip_config().valid) { if (_config.verbose_domain_state()) { @@ -168,15 +171,29 @@ void Domain::_ip_config_changed() Domain::~Domain() +{ + /* destroy rules */ + _ip_rules.destroy_each(_alloc); + _nat_rules.destroy_each(_alloc); + _udp_rules.destroy_each(_alloc); + _tcp_rules.destroy_each(_alloc); + _udp_forward_rules.destroy_each(_alloc); + _tcp_forward_rules.destroy_each(_alloc); + + /* destroy DHCP server and IP config */ + try { destroy(_alloc, &_dhcp_server.deref()); } + catch (Pointer::Invalid) { } + _ip_config.destruct(); +} + + +void Domain::__FIXME__dissolve_foreign_arp_waiters() { /* let other interfaces destroy their ARP waiters that wait for us */ while (_foreign_arp_waiters.first()) { Arp_waiter &waiter = *_foreign_arp_waiters.first()->object(); waiter.src().cancel_arp_waiting(waiter); } - /* destroy DHCP server */ - try { destroy(_alloc, &_dhcp_server.deref()); } - catch (Pointer::Invalid) { } } diff --git a/repos/os/src/server/nic_router/domain.h b/repos/os/src/server/nic_router/domain.h index 7f645f42de..8464a9620b 100644 --- a/repos/os/src/server/nic_router/domain.h +++ b/repos/os/src/server/nic_router/domain.h @@ -94,7 +94,7 @@ class Net::Domain : public Domain_base Port_allocator _tcp_port_alloc { }; Port_allocator _udp_port_alloc { }; Nat_rule_tree _nat_rules { }; - List _interfaces { }; + Interface_list _interfaces { }; unsigned long _interface_cnt { 0 }; Pointer _dhcp_server { }; Genode::Reconstructible _ip_config; @@ -120,6 +120,8 @@ class Net::Domain : public Domain_base void _ip_config_changed(); + void __FIXME__dissolve_foreign_arp_waiters(); + public: struct Invalid : Genode::Exception { }; @@ -167,14 +169,14 @@ class Net::Domain : public Domain_base bool verbose_packets() const { return _verbose_packets; } Ipv4_config const &ip_config() const { return *_ip_config; } - Domain_name const &name() { return _name; } + Domain_name const &name() const { return _name; } Ip_rule_list &ip_rules() { return _ip_rules; } Forward_rule_tree &tcp_forward_rules() { return _tcp_forward_rules; } Forward_rule_tree &udp_forward_rules() { return _udp_forward_rules; } Transport_rule_list &tcp_rules() { return _tcp_rules; } Transport_rule_list &udp_rules() { return _udp_rules; } Nat_rule_tree &nat_rules() { return _nat_rules; } - List &interfaces() { return _interfaces; } + Interface_list &interfaces() { return _interfaces; } Configuration &config() const { return _config; } Domain_avl_member &avl_member() { return _avl_member; } Dhcp_server &dhcp_server() { return _dhcp_server.deref(); } diff --git a/repos/os/src/server/nic_router/forward_rule.h b/repos/os/src/server/nic_router/forward_rule.h index 2f63765bdc..43a72a39ac 100644 --- a/repos/os/src/server/nic_router/forward_rule.h +++ b/repos/os/src/server/nic_router/forward_rule.h @@ -16,6 +16,7 @@ /* local includes */ #include +#include /* Genode includes */ #include @@ -69,7 +70,7 @@ class Net::Forward_rule : public Leaf_rule, }; -struct Net::Forward_rule_tree : Genode::Avl_tree +struct Net::Forward_rule_tree : Avl_tree { struct No_match : Genode::Exception { }; diff --git a/repos/os/src/server/nic_router/interface.cc b/repos/os/src/server/nic_router/interface.cc index d4c7749021..2d32f66596 100644 --- a/repos/os/src/server/nic_router/interface.cc +++ b/repos/os/src/server/nic_router/interface.cc @@ -29,6 +29,8 @@ using Genode::uint32_t; using Genode::log; using Genode::error; using Genode::warning; +using Genode::Signal_context_capability; +using Genode::Signal_transmitter; /*************** @@ -46,6 +48,17 @@ static void _destroy_dissolved_links(Link_list &dissolved_links, } +template +static void _destroy_link(Link &link, + Link_list &links, + Deallocator &dealloc) +{ + link.dissolve(); + links.remove(&link); + destroy(dealloc, static_cast(&link)); +} + + template static void _destroy_links(Link_list &links, Link_list &dissolved_links, @@ -53,10 +66,7 @@ static void _destroy_links(Link_list &links, { _destroy_dissolved_links(dissolved_links, dealloc); while (Link *link = links.first()) { - link->dissolve(); - links.remove(link); - destroy(dealloc, static_cast(link)); - } + _destroy_link(*link, links, dealloc); } } @@ -153,6 +163,16 @@ static void *_prot_base(L3_protocol const prot, ** Interface ** ***************/ +void Interface::_destroy_link(Link &link) +{ + L3_protocol const prot = link.protocol(); + switch (prot) { + case L3_protocol::TCP: ::_destroy_link(link, links(prot), _alloc); break; + case L3_protocol::UDP: ::_destroy_link(link, links(prot), _alloc); break; + default: throw Bad_transport_protocol(); } +} + + void Interface::_pass_prot(Ethernet_frame ð, size_t const eth_size, Ipv4_packet &ip, @@ -194,38 +214,127 @@ Interface::_transport_rules(Domain &local_domain, L3_protocol const prot) const } -void Interface::attach_to_domain(Domain &domain) +void Interface::_attach_to_domain_raw(Domain_name const &domain_name) { - _domain_ptr.set(domain); + Domain &domain = _config().domains().find_by_name(domain_name); + _domain = domain; + Signal_transmitter(_link_state_sigh).submit(); + _interfaces.remove(this); domain.attach_interface(*this); - if (!domain.ip_config().valid) { +} + + +void Interface::_detach_from_domain_raw() +{ + Domain &domain = _domain.deref(); + domain.detach_interface(*this); + _interfaces.insert(this); + _domain = Pointer(); + Signal_transmitter(_link_state_sigh).submit(); +} + + +void Interface::_attach_to_domain(Domain_name const &domain_name, + bool apply_foreign_arp) +{ + _attach_to_domain_raw(domain_name); + + /* ensure that DHCP requests are sent on each interface of the domain */ + if (!domain().ip_config().valid) { _dhcp_client.discover(); } + /* ensure that DHCP requests are sent on each interface of the domain */ + if ( apply_foreign_arp) { _apply_foreign_arp(); } + else { _apply_foreign_arp_pending = true; } +} + + +void Interface::_apply_foreign_arp() +{ + /* sent ARP request for each foreign ARP waiter */ + Domain &domain_ = domain(); + domain_.foreign_arp_waiters().for_each([&] (Arp_waiter_list_element &le) { + _broadcast_arp_request(domain_.ip_config().interface.address, + le.object()->ip()); + }); +} + + +bool Interface::link_state() +{ + try { + _domain.deref(); + return true; + } + catch (Pointer::Invalid) { } + return false; +} + + +void Interface::link_state_sigh(Signal_context_capability sigh) +{ + _link_state_sigh = sigh; +} + + +void Interface::handle_config_aftermath() +{ + /* ensure that ARP requests are sent on each interface of the domain */ + if (_apply_foreign_arp_pending) { + _apply_foreign_arp(); + _apply_foreign_arp_pending = false; + } } -struct Detach_from_domain_not_implemented : Genode::Exception { }; -void Interface::detach_from_domain() + +void Interface::detach_from_ip_config() { - _domain_ptr.unset(); - throw Detach_from_domain_not_implemented(); + /* destroy our own ARP waiters */ + Domain &domain = _domain.deref(); + while (_own_arp_waiters.first()) { + cancel_arp_waiting(*_own_arp_waiters.first()->object()); + } + /* destroy links */ + _destroy_links(_tcp_links, _dissolved_tcp_links, _alloc); + _destroy_links(_udp_links, _dissolved_udp_links, _alloc); + + /* destroy DHCP allocations */ + _destroy_released_dhcp_allocations(domain); + while (Dhcp_allocation *allocation = _dhcp_allocations.first()) { + _dhcp_allocations.remove(*allocation); + _destroy_dhcp_allocation(*allocation, domain); + } + /* dissolve ARP cache entries with the MAC address of this interface */ + domain.arp_cache().destroy_entries_with_mac(_mac); +} + + +void Interface::_detach_from_domain() +{ + try { + detach_from_ip_config(); + _detach_from_domain_raw(); + _apply_foreign_arp_pending = false; + } + catch (Pointer::Invalid) { } } void -Interface::_new_link(L3_protocol const protocol, - Link_side_id const &local, - Pointer const remote_port_alloc, - Domain &remote_domain, - Link_side_id const &remote) +Interface::_new_link(L3_protocol const protocol, + Link_side_id const &local, + Pointer remote_port_alloc, + Domain &remote_domain, + Link_side_id const &remote) { switch (protocol) { case L3_protocol::TCP: new (_alloc) Tcp_link(*this, local, remote_port_alloc, remote_domain, - remote, _timer, _config, protocol); + remote, _timer, _config(), protocol); break; case L3_protocol::UDP: new (_alloc) Udp_link(*this, local, remote_port_alloc, remote_domain, - remote, _timer, _config, protocol); + remote, _timer, _config(), protocol); break; default: throw Bad_transport_protocol(); } } @@ -233,7 +342,7 @@ Interface::_new_link(L3_protocol const protocol, void Interface::dhcp_allocation_expired(Dhcp_allocation &allocation) { - _release_dhcp_allocation(allocation, _domain_ptr.deref()); + _release_dhcp_allocation(allocation, _domain.deref()); _released_dhcp_allocations.insert(&allocation); } @@ -292,7 +401,7 @@ void Interface::_nat_link_and_pass(Ethernet_frame ð, Pointer remote_port_alloc; try { Nat_rule &nat = remote_domain.nat_rules().find_by_domain(local_domain); - if(_config.verbose()) { + if(_config().verbose()) { log("Using NAT rule: ", nat); } _src_port(prot, prot_base, nat.port_alloc(prot).alloc()); @@ -394,11 +503,11 @@ void Interface::_send_dhcp_reply(Dhcp_server const &dhcp_srv, void Interface::_release_dhcp_allocation(Dhcp_allocation &allocation, Domain &local_domain) { - if (_config.verbose()) { + if (_config().verbose()) { log("Release DHCP allocation: ", allocation, " at ", local_domain); } - _dhcp_allocations.remove(&allocation); + _dhcp_allocations.remove(allocation); } @@ -410,10 +519,10 @@ void Interface::_new_dhcp_allocation(Ethernet_frame ð, Dhcp_allocation &allocation = *new (_alloc) Dhcp_allocation(*this, dhcp_srv.alloc_ip(), dhcp.client_mac(), _timer, - _config.dhcp_offer_timeout()); + _config().dhcp_offer_timeout()); - _dhcp_allocations.insert(&allocation); - if (_config.verbose()) { + _dhcp_allocations.insert(allocation); + if (_config().verbose()) { log("Offer DHCP allocation: ", allocation, " at ", local_domain); } @@ -457,7 +566,7 @@ void Interface::_handle_dhcp_request(Ethernet_frame ð, size_t , return; } else { - allocation.lifetime(_config.dhcp_offer_timeout()); + allocation.lifetime(_config().dhcp_offer_timeout()); _send_dhcp_reply(dhcp_srv, eth.src(), allocation.ip(), Dhcp_packet::Message_type::OFFER, @@ -482,7 +591,7 @@ void Interface::_handle_dhcp_request(Ethernet_frame ð, size_t , { allocation.set_bound(); allocation.lifetime(dhcp_srv.ip_lease_time()); - if (_config.verbose()) { + if (_config().verbose()) { log("Bind DHCP allocation: ", allocation, " at ", local_domain); } @@ -618,7 +727,7 @@ void Interface::_handle_ip(Ethernet_frame ð, bool const client = local_side.is_client(); Link_side &remote_side = client ? link.server() : link.client(); Domain &remote_domain = remote_side.domain(); - if (_config.verbose()) { + if (_config().verbose()) { log("Using ", l3_protocol_name(prot), " link: ", link); } _adapt_eth(eth, remote_side.src_ip(), pkt, remote_domain); @@ -641,7 +750,7 @@ void Interface::_handle_ip(Ethernet_frame ð, Forward_rule const &rule = _forward_rules(local_domain, prot).find_by_port(local_id.dst_port); - if(_config.verbose()) { + if(_config().verbose()) { log("Using forward rule: ", l3_protocol_name(prot), " ", rule); } Domain &remote_domain = rule.domain(); @@ -661,7 +770,7 @@ void Interface::_handle_ip(Ethernet_frame ð, Permit_rule const &permit_rule = transport_rule.permit_rule(local_id.dst_port); - if(_config.verbose()) { + if(_config().verbose()) { log("Using ", l3_protocol_name(prot), " rule: ", transport_rule, " ", permit_rule); } @@ -681,7 +790,7 @@ void Interface::_handle_ip(Ethernet_frame ð, Ip_rule const &rule = local_domain.ip_rules().longest_prefix_match(ip.dst()); - if(_config.verbose()) { + if(_config().verbose()) { log("Using IP rule: ", rule); } Domain &remote_domain = rule.domain(); @@ -695,7 +804,7 @@ void Interface::_handle_ip(Ethernet_frame ð, catch (Ip_rule_list::No_match) { } /* give up and drop packet */ - if (_config.verbose()) { + if (_config().verbose()) { log("Unroutable packet"); } } @@ -742,7 +851,7 @@ void Interface::_handle_arp_reply(Ethernet_frame ð, try { /* check wether a matching ARP cache entry already exists */ local_domain.arp_cache().find_by_ip(arp.src_ip()); - if (_config.verbose()) { + if (_config().verbose()) { log("ARP entry already exists"); } } catch (Arp_cache::No_match) { @@ -905,7 +1014,7 @@ void Interface::_handle_eth(void *const eth_base, Packet_descriptor const &pkt) { try { - Domain &local_domain = _domain_ptr.deref(); + Domain &local_domain = _domain.deref(); try { local_domain.raise_rx_bytes(eth_size); @@ -938,12 +1047,12 @@ void Interface::_handle_eth(void *const eth_base, catch (Udp_packet::Bad_data_type) { warning("malformed UDP packet" ); } catch (Bad_network_protocol) { - if (_config.verbose()) { + if (_config().verbose()) { log("unknown network layer protocol"); } } catch (Drop_packet_inform exception) { - if (_config.verbose()) { + if (_config().verbose()) { log("(", local_domain, ") Drop packet: ", exception.msg); } } @@ -966,7 +1075,7 @@ void Interface::_handle_eth(void *const eth_base, error("failed to allocate IP for DHCP client"); } } catch (Pointer::Invalid) { - if (_config.verbose()) { + if (_config().verbose()) { log("(?) Drop packet: no domain"); } } @@ -994,7 +1103,7 @@ void Interface::_send_submit_pkt(Packet_descriptor &pkt, void * &pkt_base, size_t pkt_size) { - Domain &local_domain = _domain_ptr.deref(); + Domain &local_domain = _domain.deref(); _source().submit_packet(pkt); local_domain.raise_tx_bytes(pkt_size); if (local_domain.verbose_packets()) { @@ -1010,23 +1119,240 @@ Interface::Interface(Genode::Entrypoint &ep, Genode::Allocator &alloc, Mac_address const mac, Configuration &config, - Interface_policy const &policy) + Interface_list &interfaces, + Interface_policy &policy) : _sink_ack(ep, *this, &Interface::_ack_avail), _sink_submit(ep, *this, &Interface::_ready_to_submit), _source_ack(ep, *this, &Interface::_ready_to_ack), _source_submit(ep, *this, &Interface::_packet_avail), _router_mac(router_mac), _mac(mac), _config(config), - _policy(policy), _timer(timer), _alloc(alloc) + _policy(policy), _timer(timer), _alloc(alloc), + _interfaces(interfaces) { - /* try to find matching domain and attach interface to it */ - Domain_name const domain_name = _policy.determine_domain_name(); - try { attach_to_domain(config.domains().find_by_name(domain_name)); } - catch (Domain_tree::No_match) { - if (config.verbose()) { - log("No matching domain"); + _interfaces.insert(this); + try { _attach_to_domain(_policy.determine_domain_name(), true); } + catch (Domain_tree::No_match) { } +} + + +void Interface::_dismiss_link_log(Link &link, + char const *reason) +{ + if (!_config().verbose()) { + return; + } + log("[", link.client().domain(), "] dismiss link client: ", link.client(), " (", reason, ")"); + log("[", link.server().domain(), "] dismiss link server: ", link.server(), " (", reason, ")"); +} + + +void Interface::_update_link_check_nat(Link &link, + Domain &new_srv_dom, + L3_protocol prot, + Domain &cln_dom) +{ + /* if server domain or its IP config changed, dismiss link */ + Domain &old_srv_dom = link.server().domain(); + if (old_srv_dom.name() != new_srv_dom.name() || + old_srv_dom.ip_config() != new_srv_dom.ip_config()) + { + _dismiss_link_log(link, "rule targets other domain"); + throw Dismiss_link(); + } + Pointer remote_port_alloc_ptr; + if (link.client().src_ip() == link.server().dst_ip()) { + link.handle_config(cln_dom, new_srv_dom, remote_port_alloc_ptr, _config()); + return; + } + try { + if (link.server().dst_ip() != new_srv_dom.ip_config().interface.address) { + _dismiss_link_log(link, "NAT IP"); + throw Dismiss_link(); } - _config.detached_interfaces().insert(this); + Nat_rule &nat = new_srv_dom.nat_rules().find_by_domain(cln_dom); + Port_allocator_guard &remote_port_alloc = nat.port_alloc(prot); + remote_port_alloc.alloc(link.server().dst_port()); + remote_port_alloc_ptr.set(remote_port_alloc); + link.handle_config(cln_dom, new_srv_dom, remote_port_alloc_ptr, _config()); + return; + } + catch (Nat_rule_tree::No_match) { _dismiss_link_log(link, "no NAT rule"); } + catch (Port_allocator::Allocation_conflict) { _dismiss_link_log(link, "no NAT-port"); } + catch (Port_allocator_guard::Out_of_indices) { _dismiss_link_log(link, "no NAT-port quota"); } + throw Dismiss_link(); +} + + +void Interface::_update_links(L3_protocol prot, + Domain &cln_dom) +{ + links(prot).for_each([&] (Link &link) { + try { + /* try to find forward rule that matches the server port */ + Forward_rule const &rule = + _forward_rules(cln_dom, prot). + find_by_port(link.client().dst_port()); + + /* if destination IP of forwarding changed, dismiss link */ + if (rule.to() != link.server().src_ip()) { + _dismiss_link_log(link, "other forward-rule to"); + throw Dismiss_link(); + } + _update_link_check_nat(link, rule.domain(), prot, cln_dom); + return; + } + catch (Forward_rule_tree::No_match) { + try { + /* try to find transport rule that matches the server IP */ + Transport_rule const &transport_rule = + _transport_rules(cln_dom, prot). + longest_prefix_match(link.client().dst_ip()); + + /* try to find permit rule that matches the server port */ + Permit_rule const &permit_rule = + transport_rule.permit_rule(link.client().dst_port()); + + _update_link_check_nat(link, permit_rule.domain(), prot, cln_dom); + return; + } + catch (Transport_rule_list::No_match) { _dismiss_link_log(link, "no transport/forward rule"); } + catch (Permit_single_rule_tree::No_match) { _dismiss_link_log(link, "no permit rule"); } + catch (Dismiss_link) { } + } + catch (Dismiss_link) { } + _destroy_link(link); + }); +} + + +void Interface::_update_dhcp_allocations(Domain &old_domain, + Domain &new_domain) +{ + try { + Dhcp_server &old_dhcp_srv = old_domain.dhcp_server(); + Dhcp_server &new_dhcp_srv = new_domain.dhcp_server(); + if (old_dhcp_srv.dns_server() != new_dhcp_srv.dns_server()) { + throw Pointer::Invalid(); + } + if (old_dhcp_srv.ip_lease_time().value != + new_dhcp_srv.ip_lease_time().value) + { + throw Pointer::Invalid(); + } + _dhcp_allocations.for_each([&] (Dhcp_allocation &allocation) { + try { + new_dhcp_srv.alloc_ip(allocation.ip()); + + /* keep DHCP allocation */ + if (_config().verbose()) { + log("[", new_domain, "] update DHCP allocation: ", + allocation); + } + return; + } + catch (Dhcp_server::Alloc_ip_failed) { + if (_config().verbose()) { + log("[", new_domain, "] dismiss DHCP allocation: ", + allocation, " (no IP)"); + } + } + /* dismiss DHCP allocation */ + _dhcp_allocations.remove(allocation); + _destroy_dhcp_allocation(allocation, old_domain); + }); + } + catch (Pointer::Invalid) { + + /* dismiss all DHCP allocations */ + while (Dhcp_allocation *allocation = _dhcp_allocations.first()) { + if (_config().verbose()) { + log("[", new_domain, "] dismiss DHCP allocation: ", + *allocation, " (other/no DHCP server)"); + } + _dhcp_allocations.remove(*allocation); + _destroy_dhcp_allocation(*allocation, old_domain); + } + } +} + + +void Interface::_update_own_arp_waiters(Domain &domain) +{ + _own_arp_waiters.for_each([&] (Arp_waiter_list_element &le) { + Arp_waiter &arp_waiter = *le.object(); + try { + Domain &dst = _config().domains().find_by_name(arp_waiter.dst().name()); + if (dst.ip_config() != arp_waiter.dst().ip_config()) { + throw Dismiss_arp_waiter(); } + + /* keep ARP waiter */ + arp_waiter.handle_config(dst); + if (_config().verbose()) { + log("[", domain, "] update ARP waiter: ", arp_waiter); + } + return; + } + catch (Domain_tree::No_match) { } + catch (Dismiss_arp_waiter) { } + + /* dismiss ARP waiter */ + if (_config().verbose()) { + log("[", domain, "] dismiss ARP waiter: ", arp_waiter, " (no domain)"); + } + cancel_arp_waiting(*_own_arp_waiters.first()->object()); + }); +} + + +void Interface::handle_config(Configuration &config) +{ + /* deteremine the name of the new domain */ + _config = config; + _policy.handle_config(config); + Domain_name const &new_domain_name = _policy.determine_domain_name(); + try { + /* destroy state objects that are not needed anymore */ + Domain &old_domain = domain(); + _destroy_dissolved_links(_dissolved_udp_links, _alloc); + _destroy_dissolved_links(_dissolved_tcp_links, _alloc); + _destroy_released_dhcp_allocations(old_domain); + + /* if the domains differ, detach completely from the domain */ + if (old_domain.name() != new_domain_name) { + _detach_from_domain(); + _attach_to_domain(new_domain_name, false); + return; + } + /* move to new domain object without considering any state objects */ + _detach_from_domain_raw(); + _attach_to_domain_raw(new_domain_name); + Domain &new_domain = domain(); + + /* if the IP configs differ, detach completely from the IP config */ + if (old_domain.ip_config() != new_domain.ip_config()) { + detach_from_ip_config(); + return; + } + /* if there was/is no IP config, there is nothing more to update */ + if (!new_domain.ip_config().valid) { + return; + } + /* update state objects */ + _update_links(L3_protocol::TCP, new_domain); + _update_links(L3_protocol::UDP, new_domain); + _update_dhcp_allocations(old_domain, new_domain); + _update_own_arp_waiters(new_domain); + } + catch (Domain_tree::No_match) { + + /* the interface no longer has a domain */ + _detach_from_domain(); + } + catch (Pointer::Invalid) { + + /* the interface had no domain but now it gets one */ + _attach_to_domain(new_domain_name, false); } } @@ -1051,31 +1377,6 @@ void Interface::cancel_arp_waiting(Arp_waiter &waiter) Interface::~Interface() { - try { - /* try to detach from domain */ - Domain &local_domain = _domain_ptr.deref(); - local_domain.detach_interface(*this); - - /* destroy our own ARP waiters */ - while (_own_arp_waiters.first()) { - cancel_arp_waiting(*_own_arp_waiters.first()->object()); - } - /* destroy links */ - _destroy_links(_tcp_links, _dissolved_tcp_links, _alloc); - _destroy_links(_udp_links, _dissolved_udp_links, _alloc); - - /* destroy DHCP allocations */ - _destroy_released_dhcp_allocations(local_domain); - while (Dhcp_allocation *allocation = _dhcp_allocations.first()) { - _dhcp_allocations.remove(allocation); - _destroy_dhcp_allocation(*allocation, local_domain); - } - /* dissolve ARP cache entries with the MAC address of this interface */ - local_domain.arp_cache().destroy_entries_with_mac(_mac); - } - catch (Pointer::Invalid) { - - /* if not attached to a domain the interface is in a global list */ - _config.detached_interfaces().remove(this); - } + _detach_from_domain(); + _interfaces.remove(this); } diff --git a/repos/os/src/server/nic_router/interface.h b/repos/os/src/server/nic_router/interface.h index 3c82ea22d4..df79ce1a00 100644 --- a/repos/os/src/server/nic_router/interface.h +++ b/repos/os/src/server/nic_router/interface.h @@ -32,6 +32,7 @@ namespace Net { using Packet_stream_sink = ::Nic::Packet_stream_sink< ::Nic::Session::Policy>; using Packet_stream_source = ::Nic::Packet_stream_source< ::Nic::Session::Policy>; using Domain_name = Genode::String<160>; + class Leaf_rule; class Ipv4_config; class Forward_rule_tree; class Transport_rule_list; @@ -39,6 +40,7 @@ namespace Net { class Arp_packet; class Interface_policy; class Interface; + using Interface_list = List; class Dhcp_server; class Configuration; class Domain; @@ -49,16 +51,18 @@ struct Net::Interface_policy { virtual Domain_name determine_domain_name() const = 0; + virtual void handle_config(Configuration const &config) = 0; + virtual ~Interface_policy() { } }; -class Net::Interface : private Genode::List::Element +class Net::Interface : private Interface_list::Element { - protected: + friend class List; + friend class Genode::List; - friend class Genode::List; - friend class Net::List; + protected: using Signal_handler = Genode::Signal_handler; @@ -71,25 +75,31 @@ class Net::Interface : private Genode::List::Element private: - Configuration &_config; - Interface_policy const &_policy; - Timer::Connection &_timer; - Genode::Allocator &_alloc; - Pointer _domain_ptr { }; - Arp_waiter_list _own_arp_waiters { }; - Link_list _tcp_links { }; - Link_list _udp_links { }; - Link_list _dissolved_tcp_links { }; - Link_list _dissolved_udp_links { }; - Dhcp_allocation_tree _dhcp_allocations { }; - Dhcp_allocation_list _released_dhcp_allocations { }; - Dhcp_client _dhcp_client { _alloc, _timer, *this }; + struct Dismiss_link : Genode::Exception { }; + struct Dismiss_arp_waiter : Genode::Exception { }; - void _new_link(L3_protocol const protocol, - Link_side_id const &local_id, - Pointer const remote_port_alloc, - Domain &remote_domain, - Link_side_id const &remote_id); + Reference _config; + Interface_policy &_policy; + Timer::Connection &_timer; + Genode::Allocator &_alloc; + Pointer _domain { }; + Arp_waiter_list _own_arp_waiters { }; + Link_list _tcp_links { }; + Link_list _udp_links { }; + Link_list _dissolved_tcp_links { }; + Link_list _dissolved_udp_links { }; + Dhcp_allocation_tree _dhcp_allocations { }; + Dhcp_allocation_list _released_dhcp_allocations { }; + Dhcp_client _dhcp_client { _alloc, _timer, *this }; + Interface_list &_interfaces; + bool _apply_foreign_arp_pending { false }; + Genode::Signal_context_capability _link_state_sigh { }; + + void _new_link(L3_protocol const protocol, + Link_side_id const &local_id, + Pointer remote_port_alloc, + Domain &remote_domain, + Link_side_id const &remote_id); void _destroy_released_dhcp_allocations(Domain &local_domain); @@ -200,6 +210,36 @@ class Net::Interface : private Genode::List::Element void * &pkt_base, Genode::size_t pkt_size); + void _update_dhcp_allocations(Domain &old_domain, + Domain &new_domain); + + void _update_own_arp_waiters(Domain &domain); + + void _update_links(L3_protocol prot, + Domain &cln_dom); + + void _update_link_check_nat(Link &link, + Domain &new_srv_dom, + L3_protocol prot, + Domain &cln_dom); + + void _dismiss_link_log(Link &link, + char const *reason); + + void _destroy_link(Link &link); + + void _detach_from_domain_raw(); + + void _attach_to_domain_raw(Domain_name const &domain_name); + + void _detach_from_domain(); + + void _attach_to_domain(Domain_name const &domain_name, + bool apply_foreign_arp); + + void _apply_foreign_arp(); + + /*********************************** ** Packet-stream signal handlers ** @@ -241,7 +281,8 @@ class Net::Interface : private Genode::List::Element Genode::Allocator &alloc, Mac_address const mac, Configuration &config, - Interface_policy const &policy); + Interface_list &interfaces, + Interface_policy &policy); virtual ~Interface(); @@ -271,16 +312,22 @@ class Net::Interface : private Genode::List::Element void cancel_arp_waiting(Arp_waiter &waiter); - void attach_to_domain(Domain &domain); + void handle_config(Configuration &new_config); - void detach_from_domain(); + void handle_config_aftermath(); + + void detach_from_ip_config(); + + bool link_state(); + + void link_state_sigh(Genode::Signal_context_capability sigh); /*************** ** Accessors ** ***************/ - Domain &domain() { return _domain_ptr.deref(); } + Domain &domain() { return _domain.deref(); } Mac_address router_mac() const { return _router_mac; } Arp_waiter_list &own_arp_waiters() { return _own_arp_waiters; } }; diff --git a/repos/os/src/server/nic_router/link.cc b/repos/os/src/server/nic_router/link.cc index 54e9111f78..fe7ece4fe5 100644 --- a/repos/os/src/server/nic_router/link.cc +++ b/repos/os/src/server/nic_router/link.cc @@ -113,15 +113,15 @@ void Link::print(Output &output) const } -Link::Link(Interface &cln_interface, - Link_side_id const &cln_id, - Pointer const srv_port_alloc, - Domain &srv_domain, - Link_side_id const &srv_id, - Timer::Connection &timer, - Configuration &config, - L3_protocol const protocol, - Microseconds const dissolve_timeout) +Link::Link(Interface &cln_interface, + Link_side_id const &cln_id, + Pointer srv_port_alloc, + Domain &srv_domain, + Link_side_id const &srv_id, + Timer::Connection &timer, + Configuration &config, + L3_protocol const protocol, + Microseconds const dissolve_timeout) : _config(config), _client_interface(cln_interface), @@ -151,12 +151,12 @@ void Link::dissolve() { _client.domain().links(_protocol).remove(&_client); _server.domain().links(_protocol).remove(&_server); - if (_config.verbose()) { + if (_config().verbose()) { log("Dissolve ", l3_protocol_name(_protocol), " link: ", *this); } try { _server_port_alloc.deref().free(_server.dst_port()); - if (_config.verbose()) { + if (_config().verbose()) { log("Free ", l3_protocol_name(_protocol), " port ", _server.dst_port(), " at ", _server.domain(), @@ -167,18 +167,49 @@ void Link::dissolve() } +void Link::handle_config(Domain &cln_domain, + Domain &srv_domain, + Pointer srv_port_alloc, + Configuration &config) +{ + Microseconds dissolve_timeout_us(0); + switch (_protocol) { + case L3_protocol::TCP: dissolve_timeout_us = config.tcp_idle_timeout(); break; + case L3_protocol::UDP: dissolve_timeout_us = config.tcp_idle_timeout(); break; + } + _dissolve_timeout_us = dissolve_timeout_us; + _dissolve_timeout.schedule(_dissolve_timeout_us); + + _client.domain().links(_protocol).remove(&_client); + _server.domain().links(_protocol).remove(&_server); + + _config = config; + _client._domain = cln_domain; + _server._domain = srv_domain; + _server_port_alloc = srv_port_alloc; + + cln_domain.links(_protocol).insert(&_client); + srv_domain.links(_protocol).insert(&_server); + + if (config.verbose()) { + log("[", cln_domain, "] update link client: ", _client); + log("[", srv_domain, "] update link server: ", _server); + } +} + + /************** ** Tcp_link ** **************/ -Tcp_link::Tcp_link(Interface &cln_interface, - Link_side_id const &cln_id, - Pointer const srv_port_alloc, - Domain &srv_domain, - Link_side_id const &srv_id, - Timer::Connection &timer, - Configuration &config, - L3_protocol const protocol) +Tcp_link::Tcp_link(Interface &cln_interface, + Link_side_id const &cln_id, + Pointer srv_port_alloc, + Domain &srv_domain, + Link_side_id const &srv_id, + Timer::Connection &timer, + Configuration &config, + L3_protocol const protocol) : Link(cln_interface, cln_id, srv_port_alloc, srv_domain, srv_id, timer, config, protocol, config.tcp_idle_timeout()) @@ -188,7 +219,7 @@ Tcp_link::Tcp_link(Interface &cln_interface, void Tcp_link::_fin_acked() { if (_server_fin_acked && _client_fin_acked) { - _dissolve_timeout.schedule(Microseconds(config().tcp_max_segm_lifetime().value << 1)); + _dissolve_timeout.schedule(Microseconds(_config().tcp_max_segm_lifetime().value << 1)); _closed = true; } } @@ -232,14 +263,14 @@ void Tcp_link::client_packet(Tcp_packet &tcp) ** Udp_link ** **************/ -Udp_link::Udp_link(Interface &cln_interface, - Link_side_id const &cln_id, - Pointer const srv_port_alloc, - Domain &srv_domain, - Link_side_id const &srv_id, - Timer::Connection &timer, - Configuration &config, - L3_protocol const protocol) +Udp_link::Udp_link(Interface &cln_interface, + Link_side_id const &cln_id, + Pointer srv_port_alloc, + Domain &srv_domain, + Link_side_id const &srv_id, + Timer::Connection &timer, + Configuration &config, + L3_protocol const protocol) : Link(cln_interface, cln_id, srv_port_alloc, srv_domain, srv_id, timer, config, protocol, config.udp_idle_timeout()) diff --git a/repos/os/src/server/nic_router/link.h b/repos/os/src/server/nic_router/link.h index bb1e5a09e1..d53c9bf5dc 100644 --- a/repos/os/src/server/nic_router/link.h +++ b/repos/os/src/server/nic_router/link.h @@ -38,6 +38,8 @@ #include /* local includes */ +#include +#include #include #include @@ -52,7 +54,7 @@ namespace Net { class Link_side; class Link_side_tree; class Link; - struct Link_list : Genode::List { }; + struct Link_list : List { }; class Tcp_link; class Udp_link; } @@ -87,7 +89,7 @@ class Net::Link_side : public Genode::Avl_node private: - Domain &_domain; + Reference _domain; Link_side_id const _id; Link &_link; @@ -120,7 +122,7 @@ class Net::Link_side : public Genode::Avl_node ** Accessors ** ***************/ - Domain &domain() const { return _domain; } + Domain &domain() const { return _domain(); } Link &link() const { return _link; } Ipv4_address const &src_ip() const { return _id.src_ip; } Ipv4_address const &dst_ip() const { return _id.dst_ip; } @@ -141,14 +143,14 @@ class Net::Link : public Link_list::Element { protected: - Configuration &_config; - Interface &_client_interface; - Pointer const _server_port_alloc; - Timer::One_shot_timeout _dissolve_timeout; - Genode::Microseconds const _dissolve_timeout_us; - L3_protocol const _protocol; - Link_side _client; - Link_side _server; + Reference _config; + Interface &_client_interface; + Pointer _server_port_alloc; + Timer::One_shot_timeout _dissolve_timeout; + Genode::Microseconds _dissolve_timeout_us; + L3_protocol const _protocol; + Link_side _client; + Link_side _server; void _handle_dissolve_timeout(Genode::Duration); @@ -160,7 +162,7 @@ class Net::Link : public Link_list::Element Link(Interface &cln_interface, Link_side_id const &cln_id, - Pointer const srv_port_alloc, + Pointer srv_port_alloc, Domain &srv_domain, Link_side_id const &srv_id, Timer::Connection &timer, @@ -170,6 +172,10 @@ class Net::Link : public Link_list::Element void dissolve(); + void handle_config(Domain &cln_domain, + Domain &srv_domain, + Pointer srv_port_alloc, + Configuration &config); /********* ** Log ** @@ -184,7 +190,7 @@ class Net::Link : public Link_list::Element Link_side &client() { return _client; } Link_side &server() { return _server; } - Configuration &config() { return _config; } + Configuration &config() { return _config(); } L3_protocol protocol() const { return _protocol; } }; @@ -203,14 +209,14 @@ class Net::Tcp_link : public Link public: - Tcp_link(Interface &cln_interface, - Link_side_id const &cln_id, - Pointer const srv_port_alloc, - Domain &srv_domain, - Link_side_id const &srv_id, - Timer::Connection &timer, - Configuration &config, - L3_protocol const protocol); + Tcp_link(Interface &cln_interface, + Link_side_id const &cln_id, + Pointer srv_port_alloc, + Domain &srv_domain, + Link_side_id const &srv_id, + Timer::Connection &timer, + Configuration &config, + L3_protocol const protocol); void client_packet(Tcp_packet &tcp); @@ -220,14 +226,14 @@ class Net::Tcp_link : public Link struct Net::Udp_link : Link { - Udp_link(Interface &cln_interface, - Link_side_id const &cln_id, - Pointer const srv_port_alloc, - Domain &srv_domain, - Link_side_id const &srv_id, - Timer::Connection &timer, - Configuration &config, - L3_protocol const protocol); + Udp_link(Interface &cln_interface, + Link_side_id const &cln_id, + Pointer srv_port_alloc, + Domain &srv_domain, + Link_side_id const &srv_id, + Timer::Connection &timer, + Configuration &config, + L3_protocol const protocol); void packet() { _packet(); } }; diff --git a/repos/os/src/server/nic_router/main.cc b/repos/os/src/server/nic_router/main.cc index a007029ad7..8c52cf2b54 100644 --- a/repos/os/src/server/nic_router/main.cc +++ b/repos/os/src/server/nic_router/main.cc @@ -26,17 +26,39 @@ using namespace Net; using namespace Genode; +namespace Net { class Main; } -class Main + +class Net::Main { private: - Timer::Connection _timer; - Genode::Heap _heap; - Genode::Attached_rom_dataspace _config_rom; - Configuration _config; - Uplink _uplink; - Net::Root _root; + Genode::Env &_env; + Interface_list _interfaces { }; + Timer::Connection _timer { _env }; + Genode::Heap _heap { &_env.ram(), &_env.rm() }; + Genode::Attached_rom_dataspace _config_rom { _env, "config" }; + Reference _config { _init_config() }; + Signal_handler
_config_handler { _env.ep(), *this, &Main::_handle_config }; + Uplink _uplink { _env, _timer, _heap, _interfaces, _config() }; + Root _root { _env.ep(), _timer, _heap, _uplink.router_mac(), _config(), _env.ram(), _interfaces, _env.rm()}; + + void _handle_config(); + + Configuration &_init_config(); + + template + void _for_each_interface(FUNC && functor) + { + _interfaces.for_each([&] (Interface &interface) { + functor(interface); + }); + _config().domains().for_each([&] (Domain &domain) { + domain.interfaces().for_each([&] (Interface &interface) { + functor(interface); + }); + }); + } public: @@ -44,24 +66,47 @@ class Main }; -Main::Main(Env &env) -: - _timer(env), _heap(&env.ram(), &env.rm()), _config_rom(env, "config"), - _config(env, _config_rom.xml(), _heap, _timer), - _uplink(env, _timer, _heap, _config), - _root(env.ep(), _timer, _heap, _uplink.router_mac(), _config, - env.ram(), env.rm()) +Configuration &Net::Main::_init_config() { + Configuration &config_legacy = *new (_heap) + Configuration(_config_rom.xml(), _heap); + + Configuration &config = *new (_heap) + Configuration(_env, _config_rom.xml(), _heap, _timer, config_legacy); + + destroy(_heap, &config_legacy); + return config; +} + + +Net::Main::Main(Env &env) : _env(env) +{ + _config_rom.sigh(_config_handler); env.parent().announce(env.ep().manage(_root)); } +void Net::Main::_handle_config() +{ + _config_rom.update(); + Configuration &config = *new (_heap) + Configuration(_env, _config_rom.xml(), _heap, _timer, _config()); + + _root.handle_config(config); + _for_each_interface([&] (Interface &intf) { intf.handle_config(config); }); + _for_each_interface([&] (Interface &intf) { intf.handle_config_aftermath(); }); + + destroy(_heap, &_config()); + _config = Reference(config); +} + + void Component::construct(Env &env) { /* XXX execute constructors of global statics */ env.exec_static_constructors(); - try { static Main main(env); } + try { static Net::Main main(env); } catch (Net::Domain_tree::No_match) { error("failed to find configuration for domain 'uplink'"); diff --git a/repos/os/src/server/nic_router/nat_rule.h b/repos/os/src/server/nic_router/nat_rule.h index d0f9fad0b5..6e386aa77a 100644 --- a/repos/os/src/server/nic_router/nat_rule.h +++ b/repos/os/src/server/nic_router/nat_rule.h @@ -18,6 +18,7 @@ #include #include #include +#include /* Genode includes */ #include @@ -74,7 +75,7 @@ class Net::Nat_rule : public Leaf_rule, }; -struct Net::Nat_rule_tree : Genode::Avl_tree +struct Net::Nat_rule_tree : Avl_tree { struct No_match : Genode::Exception { }; diff --git a/repos/os/src/server/nic_router/permit_rule.h b/repos/os/src/server/nic_router/permit_rule.h index 00d041a762..5edda5ca98 100644 --- a/repos/os/src/server/nic_router/permit_rule.h +++ b/repos/os/src/server/nic_router/permit_rule.h @@ -16,6 +16,7 @@ /* local includes */ #include +#include /* Genode includes */ #include @@ -25,6 +26,8 @@ namespace Genode { class Output; } namespace Net { + class Interface; + class Permit_rule; class Permit_any_rule; class Permit_single_rule; @@ -34,6 +37,8 @@ namespace Net { struct Net::Permit_rule : private Leaf_rule, public Genode::Interface { + friend class Interface; + Permit_rule(Domain_tree &domains, Genode::Xml_node const node); using Leaf_rule::domain; @@ -64,11 +69,12 @@ struct Net::Permit_any_rule : Permit_rule class Net::Permit_single_rule : public Permit_rule, private Genode::Avl_node { - private: + friend class Genode::Avl_node; + friend class Genode::Avl_tree; + friend class Avl_tree; + friend class Net::Permit_single_rule_tree; - friend class Genode::Avl_node; - friend class Genode::Avl_tree; - friend class Net::Permit_single_rule_tree; + private: Port const _port; @@ -102,8 +108,10 @@ class Net::Permit_single_rule : public Permit_rule, }; -struct Net::Permit_single_rule_tree : private Genode::Avl_tree +struct Net::Permit_single_rule_tree : private Avl_tree { + friend class Transport_rule; + struct No_match : Genode::Exception { }; void insert(Permit_single_rule *rule) diff --git a/repos/os/src/server/nic_router/report.cc b/repos/os/src/server/nic_router/report.cc index 5ff8f4a032..326aafadcb 100644 --- a/repos/os/src/server/nic_router/report.cc +++ b/repos/os/src/server/nic_router/report.cc @@ -20,14 +20,14 @@ using namespace Net; using namespace Genode; -Net::Report::Report(Env &env, - Xml_node const node, +Net::Report::Report(Xml_node const node, Timer::Connection &timer, - Domain_tree &domains) + Domain_tree &domains, + Reporter &reporter) : _config(node.attribute_value("config", true)), _bytes (node.attribute_value("bytes", true)), - _reporter(env, "state"), + _reporter(reporter), _domains(domains), _timeout(timer, *this, &Report::_handle_report_timeout, read_sec_attr(node, "interval_sec", 5)) @@ -36,7 +36,6 @@ Net::Report::Report(Env &env, } - void Net::Report::_handle_report_timeout(Duration) { try { diff --git a/repos/os/src/server/nic_router/report.h b/repos/os/src/server/nic_router/report.h index 754c429abc..65fe48cc8e 100644 --- a/repos/os/src/server/nic_router/report.h +++ b/repos/os/src/server/nic_router/report.h @@ -37,7 +37,7 @@ class Net::Report bool const _config; bool const _bytes; - Genode::Reporter _reporter; + Genode::Reporter &_reporter; Domain_tree &_domains; Timer::Periodic_timeout _timeout; @@ -45,10 +45,10 @@ class Net::Report public: - Report(Genode::Env &env, - Genode::Xml_node const node, + Report(Genode::Xml_node const node, Timer::Connection &timer, - Domain_tree &domains); + Domain_tree &domains, + Genode::Reporter &reporter); /*************** diff --git a/repos/os/src/server/nic_router/transport_rule.cc b/repos/os/src/server/nic_router/transport_rule.cc index ecb1180e24..40a25dcc13 100644 --- a/repos/os/src/server/nic_router/transport_rule.cc +++ b/repos/os/src/server/nic_router/transport_rule.cc @@ -24,17 +24,19 @@ using namespace Net; using namespace Genode; -Permit_any_rule *Transport_rule::_read_permit_any(Domain_tree &domains, - Xml_node const node, - Allocator &alloc) +Pointer +Transport_rule::_read_permit_any_rule(Domain_tree &domains, + Xml_node const node, + Allocator &alloc) { try { Xml_node sub_node = node.sub_node("permit-any"); - return new (alloc) Permit_any_rule(domains, sub_node); + return Pointer(*new (alloc) + Permit_any_rule(domains, sub_node)); } catch (Xml_node::Nonexistent_sub_node) { } catch (Rule::Invalid) { warning("invalid permit-any rule"); } - return nullptr; + return Pointer(); } @@ -44,15 +46,18 @@ Transport_rule::Transport_rule(Domain_tree &domains, Cstring const &protocol, Configuration &config) : - Direct_rule(node), _permit_any(_read_permit_any(domains, node, alloc)) + Direct_rule(node), + _alloc(alloc), + _permit_any_rule(_read_permit_any_rule(domains, node, alloc)) { /* skip specific permit rules if all ports are permitted anyway */ - if (_permit_any) { + try { + Permit_any_rule &permit_any_rule = _permit_any_rule.deref(); if (config.verbose()) { - log(" ", protocol, " rule: ", _dst, " ", *_permit_any); } + log(" ", protocol, " rule: ", _dst, " ", permit_any_rule); } return; - } + } catch (Pointer::Invalid) { } /* read specific permit rules */ node.for_each_sub_node("permit", [&] (Xml_node const node) { @@ -72,8 +77,17 @@ Transport_rule::Transport_rule(Domain_tree &domains, } +Transport_rule::~Transport_rule() +{ + _permit_single_rules.destroy_each(_alloc); + try { destroy(_alloc, &_permit_any_rule.deref()); } + catch (Pointer::Invalid) { } +} + + Permit_rule const &Transport_rule::permit_rule(Port const port) const { - if (_permit_any) { return *_permit_any; } + try { return _permit_any_rule.deref(); } + catch (Pointer::Invalid) { } return _permit_single_rules.find_by_port(port); } diff --git a/repos/os/src/server/nic_router/transport_rule.h b/repos/os/src/server/nic_router/transport_rule.h index efa4999a41..d3b15ed1c7 100644 --- a/repos/os/src/server/nic_router/transport_rule.h +++ b/repos/os/src/server/nic_router/transport_rule.h @@ -17,6 +17,7 @@ /* local includes */ #include #include +#include namespace Genode { class Allocator; } @@ -32,19 +33,14 @@ class Net::Transport_rule : public Direct_rule { private: - /* - * Noncopyable - */ - Transport_rule(Transport_rule const &); - Transport_rule &operator = (Transport_rule const &); + Genode::Allocator &_alloc; + Pointer const _permit_any_rule; + Permit_single_rule_tree _permit_single_rules { }; - Permit_any_rule *const _permit_any; - Permit_single_rule_tree _permit_single_rules { }; - - static Permit_any_rule * - _read_permit_any(Domain_tree &domains, - Genode::Xml_node const node, - Genode::Allocator &alloc); + static Pointer + _read_permit_any_rule(Domain_tree &domains, + Genode::Xml_node const node, + Genode::Allocator &alloc); public: @@ -54,6 +50,8 @@ class Net::Transport_rule : public Direct_rule Genode::Cstring const &protocol, Configuration &config); + ~Transport_rule(); + Permit_rule const &permit_rule(Port const port) const; }; diff --git a/repos/os/src/server/nic_router/uplink.cc b/repos/os/src/server/nic_router/uplink.cc index e7958b3acb..97b8f602a9 100644 --- a/repos/os/src/server/nic_router/uplink.cc +++ b/repos/os/src/server/nic_router/uplink.cc @@ -26,12 +26,13 @@ using namespace Genode; Net::Uplink::Uplink(Env &env, Timer::Connection &timer, Genode::Allocator &alloc, + Interface_list &interfaces, Configuration &config) : Nic::Packet_allocator(&alloc), Nic::Connection(env, this, BUF_SIZE, BUF_SIZE), Net::Interface(env.ep(), timer, mac_address(), alloc, Mac_address(), - config, _intf_policy) + config, interfaces, _intf_policy) { rx_channel()->sigh_ready_to_ack(_sink_ack); rx_channel()->sigh_packet_avail(_sink_submit); diff --git a/repos/os/src/server/nic_router/uplink.h b/repos/os/src/server/nic_router/uplink.h index 1aae5f6b07..3e5ab0541f 100644 --- a/repos/os/src/server/nic_router/uplink.h +++ b/repos/os/src/server/nic_router/uplink.h @@ -41,6 +41,7 @@ class Net::Uplink_base ***************************/ Domain_name determine_domain_name() const override { return Genode::Cstring("uplink"); }; + void handle_config(Configuration const &) override { } }; Interface_policy _intf_policy { }; @@ -76,6 +77,7 @@ class Net::Uplink : public Uplink_base, Uplink(Genode::Env &env, Timer::Connection &timer, Genode::Allocator &alloc, + Interface_list &interfaces, Configuration &config);