diff --git a/repos/os/src/server/nic_router/README b/repos/os/src/server/nic_router/README index f3b5f0f976..e84a616b0e 100644 --- a/repos/os/src/server/nic_router/README +++ b/repos/os/src/server/nic_router/README @@ -517,6 +517,46 @@ When set to zero, the limit is deactivated, meaning that the router always handles all available packets of a NIC session. +Behavior regarding the NIC-session link state +--------------------------------------------- + +At downlinks, the NIC router applies a feedback-driven state machine for the +link state in order to ensure that clients recognize state transitions. This +means that the NIC router guarantees that a new link state remains fixed until +the client has read it using the corresponding RPC at the NIC-session +interface. If, in the meantime, further "up" and "down" edges occur, they are +memorized and executed as soon as the former state has been read by the +client. Such postponed link state edges are merged in a way that they result in +two contrary edges at a max. The following diagrams demonstrate this: + + +! client reads: 0 1 0 1 0 1 +! _____ _______ _________________ +! client link state: ____| |______| |_________| +! _ __________ _________________ +! real link state: ____| |_____| |___________| +! +! time ---> + + +! client reads: 0 1 0 1 0 1 +! ______ _____________ _____ +! client link state: ____| |______| |________| |_____ +! _ _______________________ _ _ _ +! real link state: ____| |_| |_| |_| |_| |________ +! +! time ---> + + +! client reads: 0 1 0 1 0 1 +! ________ _________ __________ +! client link state: ____| |____________| |______| +! _ __ _________ _ ___________ +! real link state: ____| |_| |______________| |_| |_| +! +! time ---> + + Examples ~~~~~~~~ diff --git a/repos/os/src/server/nic_router/component.cc b/repos/os/src/server/nic_router/component.cc index ffd0042a08..f7c91dbd5f 100644 --- a/repos/os/src/server/nic_router/component.cc +++ b/repos/os/src/server/nic_router/component.cc @@ -63,7 +63,9 @@ Interface_policy::Interface_policy(Genode::Session_label const &label, _label { label }, _config { config }, _session_env { session_env } -{ } +{ + _session_link_state_transition(UP); +} Domain_name @@ -88,6 +90,153 @@ Net::Session_component::Interface_policy::determine_domain_name() const } +void Net::Session_component:: +Interface_policy::_session_link_state_transition(Transient_link_state tls) +{ + _transient_link_state = tls; + Signal_transmitter(_session_link_state_sigh).submit(); +} + + +void Net::Session_component::Interface_policy::interface_unready() +{ + switch (_transient_link_state) { + case UP_ACKNOWLEDGED: + + _session_link_state_transition(DOWN); + break; + + case UP: + + _transient_link_state = UP_DOWN; + break; + + case DOWN_UP: + + _transient_link_state = DOWN_UP_DOWN; + break; + + case UP_DOWN: + + break; + + case UP_DOWN_UP: + + _transient_link_state = UP_DOWN; + break; + + case DOWN_ACKNOWLEDGED: break; + case DOWN: break; + case DOWN_UP_DOWN: break; + } +} + + +void Net::Session_component::Interface_policy::interface_ready() +{ + switch (_transient_link_state) { + case DOWN_ACKNOWLEDGED: + + _session_link_state_transition(UP); + break; + + case DOWN: + + _transient_link_state = DOWN_UP; + break; + + case UP_DOWN: + + _transient_link_state = UP_DOWN_UP; + break; + + case DOWN_UP: + + break; + + case DOWN_UP_DOWN: + + _transient_link_state = DOWN_UP; + break; + + case UP_ACKNOWLEDGED: break; + case UP: break; + case UP_DOWN_UP: break; + } +} + +bool +Net::Session_component::Interface_policy::interface_link_state() const +{ + switch (_transient_link_state) { + case DOWN_ACKNOWLEDGED: return false; + case DOWN: return false; + case DOWN_UP: return true; + case DOWN_UP_DOWN: return false; + case UP_ACKNOWLEDGED: return true; + case UP: return true; + case UP_DOWN: return false; + case UP_DOWN_UP: return true; + } + class Never_reached : Exception { }; + throw Never_reached { }; +} + + +bool +Net::Session_component::Interface_policy::read_and_ack_session_link_state() +{ + switch (_transient_link_state) { + case DOWN_ACKNOWLEDGED: + + return false; + + case DOWN: + + _transient_link_state = DOWN_ACKNOWLEDGED; + return false; + + case DOWN_UP: + + _session_link_state_transition(UP); + return false; + + case DOWN_UP_DOWN: + + _session_link_state_transition(UP_DOWN); + return false; + + case UP_ACKNOWLEDGED: + + return true; + + case UP: + + _transient_link_state = UP_ACKNOWLEDGED; + return true; + + case UP_DOWN: + + _session_link_state_transition(DOWN); + return true; + + case UP_DOWN_UP: + + _session_link_state_transition(DOWN_UP); + return true; + } + class Never_reached { }; + throw Never_reached { }; +} + + +void Net::Session_component::Interface_policy:: +session_link_state_sigh(Signal_context_capability sigh) +{ + _session_link_state_sigh = sigh; +} + + /*********************** ** Session_component ** ***********************/ @@ -109,7 +258,7 @@ Net::Session_component::Session_component(Session_env &sessio _interface_policy { label, _session_env, config }, _interface { _session_env.ep(), timer, router_mac, _alloc, mac, config, interfaces, *_tx.sink(), - *_rx.source(), _link_state, _interface_policy }, + *_rx.source(), _interface_policy }, _ram_ds { ram_ds } { _interface.attach_to_domain(); @@ -121,6 +270,19 @@ Net::Session_component::Session_component(Session_env &sessio } +bool Net::Session_component::link_state() +{ + return _interface_policy.read_and_ack_session_link_state(); +} + + +void Net:: +Session_component::link_state_sigh(Signal_context_capability sigh) +{ + _interface_policy.session_link_state_sigh(sigh); +} + + /********** ** Root ** **********/ diff --git a/repos/os/src/server/nic_router/component.h b/repos/os/src/server/nic_router/component.h index 43d7e673f3..812dfca53c 100644 --- a/repos/os/src/server/nic_router/component.h +++ b/repos/os/src/server/nic_router/component.h @@ -257,13 +257,45 @@ class Net::Session_component : private Session_component_base, { private: - struct Interface_policy : Net::Interface_policy + class Interface_policy : public Net::Interface_policy { private: + using Signal_context_capability = + Genode::Signal_context_capability; + + /* + * The transient link state is a combination of session and + * interface link state. The first word in the value name + * denotes the link state of the session. If the session + * link state has already been read by the client and + * can therefore be altered directly, it is marked as + * 'ACKNOWLEDGED'. Otherwise, the denoted session state + * has to stay fixed until the client has read it. In this + * case, the session link state in the value name may be + * followed by the pending link state edges. Consequently, + * the last 'UP' or 'DOWN' in each value name denotes the + * router-internal interface link-state. + */ + enum Transient_link_state + { + DOWN_ACKNOWLEDGED, + DOWN, + DOWN_UP, + DOWN_UP_DOWN, + UP_ACKNOWLEDGED, + UP, + UP_DOWN, + UP_DOWN_UP + }; + Genode::Session_label const _label; Const_reference _config; Genode::Session_env const &_session_env; + Transient_link_state _transient_link_state { DOWN_ACKNOWLEDGED }; + Signal_context_capability _session_link_state_sigh { }; + + void _session_link_state_transition(Transient_link_state tls); public: @@ -271,6 +303,10 @@ class Net::Session_component : private Session_component_base, Genode::Session_env const &session_env, Configuration const &config); + bool read_and_ack_session_link_state(); + + void session_link_state_sigh(Genode::Signal_context_capability sigh); + /*************************** ** Net::Interface_policy ** @@ -280,9 +316,11 @@ class Net::Session_component : private Session_component_base, void handle_config(Configuration const &config) override { _config = config; } Genode::Session_label const &label() const override { return _label; } void report(Genode::Xml_generator &xml) const override { _session_env.report(xml); }; + void interface_unready() override; + void interface_ready() override; + bool interface_link_state() const override; }; - bool _link_state { true }; Interface_policy _interface_policy; Interface _interface; Genode::Ram_dataspace_capability const _ram_ds; @@ -306,9 +344,8 @@ class Net::Session_component : private Session_component_base, ******************/ Mac_address mac_address() override { return _interface.mac(); } - bool link_state() override { return _interface.link_state(); } - void link_state_sigh(Genode::Signal_context_capability sigh) override { - _interface.session_link_state_sigh(sigh); } + bool link_state() override; + void link_state_sigh(Genode::Signal_context_capability sigh) override; /*************** diff --git a/repos/os/src/server/nic_router/interface.cc b/repos/os/src/server/nic_router/interface.cc index 1c2e58cbb6..80730843f9 100644 --- a/repos/os/src/server/nic_router/interface.cc +++ b/repos/os/src/server/nic_router/interface.cc @@ -318,7 +318,7 @@ Interface::_transport_rules(Domain &local_domain, L3_protocol const prot) const void Interface::_attach_to_domain_raw(Domain &domain) { _domain = domain; - Signal_transmitter(_session_link_state_sigh).submit(); + _policy.interface_ready(); _interfaces.remove(this); domain.attach_interface(*this); } @@ -330,7 +330,7 @@ void Interface::_detach_from_domain_raw() domain.detach_interface(*this); _interfaces.insert(this); _domain = Pointer(); - Signal_transmitter(_session_link_state_sigh).submit(); + _policy.interface_unready(); domain.tcp_stats().dissolve_interface(_tcp_stats); domain.udp_stats().dissolve_interface(_udp_stats); @@ -347,7 +347,7 @@ void Interface::_update_domain_object(Domain &new_domain) { old_domain.interface_updates_domain_object(*this); _interfaces.insert(this); _domain = Pointer(); - Signal_transmitter(_session_link_state_sigh).submit(); + _policy.interface_unready(); old_domain.tcp_stats().dissolve_interface(_tcp_stats); old_domain.udp_stats().dissolve_interface(_udp_stats); @@ -357,7 +357,7 @@ void Interface::_update_domain_object(Domain &new_domain) { /* attach raw */ _domain = new_domain; - Signal_transmitter(_session_link_state_sigh).submit(); + _policy.interface_ready(); _interfaces.remove(this); new_domain.attach_interface(*this); } @@ -408,12 +408,6 @@ void Interface::attach_to_ip_config(Domain &domain, } -void Interface::session_link_state_sigh(Signal_context_capability sigh) -{ - _session_link_state_sigh = sigh; -} - - void Interface::detach_from_ip_config() { /* destroy our own ARP waiters */ @@ -440,14 +434,15 @@ void Interface::detach_from_ip_config() void Interface::detach_from_remote_ip_config() { /* only the DNS server address of the local DHCP server can be remote */ - Signal_transmitter(_session_link_state_sigh).submit(); + _policy.interface_unready(); + } void Interface::attach_to_remote_ip_config() { /* only the DNS server address of the local DHCP server can be remote */ - Signal_transmitter(_session_link_state_sigh).submit(); + _policy.interface_ready(); } @@ -876,11 +871,11 @@ void Interface::_send_icmp_dst_unreachable(Ipv4_address_prefix const &local_intf bool Interface::link_state() const { - return _domain.valid() && _session_link_state; + return _policy.interface_link_state(); } -void Interface::handle_link_state() +void Interface::handle_interface_link_state() { struct Keep_ip_config : Exception { }; try { @@ -900,7 +895,7 @@ void Interface::handle_link_state() catch (Keep_ip_config) { } /* force report if configured */ - try { _config().report().handle_link_state(); } + try { _config().report().handle_interface_link_state(); } catch (Pointer::Invalid) { } } @@ -1713,12 +1708,10 @@ Interface::Interface(Genode::Entrypoint &ep, Interface_list &interfaces, Packet_stream_sink &sink, Packet_stream_source &source, - bool &session_link_state, Interface_policy &policy) : _sink { sink }, _source { source }, - _session_link_state { session_link_state }, _sink_ack { ep, *this, &Interface::_ack_avail }, _sink_submit { ep, *this, &Interface::_ready_to_submit }, _source_ack { ep, *this, &Interface::_ready_to_ack }, diff --git a/repos/os/src/server/nic_router/interface.h b/repos/os/src/server/nic_router/interface.h index 2456ce39cb..393b2ab45f 100644 --- a/repos/os/src/server/nic_router/interface.h +++ b/repos/os/src/server/nic_router/interface.h @@ -92,6 +92,12 @@ struct Net::Interface_policy virtual Genode::Session_label const &label() const = 0; + virtual void interface_ready() = 0; + + virtual void interface_unready() = 0; + + virtual bool interface_link_state() const = 0; + virtual void report(Genode::Xml_generator &) const { throw Report::Empty(); } virtual ~Interface_policy() { } @@ -129,8 +135,6 @@ class Net::Interface : private Interface_list::Element Packet_stream_sink &_sink; Packet_stream_source &_source; - bool &_session_link_state; - Signal_context_capability _session_link_state_sigh { }; Signal_handler _sink_ack; Signal_handler _sink_submit; Signal_handler _source_ack; @@ -351,6 +355,8 @@ class Net::Interface : private Interface_list::Element Ipv4_packet const &req_ip, Icmp_packet::Code const code); + bool link_state() const; + /*********************************** ** Packet-stream signal handlers ** @@ -386,7 +392,6 @@ class Net::Interface : private Interface_list::Element Interface_list &interfaces, Packet_stream_sink &sink, Packet_stream_source &source, - bool &session_link_state, Interface_policy &policy); virtual ~Interface(); @@ -442,9 +447,7 @@ class Net::Interface : private Interface_list::Element void attach_to_domain_finish(); - bool link_state() const; - - void handle_link_state(); + void handle_interface_link_state(); void report(Genode::Xml_generator &xml); @@ -467,8 +470,6 @@ class Net::Interface : private Interface_list::Element Interface_link_stats &icmp_stats() { return _icmp_stats; } Interface_object_stats &arp_stats() { return _arp_stats; } Interface_object_stats &dhcp_stats() { return _dhcp_stats; } - - void session_link_state_sigh(Genode::Signal_context_capability sigh); }; #endif /* _INTERFACE_H_ */ diff --git a/repos/os/src/server/nic_router/report.cc b/repos/os/src/server/nic_router/report.cc index 5f6dea5407..b7497d0932 100644 --- a/repos/os/src/server/nic_router/report.cc +++ b/repos/os/src/server/nic_router/report.cc @@ -90,7 +90,7 @@ void Net::Report::handle_config() _report(); } -void Net::Report::handle_link_state() +void Net::Report::handle_interface_link_state() { if (!_link_state_triggers) { return; } diff --git a/repos/os/src/server/nic_router/report.h b/repos/os/src/server/nic_router/report.h index 9dcae3cd03..49b9b1e200 100644 --- a/repos/os/src/server/nic_router/report.h +++ b/repos/os/src/server/nic_router/report.h @@ -75,7 +75,7 @@ class Net::Report void handle_config(); - void handle_link_state(); + void handle_interface_link_state(); /*************** diff --git a/repos/os/src/server/nic_router/uplink.cc b/repos/os/src/server/nic_router/uplink.cc index 4d2ede07c6..bd9fa46e98 100644 --- a/repos/os/src/server/nic_router/uplink.cc +++ b/repos/os/src/server/nic_router/uplink.cc @@ -112,13 +112,33 @@ void Net::Uplink::print(Output &output) const ***************************/ Net::Uplink_interface_base::Uplink_interface_base(Domain_name const &domain_name, - Session_label const &label) + Session_label const &label, + bool const &session_link_state) : - _domain_name { domain_name }, - _label { label } + _domain_name { domain_name }, + _label { label }, + _session_link_state { session_link_state } { } +void Net::Uplink_interface_base::interface_unready() +{ + _interface_ready = false; +}; + + +void Net::Uplink_interface_base::interface_ready() +{ + _interface_ready = true; +}; + + +bool Net::Uplink_interface_base::interface_link_state() const +{ + return _interface_ready && _session_link_state; +} + + /********************** ** Uplink_interface ** **********************/ @@ -131,14 +151,14 @@ Net::Uplink_interface::Uplink_interface(Env &env, Domain_name const &domain_name, Session_label const &label) : - Uplink_interface_base { domain_name, label }, - Nic::Packet_allocator { &alloc }, - Nic::Connection { env, this, BUF_SIZE, BUF_SIZE, label.string() }, - _link_state_handler { env.ep(), *this, - &Uplink_interface::_handle_link_state }, - _interface { env.ep(), timer, mac_address(), alloc, - Mac_address(), config, interfaces, *rx(), *tx(), - _link_state, *this } + Uplink_interface_base { domain_name, label, _session_link_state }, + Nic::Packet_allocator { &alloc }, + Nic::Connection { env, this, BUF_SIZE, BUF_SIZE, label.string() }, + _session_link_state_handler { env.ep(), *this, + &Uplink_interface::_handle_session_link_state }, + _interface { env.ep(), timer, mac_address(), alloc, + Mac_address(), config, interfaces, *rx(), *tx(), + *this } { /* install packet stream signal handlers */ rx_channel()->sigh_ready_to_ack (_interface.sink_ack()); @@ -147,13 +167,13 @@ Net::Uplink_interface::Uplink_interface(Env &env, tx_channel()->sigh_ready_to_submit(_interface.source_submit()); /* initialize link state handling */ - Nic::Connection::link_state_sigh(_link_state_handler); - _link_state = link_state(); + Nic::Connection::link_state_sigh(_session_link_state_handler); + _session_link_state = Nic::Connection::link_state(); } -void Net::Uplink_interface::_handle_link_state() +void Net::Uplink_interface::_handle_session_link_state() { - _link_state = link_state(); - _interface.handle_link_state(); + _session_link_state = Nic::Connection::link_state(); + _interface.handle_interface_link_state(); } diff --git a/repos/os/src/server/nic_router/uplink.h b/repos/os/src/server/nic_router/uplink.h index 6eefeebc35..c1f53c8c20 100644 --- a/repos/os/src/server/nic_router/uplink.h +++ b/repos/os/src/server/nic_router/uplink.h @@ -104,8 +104,10 @@ class Net::Uplink_interface_base : public Interface_policy { private: - Const_reference _domain_name; - Genode::Session_label const _label; + Const_reference _domain_name; + Genode::Session_label const _label; + bool const &_session_link_state; + bool _interface_ready { false }; /*************************** @@ -115,11 +117,15 @@ class Net::Uplink_interface_base : public Interface_policy Domain_name determine_domain_name() const override { return _domain_name(); }; void handle_config(Configuration const &) override { } Genode::Session_label const &label() const override { return _label; } + void interface_unready() override; + void interface_ready() override; + bool interface_link_state() const override; public: Uplink_interface_base(Domain_name const &domain_name, - Genode::Session_label const &label); + Genode::Session_label const &label, + bool const &session_link_state); virtual ~Uplink_interface_base() { } @@ -143,13 +149,13 @@ class Net::Uplink_interface : public Uplink_interface_base, BUF_SIZE = Nic::Session::QUEUE_SIZE * PKT_SIZE, }; - bool _link_state { false }; - Genode::Signal_handler _link_state_handler; + bool _session_link_state { false }; + Genode::Signal_handler _session_link_state_handler; Net::Interface _interface; Ipv4_address_prefix _read_interface(); - void _handle_link_state(); + void _handle_session_link_state(); public: