From ac42ade48c9176d002017f73b127a08747b19fa8 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Thu, 2 May 2024 13:25:26 +0200 Subject: [PATCH] nic_router: remove use of exception handling Remove the use of C++ exception as much as possible from the router as C++ exception handling can be resource intensive and can make code hard to understand. This also removes the garbage collection that the router used to do when a session ran out of quota. This is motivated by the fact that the garbage collection was rather simple and removed connection states regardless of their current state, thereby causing broken connections. The change is part of this commit as the approach to integrating garbage collection relied strongly on exception handling. The user story behind removing garbage collection: The router emergency-dropped an established TCP connection (with NAPT) and on the next matching packet re-created it with a different NAPT port, thereby breaking the connection. With this commit, existing connections are prioritized over new ones during resource exhaustion and the packets that attempt to create a new connection in such a state are dropped with a warning in the log (verbose_packet_drop="yes"). Note that the state resolves itself with time as existing connections time out or are closed by peers. Ref #4729 --- repos/os/include/net/dhcp.h | 43 +- repos/os/src/server/nic_router/arp_cache.cc | 5 +- repos/os/src/server/nic_router/assertion.h | 35 + .../server/nic_router/bit_allocator_dynamic.h | 101 +- .../os/src/server/nic_router/configuration.cc | 209 ++- .../os/src/server/nic_router/configuration.h | 51 +- repos/os/src/server/nic_router/dhcp.h | 5 +- repos/os/src/server/nic_router/dhcp_client.cc | 107 +- repos/os/src/server/nic_router/dhcp_client.h | 11 +- repos/os/src/server/nic_router/dhcp_server.cc | 164 +- repos/os/src/server/nic_router/dhcp_server.h | 82 +- repos/os/src/server/nic_router/direct_rule.cc | 33 - repos/os/src/server/nic_router/direct_rule.h | 8 +- repos/os/src/server/nic_router/dns.cc | 33 +- repos/os/src/server/nic_router/dns.h | 16 +- repos/os/src/server/nic_router/domain.cc | 355 ++--- repos/os/src/server/nic_router/domain.h | 94 +- .../os/src/server/nic_router/forward_rule.cc | 15 +- repos/os/src/server/nic_router/forward_rule.h | 2 +- repos/os/src/server/nic_router/interface.cc | 1316 ++++++++--------- repos/os/src/server/nic_router/interface.h | 209 +-- repos/os/src/server/nic_router/ip_rule.cc | 26 - repos/os/src/server/nic_router/ip_rule.h | 2 +- repos/os/src/server/nic_router/ipv4_config.cc | 15 +- repos/os/src/server/nic_router/l3_protocol.cc | 2 +- repos/os/src/server/nic_router/link.cc | 93 +- repos/os/src/server/nic_router/link.h | 63 +- .../os/src/server/nic_router/mac_allocator.h | 10 +- repos/os/src/server/nic_router/main.cc | 4 +- repos/os/src/server/nic_router/nat_rule.cc | 6 +- repos/os/src/server/nic_router/nat_rule.h | 2 +- repos/os/src/server/nic_router/nic_client.cc | 56 +- repos/os/src/server/nic_router/nic_client.h | 26 +- .../src/server/nic_router/nic_session_root.cc | 61 +- .../src/server/nic_router/nic_session_root.h | 15 +- .../os/src/server/nic_router/packet_result.h | 34 + repos/os/src/server/nic_router/permit_rule.cc | 17 +- repos/os/src/server/nic_router/permit_rule.h | 11 +- repos/os/src/server/nic_router/pointer.h | 80 - .../src/server/nic_router/port_allocator.cc | 44 +- .../os/src/server/nic_router/port_allocator.h | 16 +- repos/os/src/server/nic_router/report.cc | 15 +- repos/os/src/server/nic_router/report.h | 4 +- repos/os/src/server/nic_router/session_env.h | 2 + repos/os/src/server/nic_router/target.mk | 2 - .../src/server/nic_router/transport_rule.cc | 98 +- .../os/src/server/nic_router/transport_rule.h | 33 +- .../server/nic_router/uplink_session_root.cc | 8 +- .../server/nic_router/uplink_session_root.h | 1 + 49 files changed, 1727 insertions(+), 1913 deletions(-) create mode 100644 repos/os/src/server/nic_router/assertion.h delete mode 100644 repos/os/src/server/nic_router/direct_rule.cc delete mode 100644 repos/os/src/server/nic_router/ip_rule.cc create mode 100644 repos/os/src/server/nic_router/packet_result.h delete mode 100644 repos/os/src/server/nic_router/pointer.h diff --git a/repos/os/include/net/dhcp.h b/repos/os/include/net/dhcp.h index f53a30a09b..56b8fa94bc 100644 --- a/repos/os/include/net/dhcp.h +++ b/repos/os/include/net/dhcp.h @@ -205,26 +205,19 @@ class Net::Dhcp_packet /** * Domain name server option */ - class Dns_server : public Option + struct Dns_server : public Option { - private: + static constexpr Code CODE = Code::DNS_SERVER; - Ipv4_address _dns_servers[0]; + Dns_server(Genode::size_t len) : Option(CODE, (Genode::uint8_t)len) { } - public: + void for_each_address(auto const &fn) const + { + Ipv4_address const *dns_servers = (Ipv4_address const *)((Genode::addr_t)this + sizeof(*this)); + for (unsigned idx = 0; idx < len() / sizeof(*dns_servers); idx++) + fn(dns_servers[idx]); + } - static constexpr Code CODE = Code::DNS_SERVER; - - Dns_server(Genode::size_t len) : Option(CODE, (Genode::uint8_t)len) { } - - void for_each_address(auto const &fn) const - { - for (unsigned idx = 0; - idx < len() / sizeof(_dns_servers[0]); idx++) { - - fn(_dns_servers[idx]); - } - } }; /** @@ -516,6 +509,24 @@ class Net::Dhcp_packet } } + template + void with_option(auto const &found_fn, auto const ¬_found_fn) const + { + bool found = false; + for_each_option([&] (Option const &opt) { + if (found || opt.code() != T::CODE) + return; + + found = true; + found_fn(*(T const *)(&opt)); + }); + if (!found) + not_found_fn(); + } + + template + void with_option(auto const &fn) const { with_option(fn, []{}); } + /*************** ** Accessors ** diff --git a/repos/os/src/server/nic_router/arp_cache.cc b/repos/os/src/server/nic_router/arp_cache.cc index 6493abf6af..51ec24647e 100644 --- a/repos/os/src/server/nic_router/arp_cache.cc +++ b/repos/os/src/server/nic_router/arp_cache.cc @@ -69,7 +69,7 @@ void Arp_cache::new_entry(Ipv4_address const &ip, Mac_address const &mac) void Arp_cache::destroy_entries_with_mac(Mac_address const &mac) { for (unsigned curr = 0; curr < NR_OF_ENTRIES; curr++) { - try { + if (_entries[curr].constructed()) { Arp_cache_entry &entry = *_entries[curr]; if (entry.mac() != mac) { continue; @@ -79,8 +79,7 @@ void Arp_cache::destroy_entries_with_mac(Mac_address const &mac) } remove(&entry); _entries[curr].destruct(); - - } catch (Arp_cache_entry_slot::Deref_unconstructed_object) { } + } } } diff --git a/repos/os/src/server/nic_router/assertion.h b/repos/os/src/server/nic_router/assertion.h new file mode 100644 index 0000000000..5271a99ea7 --- /dev/null +++ b/repos/os/src/server/nic_router/assertion.h @@ -0,0 +1,35 @@ +/* + * \brief Assertion macros + * \author Martin Stein + * \date 2023-06-09 + */ + +/* + * Copyright (C) 2023 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _ASSERTION_H_ +#define _ASSERTION_H_ + +/* base includes */ +#include +#include + +#define ASSERT(condition) \ + do { \ + if (!(condition)) { \ + Genode::error(__FILE__, ":", __LINE__, ": ", " assertion \"", #condition, "\" failed "); \ + Genode::sleep_forever(); \ + } \ + } while (false) + +#define ASSERT_NEVER_REACHED \ + do { \ + Genode::error(__FILE__, ":", __LINE__, ": ", " should have never been reached"); \ + Genode::sleep_forever(); \ + } while (false) + +#endif /* _ASSERTION_H_ */ diff --git a/repos/os/src/server/nic_router/bit_allocator_dynamic.h b/repos/os/src/server/nic_router/bit_allocator_dynamic.h index c1db6d55ce..bcd159061c 100644 --- a/repos/os/src/server/nic_router/bit_allocator_dynamic.h +++ b/repos/os/src/server/nic_router/bit_allocator_dynamic.h @@ -17,6 +17,9 @@ /* Genode includes */ #include +/* local includes */ +#include + namespace Genode { class Bit_array_dynamic; @@ -26,14 +29,6 @@ namespace Genode { class Genode::Bit_array_dynamic { - public: - - struct Invalid_bit_count : Exception { }; - struct Invalid_index_access : Exception { }; - struct Invalid_clear : Exception { }; - struct Invalid_set : Exception { }; - struct Count_of_bits_not_word_aligned : Exception { }; - protected: enum { @@ -50,13 +45,14 @@ class Genode::Bit_array_dynamic addr_t _word(addr_t index) const { return index / BITS_PER_WORD; } - void _check_range(addr_t const index, - addr_t const width) const + [[nodiscard]] bool _check_range(addr_t const index, + addr_t const width) const { if ((index >= _word_cnt * BITS_PER_WORD) || width > _word_cnt * BITS_PER_WORD || _word_cnt * BITS_PER_WORD - width < index) - throw Invalid_index_access(); + return false; + return true; } addr_t _mask(addr_t const index, addr_t const width, @@ -71,9 +67,10 @@ class Genode::Bit_array_dynamic : ((1UL << width) - 1) << shift; } - void _set(addr_t index, addr_t width, bool free) + [[nodiscard]] bool _set(addr_t index, addr_t width, bool free) { - _check_range(index, width); + if (!_check_range(index, width)) + return false; addr_t rest, word, mask; do { @@ -82,17 +79,18 @@ class Genode::Bit_array_dynamic if (free) { if ((_words[word] & mask) != mask) - throw Invalid_clear(); + return false; _words[word] &= ~mask; } else { if (_words[word] & mask) - throw Invalid_set(); + return false; _words[word] |= mask; } index = (_word(index) + 1) * BITS_PER_WORD; width = rest; } while (rest); + return true; } public: @@ -103,7 +101,8 @@ class Genode::Bit_array_dynamic */ bool get(addr_t index, addr_t width) const { - _check_range(index, width); + if (!_check_range(index, width)) + return false; bool used = false; addr_t rest, mask; @@ -117,23 +116,18 @@ class Genode::Bit_array_dynamic return used; } - void set(addr_t const index, addr_t const width) { - _set(index, width, false); } + [[nodiscard]] bool set(addr_t const index, addr_t const width) { + return _set(index, width, false); } - void clear(addr_t const index, addr_t const width) { - _set(index, width, true); } + [[nodiscard]] bool clear(addr_t const index, addr_t const width) { + return _set(index, width, true); } Bit_array_dynamic(addr_t *addr, unsigned bits) : _bit_cnt(bits), _word_cnt(_bit_cnt / BITS_PER_WORD), _words(addr) { - if (!bits || bits % BITS_PER_WORD) - throw Invalid_bit_count(); - + ASSERT(bits && bits % BITS_PER_WORD == 0); memset(_words, 0, sizeof(addr_t)*_word_cnt); - - if (bits % BITS_PER_WORD) - throw Count_of_bits_not_word_aligned(); } }; @@ -164,11 +158,11 @@ class Genode::Bit_allocator_dynamic * * \noapi */ - void _reserve(addr_t bit_start, size_t const num) + [[nodiscard]] bool _reserve(addr_t bit_start, size_t const num) { - if (!num) return; + if (!num) return true; - _array.set(bit_start, num); + return _array.set(bit_start, num); } size_t _ram_size() const @@ -178,50 +172,53 @@ class Genode::Bit_allocator_dynamic public: - struct Out_of_indices : Exception { }; - struct Range_conflict : Exception { }; + struct Alloc_error { }; + using Alloc_result = Attempt; - addr_t alloc(size_t const num_log2 = 0) + [[nodiscard]] Alloc_result alloc() { - addr_t const step = 1UL << num_log2; + addr_t const step = 1UL; addr_t max = ~0UL; do { - try { - /* throws exception if array is accessed outside bounds */ - for (addr_t i = _next & ~(step - 1); i < max; i += step) { - if (_array.get(i, step)) - continue; + for (addr_t i = _next & ~(step - 1); i < max; i += step) { + if (_array.get(i, step)) + continue; - _array.set(i, step); - _next = i + step; - return i; - } - } catch (Bit_array_dynamic::Invalid_index_access) { } + if (!_array.set(i, step)) + break; + _next = i + step; + return i; + } max = _next; _next = 0; } while (max != 0); - throw Out_of_indices(); + return Alloc_error(); } - void alloc_addr(addr_t const bit_start, size_t const num_log2 = 0) + [[nodiscard]] bool alloc_addr(addr_t const bit_start) { - addr_t const step = 1UL << num_log2; + addr_t const step = 1UL; if (_array.get(bit_start, step)) - throw Range_conflict(); + return false; + + if (!_array.set(bit_start, step)) + return false; - _array.set(bit_start, step); _next = bit_start + step; - return; + return true; } - void free(addr_t const bit_start, size_t const num_log2 = 0) + [[nodiscard]] bool free(addr_t const bit_start) { - _array.clear(bit_start, 1UL << num_log2); + if (!_array.clear(bit_start, 1UL)) + return false; + _next = bit_start; + return true; } Bit_allocator_dynamic(Allocator &alloc, unsigned bits) @@ -233,7 +230,7 @@ class Genode::Bit_allocator_dynamic _ram((addr_t *)_alloc.alloc(_ram_size())), _array(_ram, _bits_aligned) { - _reserve(bits, _bits_aligned - bits); + ASSERT(_reserve(bits, _bits_aligned - bits)); } ~Bit_allocator_dynamic() diff --git a/repos/os/src/server/nic_router/configuration.cc b/repos/os/src/server/nic_router/configuration.cc index 4549bd8877..25f03e865b 100644 --- a/repos/os/src/server/nic_router/configuration.cc +++ b/repos/os/src/server/nic_router/configuration.cc @@ -63,31 +63,20 @@ void Configuration::_invalid_domain(Domain &domain, Icmp_packet::Code Configuration::_init_icmp_type_3_code_on_fragm_ipv4(Xml_node const &node) const { - char const *const attr_name { "icmp_type_3_code_on_fragm_ipv4" }; - try { - Xml_attribute const &attr { node.attribute(attr_name) }; - if (attr.has_value("no")) { - return Icmp_packet::Code::INVALID; - } - uint8_t attr_val { }; - bool const attr_transl_succeeded { attr.value(attr_val) }; - Icmp_packet::Code const result { - Icmp_packet::code_from_uint8( - Icmp_packet::Type::DST_UNREACHABLE, attr_val) }; - - if (!attr_transl_succeeded || - result == Icmp_packet::Code::INVALID) { - - warning("attribute 'icmp_type_3_code_on_fragm_ipv4' has invalid " - "value, assuming value \"no\""); - - return Icmp_packet::Code::INVALID; - } + using Attribute_string = String<16>; + Icmp_packet::Code result = Icmp_packet::Code::INVALID; + Attribute_string attr_str = node.attribute_value("icmp_type_3_code_on_fragm_ipv4", Attribute_string()); + if (attr_str == "no" || attr_str == Attribute_string()) return result; + + uint8_t attr_u8 { }; + if (Genode::ascii_to(attr_str.string(), attr_u8) == attr_str.length() - 1) { + result = Icmp_packet::code_from_uint8(Icmp_packet::Type::DST_UNREACHABLE, attr_u8); + if (result != Icmp_packet::Code::INVALID) + return result; } - catch (Xml_node::Nonexistent_attribute) { - return Icmp_packet::Code::INVALID; - } + warning("attribute 'icmp_type_3_code_on_fragm_ipv4' has invalid value"); + return result; } @@ -120,119 +109,101 @@ Configuration::Configuration(Env &env, { /* do parts of domain initialization that do not lookup other domains */ node.for_each_sub_node("domain", [&] (Xml_node const node) { - try { - Domain_name const name { - node.attribute_value("name", Domain_name { }) }; + Domain_name const name { + node.attribute_value("name", Domain_name { }) }; - _domains.with_element( - name, - [&] /* match_fn */ (Domain &other_domain) - { - if (_verbose) { + _domains.with_element( + name, + [&] /* match_fn */ (Domain &other_domain) + { + if (_verbose) { - log("[", name, - "] invalid domain (name not unique) "); + log("[", name, + "] invalid domain (name not unique) "); - log("[", other_domain, - "] invalid domain (name not unique) "); - } - destroy(_alloc, &other_domain); - }, - [&] /* no_match_fn */ () - { - new (_alloc) Domain { - *this, node, name, _alloc, _domains }; + log("[", other_domain, + "] invalid domain (name not unique) "); } - ); - } - catch (Domain::Invalid) { } + destroy(_alloc, &other_domain); + }, + [&] /* no_match_fn */ () + { + Domain &domain = *new (_alloc) Domain { *this, node, name, _alloc, _domains }; + if (!domain.finish_construction()) + destroy(_alloc, &domain); + } + ); }); /* do parts of domain initialization that may lookup other domains */ while (true) { + Domain *invalid_domain_ptr { }; + _domains.for_each([&] (Domain &domain) { + if (invalid_domain_ptr) + return; - struct Retry_without_domain : Genode::Exception - { - Domain &domain; + if (!domain.init(_domains)) { + invalid_domain_ptr = &domain; + return; + } + if (_verbose) { + log("[", domain, "] initiated domain"); } + }); + if (!invalid_domain_ptr) + break; - Retry_without_domain(Domain &domain) : domain(domain) { } - }; - try { - _domains.for_each([&] (Domain &domain) { - try { domain.init(_domains); } - catch (Domain::Invalid) { throw Retry_without_domain(domain); } - if (_verbose) { - log("[", domain, "] initiated domain"); } - }); - } - catch (Retry_without_domain exception) { + /* destroy domain that became invalid during initialization */ + destroy(_alloc, invalid_domain_ptr); - /* destroy domain that became invalid during initialization */ - destroy(_alloc, &exception.domain); - - /* deinitialize the remaining domains again */ - _domains.for_each([&] (Domain &domain) { - domain.deinit(); - if (_verbose) { - log("[", domain, "] deinitiated domain"); } - }); - /* retry to initialize the remaining domains */ - continue; - } - break; + /* deinitialize the remaining domains again */ + _domains.for_each([&] (Domain &domain) { + domain.deinit(); + if (_verbose) { + log("[", domain, "] deinitiated domain"); } + }); } - try { - /* 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 = old_config._reporter(); - old_config._reporter = Pointer(); - } - catch (Pointer::Invalid) { - + node.with_optional_sub_node("report", [&] (Xml_node const &report_node) { + if (old_config._reporter_ptr) { + /* re-use existing reporter */ + _reporter_ptr = old_config._reporter_ptr; + old_config._reporter_ptr = nullptr; + } else { /* there is no reporter by now, create a new one */ - _reporter = *new (_alloc) Reporter(env, "state", nullptr, 4096 * 4); + _reporter_ptr = new (_alloc) Reporter(env, "state", nullptr, 4096 * 4); } /* create report generator */ - _report = *new (_alloc) - Report { - _verbose, report_node, timer, _domains, shared_quota, env.pd(), - _reporter(), report_signal_cap }; - } - catch (Genode::Xml_node::Nonexistent_sub_node) { } - + _report.construct( + _verbose, report_node, timer, _domains, shared_quota, env.pd(), + *_reporter_ptr, report_signal_cap); + }); /* initialize NIC clients */ _node.for_each_sub_node("nic-client", [&] (Xml_node const node) { - try { - Session_label const label { - node.attribute_value("label", Session_label::String { }) }; + Session_label const label { + node.attribute_value("label", Session_label::String { }) }; - Domain_name const domain { - node.attribute_value("domain", Domain_name { }) }; + Domain_name const domain { + node.attribute_value("domain", Domain_name { }) }; - _nic_clients.with_element( - label, - [&] /* match */ (Nic_client &nic_client) - { - if (_verbose) { + _nic_clients.with_element( + label, + [&] /* match */ (Nic_client &nic_client) + { + if (_verbose) { - log("[", domain, "] invalid NIC client: ", - label, " (label not unique)"); + log("[", domain, "] invalid NIC client: ", + label, " (label not unique)"); - log("[", nic_client.domain(), "] invalid NIC client: ", - nic_client.label(), " (label not unique)"); - } - destroy(_alloc, &nic_client); - }, - [&] /* no_match */ () - { - new (_alloc) Nic_client { - label, domain, alloc, old_config._nic_clients, - _nic_clients, env, timer, interfaces, *this }; + log("[", nic_client.domain(), "] invalid NIC client: ", + nic_client.label(), " (label not unique)"); } - ); - } - catch (Nic_client::Invalid) { } + destroy(_alloc, &nic_client); + }, + [&] /* no_match */ () + { + Nic_client &nic_client = *new (_alloc) Nic_client { label, domain, alloc, _nic_clients, *this }; + if (!nic_client.finish_construction(env, timer, interfaces, old_config._nic_clients)) + destroy(_alloc, &nic_client); + } + ); }); /* * Destroy old NIC clients to ensure that NIC client interfaces that were @@ -248,12 +219,8 @@ Configuration::~Configuration() _nic_clients.destroy_each(_alloc); /* destroy reporter */ - try { destroy(_alloc, &_reporter()); } - catch (Pointer::Invalid) { } - - /* destroy report generator */ - try { destroy(_alloc, &_report()); } - catch (Pointer::Invalid) { } + if (_reporter_ptr) + destroy(_alloc, _reporter_ptr); /* 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 a6c4fca841..2c6679707c 100644 --- a/repos/os/src/server/nic_router/configuration.h +++ b/repos/os/src/server/nic_router/configuration.h @@ -33,27 +33,33 @@ class Net::Configuration using Mac_string = Genode::String<17>; - Genode::Allocator &_alloc; - unsigned long const _max_packets_per_signal; - bool const _verbose; - bool const _verbose_packets; - bool const _verbose_packet_drop; - bool const _verbose_domain_state; - bool const _trace_packets; - bool const _icmp_echo_server; - Icmp_packet::Code const _icmp_type_3_code_on_fragm_ipv4; - Genode::Microseconds const _dhcp_discover_timeout; - Genode::Microseconds const _dhcp_request_timeout; - Genode::Microseconds const _dhcp_offer_timeout; - Genode::Microseconds const _icmp_idle_timeout; - Genode::Microseconds const _udp_idle_timeout; - Genode::Microseconds const _tcp_idle_timeout; - Genode::Microseconds const _tcp_max_segm_lifetime; - Pointer _report { }; - Pointer _reporter { }; - Domain_dict _domains { }; - Nic_client_dict _nic_clients { }; - Genode::Xml_node const _node; + Genode::Allocator &_alloc; + unsigned long const _max_packets_per_signal; + bool const _verbose; + bool const _verbose_packets; + bool const _verbose_packet_drop; + bool const _verbose_domain_state; + bool const _trace_packets; + bool const _icmp_echo_server; + Icmp_packet::Code const _icmp_type_3_code_on_fragm_ipv4; + Genode::Microseconds const _dhcp_discover_timeout; + Genode::Microseconds const _dhcp_request_timeout; + Genode::Microseconds const _dhcp_offer_timeout; + Genode::Microseconds const _icmp_idle_timeout; + Genode::Microseconds const _udp_idle_timeout; + Genode::Microseconds const _tcp_idle_timeout; + Genode::Microseconds const _tcp_max_segm_lifetime; + Genode::Constructible _report { }; + Genode::Reporter *_reporter_ptr { }; + Domain_dict _domains { }; + Nic_client_dict _nic_clients { }; + Genode::Xml_node const _node; + + /* + * Noncopyable + */ + Configuration(Configuration const &); + Configuration &operator = (Configuration const &); Icmp_packet::Code _init_icmp_type_3_code_on_fragm_ipv4(Genode::Xml_node const &node) const; @@ -77,6 +83,8 @@ class Net::Configuration ~Configuration(); + void with_report(auto const &fn) { if (_report.constructed()) fn(*_report); } + /*************** ** Accessors ** @@ -98,7 +106,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_dict &domains() { return _domains; } - Report &report() { return _report(); } Genode::Xml_node node() const { return _node; } }; diff --git a/repos/os/src/server/nic_router/dhcp.h b/repos/os/src/server/nic_router/dhcp.h index 87e52162ab..7325bc3b59 100644 --- a/repos/os/src/server/nic_router/dhcp.h +++ b/repos/os/src/server/nic_router/dhcp.h @@ -22,8 +22,9 @@ namespace Net { template static Ipv4_address dhcp_ipv4_option(Dhcp_packet &dhcp) { - try { return dhcp.option().value(); } - catch (Dhcp_packet::Option_not_found) { return Ipv4_address { }; } + Ipv4_address result = { }; + dhcp.with_option([&] (auto opt) { result = opt.value(); }); + return result; } } diff --git a/repos/os/src/server/nic_router/dhcp_client.cc b/repos/os/src/server/nic_router/dhcp_client.cc index dd8553897f..04ded4570f 100644 --- a/repos/os/src/server/nic_router/dhcp_client.cc +++ b/repos/os/src/server/nic_router/dhcp_client.cc @@ -20,7 +20,6 @@ using namespace Genode; using namespace Net; using Message_type = Dhcp_packet::Message_type; -using Drop_packet = Net::Interface::Drop_packet; using Dhcp_options = Dhcp_packet::Options_aggregator; @@ -46,12 +45,6 @@ void append_param_req_list(Dhcp_options &dhcp_opts) ** Dhcp_client ** *****************/ -Configuration &Dhcp_client::_config() { return _domain().config(); }; - - -Domain &Dhcp_client::_domain() { return _interface.domain(); } - - Dhcp_client::Dhcp_client(Cached_timer &timer, Interface &interface) : @@ -63,17 +56,17 @@ Dhcp_client::Dhcp_client(Cached_timer &timer, void Dhcp_client::discover() { enum { DISCOVER_PKT_SIZE = 309 }; - _set_state(State::SELECT, _config().dhcp_discover_timeout()); + _set_state(State::SELECT, _interface.config().dhcp_discover_timeout()); _send(Message_type::DISCOVER, Ipv4_address(), Ipv4_address(), Ipv4_address(), DISCOVER_PKT_SIZE); } -void Dhcp_client::_rerequest(State next_state) +void Dhcp_client::_rerequest(State next_state, Domain &domain) { enum { REREQUEST_PKT_SIZE = 309 }; - _set_state(next_state, _rerequest_timeout(2)); - Ipv4_address const client_ip = _domain().ip_config().interface().address; + _set_state(next_state, _rerequest_timeout(2, domain)); + Ipv4_address client_ip = domain.ip_config().interface().address; _send(Message_type::REQUEST, client_ip, Ipv4_address(), client_ip, REREQUEST_PKT_SIZE); } @@ -86,7 +79,7 @@ void Dhcp_client::_set_state(State state, Microseconds timeout) } -Microseconds Dhcp_client::_rerequest_timeout(unsigned lease_time_div_log2) +Microseconds Dhcp_client::_rerequest_timeout(unsigned lease_time_div_log2, Domain &domain) { /* FIXME limit the time because of shortcomings in timeout framework */ enum { MAX_TIMEOUT_SEC = 3600 }; @@ -94,14 +87,8 @@ Microseconds Dhcp_client::_rerequest_timeout(unsigned lease_time_div_log2) if (timeout_sec > MAX_TIMEOUT_SEC) { timeout_sec = MAX_TIMEOUT_SEC; - if (_interface.config().verbose()) { - try { - log("[", _interface.domain(), "] prune re-request timeout of " - "DHCP client"); - } - catch (Pointer::Invalid) { - log("[?] prune re-request timeout of DHCP client"); } - } + if (_interface.config().verbose()) + log("[", domain, "] prune re-request timeout of DHCP client"); } return Microseconds(timeout_sec * 1000 * 1000); } @@ -109,35 +96,40 @@ Microseconds Dhcp_client::_rerequest_timeout(unsigned lease_time_div_log2) void Dhcp_client::_handle_timeout(Duration) { - switch (_state) { - case State::BOUND: _rerequest(State::RENEW); break; - case State::RENEW: _rerequest(State::REBIND); break; - case State::REBIND: _domain().discard_ip_config(); [[fallthrough]]; - default: discover(); - } + _interface.with_domain( + [&] /* domain_fn */ (Domain &domain) { + switch (_state) { + case State::BOUND: _rerequest(State::RENEW, domain); break; + case State::RENEW: _rerequest(State::REBIND, domain); break; + case State::REBIND: + + domain.discard_ip_config(); + discover(); + break; + + default: discover(); break; + } + }, + [&] /* no_domain_fn */ { + if (_interface.config().verbose()) + log("[?] no domain on DHCP timeout"); }); } -void Dhcp_client::handle_dhcp_reply(Dhcp_packet &dhcp) +Packet_result Dhcp_client::handle_dhcp_reply(Dhcp_packet &dhcp, Domain &domain) { - try { - Message_type const msg_type = - dhcp.option().value(); + Packet_result result { }; + auto no_msg_type_fn = [&] { result = packet_drop("DHCP request misses option \"Message Type\""); }; + auto msg_type_fn = [&] (Dhcp_packet::Message_type_option const &msg_type) { if (_interface.config().verbose_domain_state()) { - if (msg_type == Message_type::OFFER) { - Ipv4_address dns_server; - Ipv4_address subnet_mask; - Ipv4_address router_ip; + if (msg_type.value() == Message_type::OFFER) { + Ipv4_address dns_server, subnet_mask, router_ip; + dhcp.with_option([&] (auto opt) { dns_server = opt.value(); }); + dhcp.with_option([&] (auto opt) { subnet_mask = opt.value(); }); + dhcp.with_option([&] (auto opt) { router_ip = opt.value(); }); - try { dns_server = dhcp.option().value(); } - catch (Dhcp_packet::Option_not_found) { } - try { subnet_mask = dhcp.option().value(); } - catch (Dhcp_packet::Option_not_found) { } - try { router_ip = dhcp.option().value(); } - catch (Net::Dhcp_packet::Option_not_found) { } - - log("[", _interface.domain(), "] dhcp offer from ", + log("[", domain, "] dhcp offer from ", dhcp.siaddr(), ", offering ", dhcp.yiaddr(), ", subnet-mask ", subnet_mask, @@ -145,38 +137,40 @@ void Dhcp_client::handle_dhcp_reply(Dhcp_packet &dhcp) ", DNS server ", dns_server); } } - switch (_state) { case State::SELECT: - if (msg_type != Message_type::OFFER) { - throw Drop_packet("DHCP client expects an offer"); + if (msg_type.value() != Message_type::OFFER) { + result = packet_drop("DHCP client expects an offer"); + break; } enum { REQUEST_PKT_SIZE = 321 }; - _set_state(State::REQUEST, _config().dhcp_request_timeout()); + _set_state(State::REQUEST, _interface.config().dhcp_request_timeout()); _send(Message_type::REQUEST, Ipv4_address(), dhcp.option().value(), dhcp.yiaddr(), REQUEST_PKT_SIZE); + result = packet_handled(); break; case State::REQUEST: case State::RENEW: case State::REBIND: { - if (msg_type != Message_type::ACK) { - throw Drop_packet("DHCP client expects an acknowledgement"); + if (msg_type.value() != Message_type::ACK) { + result = packet_drop("DHCP client expects an acknowledgement"); + break; } _lease_time_sec = dhcp.option().value(); - _set_state(State::BOUND, _rerequest_timeout(1)); - _domain().ip_config_from_dhcp_ack(dhcp); + _set_state(State::BOUND, _rerequest_timeout(1, domain)); + domain.ip_config_from_dhcp_ack(dhcp); + result = packet_handled(); break; } - default: throw Drop_packet("DHCP client doesn't expect a packet"); + default: result = packet_drop("DHCP client doesn't expect a packet"); break; } - } - catch (Dhcp_packet::Option_not_found) { - throw Drop_packet("DHCP reply misses required option"); - } + }; + dhcp.with_option(msg_type_fn, no_msg_type_fn); + return result; } @@ -243,8 +237,7 @@ void Dhcp_client::_send(Message_type msg_type, } break; - default: - throw Interface::Bad_send_dhcp_args(); + default: ASSERT_NEVER_REACHED; } dhcp_opts.append_option(); diff --git a/repos/os/src/server/nic_router/dhcp_client.h b/repos/os/src/server/nic_router/dhcp_client.h index 066cdc726a..ab1e0dd453 100644 --- a/repos/os/src/server/nic_router/dhcp_client.h +++ b/repos/os/src/server/nic_router/dhcp_client.h @@ -16,6 +16,7 @@ /* local includes */ #include +#include /* Genode includes */ #include @@ -45,9 +46,9 @@ class Net::Dhcp_client void _handle_timeout(Genode::Duration); - void _rerequest(State next_state); + void _rerequest(State next_state, Domain &domain); - Genode::Microseconds _rerequest_timeout(unsigned lease_time_div_log2); + Genode::Microseconds _rerequest_timeout(unsigned lease_time_div_log2, Domain &domain); void _set_state(State state, Genode::Microseconds timeout); @@ -57,16 +58,12 @@ class Net::Dhcp_client Ipv4_address requested_ip, Genode::size_t pkt_size); - Configuration &_config(); - - Domain &_domain(); - public: Dhcp_client(Cached_timer &timer, Interface &interface); - void handle_dhcp_reply(Dhcp_packet &dhcp); + [[nodiscard]] Packet_result handle_dhcp_reply(Dhcp_packet &dhcp, Domain &domain); void discover(); }; diff --git a/repos/os/src/server/nic_router/dhcp_server.cc b/repos/os/src/server/nic_router/dhcp_server.cc index 756c22cb0e..2692c80e49 100644 --- a/repos/os/src/server/nic_router/dhcp_server.cc +++ b/repos/os/src/server/nic_router/dhcp_server.cc @@ -26,26 +26,31 @@ using namespace Genode; ** Dhcp_server_base ** **********************/ -Dhcp_server_base::Dhcp_server_base(Xml_node const &node, - Domain const &domain, - Allocator &alloc) -: - _alloc { alloc } +Dhcp_server_base::Dhcp_server_base(Allocator &alloc) : _alloc { alloc } { } + + +bool Dhcp_server_base::finish_construction(Xml_node const &node, Domain const &domain) { + bool result = true; node.for_each_sub_node("dns-server", [&] (Xml_node const &sub_node) { + if (!result) + return; Dns_server::construct( - alloc, sub_node.attribute_value("ip", Ipv4_address { }), + _alloc, sub_node.attribute_value("ip", Ipv4_address { }), [&] /* handle_success */ (Dns_server &server) { _dns_servers.insert_as_tail(server); }, [&] /* handle_failure */ () { - _invalid(domain, "invalid DNS server entry"); + result = _invalid(domain, "invalid DNS server entry"); } ); }); + if (!result) + return result; + node.with_optional_sub_node("dns-domain", [&] (Xml_node const &sub_node) { xml_node_with_attribute(sub_node, "name", [&] (Xml_attribute const &attr) { _dns_domain_name.set_to(attr); @@ -58,6 +63,7 @@ Dhcp_server_base::Dhcp_server_base(Xml_node const &node, } }); }); + return result; } @@ -67,13 +73,13 @@ Dhcp_server_base::~Dhcp_server_base() } -void Dhcp_server_base::_invalid(Domain const &domain, +bool Dhcp_server_base::_invalid(Domain const &domain, char const *reason) { if (domain.config().verbose()) { log("[", domain, "] invalid DHCP server (", reason, ")"); } - throw Domain::Invalid(); + return false; } @@ -83,37 +89,53 @@ void Dhcp_server_base::_invalid(Domain const &domain, bool Dhcp_server::dns_servers_empty() const { - if (_dns_config_from.valid()) { - + if (_dns_config_from_ptr) return _resolve_dns_config_from().dns_servers_empty(); - } return _dns_servers.empty(); } -Dhcp_server::Dhcp_server(Xml_node const node, - Domain &domain, - Allocator &alloc, - Ipv4_address_prefix const &interface, - Domain_dict &domains) +Dhcp_server::Dhcp_server(Xml_node const node, Allocator &alloc) : - Dhcp_server_base(node, domain, alloc), - _dns_config_from(_init_dns_config_from(node, domains)), + Dhcp_server_base(alloc), _ip_lease_time (_init_ip_lease_time(node)), _ip_first(node.attribute_value("ip_first", Ipv4_address())), _ip_last(node.attribute_value("ip_last", Ipv4_address())), _ip_first_raw(_ip_first.to_uint32_little_endian()), _ip_count(_ip_last.to_uint32_little_endian() - _ip_first_raw + 1), _ip_alloc(alloc, _ip_count) +{ } + + +bool Dhcp_server::finish_construction(Xml_node const node, + Domain_dict &domains, + Domain &domain, + Ipv4_address_prefix const &interface) { + if (!Dhcp_server_base::finish_construction(node, domain)) + return false; + + if (_dns_servers.empty() && !_dns_domain_name.valid()) { + Domain_name dns_config_from = node.attribute_value("dns_config_from", Domain_name()); + if (dns_config_from != Domain_name()) { + bool result = true; + domains.with_element(dns_config_from, + [&] (Domain &remote_domain) { _dns_config_from_ptr = &remote_domain; }, + [&] { result = _invalid(domain, "invalid dns_config_from attribute"); }); + if (!result) + return result; + } + } if (!interface.prefix_matches(_ip_first)) { - _invalid(domain, "first IP does not match domain subnet"); } + return _invalid(domain, "first IP does not match domain subnet"); } if (!interface.prefix_matches(_ip_last)) { - _invalid(domain, "last IP does not match domain subnet"); } + return _invalid(domain, "last IP does not match domain subnet"); } if (interface.address.is_in_range(_ip_first, _ip_last)) { - _invalid(domain, "IP range contains IP address of domain"); } + return _invalid(domain, "IP range contains IP address of domain"); } + + return true; } @@ -137,8 +159,8 @@ void Dhcp_server::print(Output &output) const _dns_domain_name.with_string([&] (Dns_domain_name::String const &str) { Genode::print(output, "DNS domain name ", str, ", "); }); - try { Genode::print(output, "DNS config from ", _dns_config_from(), ", "); } - catch (Pointer::Invalid) { } + with_dns_config_from([&] (Domain &domain) { + Genode::print(output, "DNS config from ", domain, ", "); }); Genode::print(output, "IP first ", _ip_first, ", last ", _ip_last, @@ -157,77 +179,36 @@ bool Dhcp_server::config_equal_to_that_of(Dhcp_server const &other) const Ipv4_config const &Dhcp_server::_resolve_dns_config_from() const { - return _dns_config_from().ip_config(); + return _dns_config_from_ptr->ip_config(); } -Ipv4_address Dhcp_server::alloc_ip() +Dhcp_server::Alloc_ip_result Dhcp_server::alloc_ip() { - try { - return Ipv4_address::from_uint32_little_endian(_ip_alloc.alloc() + - _ip_first_raw); - } - catch (Bit_allocator_dynamic::Out_of_indices) { - throw Alloc_ip_failed(); - } + Alloc_ip_result result = Alloc_ip_error(); + _ip_alloc.alloc().with_result( + [&] (addr_t ip_raw) { result = Alloc_ip_result(Ipv4_address::from_uint32_little_endian(ip_raw + _ip_first_raw)); }, + [&] (auto) { }); + return result; } -void Dhcp_server::alloc_ip(Ipv4_address const &ip) +bool 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(); } + return _ip_alloc.alloc_addr(ip.to_uint32_little_endian() - _ip_first_raw); } -void Dhcp_server::free_ip(Domain const &domain, - Ipv4_address const &ip) +void Dhcp_server::free_ip(Ipv4_address const &ip) { - /* - * The messages in the catch directives are printed as errors and - * independent from the routers verbosity configuration because the - * exceptions they indicate should never be thrown. - */ - try { - _ip_alloc.free(ip.to_uint32_little_endian() - _ip_first_raw); - } - catch (Bit_allocator_dynamic::Out_of_indices) { - - error("[", domain, "] DHCP server: out of indices while freeing IP ", - ip, " (IP range: first ", _ip_first, " last ", _ip_last, ")"); - } - catch (Bit_array_dynamic::Invalid_index_access) { - - error("[", domain, "] DHCP server: invalid index while freeing IP ", - ip, " (IP range: first ", _ip_first, " last ", _ip_last, ")"); - } -} - - -Pointer Dhcp_server::_init_dns_config_from(Genode::Xml_node const node, - Domain_dict &domains) -{ - if (!_dns_servers.empty() || - _dns_domain_name.valid()) { - - return Pointer(); - } - Domain_name dns_config_from = - node.attribute_value("dns_config_from", Domain_name()); - - if (dns_config_from == Domain_name()) { - return Pointer(); - } - return domains.deprecated_find_by_name(dns_config_from); + ASSERT(_ip_alloc.free(ip.to_uint32_little_endian() - _ip_first_raw)); } bool Dhcp_server::has_invalid_remote_dns_cfg() const { - if (_dns_config_from.valid()) { - return !_dns_config_from().ip_config().valid(); - } + if (_dns_config_from_ptr) + return !_dns_config_from_ptr->ip_config().valid(); return false; } @@ -269,19 +250,6 @@ bool Dhcp_allocation::_higher(Mac_address const &mac) const } -Dhcp_allocation &Dhcp_allocation::find_by_mac(Mac_address const &mac) -{ - if (mac == _mac) { - return *this; } - - Dhcp_allocation *const allocation = child(_higher(mac)); - if (!allocation) { - throw Dhcp_allocation_tree::No_match(); } - - return allocation->find_by_mac(mac); -} - - void Dhcp_allocation::print(Output &output) const { Genode::print(output, "MAC ", _mac, " IP ", _ip); @@ -292,17 +260,3 @@ void Dhcp_allocation::_handle_timeout(Duration) { _interface.dhcp_allocation_expired(*this); } - - -/************************** - ** Dhcp_allocation_tree ** - **************************/ - -Dhcp_allocation & -Dhcp_allocation_tree::find_by_mac(Mac_address const &mac) const -{ - if (!_tree.first()) { - throw No_match(); } - - 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 6022272797..e48cc25f01 100644 --- a/repos/os/src/server/nic_router/dhcp_server.h +++ b/repos/os/src/server/nic_router/dhcp_server.h @@ -17,7 +17,6 @@ /* local includes */ #include #include -#include #include #include #include @@ -52,14 +51,15 @@ class Net::Dhcp_server_base Dns_server_list _dns_servers { }; Dns_domain_name _dns_domain_name { _alloc }; - void _invalid(Domain const &domain, - char const *reason); + [[nodiscard]] bool _invalid(Domain const &domain, + char const *reason); public: - Dhcp_server_base(Genode::Xml_node const &node, - Domain const &domain, - Genode::Allocator &alloc); + Dhcp_server_base(Genode::Allocator &alloc); + + [[nodiscard]] bool finish_construction(Genode::Xml_node const &node, + Domain const &domain); ~Dhcp_server_base(); }; @@ -70,7 +70,7 @@ class Net::Dhcp_server : private Genode::Noncopyable, { private: - Pointer const _dns_config_from; + Domain *_dns_config_from_ptr { }; Genode::Microseconds const _ip_lease_time; Ipv4_address const _ip_first; Ipv4_address const _ip_last; @@ -80,37 +80,43 @@ class Net::Dhcp_server : private Genode::Noncopyable, Genode::Microseconds _init_ip_lease_time(Genode::Xml_node const node); - Pointer _init_dns_config_from(Genode::Xml_node const node, - Domain_dict &domains); - Ipv4_config const &_resolve_dns_config_from() const; + /* + * Noncopyable + */ + Dhcp_server(Dhcp_server const &); + Dhcp_server &operator = (Dhcp_server const &); + public: enum { DEFAULT_IP_LEASE_TIME_SEC = 3600 }; - struct Alloc_ip_failed : Genode::Exception { }; - struct Invalid : Genode::Exception { }; + struct Invalid : Genode::Exception { }; - Dhcp_server(Genode::Xml_node const node, - Domain &domain, - Genode::Allocator &alloc, - Ipv4_address_prefix const &interface, - Domain_dict &domains); + Dhcp_server(Genode::Xml_node const node, + Genode::Allocator &alloc); - Ipv4_address alloc_ip(); + [[nodiscard]] bool finish_construction(Genode::Xml_node const node, + Domain_dict &domains, + Domain &domain, + Ipv4_address_prefix const &interface); - void alloc_ip(Ipv4_address const &ip); + struct Alloc_ip_error { }; + using Alloc_ip_result = Genode::Attempt; - void free_ip(Domain const &domain, - Ipv4_address const &ip); + [[nodiscard]] Alloc_ip_result alloc_ip(); + + [[nodiscard]] bool alloc_ip(Ipv4_address const &ip); + + void free_ip(Ipv4_address const &ip); bool has_invalid_remote_dns_cfg() const; template void for_each_dns_server_ip(FUNC && functor) const { - if (_dns_config_from.valid()) { + if (_dns_config_from_ptr) { _resolve_dns_config_from().for_each_dns_server( [&] (Dns_server const &dns_server) { @@ -129,7 +135,7 @@ class Net::Dhcp_server : private Genode::Noncopyable, Dns_domain_name const &dns_domain_name() const { - if (_dns_config_from.valid()) { + if (_dns_config_from_ptr) { return _resolve_dns_config_from().dns_domain_name(); } return _dns_domain_name; @@ -137,6 +143,12 @@ class Net::Dhcp_server : private Genode::Noncopyable, bool config_equal_to_that_of(Dhcp_server const &dhcp_server) const; + void with_dns_config_from(auto const &fn) const + { + if (_dns_config_from_ptr) + fn(*_dns_config_from_ptr); + } + /********* ** log ** @@ -149,7 +161,6 @@ class Net::Dhcp_server : private Genode::Noncopyable, ** Accessors ** ***************/ - Domain &dns_config_from() { return _dns_config_from(); } Genode::Microseconds ip_lease_time() const { return _ip_lease_time; } }; @@ -179,7 +190,18 @@ class Net::Dhcp_allocation : public Genode::Avl_node, ~Dhcp_allocation(); - Dhcp_allocation &find_by_mac(Mac_address const &mac); + void find_by_mac(Mac_address const &mac, auto const &match_fn, auto const &no_match_fn) + { + if (mac == _mac) { + match_fn(*this); + return; + } + Dhcp_allocation *allocation_ptr = child(_higher(mac)); + if (allocation_ptr) + allocation_ptr->find_by_mac(mac, match_fn, no_match_fn); + else + no_match_fn(); + } void lifetime(Genode::Microseconds lifetime); @@ -218,9 +240,13 @@ struct Net::Dhcp_allocation_tree public: - struct No_match : Genode::Exception { }; - - Dhcp_allocation &find_by_mac(Mac_address const &mac) const; + void find_by_mac(Mac_address const &mac, auto const &match_fn, auto const &no_match_fn) const + { + if (_tree.first()) + _tree.first()->find_by_mac(mac, match_fn, no_match_fn); + else + no_match_fn(); + } void insert(Dhcp_allocation &dhcp_alloc) { diff --git a/repos/os/src/server/nic_router/direct_rule.cc b/repos/os/src/server/nic_router/direct_rule.cc deleted file mode 100644 index 3700124cd7..0000000000 --- a/repos/os/src/server/nic_router/direct_rule.cc +++ /dev/null @@ -1,33 +0,0 @@ -/* - * \brief Routing rule for direct traffic between two interfaces - * \author Martin Stein - * \date 2016-08-19 - */ - -/* - * Copyright (C) 2016-2017 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU Affero General Public License version 3. - */ - -/* local includes */ -#include - -using namespace Net; -using namespace Genode; - - -Direct_rule_base::Direct_rule_base(Xml_node const node) -: - _dst(node.attribute_value("dst", Ipv4_address_prefix())) -{ - if (!_dst.valid()) { - throw Invalid(); } -} - - -void Direct_rule_base::print(Output &output) const -{ - Genode::print(output, _dst); -} diff --git a/repos/os/src/server/nic_router/direct_rule.h b/repos/os/src/server/nic_router/direct_rule.h index a33bb69590..0c6c4c924b 100644 --- a/repos/os/src/server/nic_router/direct_rule.h +++ b/repos/os/src/server/nic_router/direct_rule.h @@ -40,16 +40,14 @@ class Net::Direct_rule_base public: - struct Invalid : Genode::Exception { }; - - Direct_rule_base(Genode::Xml_node const node); + Direct_rule_base(Ipv4_address_prefix const dst) : _dst(dst) { } /********* ** log ** *********/ - void print(Genode::Output &output) const; + void print(Genode::Output &output) const { Genode::print(output, "dst ", _dst); } /*************** @@ -64,7 +62,7 @@ template struct Net::Direct_rule : Direct_rule_base, Direct_rule_list::Element { - Direct_rule(Genode::Xml_node const node) : Direct_rule_base(node) { } + Direct_rule(Ipv4_address_prefix const &dst) : Direct_rule_base(dst) { } }; diff --git a/repos/os/src/server/nic_router/dns.cc b/repos/os/src/server/nic_router/dns.cc index d4cbe71f83..874e2eb010 100644 --- a/repos/os/src/server/nic_router/dns.cc +++ b/repos/os/src/server/nic_router/dns.cc @@ -47,10 +47,10 @@ void Dns_domain_name::set_to(Dns_domain_name const &name) { if (name.valid()) { name.with_string([&] (String const &string) { - if (_string.valid()) { - _string() = string; + if (_string_ptr) { + *_string_ptr = string; } else { - _string = *new (_alloc) String { string }; + _string_ptr = new (_alloc) String { string }; } }); } else { @@ -63,10 +63,10 @@ void Dns_domain_name::set_to(Xml_attribute const &name_attr) { name_attr.with_raw_value([&] (char const *base, size_t size) { if (size < STRING_CAPACITY) { - if (_string.valid()) { - _string() = Cstring { base, size }; + if (_string_ptr) { + *_string_ptr = Cstring { base, size }; } else { - _string = *new (_alloc) String { Cstring { base, size } }; + _string_ptr = new (_alloc) String { Cstring { base, size } }; } } else { set_invalid(); @@ -79,10 +79,10 @@ void Dns_domain_name::set_to(Dhcp_packet::Domain_name const &name_option) { name_option.with_string([&] (char const *base, size_t size) { if (size < STRING_CAPACITY) { - if (_string.valid()) { - _string() = Cstring { base, size }; + if (_string_ptr) { + *_string_ptr = Cstring { base, size }; } else { - _string = *new (_alloc) String { Cstring { base, size } }; + _string_ptr = new (_alloc) String { Cstring { base, size } }; } } else { set_invalid(); @@ -93,22 +93,21 @@ void Dns_domain_name::set_to(Dhcp_packet::Domain_name const &name_option) void Dns_domain_name::set_invalid() { - if (_string.valid()) { - _alloc.free(&_string(), sizeof(String)); - _string = { }; + if (_string_ptr) { + _alloc.free(_string_ptr, sizeof(String)); + _string_ptr = nullptr; } } bool Dns_domain_name::equal_to(Dns_domain_name const &other) const { - if (_string.valid()) { - if (other._string.valid()) { - return _string() == other._string(); - } + if (_string_ptr) { + if (other._string_ptr) + return *_string_ptr == *other._string_ptr; return false; } - return !other._string.valid(); + return !other._string_ptr; } diff --git a/repos/os/src/server/nic_router/dns.h b/repos/os/src/server/nic_router/dns.h index 2f8fb6e2a8..3f530194b3 100644 --- a/repos/os/src/server/nic_router/dns.h +++ b/repos/os/src/server/nic_router/dns.h @@ -16,7 +16,6 @@ /* local includes */ #include -#include /* Genode includes */ #include @@ -84,7 +83,13 @@ class Net::Dns_domain_name : private Genode::Noncopyable private: Genode::Allocator &_alloc; - Pointer _string { }; + String *_string_ptr { }; + + /* + * Noncopyable + */ + Dns_domain_name(Dns_domain_name const &); + Dns_domain_name &operator = (Dns_domain_name const &); public: @@ -100,14 +105,13 @@ class Net::Dns_domain_name : private Genode::Noncopyable void set_invalid(); - bool valid() const { return _string.valid(); } + bool valid() const { return _string_ptr; } template void with_string(FUNC && func) const { - if (_string.valid()) { - func(_string()); - } + if (_string_ptr) + func(*_string_ptr); } bool equal_to(Dns_domain_name const &other) const; diff --git a/repos/os/src/server/nic_router/domain.cc b/repos/os/src/server/nic_router/domain.cc index 491c8a1a6c..0fc0bb3559 100644 --- a/repos/os/src/server/nic_router/domain.cc +++ b/repos/os/src/server/nic_router/domain.cc @@ -41,10 +41,9 @@ void Domain::_log_ip_config() const bool Domain::ready() const { - if (_dhcp_server.valid()) { - if (_dhcp_server().has_invalid_remote_dns_cfg()) { + if (_dhcp_server_ptr) { + if (_dhcp_server_ptr->has_invalid_remote_dns_cfg()) return false; - } } return true; } @@ -61,8 +60,7 @@ void Domain::update_ready_state() void Domain::_prepare_reconstructing_ip_config() { - if (!_ip_config_dynamic) { - throw Ip_config_static(); } + ASSERT(_ip_config_dynamic); /* discard old IP config if any */ if (ip_config().valid()) { @@ -124,8 +122,7 @@ void Domain::_finish_reconstructing_ip_config() }); } /* force report if configured */ - try { _config.report().handle_config(); } - catch (Pointer::Invalid) { } + _config.with_report([&] (Report &r) { r.handle_config(); }); } @@ -168,47 +165,75 @@ void Domain::try_reuse_ip_config(Domain const &domain) } -void Domain::_read_forward_rules(Cstring const &protocol, +bool Domain::_read_forward_rules(Cstring const &protocol, Domain_dict &domains, Xml_node const node, char const *type, Forward_rule_tree &rules) { + bool result = true; node.for_each_sub_node(type, [&] (Xml_node const node) { - try { - Forward_rule &rule = *new (_alloc) Forward_rule(domains, node); - rules.insert(&rule); - if (_config.verbose()) { - log("[", *this, "] ", protocol, " forward rule: ", rule); } + if (!result) + return; + + Port port = node.attribute_value("port", Port(0)); + if (port == Port(0) || dynamic_port(port)) { + result = _invalid("invalid forward rule"); + return; } - catch (Forward_rule::Invalid) { _invalid("invalid forward rule"); } + Ipv4_address to_ip = node.attribute_value("to", Ipv4_address()); + if (!to_ip.valid()) { + result = _invalid("invalid forward rule"); + return; + } + domains.find_by_domain_attr(node, + [&] (Domain &domain) { + Forward_rule &rule = *new (_alloc) + Forward_rule(port, to_ip, node.attribute_value("to_port", Port(0)), domain); + rules.insert(&rule); + if (_config.verbose()) + log("[", *this, "] ", protocol, " forward rule: ", rule); }, + [&] { result = _invalid("invalid forward rule"); }); }); + return result; } -void Domain::_invalid(char const *reason) const +bool Domain::_invalid(char const *reason) const { if (_config.verbose()) { log("[", *this, "] invalid domain (", reason, ")"); } - throw Invalid(); + return false; } -void Domain::_read_transport_rules(Cstring const &protocol, +bool Domain::_read_transport_rules(Cstring const &protocol, Domain_dict &domains, Xml_node const node, char const *type, Transport_rule_list &rules) { + bool result = true; node.for_each_sub_node(type, [&] (Xml_node const node) { - try { - rules.insert(*new (_alloc) - Transport_rule(domains, node, _alloc, protocol, _config, *this)); + if (!result) + return; + + Ipv4_address_prefix dst = node.attribute_value("dst", Ipv4_address_prefix()); + if (!dst.valid()) { + result = _invalid("invalid transport rule"); + return; } - catch (Transport_rule::Invalid) { _invalid("invalid transport rule"); } - catch (Permit_any_rule::Invalid) { _invalid("invalid permit-any rule"); } - catch (Permit_single_rule::Invalid) { _invalid("invalid permit rule"); } + Transport_rule &rule = *new (_alloc) Transport_rule(dst, _alloc); + if (!rule.finish_construction(domains, node, protocol, _config, *this)) { + destroy(_alloc, &rule); + result = _invalid("invalid transport rule"); + return; + } + rules.insert(rule); + if (_config.verbose()) + log("[", *this, "] ", protocol, " rule: ", rule); }); + return result; } @@ -245,12 +270,18 @@ Domain::Domain(Configuration &config, String<160>()).string() } { _log_ip_config(); +} - if (Domain::name() == Domain_name()) { - _invalid("missing name attribute"); } + +bool Domain::finish_construction() const +{ + if (Domain::name() == Domain_name()) + return _invalid("missing name attribute"); if (_config.verbose_domain_state()) { log("[", *this, "] NIC sessions: ", _interface_cnt); } + + return true; } @@ -260,7 +291,7 @@ Link_side_tree &Domain::links(L3_protocol const protocol) case L3_protocol::TCP: return _tcp_links; case L3_protocol::UDP: return _udp_links; case L3_protocol::ICMP: return _icmp_links; - default: throw Interface::Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } } @@ -271,96 +302,84 @@ Domain::~Domain() } -Dhcp_server &Domain::dhcp_server() -{ - Dhcp_server &dhcp_server = _dhcp_server(); - if (dhcp_server.has_invalid_remote_dns_cfg()) { - throw Pointer::Invalid(); - } - return dhcp_server; -} - - -void Domain::init(Domain_dict &domains) +bool Domain::init(Domain_dict &domains) { /* read DHCP server configuration */ - try { - Xml_node const dhcp_server_node = _node.sub_node("dhcp-server"); + bool result = true; + _node.with_optional_sub_node("dhcp-server", [&] (Xml_node const &dhcp_server_node) { if (_ip_config_dynamic) { - _invalid("DHCP server and client at once"); } - - try { - Dhcp_server &dhcp_server = *new (_alloc) - Dhcp_server(dhcp_server_node, *this, _alloc, - ip_config().interface(), domains); - - try { - dhcp_server. - dns_config_from().ip_config_dependents().insert(this); - } - catch (Pointer::Invalid) { } - - _dhcp_server = dhcp_server; - if (_config.verbose()) { - log("[", *this, "] DHCP server: ", _dhcp_server()); } + result = _invalid("DHCP server and client at once"); + return; } - catch (Bit_allocator_dynamic::Out_of_indices) { - - /* - * This message is printed independent from the routers - * verbosity configuration in order to track down an exception - * of type Bit_allocator_dynamic::Out_of_indices that was - * previously not caught. We have observed this exception once, - * but without a specific use pattern that would - * enable for a systematic reproduction of the issue. - * The uncaught exception was observed in a 21.03 Sculpt OS - * with a manually configured router, re-configuration involved. - */ - log("[", *this, "] DHCP server: failed to initialize ", - "(IP range: first ", - dhcp_server_node.attribute_value("ip_first", Ipv4_address()), - " last ", - dhcp_server_node.attribute_value("ip_last", Ipv4_address()), - ")"); - - throw Dhcp_server::Invalid { }; + Dhcp_server &dhcp_server = *new (_alloc) Dhcp_server(dhcp_server_node, _alloc); + if (!dhcp_server.finish_construction(dhcp_server_node, domains, *this, ip_config().interface())) { + result = _invalid("invalid DHCP server"); + return; } - } - catch (Xml_node::Nonexistent_sub_node) { } - catch (Dhcp_server::Invalid) { _invalid("invalid DHCP server"); } + dhcp_server.with_dns_config_from([&] (Domain &domain) { + domain.ip_config_dependents().insert(this); }); - /* read forward rules */ - _read_forward_rules(tcp_name(), domains, _node, "tcp-forward", - _tcp_forward_rules); - _read_forward_rules(udp_name(), domains, _node, "udp-forward", - _udp_forward_rules); + _dhcp_server_ptr = &dhcp_server; + if (_config.verbose()) { + log("[", *this, "] DHCP server: ", dhcp_server); } + }); + if (!result) + return result; - /* read UDP and TCP rules */ - _read_transport_rules(tcp_name(), domains, _node, "tcp", _tcp_rules); - _read_transport_rules(udp_name(), domains, _node, "udp", _udp_rules); + /* read forward and transport rules */ + if (!_read_forward_rules(tcp_name(), domains, _node, "tcp-forward", _tcp_forward_rules) || + !_read_forward_rules(udp_name(), domains, _node, "udp-forward", _udp_forward_rules) || + !_read_transport_rules(tcp_name(), domains, _node, "tcp", _tcp_rules) || + !_read_transport_rules(udp_name(), domains, _node, "udp", _udp_rules)) + return false; /* read NAT rules */ _node.for_each_sub_node("nat", [&] (Xml_node const node) { - try { - Nat_rule &rule = *new (_alloc) - Nat_rule(domains, _tcp_port_alloc, _udp_port_alloc, - _icmp_port_alloc, node, _config.verbose()); - _nat_rules.insert(&rule); - if (_config.verbose()) { - log("[", *this, "] NAT rule: ", rule); } - } - catch (Nat_rule::Invalid) { _invalid("invalid NAT rule"); } + if (!result) + return; + + domains.find_by_domain_attr(node, + [&] (Domain &domain) { + Nat_rule &rule = *new (_alloc) + Nat_rule(domain, _tcp_port_alloc, _udp_port_alloc, + _icmp_port_alloc, node, _config.verbose()); + _nat_rules.insert(&rule); + if (_config.verbose()) + log("[", *this, "] NAT rule: ", rule); }, + [&] { result = _invalid("invalid NAT rule"); }); }); + if (!result) + return result; + /* read ICMP rules */ _node.for_each_sub_node("icmp", [&] (Xml_node const node) { - try { _icmp_rules.insert(*new (_alloc) Ip_rule(domains, node)); } - catch (Ip_rule::Invalid) { _invalid("invalid ICMP rule"); } + if (!result) + return; + + Ipv4_address_prefix dst = node.attribute_value("dst", Ipv4_address_prefix()); + if (!dst.valid()) { + result = _invalid("invalid ICMP rule"); + return; + } + domains.find_by_domain_attr(node, + [&] (Domain &domain) { _icmp_rules.insert(*new (_alloc) Ip_rule(dst, domain)); }, + [&] { result = _invalid("invalid ICMP rule"); }); }); /* read IP rules */ _node.for_each_sub_node("ip", [&] (Xml_node const node) { - try { _ip_rules.insert(*new (_alloc) Ip_rule(domains, node)); } - catch (Ip_rule::Invalid) { _invalid("invalid IP rule"); } + if (!result) + return; + + Ipv4_address_prefix dst = node.attribute_value("dst", Ipv4_address_prefix()); + if (!dst.valid()) { + result = _invalid("invalid IP rule"); + return; + } + domains.find_by_domain_attr(node, + [&] (Domain &domain) { _ip_rules.insert(*new (_alloc) Ip_rule(dst, domain)); }, + [&] { result = _invalid("invalid IP rule"); }); }); + return result; } @@ -373,22 +392,11 @@ void Domain::deinit() _tcp_rules.destroy_each(_alloc); _udp_forward_rules.destroy_each(_alloc); _tcp_forward_rules.destroy_each(_alloc); - try { - Dhcp_server &dhcp_server = _dhcp_server(); - _dhcp_server = Pointer(); - try { dhcp_server.dns_config_from().ip_config_dependents().remove(this); } - catch (Pointer::Invalid) { } - destroy(_alloc, &dhcp_server); - } - catch (Pointer::Invalid) { } -} - - -Ipv4_address const &Domain::next_hop(Ipv4_address const &ip) const -{ - if (ip_config().interface().prefix_matches(ip)) { return ip; } - if (ip_config().gateway_valid()) { return ip_config().gateway(); } - throw No_next_hop(); + with_dhcp_server([&] (Dhcp_server &dhcp_server) { + _dhcp_server_ptr = nullptr; + dhcp_server.with_dns_config_from([&] (Domain &domain) { + domain.ip_config_dependents().remove(this); }); + destroy(_alloc, &dhcp_server); }); } @@ -424,54 +432,53 @@ void Domain::interface_updates_domain_object(Interface &interface) } -void Domain::report(Xml_generator &xml) +bool Domain::report_empty(Report const &report_cfg) const { - xml.node("domain", [&] () { - bool empty = true; - xml.attribute("name", name()); - if (_config.report().bytes()) { - xml.attribute("rx_bytes", _tx_bytes); - xml.attribute("tx_bytes", _rx_bytes); - empty = false; - } - if (_config.report().config()) { - xml.attribute("ipv4", String<19>(ip_config().interface())); - xml.attribute("gw", String<16>(ip_config().gateway())); - ip_config().for_each_dns_server([&] (Dns_server const &dns_server) { - xml.node("dns", [&] () { - xml.attribute("ip", String<16>(dns_server.ip())); - }); - }); - ip_config().dns_domain_name().with_string( - [&] (Dns_domain_name::String const &str) - { - xml.node("dns-domain", [&] () { - xml.attribute("name", str); - }); - }); - empty = false; - } - if (_config.report().stats()) { - try { xml.node("tcp-links", [&] () { _tcp_stats.report(xml); }); empty = false; } catch (Report::Empty) { } - try { xml.node("udp-links", [&] () { _udp_stats.report(xml); }); empty = false; } catch (Report::Empty) { } - try { xml.node("icmp-links", [&] () { _icmp_stats.report(xml); }); empty = false; } catch (Report::Empty) { } - try { xml.node("arp-waiters", [&] () { _arp_stats.report(xml); }); empty = false; } catch (Report::Empty) { } - try { xml.node("dhcp-allocations", [&] () { _dhcp_stats.report(xml); }); empty = false; } catch (Report::Empty) { } - } - if (_config.report().dropped_fragm_ipv4() && _dropped_fragm_ipv4) { - xml.node("dropped-fragm-ipv4", [&] () { - xml.attribute("value", _dropped_fragm_ipv4); - }); - empty = false; - } - _interfaces.for_each([&] (Interface &interface) { - try { - interface.report(xml); - empty = false; - } catch (Report::Empty) { } - }); - if (empty) { - throw Report::Empty(); } + bool bytes = report_cfg.bytes(); + bool cfg = report_cfg.config(); + bool stats = report_cfg.stats() && ( + !_tcp_stats.report_empty() || !_udp_stats.report_empty() || + !_icmp_stats.report_empty() || !_arp_stats.report_empty() || _dhcp_stats.report_empty()); + bool fragm_ip = report_cfg.dropped_fragm_ipv4() && _dropped_fragm_ipv4; + bool interfaces = false; + _interfaces.for_each([&] (Interface const &interface) { + if (!interface.report_empty(report_cfg)) + interfaces = true; }); + + return !bytes && !cfg && !stats && !fragm_ip && !interfaces; +} + + +void Domain::report(Xml_generator &xml, Report const &report_cfg) const +{ + xml.attribute("name", name()); + if (report_cfg.bytes()) { + xml.attribute("rx_bytes", _tx_bytes); + xml.attribute("tx_bytes", _rx_bytes); + } + if (report_cfg.config()) { + xml.attribute("ipv4", String<19>(ip_config().interface())); + xml.attribute("gw", String<16>(ip_config().gateway())); + ip_config().for_each_dns_server([&] (Dns_server const &dns_server) { + xml.node("dns", [&] () { + xml.attribute("ip", String<16>(dns_server.ip())); }); }); + ip_config().dns_domain_name().with_string([&] (Dns_domain_name::String const &str) { + xml.node("dns-domain", [&] () { + xml.attribute("name", str); }); }); + } + if (report_cfg.stats()) { + if (!_tcp_stats.report_empty()) xml.node("tcp-links", [&] { _tcp_stats.report(xml); }); + if (!_udp_stats.report_empty()) xml.node("udp-links", [&] { _udp_stats.report(xml); }); + if (!_icmp_stats.report_empty()) xml.node("icmp-links", [&] { _icmp_stats.report(xml); }); + if (!_arp_stats.report_empty()) xml.node("arp-waiters", [&] { _arp_stats.report(xml); }); + if (!_dhcp_stats.report_empty()) xml.node("dhcp-allocations", [&] { _dhcp_stats.report(xml); }); + } + if (report_cfg.dropped_fragm_ipv4() && _dropped_fragm_ipv4) + xml.node("dropped-fragm-ipv4", [&] () { + xml.attribute("value", _dropped_fragm_ipv4); }); + _interfaces.for_each([&] (Interface const &interface) { + if (!interface.report_empty(report_cfg)) + xml.node("interface", [&] { interface.report(xml, report_cfg); }); }); } @@ -495,15 +502,14 @@ Domain_link_stats::dissolve_interface(Interface_link_stats const &stats) } -void Domain_link_stats::report(Genode::Xml_generator &xml) +bool Domain_link_stats::report_empty() const { return !refused_for_ram && !refused_for_ports && !destroyed; } + + +void Domain_link_stats::report(Genode::Xml_generator &xml) const { - bool empty = true; - - if (refused_for_ram) { xml.node("refused_for_ram", [&] () { xml.attribute("value", refused_for_ram); }); empty = false; } - if (refused_for_ports) { xml.node("refused_for_ports", [&] () { xml.attribute("value", refused_for_ports); }); empty = false; } - if (destroyed) { xml.node("destroyed", [&] () { xml.attribute("value", destroyed); }); empty = false; } - - if (empty) { throw Report::Empty(); } + if (refused_for_ram) xml.node("refused_for_ram", [&] { xml.attribute("value", refused_for_ram); }); + if (refused_for_ports) xml.node("refused_for_ports", [&] { xml.attribute("value", refused_for_ports); }); + if (destroyed) xml.node("destroyed", [&] { xml.attribute("value", destroyed); }); } @@ -518,9 +524,10 @@ Domain_object_stats::dissolve_interface(Interface_object_stats const &stats) } -void Domain_object_stats::report(Genode::Xml_generator &xml) +bool Domain_object_stats::report_empty() const { return !destroyed; } + + +void Domain_object_stats::report(Genode::Xml_generator &xml) const { - bool empty = true; - if (destroyed) { xml.node("destroyed", [&] () { xml.attribute("value", destroyed); }); empty = false; } - if (empty) { throw Report::Empty(); } + if (destroyed) xml.node("destroyed", [&] { xml.attribute("value", destroyed); }); } diff --git a/repos/os/src/server/nic_router/domain.h b/repos/os/src/server/nic_router/domain.h index fb8ce11403..c097046747 100644 --- a/repos/os/src/server/nic_router/domain.h +++ b/repos/os/src/server/nic_router/domain.h @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -54,7 +53,8 @@ struct Net::Domain_object_stats void dissolve_interface(Interface_object_stats const &stats); - void report(Genode::Xml_generator &xml); + bool report_empty() const; + void report(Genode::Xml_generator &xml) const; }; @@ -66,7 +66,8 @@ struct Net::Domain_link_stats : Domain_object_stats void dissolve_interface(Interface_link_stats const &stats); - void report(Genode::Xml_generator &xml); + bool report_empty() const; + void report(Genode::Xml_generator &xml) const; }; @@ -74,26 +75,12 @@ class Net::Domain_dict : public Dictionary { public: - template - Domain &deprecated_find_by_name(Domain_name const &domain_name) + void find_by_domain_attr(Genode::Xml_node const &node, auto const &ok_fn, auto const &failed_fn) { - Domain *dom_ptr { nullptr }; - with_element( - domain_name, - [&] /* match_fn */ (Domain &dom) { dom_ptr = &dom; }, - [&] /* no_match_fn */ () { throw NO_MATCH_EXCEPTION { }; } - ); - return *dom_ptr; + with_element(node.attribute_value("domain", Domain_name()), + [&] (Domain &domain) { ok_fn(domain); }, [&] { failed_fn(); }); } - template - Domain &deprecated_find_by_domain_attr(Genode::Xml_node const &node) - { - Domain_name const domain_name { - node.attribute_value("domain", Domain_name { }) }; - - return deprecated_find_by_name(domain_name); - } }; @@ -117,7 +104,7 @@ class Net::Domain : public List::Element, Nat_rule_tree _nat_rules { }; Interface_list _interfaces { }; unsigned long _interface_cnt { 0 }; - Pointer _dhcp_server { }; + Dhcp_server *_dhcp_server_ptr { }; Genode::Reconstructible _ip_config; bool const _ip_config_dynamic { !ip_config().valid() }; List _ip_config_dependents { }; @@ -141,19 +128,19 @@ class Net::Domain : public List::Element, Domain_object_stats _dhcp_stats { }; unsigned long _dropped_fragm_ipv4 { 0 }; - void _read_forward_rules(Genode::Cstring const &protocol, - Domain_dict &domains, - Genode::Xml_node const node, - char const *type, - Forward_rule_tree &rules); + [[nodiscard]] bool _read_forward_rules(Genode::Cstring const &protocol, + Domain_dict &domains, + Genode::Xml_node const node, + char const *type, + Forward_rule_tree &rules); - void _read_transport_rules(Genode::Cstring const &protocol, - Domain_dict &domains, - Genode::Xml_node const node, - char const *type, - Transport_rule_list &rules); + [[nodiscard]] bool _read_transport_rules(Genode::Cstring const &protocol, + Domain_dict &domains, + Genode::Xml_node const node, + char const *type, + Transport_rule_list &rules); - void _invalid(char const *reason) const; + [[nodiscard]] bool _invalid(char const *reason) const; void _log_ip_config() const; @@ -171,11 +158,15 @@ class Net::Domain : public List::Element, void __FIXME__dissolve_foreign_arp_waiters(); + /* + * Noncopyable + */ + Domain(Domain const &); + Domain &operator = (Domain const &); + public: - struct Invalid : Genode::Exception { }; - struct Ip_config_static : Genode::Exception { }; - struct No_next_hop : Genode::Exception { }; + struct Invalid : Genode::Exception { }; Domain(Configuration &config, Genode::Xml_node const &node, @@ -185,11 +176,21 @@ class Net::Domain : public List::Element, ~Domain(); - void init(Domain_dict &domains); + [[nodiscard]] bool finish_construction() const; + + [[nodiscard]] bool init(Domain_dict &domains); void deinit(); - Ipv4_address const &next_hop(Ipv4_address const &ip) const; + void with_next_hop(Ipv4_address const &ip, auto const &ok_fn, auto const &error_fn) const + { + if (ip_config().interface().prefix_matches(ip)) + ok_fn(ip); + else if (ip_config().gateway_valid()) + ok_fn(ip_config().gateway()); + else + error_fn(); + } void ip_config_from_dhcp_ack(Dhcp_packet &dhcp_ack); @@ -209,7 +210,9 @@ class Net::Domain : public List::Element, void raise_tx_bytes(Genode::size_t bytes) { _tx_bytes += bytes; } - void report(Genode::Xml_generator &xml); + bool report_empty(Report const &) const; + + void report(Genode::Xml_generator &xml, Report const &) const; void add_dropped_fragm_ipv4(unsigned long dropped_fragm_ipv4); @@ -217,6 +220,20 @@ class Net::Domain : public List::Element, void update_ready_state(); + void with_dhcp_server(auto const &dhcp_server_fn, auto const &no_dhcp_server_fn) + { + if (!_dhcp_server_ptr) { + no_dhcp_server_fn(); + return; + } + if (_dhcp_server_ptr->has_invalid_remote_dns_cfg()) { + no_dhcp_server_fn(); + return; + } + dhcp_server_fn(*_dhcp_server_ptr); + } + + void with_dhcp_server(auto const &fn) { with_dhcp_server(fn, []{}); } /********* ** log ** @@ -247,7 +264,6 @@ class Net::Domain : public List::Element, Nat_rule_tree &nat_rules() { return _nat_rules; } Interface_list &interfaces() { return _interfaces; } Configuration &config() const { return _config; } - Dhcp_server &dhcp_server(); Arp_cache &arp_cache() { return _arp_cache; } Arp_waiter_list &foreign_arp_waiters() { return _foreign_arp_waiters; } Link_side_tree &tcp_links() { return _tcp_links; } diff --git a/repos/os/src/server/nic_router/forward_rule.cc b/repos/os/src/server/nic_router/forward_rule.cc index 24bfc8d751..af160b793f 100644 --- a/repos/os/src/server/nic_router/forward_rule.cc +++ b/repos/os/src/server/nic_router/forward_rule.cc @@ -33,13 +33,10 @@ void Forward_rule::print(Output &output) const } -Forward_rule::Forward_rule(Domain_dict &domains, Xml_node const node) +Forward_rule::Forward_rule(Port port, Ipv4_address to_ip, Port to_port, Domain &domain) : - _port { node.attribute_value("port", Port(0)) }, - _to_ip { node.attribute_value("to", Ipv4_address()) }, - _to_port { node.attribute_value("to_port", Port(0)) }, - _domain { domains.deprecated_find_by_domain_attr(node) } -{ - if (_port == Port(0) || !_to_ip.valid() || dynamic_port(_port)) { - throw Invalid(); } -} + _port { port }, + _to_ip { to_ip }, + _to_port { to_port }, + _domain { domain } +{ } diff --git a/repos/os/src/server/nic_router/forward_rule.h b/repos/os/src/server/nic_router/forward_rule.h index fde85bdb19..b6a6805019 100644 --- a/repos/os/src/server/nic_router/forward_rule.h +++ b/repos/os/src/server/nic_router/forward_rule.h @@ -49,7 +49,7 @@ class Net::Forward_rule : public Genode::Avl_node struct Invalid : Genode::Exception { }; - Forward_rule(Domain_dict &domains, Genode::Xml_node const node); + Forward_rule(Port port, Ipv4_address to_ip, Port to_port, Domain &domain); template diff --git a/repos/os/src/server/nic_router/interface.cc b/repos/os/src/server/nic_router/interface.cc index a90985e826..d859a004b9 100644 --- a/repos/os/src/server/nic_router/interface.cc +++ b/repos/os/src/server/nic_router/interface.cc @@ -23,6 +23,7 @@ #include #include #include +#include using namespace Net; using Genode::Deallocator; @@ -151,7 +152,7 @@ static void _link_packet(L3_protocol const prot, return; } return; - default: throw Interface::Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } } @@ -176,7 +177,7 @@ static void _update_checksum(L3_protocol const prot, sizeof(Icmp_packet)); return; } - default: throw Interface::Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } } @@ -186,7 +187,7 @@ static Port _dst_port(L3_protocol const prot, void *const prot_base) case L3_protocol::TCP: return (*(Tcp_packet *)prot_base).dst_port(); case L3_protocol::UDP: return (*(Udp_packet *)prot_base).dst_port(); case L3_protocol::ICMP: return Port((*(Icmp_packet *)prot_base).query_id()); - default: throw Interface::Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } } @@ -198,7 +199,7 @@ static void _dst_port(L3_protocol const prot, case L3_protocol::TCP: (*(Tcp_packet *)prot_base).dst_port(port); return; case L3_protocol::UDP: (*(Udp_packet *)prot_base).dst_port(port); return; case L3_protocol::ICMP: (*(Icmp_packet *)prot_base).query_id(port.value); return; - default: throw Interface::Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } } @@ -208,7 +209,7 @@ static Port _src_port(L3_protocol const prot, void *const prot_base) case L3_protocol::TCP: return (*(Tcp_packet *)prot_base).src_port(); case L3_protocol::UDP: return (*(Udp_packet *)prot_base).src_port(); case L3_protocol::ICMP: return Port((*(Icmp_packet *)prot_base).query_id()); - default: throw Interface::Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } } @@ -220,7 +221,7 @@ static void _src_port(L3_protocol const prot, case L3_protocol::TCP: ((Tcp_packet *)prot_base)->src_port(port); return; case L3_protocol::UDP: ((Udp_packet *)prot_base)->src_port(port); return; case L3_protocol::ICMP: ((Icmp_packet *)prot_base)->query_id(port.value); return; - default: throw Interface::Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } } @@ -232,35 +233,62 @@ static void *_prot_base(L3_protocol const prot, case L3_protocol::TCP: return &ip.data(size_guard); case L3_protocol::UDP: return &ip.data(size_guard); case L3_protocol::ICMP: return &ip.data(size_guard); - default: throw Interface::Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } } +static bool _supported_transport_prot(L3_protocol prot) +{ + return + prot == L3_protocol::TCP || + prot == L3_protocol::UDP || + prot == L3_protocol::ICMP; +} + + +static void _with_forward_rule(Domain &dom, L3_protocol prot, Port port, auto const &fn) +{ + switch (prot) { + case L3_protocol::TCP: dom.tcp_forward_rules().find_by_port(port, [&] (Forward_rule const &r) { fn(r); }, []{}); break; + case L3_protocol::UDP: dom.udp_forward_rules().find_by_port(port, [&] (Forward_rule const &r) { fn(r); }, []{}); break; + default: break; } +} + + +static void _with_transport_rule(Domain &dom, L3_protocol prot, Ipv4_address const &ip, Port port, auto const &fn) +{ + switch (prot) { + case L3_protocol::TCP: dom.tcp_rules().find_best_match(ip, port, [&] (Transport_rule const &tr, Permit_rule const &pr) { fn(tr, pr); }, []{}); break; + case L3_protocol::UDP: dom.udp_rules().find_best_match(ip, port, [&] (Transport_rule const &tr, Permit_rule const &pr) { fn(tr, pr); }, []{}); break; + default: break; } +} + /************************** ** Interface_link_stats ** **************************/ -void Interface_link_stats::report(Genode::Xml_generator &xml) +bool Interface_link_stats::report_empty() const { - bool empty = true; + return + !refused_for_ram && !refused_for_ports && !opening && !open && !closing && !closed && !dissolved_timeout_opening && + !dissolved_timeout_open && !dissolved_timeout_closing && !dissolved_timeout_closed && !dissolved_no_timeout && !destroyed; +} - if (refused_for_ram) { xml.node("refused_for_ram", [&] () { xml.attribute("value", refused_for_ram); }); empty = false; } - if (refused_for_ports) { xml.node("refused_for_ports", [&] () { xml.attribute("value", refused_for_ports); }); empty = false; } - if (opening) { xml.node("opening", [&] () { xml.attribute("value", opening); }); empty = false; } - if (open) { xml.node("open", [&] () { xml.attribute("value", open); }); empty = false; } - if (closing) { xml.node("closing", [&] () { xml.attribute("value", closing); }); empty = false; } - if (closed) { xml.node("closed", [&] () { xml.attribute("value", closed); }); empty = false; } - - if (dissolved_timeout_opening) { xml.node("dissolved_timeout_opening", [&] () { xml.attribute("value", dissolved_timeout_opening); }); empty = false; } - if (dissolved_timeout_open) { xml.node("dissolved_timeout_open", [&] () { xml.attribute("value", dissolved_timeout_open); }); empty = false; } - if (dissolved_timeout_closing) { xml.node("dissolved_timeout_closing", [&] () { xml.attribute("value", dissolved_timeout_closing); }); empty = false; } - if (dissolved_timeout_closed) { xml.node("dissolved_timeout_closed", [&] () { xml.attribute("value", dissolved_timeout_closed); }); empty = false; } - if (dissolved_no_timeout) { xml.node("dissolved_no_timeout", [&] () { xml.attribute("value", dissolved_no_timeout); }); empty = false; } - - if (destroyed) { xml.node("destroyed", [&] () { xml.attribute("value", destroyed); }); empty = false; } - - if (empty) { throw Report::Empty(); } +void Interface_link_stats::report(Genode::Xml_generator &xml) const +{ + if (refused_for_ram) xml.node("refused_for_ram", [&] { xml.attribute("value", refused_for_ram); }); + if (refused_for_ports) xml.node("refused_for_ports", [&] { xml.attribute("value", refused_for_ports); }); + if (opening) xml.node("opening", [&] { xml.attribute("value", opening); }); + if (open) xml.node("open", [&] { xml.attribute("value", open); }); + if (closing) xml.node("closing", [&] { xml.attribute("value", closing); }); + if (closed) xml.node("closed", [&] { xml.attribute("value", closed); }); + if (dissolved_timeout_opening) xml.node("dissolved_timeout_opening", [&] { xml.attribute("value", dissolved_timeout_opening); }); + if (dissolved_timeout_open) xml.node("dissolved_timeout_open", [&] { xml.attribute("value", dissolved_timeout_open); }); + if (dissolved_timeout_closing) xml.node("dissolved_timeout_closing", [&] { xml.attribute("value", dissolved_timeout_closing); }); + if (dissolved_timeout_closed) xml.node("dissolved_timeout_closed", [&] { xml.attribute("value", dissolved_timeout_closed); }); + if (dissolved_no_timeout) xml.node("dissolved_no_timeout", [&] { xml.attribute("value", dissolved_no_timeout); }); + if (destroyed) xml.node("destroyed", [&] { xml.attribute("value", destroyed); }); } @@ -268,14 +296,13 @@ void Interface_link_stats::report(Genode::Xml_generator &xml) ** Interface_object_stats ** ****************************/ -void Interface_object_stats::report(Genode::Xml_generator &xml) +bool Interface_object_stats::report_empty() const { return !alive && !destroyed; } + + +void Interface_object_stats::report(Genode::Xml_generator &xml) const { - bool empty = true; - - if (alive) { xml.node("alive", [&] () { xml.attribute("value", alive); }); empty = false; } - if (destroyed) { xml.node("destroyed", [&] () { xml.attribute("value", destroyed); }); empty = false; } - - if (empty) { throw Report::Empty(); } + if (alive) xml.node("alive", [&] { xml.attribute("value", alive); }); + if (destroyed) xml.node("destroyed", [&] { xml.attribute("value", destroyed); }); } @@ -290,7 +317,7 @@ void Interface::destroy_link(Link &link) case L3_protocol::TCP: ::_destroy_link(link, links(prot), _alloc); break; case L3_protocol::UDP: ::_destroy_link(link, links(prot), _alloc); break; case L3_protocol::ICMP: ::_destroy_link(link, links(prot), _alloc); break; - default: throw Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } } @@ -318,29 +345,9 @@ void Interface::_pass_prot_to_domain(Domain &domain, } -Forward_rule_tree & -Interface::_forward_rules(Domain &local_domain, L3_protocol const prot) const -{ - switch (prot) { - case L3_protocol::TCP: return local_domain.tcp_forward_rules(); - case L3_protocol::UDP: return local_domain.udp_forward_rules(); - default: throw Bad_transport_protocol(); } -} - - -Transport_rule_list & -Interface::_transport_rules(Domain &local_domain, L3_protocol const prot) const -{ - switch (prot) { - case L3_protocol::TCP: return local_domain.tcp_rules(); - case L3_protocol::UDP: return local_domain.udp_rules(); - default: throw Bad_transport_protocol(); } -} - - void Interface::_attach_to_domain_raw(Domain &domain) { - _domain = domain; + _domain_ptr = &domain; _refetch_domain_ready_state(); _interfaces.remove(this); domain.attach_interface(*this); @@ -349,10 +356,10 @@ void Interface::_attach_to_domain_raw(Domain &domain) void Interface::_detach_from_domain_raw() { - Domain &domain = _domain(); + Domain &domain = *_domain_ptr; domain.detach_interface(*this); _interfaces.insert(this); - _domain = Pointer(); + _domain_ptr = nullptr; _refetch_domain_ready_state(); domain.add_dropped_fragm_ipv4(_dropped_fragm_ipv4); @@ -367,10 +374,10 @@ void Interface::_detach_from_domain_raw() void Interface::_update_domain_object(Domain &new_domain) { /* detach raw */ - Domain &old_domain = _domain(); + Domain &old_domain = *_domain_ptr; old_domain.interface_updates_domain_object(*this); _interfaces.insert(this); - _domain = Pointer(); + _domain_ptr = nullptr; _refetch_domain_ready_state(); old_domain.add_dropped_fragm_ipv4(_dropped_fragm_ipv4); @@ -381,7 +388,7 @@ void Interface::_update_domain_object(Domain &new_domain) { old_domain.dhcp_stats().dissolve_interface(_dhcp_stats); /* attach raw */ - _domain = new_domain; + _domain_ptr = &new_domain; _refetch_domain_ready_state(); _interfaces.remove(this); new_domain.attach_interface(*this); @@ -413,7 +420,7 @@ void Interface::attach_to_domain_finish() return; } /* if domain has yet no IP config, participate in requesting one */ - Domain &domain = _domain(); + Domain &domain = *_domain_ptr; Ipv4_config const &ip_config = domain.ip_config(); if (!ip_config.valid()) { _dhcp_client->discover(); @@ -464,11 +471,10 @@ void Interface::handle_domain_ready_state(bool state) void Interface::_refetch_domain_ready_state() { - if (_domain.valid()) { - handle_domain_ready_state(_domain().ready()); - } else { + if (_domain_ptr) + handle_domain_ready_state(_domain_ptr->ready()); + else handle_domain_ready_state(false); - } } @@ -481,59 +487,74 @@ void Interface::_reset_and_refetch_domain_ready_state() void Interface::_detach_from_domain() { - try { - detach_from_ip_config(domain()); - _detach_from_domain_raw(); - } - catch (Pointer::Invalid) { } + with_domain([&] (Domain &domain) { + detach_from_ip_config(domain); + _detach_from_domain_raw(); }); } -void -Interface::_new_link(L3_protocol const protocol, - Link_side_id const &local, - Pointer remote_port_alloc, - Domain &remote_domain, - Link_side_id const &remote) +Packet_result Interface::_new_link(L3_protocol const protocol, + Domain &local_domain, + Link_side_id const &local, + Port_allocator_guard *remote_port_alloc_ptr, + Domain &remote_domain, + Link_side_id const &remote) { + Packet_result result { }; switch (protocol) { case L3_protocol::TCP: try { new (_alloc) - Tcp_link { *this, local, remote_port_alloc, remote_domain, + Tcp_link { *this, local_domain, local, remote_port_alloc_ptr, remote_domain, remote, _timer, _config(), protocol, _tcp_stats }; } - catch (Out_of_ram) { throw Free_resources_and_retry_handle_eth(L3_protocol::TCP); } - catch (Out_of_caps) { throw Free_resources_and_retry_handle_eth(L3_protocol::TCP); } - + catch (Out_of_ram) { + _tcp_stats.refused_for_ram++; + result = packet_drop("out of RAM while creating TCP link"); + } + catch (Out_of_caps) { + _tcp_stats.refused_for_ram++; + result = packet_drop("out of CAPs while creating TCP link"); + } break; case L3_protocol::UDP: try { new (_alloc) - Udp_link { *this, local, remote_port_alloc, remote_domain, + Udp_link { *this, local_domain, local, remote_port_alloc_ptr, remote_domain, remote, _timer, _config(), protocol, _udp_stats }; } - catch (Out_of_ram) { throw Free_resources_and_retry_handle_eth(L3_protocol::UDP); } - catch (Out_of_caps) { throw Free_resources_and_retry_handle_eth(L3_protocol::UDP); } - + catch (Out_of_ram) { + _udp_stats.refused_for_ram++; + result = packet_drop("out of RAM while creating UDP link"); + } + catch (Out_of_caps) { + _udp_stats.refused_for_ram++; + result = packet_drop("out of CAPs while creating UDP link"); + } break; case L3_protocol::ICMP: try { new (_alloc) - Icmp_link { *this, local, remote_port_alloc, remote_domain, + Icmp_link { *this, local_domain, local, remote_port_alloc_ptr, remote_domain, remote, _timer, _config(), protocol, _icmp_stats }; } - catch (Out_of_ram) { throw Free_resources_and_retry_handle_eth(L3_protocol::ICMP); } - catch (Out_of_caps) { throw Free_resources_and_retry_handle_eth(L3_protocol::ICMP); } - + catch (Out_of_ram) { + _icmp_stats.refused_for_ram++; + result = packet_drop("out of RAM while creating ICMP link"); + } + catch (Out_of_caps) { + _icmp_stats.refused_for_ram++; + result = packet_drop("out of CAPs while creating ICMP link"); + } break; - default: throw Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } + return result; } void Interface::dhcp_allocation_expired(Dhcp_allocation &allocation) { - _release_dhcp_allocation(allocation, _domain()); + _release_dhcp_allocation(allocation, *_domain_ptr); _released_dhcp_allocations.insert(&allocation); } @@ -544,7 +565,7 @@ Link_list &Interface::links(L3_protocol const protocol) case L3_protocol::TCP: return _tcp_links; case L3_protocol::UDP: return _udp_links; case L3_protocol::ICMP: return _icmp_links; - default: throw Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } } @@ -554,22 +575,24 @@ Link_list &Interface::dissolved_links(L3_protocol const protocol) case L3_protocol::TCP: return _dissolved_tcp_links; case L3_protocol::UDP: return _dissolved_udp_links; case L3_protocol::ICMP: return _dissolved_icmp_links; - default: throw Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } } -void Interface::_adapt_eth(Ethernet_frame ð, - Ipv4_address const &dst_ip, - Packet_descriptor const &pkt, - Domain &remote_domain) +Packet_result Interface::_adapt_eth(Ethernet_frame ð, + Ipv4_address const &dst_ip, + Packet_descriptor const &pkt, + Domain &remote_domain) { + Packet_result result { }; Ipv4_config const &remote_ip_cfg = remote_domain.ip_config(); if (!remote_ip_cfg.valid()) { - throw Drop_packet("target domain has yet no IP config"); + result = packet_drop("target domain has yet no IP config"); } - if (remote_domain.use_arp()) { + if (!remote_domain.use_arp()) + return result; - Ipv4_address const &hop_ip = remote_domain.next_hop(dst_ip); + auto with_next_hop = [&] (Ipv4_address const &hop_ip) { remote_domain.arp_cache().find_by_ip( hop_ip, [&] /* handle_match */ (Arp_cache_entry const &entry) @@ -583,56 +606,68 @@ void Interface::_adapt_eth(Ethernet_frame ð, interface._broadcast_arp_request( remote_ip_cfg.interface().address, hop_ip); }); - try { new (_alloc) Arp_waiter { *this, remote_domain, hop_ip, pkt }; } - catch (Out_of_ram) { throw Free_resources_and_retry_handle_eth(); } - catch (Out_of_caps) { throw Free_resources_and_retry_handle_eth(); } - throw Packet_postponed(); + try { + new (_alloc) Arp_waiter { *this, remote_domain, hop_ip, pkt }; + result = packet_postponed(); + } + catch (Out_of_ram) { result = packet_drop("out of RAM while creating ARP waiter"); } + catch (Out_of_caps) { result = packet_drop("out of CAPs while creating ARP waiter"); } } ); - } + }; + auto without_next_hop = [&] { result = packet_drop("cannot find next hop"); }; + remote_domain.with_next_hop(dst_ip, with_next_hop, without_next_hop); + return result; } -void Interface::_nat_link_and_pass(Ethernet_frame ð, - Size_guard &size_guard, - Ipv4_packet &ip, - Internet_checksum_diff &ip_icd, - L3_protocol const prot, - void *const prot_base, - size_t const prot_size, - Link_side_id const &local_id, - Domain &local_domain, - Domain &remote_domain) +Packet_result Interface::_nat_link_and_pass(Ethernet_frame ð, + Size_guard &size_guard, + Ipv4_packet &ip, + Internet_checksum_diff &ip_icd, + L3_protocol const prot, + void *const prot_base, + size_t const prot_size, + Link_side_id const &local_id, + Domain &local_domain, + Domain &remote_domain) { - try { - Pointer remote_port_alloc; - remote_domain.nat_rules().find_by_domain( - local_domain, - [&] /* handle_match */ (Nat_rule &nat) - { - if(_config().verbose()) { - log("[", local_domain, "] using NAT rule: ", nat); } + Packet_result result { }; + Port_allocator_guard *remote_port_alloc_ptr { }; + remote_domain.nat_rules().find_by_domain( + local_domain, + [&] /* handle_match */ (Nat_rule &nat) + { + if(_config().verbose()) { + log("[", local_domain, "] using NAT rule: ", nat); } - _src_port(prot, prot_base, nat.port_alloc(prot).alloc()); - ip.src(remote_domain.ip_config().interface().address, ip_icd); - remote_port_alloc = nat.port_alloc(prot); - }, - [&] /* no_match */ () { } - ); - Link_side_id const remote_id = { ip.dst(), _dst_port(prot, prot_base), - ip.src(), _src_port(prot, prot_base) }; - _new_link(prot, local_id, remote_port_alloc, remote_domain, remote_id); - _pass_prot_to_domain( - remote_domain, eth, size_guard, ip, ip_icd, prot, prot_base, - prot_size); + Port src_port(0); + nat.port_alloc(prot).alloc().with_result( + [&] (Port src_port) { + _src_port(prot, prot_base, src_port); + ip.src(remote_domain.ip_config().interface().address, ip_icd); + remote_port_alloc_ptr = &nat.port_alloc(prot); }, + [&] (auto) { + result = packet_drop("no available NAT ports"); + switch (prot) { + case L3_protocol::TCP: _tcp_stats.refused_for_ports++; break; + case L3_protocol::UDP: _udp_stats.refused_for_ports++; break; + case L3_protocol::ICMP: _icmp_stats.refused_for_ports++; break; + default: ASSERT_NEVER_REACHED; } }); + }, + [&] /* no_match */ () { } + ); + if (result.valid()) + return result; - } catch (Port_allocator_guard::Out_of_indices) { - switch (prot) { - case L3_protocol::TCP: _tcp_stats.refused_for_ports++; break; - case L3_protocol::UDP: _udp_stats.refused_for_ports++; break; - case L3_protocol::ICMP: _icmp_stats.refused_for_ports++; break; - default: throw Bad_transport_protocol(); } - } + Link_side_id const remote_id = { ip.dst(), _dst_port(prot, prot_base), + ip.src(), _src_port(prot, prot_base) }; + result = _new_link(prot, local_domain, local_id, remote_port_alloc_ptr, remote_domain, remote_id); + if (result.valid()) + return result; + + _pass_prot_to_domain(remote_domain, eth, size_guard, ip, ip_icd, prot, prot_base, prot_size); + return packet_handled(); } @@ -733,14 +768,15 @@ void Interface::_release_dhcp_allocation(Dhcp_allocation &allocation, } -void Interface::_new_dhcp_allocation(Ethernet_frame ð, - Dhcp_packet &dhcp, - Dhcp_server &dhcp_srv, - Domain &local_domain) +Packet_result Interface::_new_dhcp_allocation(Ethernet_frame ð, + Dhcp_packet &dhcp, + Dhcp_server &dhcp_srv, + Domain &local_domain) { - try { + Packet_result result { }; + auto ok_fn = [&] (Ipv4_address const &ip) { Dhcp_allocation &allocation = *new (_alloc) - Dhcp_allocation { *this, dhcp_srv.alloc_ip(), dhcp.client_mac(), + Dhcp_allocation { *this, ip, dhcp.client_mac(), _timer, _config().dhcp_offer_timeout() }; _dhcp_allocations.insert(allocation); @@ -752,38 +788,37 @@ void Interface::_new_dhcp_allocation(Ethernet_frame ð, Dhcp_packet::Message_type::OFFER, dhcp.xid(), local_domain.ip_config().interface()); - } - catch (Out_of_ram) { throw Free_resources_and_retry_handle_eth(); } - catch (Out_of_caps) { throw Free_resources_and_retry_handle_eth(); } + + result = packet_handled(); + }; + try { dhcp_srv.alloc_ip().with_result(ok_fn, [&] (auto) { result = packet_drop("failed to allocate IP for DHCP client"); }); } + catch (Out_of_ram) { result = packet_drop("out of RAM while creating DHCP allocation"); } + catch (Out_of_caps) { result = packet_drop("out of CAPs while creating DHCP allocation"); } + return result; } -void Interface::_handle_dhcp_request(Ethernet_frame ð, - Dhcp_packet &dhcp, - Domain &local_domain, - Ipv4_address_prefix const &local_intf) +Packet_result Interface::_handle_dhcp_request(Ethernet_frame ð, + Dhcp_server &dhcp_srv, + Dhcp_packet &dhcp, + Domain &local_domain, + Ipv4_address_prefix const &local_intf) { - try { - /* try to get the DHCP server config of this interface */ - Dhcp_server &dhcp_srv = local_domain.dhcp_server(); + Packet_result result { }; + auto no_msg_type_fn = [&] { result = packet_drop("DHCP request misses option \"Message Type\""); }; + auto msg_type_fn = [&] (Dhcp_packet::Message_type_option const &msg_type) { - /* determine type of DHCP request */ - Dhcp_packet::Message_type const msg_type = - dhcp.option().value(); + /* look up existing DHCP configuration for client */ + auto dhcp_allocation_fn = [&] (Dhcp_allocation &allocation) { - try { - /* look up existing DHCP configuration for client */ - Dhcp_allocation &allocation = - _dhcp_allocations.find_by_mac(dhcp.client_mac()); - - switch (msg_type) { + switch (msg_type.value()) { case Dhcp_packet::Message_type::DISCOVER: if (allocation.bound()) { _release_dhcp_allocation(allocation, local_domain); _destroy_dhcp_allocation(allocation, local_domain); - _new_dhcp_allocation(eth, dhcp, dhcp_srv, local_domain); + result = _new_dhcp_allocation(eth, dhcp, dhcp_srv, local_domain); return; } else { @@ -792,6 +827,7 @@ void Interface::_handle_dhcp_request(Ethernet_frame ð, allocation.ip(), Dhcp_packet::Message_type::OFFER, dhcp.xid(), local_intf); + result = packet_handled(); return; } case Dhcp_packet::Message_type::REQUEST: @@ -802,32 +838,39 @@ void Interface::_handle_dhcp_request(Ethernet_frame ð, allocation.ip(), Dhcp_packet::Message_type::ACK, dhcp.xid(), local_intf); + result = packet_handled(); return; } else { - Dhcp_packet::Server_ipv4 &dhcp_srv_ip = - dhcp.option(); - if (dhcp_srv_ip.value() == local_intf.address) - { - allocation.set_bound(); - allocation.lifetime(dhcp_srv.ip_lease_time()); - if (_config().verbose()) { - log("[", local_domain, "] bind DHCP allocation: ", - allocation); + auto no_server_ipv4_fn = [&] { result = packet_drop("DHCP request misses option \"Server IPv4\""); }; + auto server_ipv4_fn = [&] (Dhcp_packet::Server_ipv4 const &dhcp_srv_ip) { + + if (dhcp_srv_ip.value() == local_intf.address) + { + allocation.set_bound(); + allocation.lifetime(dhcp_srv.ip_lease_time()); + if (_config().verbose()) { + log("[", local_domain, "] bind DHCP allocation: ", + allocation); + } + _send_dhcp_reply(dhcp_srv, eth.src(), dhcp.client_mac(), + allocation.ip(), + Dhcp_packet::Message_type::ACK, + dhcp.xid(), local_intf); + result = packet_handled(); + return; + + } else { + + _release_dhcp_allocation(allocation, local_domain); + _destroy_dhcp_allocation(allocation, local_domain); + result = packet_handled(); + return; } - _send_dhcp_reply(dhcp_srv, eth.src(), dhcp.client_mac(), - allocation.ip(), - Dhcp_packet::Message_type::ACK, - dhcp.xid(), local_intf); - return; - - } else { - - _release_dhcp_allocation(allocation, local_domain); - _destroy_dhcp_allocation(allocation, local_domain); - return; - } + }; + dhcp.with_option(server_ipv4_fn, no_server_ipv4_fn); + return; } case Dhcp_packet::Message_type::INFORM: @@ -835,6 +878,7 @@ void Interface::_handle_dhcp_request(Ethernet_frame ð, allocation.ip(), Dhcp_packet::Message_type::ACK, dhcp.xid(), local_intf); + result = packet_handled(); return; case Dhcp_packet::Message_type::DECLINE: @@ -842,20 +886,21 @@ void Interface::_handle_dhcp_request(Ethernet_frame ð, _release_dhcp_allocation(allocation, local_domain); _destroy_dhcp_allocation(allocation, local_domain); + result = packet_handled(); return; - case Dhcp_packet::Message_type::NAK: throw Drop_packet("DHCP NAK from client"); - case Dhcp_packet::Message_type::OFFER: throw Drop_packet("DHCP OFFER from client"); - case Dhcp_packet::Message_type::ACK: throw Drop_packet("DHCP ACK from client"); - default: throw Drop_packet("DHCP request with broken message type"); + case Dhcp_packet::Message_type::NAK: result = packet_drop("DHCP NAK from client"); return; + case Dhcp_packet::Message_type::OFFER: result = packet_drop("DHCP OFFER from client"); return; + case Dhcp_packet::Message_type::ACK: result = packet_drop("DHCP ACK from client"); return; + default: result = packet_drop("DHCP request with broken message type"); return; } - } - catch (Dhcp_allocation_tree::No_match) { + }; + auto no_dhcp_allocation_fn = [&] { - switch (msg_type) { + switch (msg_type.value()) { case Dhcp_packet::Message_type::DISCOVER: - _new_dhcp_allocation(eth, dhcp, dhcp_srv, local_domain); + result = _new_dhcp_allocation(eth, dhcp, dhcp_srv, local_domain); return; case Dhcp_packet::Message_type::REQUEST: @@ -864,19 +909,21 @@ void Interface::_handle_dhcp_request(Ethernet_frame ð, Ipv4_address { }, Dhcp_packet::Message_type::NAK, dhcp.xid(), local_intf); + result = packet_handled(); return; - case Dhcp_packet::Message_type::DECLINE: throw Drop_packet("DHCP DECLINE from client without offered/acked IP"); - case Dhcp_packet::Message_type::RELEASE: throw Drop_packet("DHCP RELEASE from client without offered/acked IP"); - case Dhcp_packet::Message_type::NAK: throw Drop_packet("DHCP NAK from client"); - case Dhcp_packet::Message_type::OFFER: throw Drop_packet("DHCP OFFER from client"); - case Dhcp_packet::Message_type::ACK: throw Drop_packet("DHCP ACK from client"); - default: throw Drop_packet("DHCP request with broken message type"); + case Dhcp_packet::Message_type::DECLINE: result = packet_drop("DHCP DECLINE from client without offered/acked IP"); return; + case Dhcp_packet::Message_type::RELEASE: result = packet_drop("DHCP RELEASE from client without offered/acked IP"); return; + case Dhcp_packet::Message_type::NAK: result = packet_drop("DHCP NAK from client"); return; + case Dhcp_packet::Message_type::OFFER: result = packet_drop("DHCP OFFER from client"); return; + case Dhcp_packet::Message_type::ACK: result = packet_drop("DHCP ACK from client"); return; + default: result = packet_drop("DHCP request with broken message type"); return; } - } - } - catch (Dhcp_packet::Option_not_found exception) { - throw Drop_packet("DHCP request misses required option"); } + }; + _dhcp_allocations.find_by_mac(dhcp.client_mac(), dhcp_allocation_fn, no_dhcp_allocation_fn); + }; + dhcp.with_option(msg_type_fn, no_msg_type_fn); + return result; } @@ -947,28 +994,23 @@ bool Interface::link_state() const void Interface::handle_interface_link_state() { - struct Keep_ip_config : Exception { }; - try { - attach_to_domain_finish(); + attach_to_domain_finish(); - /* if the whole domain is down, discard IP config */ - Domain &domain_ = domain(); - if (!link_state() && domain_.ip_config().valid()) { - domain_.interfaces().for_each([&] (Interface &interface) { - if (interface.link_state()) { - throw Keep_ip_config(); } - }); - domain_.discard_ip_config(); - domain_.arp_cache().destroy_all_entries(); + /* discard dynamic IP config if the entire domain is down */ + with_domain([&] (Domain &domain) { + if (domain.ip_config_dynamic() && !link_state() && domain.ip_config().valid()) { + bool discard_ip_config = true; + domain.interfaces().for_each([&] (Interface &interface) { + if (interface.link_state()) + discard_ip_config = false; }); + if (discard_ip_config) { + domain.discard_ip_config(); + domain.arp_cache().destroy_all_entries(); + } } - } - catch (Pointer::Invalid) { } - catch (Domain::Ip_config_static) { } - catch (Keep_ip_config) { } - + }); /* force report if configured */ - try { _config().report().handle_interface_link_state(); } - catch (Pointer::Invalid) { } + _config().with_report([&] (Report &r) { r.handle_interface_link_state(); }); } @@ -1003,21 +1045,21 @@ void Interface::_send_icmp_echo_reply(Ethernet_frame ð, } -void Interface::_handle_icmp_query(Ethernet_frame ð, - Size_guard &size_guard, - Ipv4_packet &ip, - Internet_checksum_diff &ip_icd, - Packet_descriptor const &pkt, - L3_protocol prot, - void *prot_base, - size_t prot_size, - Domain &local_domain) +Packet_result Interface::_handle_icmp_query(Ethernet_frame ð, + Size_guard &size_guard, + Ipv4_packet &ip, + Internet_checksum_diff &ip_icd, + Packet_descriptor const &pkt, + L3_protocol prot, + void *prot_base, + size_t prot_size, + Domain &local_domain) { + Packet_result result { }; Link_side_id const local_id = { ip.src(), _src_port(prot, prot_base), ip.dst(), _dst_port(prot, prot_base) }; /* try to route via existing ICMP links */ - bool done { false }; local_domain.links(prot).find_by_id( local_id, [&] /* handle_match */ (Link_side const &local_side) @@ -1030,7 +1072,9 @@ void Interface::_handle_icmp_query(Ethernet_frame ð, log("[", local_domain, "] using ", l3_protocol_name(prot), " link: ", link); } - _adapt_eth(eth, remote_side.src_ip(), pkt, remote_domain); + result = _adapt_eth(eth, remote_side.src_ip(), pkt, remote_domain); + if (result.valid()) + return; ip.src(remote_side.dst_ip(), ip_icd); ip.dst(remote_side.src_ip(), ip_icd); _src_port(prot, prot_base, remote_side.dst_port()); @@ -1040,13 +1084,12 @@ void Interface::_handle_icmp_query(Ethernet_frame ð, prot_base, prot_size); _link_packet(prot, prot_base, link, client); - done = true; + result = packet_handled(); }, [&] /* handle_no_match */ () { } ); - if (done) { - return; - } + if (result.valid()) + return result; /* try to route via ICMP rules */ local_domain.icmp_rules().find_longest_prefix_match( @@ -1057,38 +1100,34 @@ void Interface::_handle_icmp_query(Ethernet_frame ð, log("[", local_domain, "] using ICMP rule: ", rule); } Domain &remote_domain = rule.domain(); - _adapt_eth(eth, local_id.dst_ip, pkt, remote_domain); - _nat_link_and_pass(eth, size_guard, ip, ip_icd, prot, prot_base, - prot_size, local_id, local_domain, - remote_domain); - - done = true; + result = _adapt_eth(eth, local_id.dst_ip, pkt, remote_domain); + if (result.valid()) + return; + result = _nat_link_and_pass( + eth, size_guard, ip, ip_icd, prot, prot_base, prot_size, local_id, local_domain, remote_domain); }, [&] /* handle_no_match */ () { } ); - if (done) { - return; - } - - throw Bad_transport_protocol(); + return result; } -void Interface::_handle_icmp_error(Ethernet_frame ð, - Size_guard &size_guard, - Ipv4_packet &ip, - Internet_checksum_diff &ip_icd, - Packet_descriptor const &pkt, - Domain &local_domain, - Icmp_packet &icmp, - size_t icmp_sz) +Packet_result Interface::_handle_icmp_error(Ethernet_frame ð, + Size_guard &size_guard, + Ipv4_packet &ip, + Internet_checksum_diff &ip_icd, + Packet_descriptor const &pkt, + Domain &local_domain, + Icmp_packet &icmp, + size_t icmp_sz) { + Packet_result result { }; Ipv4_packet &embed_ip { icmp.data(size_guard) }; Internet_checksum_diff embed_ip_icd { }; /* drop packet if embedded IP checksum invalid */ if (embed_ip.checksum_error()) { - throw Drop_packet("bad checksum in IP packet embedded in ICMP error"); + return packet_drop("bad checksum in IP packet embedded in ICMP error"); } /* get link identity of the embeddeded transport packet */ L3_protocol const embed_prot = embed_ip.protocol(); @@ -1112,7 +1151,9 @@ void Interface::_handle_icmp_error(Ethernet_frame ð, l3_protocol_name(embed_prot), " link: ", link); } /* adapt source and destination of Ethernet frame and IP packet */ - _adapt_eth(eth, remote_side.src_ip(), pkt, remote_domain); + result = _adapt_eth(eth, remote_side.src_ip(), pkt, remote_domain); + if (result.valid()) + return; if (remote_side.dst_ip() == remote_domain.ip_config().interface().address) { ip.src(remote_side.dst_ip(), ip_icd); } @@ -1136,31 +1177,33 @@ void Interface::_handle_icmp_error(Ethernet_frame ð, /* refresh link only if the error is not about an ICMP query */ if (embed_prot != L3_protocol::ICMP) { _link_packet(embed_prot, embed_prot_base, link, client); } + result = packet_handled(); }, [&] /* handle_no_match */ () { - throw Drop_packet("no link that matches packet embedded in " - "ICMP error"); + result = packet_drop("no link that matches packet embedded in ICMP error"); } ); + return result; } -void Interface::_handle_icmp(Ethernet_frame ð, - Size_guard &size_guard, - Ipv4_packet &ip, - Internet_checksum_diff &ip_icd, - Packet_descriptor const &pkt, - L3_protocol prot, - void *prot_base, - size_t prot_size, - Domain &local_domain, - Ipv4_address_prefix const &local_intf) +Packet_result Interface::_handle_icmp(Ethernet_frame ð, + Size_guard &size_guard, + Ipv4_packet &ip, + Internet_checksum_diff &ip_icd, + Packet_descriptor const &pkt, + L3_protocol prot, + void *prot_base, + size_t prot_size, + Domain &local_domain, + Ipv4_address_prefix const &local_intf) { /* drop packet if ICMP checksum is invalid */ + Packet_result result { }; Icmp_packet &icmp = *reinterpret_cast(prot_base); if (icmp.checksum_error(size_guard.unconsumed())) { - throw Drop_packet("bad ICMP checksum"); } + return packet_drop("bad ICMP checksum"); } /* try to act as ICMP Echo server */ if (icmp.type() == Icmp_packet::Type::ECHO_REQUEST && @@ -1171,22 +1214,24 @@ void Interface::_handle_icmp(Ethernet_frame ð, log("[", local_domain, "] act as ICMP Echo server"); } _send_icmp_echo_reply(eth, ip, icmp, prot_size, size_guard); - return; + return packet_handled(); } /* try to act as ICMP router */ switch (icmp.type()) { case Icmp_packet::Type::ECHO_REPLY: - case Icmp_packet::Type::ECHO_REQUEST: _handle_icmp_query(eth, size_guard, ip, ip_icd, pkt, prot, prot_base, prot_size, local_domain); break; - case Icmp_packet::Type::DST_UNREACHABLE: _handle_icmp_error(eth, size_guard, ip, ip_icd, pkt, local_domain, icmp, prot_size); break; - default: Drop_packet("unhandled type in ICMP"); } + case Icmp_packet::Type::ECHO_REQUEST: result = _handle_icmp_query(eth, size_guard, ip, ip_icd, pkt, prot, prot_base, prot_size, local_domain); break; + case Icmp_packet::Type::DST_UNREACHABLE: result = _handle_icmp_error(eth, size_guard, ip, ip_icd, pkt, local_domain, icmp, prot_size); break; + default: result = packet_drop("unhandled type in ICMP"); } + return result; } -void Interface::_handle_ip(Ethernet_frame ð, - Size_guard &size_guard, - Packet_descriptor const &pkt, - Domain &local_domain) +Packet_result Interface::_handle_ip(Ethernet_frame ð, + Size_guard &size_guard, + Packet_descriptor const &pkt, + Domain &local_domain) { + Packet_result result { }; Ipv4_packet &ip { eth.data(size_guard) }; Internet_checksum_diff ip_icd { }; @@ -1200,7 +1245,7 @@ void Interface::_handle_ip(Ethernet_frame ð, _send_icmp_dst_unreachable( local_intf, eth, ip, _config().icmp_type_3_code_on_fragm_ipv4()); } - throw Drop_packet("fragmented IPv4 not supported"); + return packet_drop("fragmented IPv4 not supported"); } /* try handling subnet-local IP packets */ if (local_intf.prefix_matches(ip.dst()) && @@ -1211,15 +1256,15 @@ void Interface::_handle_ip(Ethernet_frame ð, * the router. Thus, forward it to all other interfaces of the domain. */ _domain_broadcast(eth, size_guard, local_domain); - return; + return packet_handled(); } /* try to route via transport layer rules */ - bool done { false }; - try { - L3_protocol const prot = ip.protocol(); - size_t const prot_size = size_guard.unconsumed(); - void *const prot_base = _prot_base(prot, size_guard, ip); + L3_protocol const prot = ip.protocol(); + if (_supported_transport_prot(prot)) { + + size_t const prot_size = size_guard.unconsumed(); + void *const prot_base = _prot_base(prot, size_guard, ip); /* try handling DHCP requests before trying any routing */ if (prot == L3_protocol::UDP) { @@ -1232,135 +1277,116 @@ void Interface::_handle_ip(Ethernet_frame ð, switch (dhcp.op()) { case Dhcp_packet::REQUEST: - try { - _handle_dhcp_request( - eth, dhcp, local_domain, local_intf); - } - catch (Pointer::Invalid) { - throw Drop_packet("DHCP request while DHCP server inactive"); - } - return; + local_domain.with_dhcp_server( + [&] /* dhcp_server_fn */ (Dhcp_server &srv) { + result = _handle_dhcp_request(eth, srv, dhcp, local_domain, local_intf); }, + [&] /* no_dhcp_server_fn */ { + result = packet_drop("DHCP request while DHCP server inactive"); }); + return result; case Dhcp_packet::REPLY: if (eth.dst() != router_mac() && eth.dst() != Mac_address(0xff)) { - throw Drop_packet("Ethernet of DHCP reply doesn't target router"); } + return packet_drop("Ethernet of DHCP reply doesn't target router"); } if (dhcp.client_mac() != router_mac()) { - throw Drop_packet("DHCP reply doesn't target router"); } + return packet_drop("DHCP reply doesn't target router"); } if (!_dhcp_client.constructed()) { - throw Drop_packet("DHCP reply while DHCP client inactive"); } + return packet_drop("DHCP reply while DHCP client inactive"); } - _dhcp_client->handle_dhcp_reply(dhcp); - return; + return _dhcp_client->handle_dhcp_reply(dhcp, local_domain); - default: - - throw Drop_packet("Bad DHCP opcode"); + default: return packet_drop("Bad DHCP opcode"); } } } - else if (prot == L3_protocol::ICMP) { - _handle_icmp(eth, size_guard, ip, ip_icd, pkt, prot, prot_base, - prot_size, local_domain, local_intf); - return; - } + if (prot == L3_protocol::ICMP) { + result = _handle_icmp(eth, size_guard, ip, ip_icd, pkt, prot, prot_base, + prot_size, local_domain, local_intf); + } else { - Link_side_id const local_id = { ip.src(), _src_port(prot, prot_base), - ip.dst(), _dst_port(prot, prot_base) }; + Link_side_id const local_id = { ip.src(), _src_port(prot, prot_base), + ip.dst(), _dst_port(prot, prot_base) }; - /* try to route via existing UDP/TCP links */ - local_domain.links(prot).find_by_id( - local_id, - [&] /* handle_match */ (Link_side const &local_side) - { - Link &link = local_side.link(); - 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()) { - log("[", local_domain, "] using ", l3_protocol_name(prot), - " link: ", link); - } - _adapt_eth(eth, remote_side.src_ip(), pkt, remote_domain); - ip.src(remote_side.dst_ip(), ip_icd); - ip.dst(remote_side.src_ip(), ip_icd); - _src_port(prot, prot_base, remote_side.dst_port()); - _dst_port(prot, prot_base, remote_side.src_port()); - _pass_prot_to_domain( - remote_domain, eth, size_guard, ip, ip_icd, prot, - prot_base, prot_size); - - _link_packet(prot, prot_base, link, client); - done = true; - }, - [&] /* handle_no_match */ () { } - ); - if (done) { - return; - } - - /* try to route via forward rules */ - if (local_id.dst_ip == local_intf.address) { - - _forward_rules(local_domain, prot).find_by_port( - local_id.dst_port, - [&] /* handle_match */ (Forward_rule const &rule) + /* try to route via existing UDP/TCP links */ + local_domain.links(prot).find_by_id( + local_id, + [&] /* handle_match */ (Link_side const &local_side) { + Link &link = local_side.link(); + 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()) { + log("[", local_domain, "] using ", l3_protocol_name(prot), + " link: ", link); + } + result = _adapt_eth(eth, remote_side.src_ip(), pkt, remote_domain); + if (result.valid()) + return; + ip.src(remote_side.dst_ip(), ip_icd); + ip.dst(remote_side.src_ip(), ip_icd); + _src_port(prot, prot_base, remote_side.dst_port()); + _dst_port(prot, prot_base, remote_side.src_port()); + _pass_prot_to_domain( + remote_domain, eth, size_guard, ip, ip_icd, prot, + prot_base, prot_size); + + _link_packet(prot, prot_base, link, client); + result = packet_handled(); + }, + [&] /* handle_no_match */ () { } + ); + if (result.valid()) + return result; + + /* try to route via forward rules */ + if (local_id.dst_ip == local_intf.address) { + _with_forward_rule(local_domain, prot, local_id.dst_port, [&] (Forward_rule const &rule) { if(_config().verbose()) { log("[", local_domain, "] using forward rule: ", - l3_protocol_name(prot), " ", rule); + l3_protocol_name(prot), " ", rule); } Domain &remote_domain = rule.domain(); - _adapt_eth(eth, rule.to_ip(), pkt, remote_domain); + result = _adapt_eth(eth, rule.to_ip(), pkt, remote_domain); + if (result.valid()) + return; ip.dst(rule.to_ip(), ip_icd); if (!(rule.to_port() == Port(0))) { _dst_port(prot, prot_base, rule.to_port()); } - _nat_link_and_pass( + result = _nat_link_and_pass( eth, size_guard, ip, ip_icd, prot, prot_base, prot_size, local_id, local_domain, remote_domain); - - done = true; - }, - [&] /* handle_no_match */ () { } - ); - if (done) { - return; + }); + if (result.valid()) + return result; } - } - /* try to route via transport and permit rules */ - _transport_rules(local_domain, prot).find_best_match( - local_id.dst_ip, - local_id.dst_port, - [&] /* handle_match */ (Transport_rule const &transport_rule, - Permit_rule const &permit_rule) - { - if(_config().verbose()) { - log("[", local_domain, "] using ", - l3_protocol_name(prot), " rule: ", - transport_rule, " ", permit_rule); - } - Domain &remote_domain = permit_rule.domain(); - _adapt_eth(eth, local_id.dst_ip, pkt, remote_domain); - _nat_link_and_pass( - eth, size_guard, ip, ip_icd, prot, prot_base, prot_size, - local_id, local_domain, remote_domain); - - done = true; - }, - [&] /* handle_no_match */ () { } - ); - if (done) { - return; + /* try to route via transport and permit rules */ + _with_transport_rule(local_domain, prot, local_id.dst_ip, local_id.dst_port, + [&] (Transport_rule const &transport_rule, Permit_rule const &permit_rule) { + if(_config().verbose()) { + log("[", local_domain, "] using ", + l3_protocol_name(prot), " rule: ", + transport_rule, " ", permit_rule); + } + Domain &remote_domain = permit_rule.domain(); + result = _adapt_eth(eth, local_id.dst_ip, pkt, remote_domain); + if (result.valid()) + return; + result = _nat_link_and_pass( + eth, size_guard, ip, ip_icd, prot, prot_base, prot_size, + local_id, local_domain, remote_domain); + }); } } - catch (Interface::Bad_transport_protocol) { } + if (result.valid()) + return result; /* try to route via IP rules */ local_domain.ip_rules().find_longest_prefix_match( @@ -1371,17 +1397,18 @@ void Interface::_handle_ip(Ethernet_frame ð, log("[", local_domain, "] using IP rule: ", rule); } Domain &remote_domain = rule.domain(); - _adapt_eth(eth, ip.dst(), pkt, remote_domain); + result = _adapt_eth(eth, ip.dst(), pkt, remote_domain); + if (result.valid()) + return; remote_domain.interfaces().for_each([&] (Interface &interface) { interface.send(eth, size_guard); }); - done = true; + result = packet_handled(); }, [&] /* handle_no_match */ () { } ); - if (done) { - return; - } + if (result.valid()) + return result; /* * Give up and drop packet. According to RFC 1812 section 4.3.2.7, an ICMP @@ -1398,8 +1425,7 @@ void Interface::_handle_ip(Ethernet_frame ð, _send_icmp_dst_unreachable(local_intf, eth, ip, Icmp_packet::Code::DST_HOST_UNREACHABLE); } - if (_config().verbose()) { - log("[", local_domain, "] unroutable packet"); } + return packet_drop("unroutable"); } @@ -1455,7 +1481,7 @@ void Interface::_handle_arp_reply(Ethernet_frame ð, Arp_waiter &waiter = *waiter_le->object(); waiter_le = waiter_le->next(); if (ip != waiter.ip()) { continue; } - waiter.src()._continue_handle_eth(local_domain, waiter.packet()); + waiter.src()._continue_handle_eth(waiter.packet()); destroy(waiter.src()._alloc, &waiter); } } @@ -1504,10 +1530,10 @@ void Interface::_send_arp_reply(Ethernet_frame &request_eth, } -void Interface::_handle_arp_request(Ethernet_frame ð, - Size_guard &size_guard, - Arp_packet &arp, - Domain &local_domain) +Packet_result Interface::_handle_arp_request(Ethernet_frame ð, + Size_guard &size_guard, + Arp_packet &arp, + Domain &local_domain) { Ipv4_config const &local_ip_cfg = local_domain.ip_config(); Ipv4_address_prefix const &local_intf = local_ip_cfg.interface(); @@ -1517,7 +1543,7 @@ void Interface::_handle_arp_request(Ethernet_frame ð, if (arp.src_ip() == arp.dst_ip()) { /* gratuitous ARP requests are not really necessary */ - throw Drop_packet("gratuitous ARP request"); + return packet_drop("gratuitous ARP request"); } else if (arp.dst_ip() == local_intf.address) { @@ -1542,7 +1568,7 @@ void Interface::_handle_arp_request(Ethernet_frame ð, if (local_ip_cfg.gateway_valid()) { /* leave request up to the gateway of the domain */ - throw Drop_packet("leave ARP request up to gateway"); + return packet_drop("leave ARP request up to gateway"); } else { @@ -1553,35 +1579,54 @@ void Interface::_handle_arp_request(Ethernet_frame ð, _send_arp_reply(eth, arp); } } + return packet_handled(); } -void Interface::_handle_arp(Ethernet_frame ð, - Size_guard &size_guard, - Domain &local_domain) +Packet_result Interface::_handle_arp(Ethernet_frame ð, + Size_guard &size_guard, + Domain &local_domain) { /* ignore ARP regarding protocols other than IPv4 via ethernet */ Arp_packet &arp = eth.data(size_guard); if (!arp.ethernet_ipv4()) { - throw Drop_packet("ARP for unknown protocol"); } + return packet_drop("ARP for unknown protocol"); } switch (arp.opcode()) { - case Arp_packet::REPLY: _handle_arp_reply(eth, size_guard, arp, local_domain); break; - case Arp_packet::REQUEST: _handle_arp_request(eth, size_guard, arp, local_domain); break; - default: throw Drop_packet("unknown ARP operation"); } + case Arp_packet::REPLY: _handle_arp_reply(eth, size_guard, arp, local_domain); break; + case Arp_packet::REQUEST: return _handle_arp_request(eth, size_guard, arp, local_domain); + default: return packet_drop("unknown ARP operation"); } + return packet_handled(); +} + + +void Interface::_drop_packet(Packet_descriptor const &pkt, char const *reason) +{ + _ack_packet(pkt); + with_domain( + [&] /* domain_fn */ (Domain &domain) { + if (domain .verbose_packet_drop()) + log("[", domain, "] drop packet (", reason, ")"); }, + [&] /* no_domain_fn */ { + if (_config().verbose()) + log("[?] drop packet (", reason, ")"); }); } void Interface::_handle_pkt() { Packet_descriptor const pkt = _sink.get_packet(); - Size_guard size_guard(pkt.size()); - try { - _handle_eth(_sink.packet_content(pkt), size_guard, pkt); - _ack_packet(pkt); + if (!_sink.packet_valid(pkt) || pkt.size() < sizeof(Packet_stream_sink::Content_type)) { + _drop_packet(pkt, "invalid Nic packet"); + return; } - catch (Packet_postponed) { } - catch (Genode::Packet_descriptor::Invalid_packet) { } + Size_guard size_guard(pkt.size()); + Packet_result result = _handle_eth(_sink.packet_content(pkt), size_guard, pkt); + switch (result.type) { + case Packet_result::HANDLED: _ack_packet(pkt); break; + case Packet_result::POSTPONED: break; + case Packet_result::DROP: _drop_packet(pkt, result.drop_reason); break; + case Packet_result::INVALID: ASSERT_NEVER_REACHED; } } @@ -1644,29 +1689,27 @@ void Interface::_handle_pkt_stream_signal() } -void Interface::_continue_handle_eth(Domain const &domain, - Packet_descriptor const &pkt) +void Interface::_continue_handle_eth(Packet_descriptor const &pkt) { + if (!_sink.packet_valid(pkt) || pkt.size() < sizeof(Packet_stream_sink::Content_type)) { + _drop_packet(pkt, "invalid Nic packet"); + return; + } Size_guard size_guard(pkt.size()); - try { _handle_eth(_sink.packet_content(pkt), size_guard, pkt); } - catch (Packet_postponed) { - if (domain.verbose_packet_drop()) { - log("[", domain, "] drop packet (handling postponed twice)"); } - } - catch (Genode::Packet_descriptor::Invalid_packet) { - if (domain.verbose_packet_drop()) { - log("[", domain, "] invalid Nic packet received"); - } - } - _ack_packet(pkt); + Packet_result result = _handle_eth(_sink.packet_content(pkt), size_guard, pkt); + switch (result.type) { + case Packet_result::HANDLED: _ack_packet(pkt); break; + case Packet_result::POSTPONED: _drop_packet(pkt, "postponed twice"); break; + case Packet_result::DROP: _drop_packet(pkt, result.drop_reason); break; + case Packet_result::INVALID: ASSERT_NEVER_REACHED; } } void Interface::_destroy_dhcp_allocation(Dhcp_allocation &allocation, Domain &local_domain) { - try { local_domain.dhcp_server().free_ip(local_domain, allocation.ip()); } - catch (Pointer::Invalid) { } + local_domain.with_dhcp_server([&] (Dhcp_server &srv) { + srv.free_ip(allocation.ip()); }); destroy(_alloc, &allocation); } @@ -1680,17 +1723,17 @@ void Interface::_destroy_released_dhcp_allocations(Domain &local_domain) } -void Interface::_handle_eth(Ethernet_frame ð, - Size_guard &size_guard, - Packet_descriptor const &pkt, - Domain &local_domain) +Packet_result Interface::_handle_eth(Ethernet_frame ð, + Size_guard &size_guard, + Packet_descriptor const &pkt, + Domain &local_domain) { if (local_domain.ip_config().valid()) { switch (eth.type()) { - case Ethernet_frame::Type::ARP: _handle_arp(eth, size_guard, local_domain); break; - case Ethernet_frame::Type::IPV4: _handle_ip(eth, size_guard, pkt, local_domain); break; - default: throw Bad_network_protocol(); } + case Ethernet_frame::Type::ARP: return _handle_arp(eth, size_guard, local_domain); + case Ethernet_frame::Type::IPV4: return _handle_ip(eth, size_guard, pkt, local_domain); + default: return packet_drop("unknown network layer protocol"); } } else { @@ -1700,161 +1743,81 @@ void Interface::_handle_eth(Ethernet_frame ð, if (eth.dst() != router_mac() && eth.dst() != Mac_address(0xff)) { - throw Drop_packet("Expecting Ethernet targeting the router"); } + return packet_drop("Expecting Ethernet targeting the router"); } Ipv4_packet &ip = eth.data(size_guard); if (ip.protocol() != Ipv4_packet::Protocol::UDP) { - throw Drop_packet("Expecting UDP packet"); } + return packet_drop("Expecting UDP packet"); } Udp_packet &udp = ip.data(size_guard); if (!Dhcp_packet::is_dhcp(&udp)) { - throw Drop_packet("Expecting DHCP packet"); } + return packet_drop("Expecting DHCP packet"); } Dhcp_packet &dhcp = udp.data(size_guard); switch (dhcp.op()) { case Dhcp_packet::REPLY: if (dhcp.client_mac() != router_mac()) { - throw Drop_packet("Expecting DHCP targeting the router"); } + return packet_drop("Expecting DHCP targeting the router"); } if (!_dhcp_client.constructed()) { - throw Drop_packet("Expecting DHCP client to be active"); } + return packet_drop("Expecting DHCP client to be active"); } - _dhcp_client->handle_dhcp_reply(dhcp); - break; + return _dhcp_client->handle_dhcp_reply(dhcp, local_domain); default: - throw Drop_packet("Expecting DHCP reply"); + return packet_drop("Expecting DHCP reply"); } break; } case Ethernet_frame::Type::ARP: { - throw Drop_packet("Ignore ARP request on unconfigured interface"); + return packet_drop("Ignore ARP request on unconfigured interface"); } - default: - - throw Bad_network_protocol(); + default: return packet_drop("unknown network layer protocol"); } } + ASSERT_NEVER_REACHED; } -void Interface::_handle_eth(void *const eth_base, - Size_guard &size_guard, - Packet_descriptor const &pkt) +Packet_result Interface::_handle_eth(void *const eth_base, + Size_guard &size_guard, + Packet_descriptor const &pkt) { + Packet_result result { }; try { - Domain &local_domain = _domain(); - local_domain.raise_rx_bytes(size_guard.total_size()); - try { - Ethernet_frame ð = Ethernet_frame::cast_from(eth_base, size_guard); - try { - /* do garbage collection over transport-layer links and DHCP allocations */ - _destroy_dissolved_links(_dissolved_icmp_links, _alloc); - _destroy_dissolved_links(_dissolved_udp_links, _alloc); - _destroy_dissolved_links(_dissolved_tcp_links, _alloc); - _destroy_released_dhcp_allocations(local_domain); + Ethernet_frame ð = Ethernet_frame::cast_from(eth_base, size_guard); + auto domain_fn = [&] (Domain &domain) { - /* log received packet if desired */ - if (local_domain.verbose_packets()) { - log("[", local_domain, "] rcv ", eth); } + domain.raise_rx_bytes(size_guard.total_size()); - if (local_domain.trace_packets()) - Genode::Trace::Ethernet_packet(local_domain.name().string(), - Genode::Trace::Ethernet_packet::Direction::RECV, - eth_base, - size_guard.total_size()); + /* do garbage collection over transport-layer links and DHCP allocations */ + _destroy_dissolved_links(_dissolved_icmp_links, _alloc); + _destroy_dissolved_links(_dissolved_udp_links, _alloc); + _destroy_dissolved_links(_dissolved_tcp_links, _alloc); + _destroy_released_dhcp_allocations(domain); - /* try to handle ethernet frame */ - try { _handle_eth(eth, size_guard, pkt, local_domain); } - catch (Free_resources_and_retry_handle_eth) { - try { - if (_config().verbose()) { - log("[", local_domain, "] free resources and retry to handle packet"); } + /* log received packet if desired */ + if (domain.verbose_packets()) { + log("[", domain, "] rcv ", eth); } - /* - * Resources do not suffice, destroy some links - * - * Limit number of links to destroy because otherwise, - * this could block the router for a significant - * amount of time. - */ - unsigned long max = MAX_FREE_OPS_PER_EMERGENCY; - _destroy_some_links (_tcp_links, _dissolved_tcp_links, _alloc, max); - _destroy_some_links (_udp_links, _dissolved_udp_links, _alloc, max); - _destroy_some_links(_icmp_links, _dissolved_icmp_links, _alloc, max); + if (domain.trace_packets()) + Genode::Trace::Ethernet_packet( + domain.name().string(), Genode::Trace::Ethernet_packet::Direction::RECV, + eth_base, size_guard.total_size()); - /* retry to handle ethernet frame */ - _handle_eth(eth, size_guard, pkt, local_domain); - } - catch (Free_resources_and_retry_handle_eth exception) { - if (exception.prot != (L3_protocol)0) { - switch (exception.prot) { - case L3_protocol::TCP: _tcp_stats.refused_for_ram++; break; - case L3_protocol::UDP: _udp_stats.refused_for_ram++; break; - case L3_protocol::ICMP: _icmp_stats.refused_for_ram++; break; - default: throw Bad_transport_protocol(); } - } - - /* give up if the resources still not suffice */ - throw Drop_packet("insufficient resources"); - } - } - } - catch (Dhcp_server::Alloc_ip_failed) { - if (_config().verbose()) { - log("[", local_domain, "] failed to allocate IP for DHCP " - "client"); - } - } - catch (Port_allocator_guard::Out_of_indices) { - if (_config().verbose()) { - log("[", local_domain, "] no available NAT ports"); } - } - catch (Domain::No_next_hop) { - if (_config().verbose()) { - log("[", local_domain, "] cannot find next hop"); } - } - catch (Alloc_dhcp_msg_buffer_failed) { - if (_config().verbose()) { - log("[", local_domain, "] failed to allocate buffer for " - "DHCP reply"); - } - } - catch (Bad_network_protocol) { - if (_config().verbose()) { - log("[", local_domain, "] unknown network layer " - "protocol"); - } - } - catch (Drop_packet exception) { - if (local_domain.verbose_packet_drop()) { - log("[", local_domain, "] drop packet (", - exception.reason, ")"); - } - } - } - catch (Size_guard::Exceeded) { - if (_config().verbose()) { - log("[", local_domain, "] drop packet: packet size-guard " - "exceeded"); - } - } - } - catch (Pointer::Invalid) { - try { - Ethernet_frame ð = Ethernet_frame::cast_from(eth_base, size_guard); - if (_config().verbose_packets()) { - log("[?] rcv ", eth); } - } - catch (Size_guard::Exceeded) { - if (_config().verbose_packets()) { - log("[?] rcv ?"); } - } - if (_config().verbose()) { - log("[?] drop packet: no domain"); } + result = _handle_eth(eth, size_guard, pkt, domain); + }; + auto no_domain_fn = [&] /* no_domain_fn */ { + if (_config().verbose_packets()) + log("[?] rcv ", eth); + result = packet_drop("no domain"); + }; + with_domain(domain_fn, no_domain_fn); } + catch (Size_guard::Exceeded) { result = packet_drop("packet size-guard exceeded"); } + return result; } @@ -1871,7 +1834,7 @@ void Interface::_send_submit_pkt(Packet_descriptor &pkt, void * &pkt_base, size_t pkt_size) { - Domain &local_domain = _domain(); + Domain &local_domain = *_domain_ptr; local_domain.raise_tx_bytes(pkt_size); if (local_domain.verbose_packets()) { try { @@ -1915,8 +1878,7 @@ Interface::Interface(Genode::Entrypoint &ep, _interfaces { interfaces } { _interfaces.insert(this); - try { _config().report().handle_interface_link_state(); } - catch (Pointer::Invalid) { } + _config().with_report([&] (Report &r) { r.handle_interface_link_state(); }); } @@ -1941,11 +1903,7 @@ bool Interface::_try_update_link(Link &link, return false; if (link.client().src_ip() == link.server().dst_ip()) { - - link.handle_config( - cln_dom, new_srv_dom, Pointer { }, - _config()); - + link.handle_config(cln_dom, new_srv_dom, nullptr, _config()); return true; } if (link.server().dst_ip() != new_srv_dom.ip_config().interface().address) @@ -1957,13 +1915,10 @@ bool Interface::_try_update_link(Link &link, [&] /* handle_match */ (Nat_rule &nat) { Port_allocator_guard &remote_port_alloc { nat.port_alloc(prot) }; - try { remote_port_alloc.alloc(link.server().dst_port()); } - catch (Port_allocator::Allocation_conflict) { return; } - catch (Port_allocator_guard::Out_of_indices) { return; } - - link.handle_config( - cln_dom, new_srv_dom, remote_port_alloc, _config()); + if (!remote_port_alloc.alloc(link.server().dst_port())) + return; + link.handle_config(cln_dom, new_srv_dom, &remote_port_alloc, _config()); keep_link = true; }, [&] /* handle_no_match */ () { } @@ -1977,49 +1932,35 @@ void Interface::_update_udp_tcp_links(L3_protocol prot, { links(prot).for_each([&] (Link &link) { - bool link_has_been_updated { false }; + bool link_updated { false }; if (link.server().src_ip() != link.client().dst_ip()) { - _forward_rules(cln_dom, prot).find_by_port( - link.client().dst_port(), - [&] /* handle_match */ (Forward_rule const &rule) - { - if (rule.to_ip() != link.server().src_ip()) + _with_forward_rule(cln_dom, prot, link.client().dst_port(), [&] (Forward_rule const &rule) { + if (rule.to_ip() != link.server().src_ip()) + return; + + if (rule.to_port() == Port { 0 }) { + + if (link.server().src_port() != + link.client().dst_port()) return; - if (rule.to_port() == Port { 0 }) { + } else { - if (link.server().src_port() != - link.client().dst_port()) - return; - - } else { - - if (rule.to_port() != link.server().src_port()) - return; - } - link_has_been_updated = - _try_update_link(link, rule.domain(), prot, cln_dom); - }, - [&] /* handle_no_match */ () { } - ); + if (rule.to_port() != link.server().src_port()) + return; + } + link_updated = _try_update_link(link, rule.domain(), prot, cln_dom); + }); } else { - _transport_rules(cln_dom, prot).find_best_match( - link.client().dst_ip(), - link.client().dst_port(), - [&] /* handle_match */ (Transport_rule const &, - Permit_rule const &rule) - { - link_has_been_updated = - _try_update_link(link, rule.domain(), prot, cln_dom); - }, - [&] /* handle_no_match */ () { } - ); + _with_transport_rule(cln_dom, prot, link.client().dst_ip(), link.client().dst_port(), + [&] (Transport_rule const &, Permit_rule const &rule) { + link_updated = _try_update_link(link, rule.domain(), prot, cln_dom); }); } - if (link_has_been_updated) + if (link_updated) return; _dismiss_link(link); @@ -2055,51 +1996,37 @@ void Interface::_update_dhcp_allocations(Domain &old_domain, Domain &new_domain) { bool dhcp_clients_outdated { false }; - try { - Dhcp_server &old_dhcp_srv = old_domain.dhcp_server(); - Dhcp_server &new_dhcp_srv = new_domain.dhcp_server(); - if (!old_dhcp_srv.config_equal_to_that_of(new_dhcp_srv)) { - throw Pointer::Invalid(); - } - _dhcp_allocations.for_each([&] (Dhcp_allocation &allocation) { - try { - new_dhcp_srv.alloc_ip(allocation.ip()); + old_domain.with_dhcp_server([&] (Dhcp_server &old_dhcp_srv) { + new_domain.with_dhcp_server([&] (Dhcp_server &new_dhcp_srv) { + if (old_dhcp_srv.config_equal_to_that_of(new_dhcp_srv)) { + /* try to re-use existing DHCP allocations */ + _dhcp_allocations.for_each([&] (Dhcp_allocation &allocation) { + if (!new_dhcp_srv.alloc_ip(allocation.ip())) { + if (_config().verbose()) + log("[", new_domain, "] dismiss DHCP allocation: ", allocation, " (no IP)"); - /* keep DHCP allocation */ - if (_config().verbose()) { - log("[", new_domain, "] update DHCP allocation: ", - allocation); + dhcp_clients_outdated = true; + _dhcp_allocations.remove(allocation); + _destroy_dhcp_allocation(allocation, old_domain); + return; } - return; - } - catch (Dhcp_server::Alloc_ip_failed) { - if (_config().verbose()) { - log("[", new_domain, "] dismiss DHCP allocation: ", - allocation, " (no IP)"); - } - } - /* dismiss DHCP allocation */ + if (_config().verbose()) + log("[", new_domain, "] update DHCP allocation: ", allocation); + }); + } else { + /* dismiss all DHCP allocations */ dhcp_clients_outdated = true; - _dhcp_allocations.remove(allocation); - _destroy_dhcp_allocation(allocation, old_domain); - }); - } - catch (Pointer::Invalid) { - - /* dismiss all DHCP allocations */ - dhcp_clients_outdated = true; - while (Dhcp_allocation *allocation = _dhcp_allocations.first()) { - if (_config().verbose()) { - log("[", new_domain, "] dismiss DHCP allocation: ", - *allocation, " (other/no DHCP server)"); + 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); } - _dhcp_allocations.remove(*allocation); - _destroy_dhcp_allocation(*allocation, old_domain); } - } - if (dhcp_clients_outdated) { - _reset_and_refetch_domain_ready_state(); - } + if (dhcp_clients_outdated) + _reset_and_refetch_domain_ready_state(); + });}); } @@ -2146,9 +2073,9 @@ void Interface::handle_config_1(Configuration &config) _config = config; _policy.handle_config(config); Domain_name const &new_domain_name = _policy.determine_domain_name(); - try { + with_domain([&] (Domain &old_domain) { + /* destroy state objects that are not needed anymore */ - Domain &old_domain = domain(); _destroy_dissolved_links(_dissolved_icmp_links, _alloc); _destroy_dissolved_links (_dissolved_udp_links, _alloc); _destroy_dissolved_links (_dissolved_tcp_links, _alloc); @@ -2167,37 +2094,35 @@ void Interface::handle_config_1(Configuration &config) }, [&] /* no_match_fn */ () { } ); - } - catch (Pointer::Invalid) { } + }); } void Interface::_failed_to_send_packet_link() { if (_config().verbose()) { - log("[", _domain(), "] failed to send packet (link down)"); } + log("[", *_domain_ptr, "] failed to send packet (link down)"); } } void Interface::_failed_to_send_packet_submit() { if (_config().verbose()) { - log("[", _domain(), "] failed to send packet (queue full)"); } + log("[", *_domain_ptr, "] failed to send packet (queue full)"); } } void Interface::_failed_to_send_packet_alloc() { if (_config().verbose()) { - log("[", _domain(), "] failed to send packet (packet alloc failed)"); } + log("[", *_domain_ptr, "] failed to send packet (packet alloc failed)"); } } void Interface::handle_config_2() { Domain_name const &new_domain_name = _policy.determine_domain_name(); - try { - Domain &old_domain = domain(); + auto domain_fn = [&] (Domain &old_domain) { _config().domains().with_element( new_domain_name, [&] /* match_fn */ (Domain &new_domain) @@ -2245,9 +2170,8 @@ void Interface::handle_config_2() } } ); - } - catch (Pointer::Invalid) { - + }; + auto no_domain_fn = [&] { /* the interface had no domain but now it may get one */ _config().domains().with_element( new_domain_name, @@ -2262,13 +2186,14 @@ void Interface::handle_config_2() }, [&] /* no_match_fn */ () { } ); - } + }; + with_domain(domain_fn, no_domain_fn); } void Interface::handle_config_3() { - try { + if (_update_domain.constructed()) { /* * Update the domain object only if handle_config_2 determined that * the interface stays attached to the same domain. Otherwise the @@ -2296,13 +2221,9 @@ void Interface::handle_config_3() _update_icmp_links(new_domain); _update_dhcp_allocations(old_domain, new_domain); _update_own_arp_waiters(new_domain); - } - catch (Constructible::Deref_unconstructed_object) { - + } else /* if the interface moved to another domain, finish the operation */ - try { attach_to_domain_finish(); } - catch (Pointer::Invalid) { } - } + with_domain([&] (Domain &) { attach_to_domain_finish(); }); } @@ -2310,7 +2231,7 @@ void Interface::_ack_packet(Packet_descriptor const &pkt) { if (!_sink.try_ack_packet(pkt)) { if (_config().verbose()) { - log("[", _domain(), "] leak packet (sink not ready to " + log("[", *_domain_ptr, "] leak packet (sink not ready to " "acknowledge)"); } return; @@ -2320,59 +2241,48 @@ void Interface::_ack_packet(Packet_descriptor const &pkt) void Interface::cancel_arp_waiting(Arp_waiter &waiter) { - try { - Domain &domain = _domain(); - if (domain.verbose_packet_drop()) { - log("[", domain, "] drop packet (ARP got cancelled)"); } - } - catch (Pointer::Invalid) { - if (_config().verbose_packet_drop()) { - log("[?] drop packet (ARP got cancelled)"); } - } - _ack_packet(waiter.packet()); + _drop_packet(waiter.packet(), "ARP got cancelled"); destroy(_alloc, &waiter); } Interface::~Interface() { - try { _config().report().handle_interface_link_state(); } - catch (Pointer::Invalid) { } + _config().with_report([&] (Report &r) { r.handle_interface_link_state(); }); _detach_from_domain(); _interfaces.remove(this); } -void Interface::report(Genode::Xml_generator &xml) -{ - xml.node("interface", [&] () { - bool empty { true }; - xml.attribute("label", _policy.label()); - if (_config().report().link_state()) { - xml.attribute("link_state", link_state()); - empty = false; - } - if (_config().report().stats()) { - try { - _policy.report(xml); - empty = false; - } - catch (Report::Empty) { } - try { xml.node("tcp-links", [&] () { _tcp_stats.report(xml); }); empty = false; } catch (Report::Empty) { } - try { xml.node("udp-links", [&] () { _udp_stats.report(xml); }); empty = false; } catch (Report::Empty) { } - try { xml.node("icmp-links", [&] () { _icmp_stats.report(xml); }); empty = false; } catch (Report::Empty) { } - try { xml.node("arp-waiters", [&] () { _arp_stats.report(xml); }); empty = false; } catch (Report::Empty) { } - try { xml.node("dhcp-allocations", [&] () { _dhcp_stats.report(xml); }); empty = false; } catch (Report::Empty) { } - } - if (_config().report().dropped_fragm_ipv4() && _dropped_fragm_ipv4) { - xml.node("dropped-fragm-ipv4", [&] () { - xml.attribute("value", _dropped_fragm_ipv4); - }); - empty = false; - } - if (empty) { throw Report::Empty(); } - }); +bool Interface::report_empty(Report const &report_cfg) const +{ + bool stats = report_cfg.stats() && ( + !_policy.report_empty() || !_tcp_stats.report_empty() || !_udp_stats.report_empty() || + !_icmp_stats.report_empty() || !_arp_stats.report_empty() || _dhcp_stats.report_empty()); + bool lnk_state = report_cfg.link_state(); + bool fragm_ip = report_cfg.dropped_fragm_ipv4() && _dropped_fragm_ipv4; + return !lnk_state && !stats && !fragm_ip; +} + + +void Interface::report(Genode::Xml_generator &xml, Report const &report_cfg) const +{ + xml.attribute("label", _policy.label()); + if (report_cfg.link_state()) + xml.attribute("link_state", link_state()); + + if (report_cfg.stats()) { + _policy.report(xml); + if (!_tcp_stats.report_empty()) xml.node("tcp-links", [&] { _tcp_stats.report(xml); }); + if (!_udp_stats.report_empty()) xml.node("udp-links", [&] { _udp_stats.report(xml); }); + if (!_icmp_stats.report_empty()) xml.node("icmp-links", [&] { _icmp_stats.report(xml); }); + if (!_arp_stats.report_empty()) xml.node("arp-waiters", [&] { _arp_stats.report(xml); }); + if (!_dhcp_stats.report_empty()) xml.node("dhcp-allocations", [&] { _dhcp_stats.report(xml); }); + } + if (report_cfg.dropped_fragm_ipv4() && _dropped_fragm_ipv4) + xml.node("dropped-fragm-ipv4", [&] { + xml.attribute("value", _dropped_fragm_ipv4); }); } diff --git a/repos/os/src/server/nic_router/interface.h b/repos/os/src/server/nic_router/interface.h index 99da02c662..beea19c9a3 100644 --- a/repos/os/src/server/nic_router/interface.h +++ b/repos/os/src/server/nic_router/interface.h @@ -68,7 +68,8 @@ struct Net::Interface_object_stats Genode::size_t alive { 0 }; Genode::size_t destroyed { 0 }; - void report(Genode::Xml_generator &xml); + bool report_empty() const; + void report(Genode::Xml_generator &xml) const; ~Interface_object_stats(); }; @@ -89,13 +90,14 @@ struct Net::Interface_link_stats Genode::size_t dissolved_no_timeout { 0 }; Genode::size_t destroyed { 0 }; - void report(Genode::Xml_generator &xml); + bool report_empty() const; + void report(Genode::Xml_generator &xml) const; ~Interface_link_stats(); }; -struct Net::Interface_policy +struct Net::Interface_policy : Genode::Interface { virtual Domain_name determine_domain_name() const = 0; @@ -107,7 +109,9 @@ struct Net::Interface_policy virtual bool interface_link_state() const = 0; - virtual void report(Genode::Xml_generator &) const { throw Report::Empty(); } + virtual bool report_empty() const = 0; + + virtual void report(Genode::Xml_generator &) const = 0; virtual ~Interface_policy() { } }; @@ -126,9 +130,6 @@ class Net::Interface : private Interface_list::Element enum { IPV4_TIME_TO_LIVE = 64 }; enum { MAX_FREE_OPS_PER_EMERGENCY = 1024 }; - struct Dismiss_link : Genode::Exception { }; - struct Dismiss_arp_waiter : Genode::Exception { }; - struct Update_domain { Domain &old_domain; @@ -151,7 +152,7 @@ class Net::Interface : private Interface_list::Element Interface_policy &_policy; Cached_timer &_timer; Genode::Allocator &_alloc; - Pointer _domain { }; + Domain *_domain_ptr { }; Arp_waiter_list _own_arp_waiters { }; Link_list _tcp_links { }; Link_list _udp_links { }; @@ -171,11 +172,18 @@ class Net::Interface : private Interface_list::Element Interface_object_stats _dhcp_stats { }; unsigned long _dropped_fragm_ipv4 { 0 }; - 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); + /* + * Noncopyable + */ + Interface(Interface const &); + Interface &operator = (Interface const &); + + [[nodiscard]] Packet_result _new_link(L3_protocol const protocol, + Domain &local_domain, + Link_side_id const &local_id, + Port_allocator_guard *remote_port_alloc_ptr, + Domain &remote_domain, + Link_side_id const &remote_id); void _destroy_released_dhcp_allocations(Domain &local_domain); @@ -185,10 +193,10 @@ class Net::Interface : private Interface_list::Element void _release_dhcp_allocation(Dhcp_allocation &allocation, Domain &local_domain); - void _new_dhcp_allocation(Ethernet_frame ð, - Dhcp_packet &dhcp, - Dhcp_server &dhcp_srv, - Domain &local_domain); + [[nodiscard]] Packet_result _new_dhcp_allocation(Ethernet_frame ð, + Dhcp_packet &dhcp, + Dhcp_server &dhcp_srv, + Domain &local_domain); void _send_dhcp_reply(Dhcp_server const &dhcp_srv, Mac_address const ð_dst, @@ -204,84 +212,79 @@ class Net::Interface : private Interface_list::Element Genode::size_t icmp_sz, Size_guard &size_guard); - Forward_rule_tree &_forward_rules(Domain &local_domain, - L3_protocol const prot) const; - - Transport_rule_list &_transport_rules(Domain &local_domain, - L3_protocol const prot) const; - - void _handle_arp(Ethernet_frame ð, - Size_guard &size_guard, - Domain &local_domain); + [[nodiscard]] Packet_result _handle_arp(Ethernet_frame ð, + Size_guard &size_guard, + Domain &local_domain); void _handle_arp_reply(Ethernet_frame ð, Size_guard &size_guard, Arp_packet &arp, Domain &local_domain); - void _handle_arp_request(Ethernet_frame ð, - Size_guard &size_guard, - Arp_packet &arp, - Domain &local_domain); + [[nodiscard]] Packet_result _handle_arp_request(Ethernet_frame ð, + Size_guard &size_guard, + Arp_packet &arp, + Domain &local_domain); void _send_arp_reply(Ethernet_frame &request_eth, Arp_packet &request_arp); - void _handle_dhcp_request(Ethernet_frame ð, - Dhcp_packet &dhcp, - Domain &local_domain, - Ipv4_address_prefix const &local_intf); + [[nodiscard]] Packet_result _handle_dhcp_request(Ethernet_frame ð, + Dhcp_server &dhcp_srv, + Dhcp_packet &dhcp, + Domain &local_domain, + Ipv4_address_prefix const &local_intf); - void _handle_ip(Ethernet_frame ð, - Size_guard &size_guard, - Packet_descriptor const &pkt, - Domain &local_domain); + [[nodiscard]] Packet_result _handle_ip(Ethernet_frame ð, + Size_guard &size_guard, + Packet_descriptor const &pkt, + Domain &local_domain); - void _handle_icmp_query(Ethernet_frame ð, - Size_guard &size_guard, - Ipv4_packet &ip, - Internet_checksum_diff &ip_icd, - Packet_descriptor const &pkt, - L3_protocol prot, - void *prot_base, - Genode::size_t prot_size, - Domain &local_domain); + [[nodiscard]] Packet_result _handle_icmp_query(Ethernet_frame ð, + Size_guard &size_guard, + Ipv4_packet &ip, + Internet_checksum_diff &ip_icd, + Packet_descriptor const &pkt, + L3_protocol prot, + void *prot_base, + Genode::size_t prot_size, + Domain &local_domain); - void _handle_icmp_error(Ethernet_frame ð, - Size_guard &size_guard, - Ipv4_packet &ip, - Internet_checksum_diff &ip_icd, - Packet_descriptor const &pkt, - Domain &local_domain, - Icmp_packet &icmp, - Genode::size_t icmp_sz); + [[nodiscard]] Packet_result _handle_icmp_error(Ethernet_frame ð, + Size_guard &size_guard, + Ipv4_packet &ip, + Internet_checksum_diff &ip_icd, + Packet_descriptor const &pkt, + Domain &local_domain, + Icmp_packet &icmp, + Genode::size_t icmp_sz); - void _handle_icmp(Ethernet_frame ð, - Size_guard &size_guard, - Ipv4_packet &ip, - Internet_checksum_diff &ip_icd, - Packet_descriptor const &pkt, - L3_protocol prot, - void *prot_base, - Genode::size_t prot_size, - Domain &local_domain, - Ipv4_address_prefix const &local_intf); + [[nodiscard]] Packet_result _handle_icmp(Ethernet_frame ð, + Size_guard &size_guard, + Ipv4_packet &ip, + Internet_checksum_diff &ip_icd, + Packet_descriptor const &pkt, + L3_protocol prot, + void *prot_base, + Genode::size_t prot_size, + Domain &local_domain, + Ipv4_address_prefix const &local_intf); - void _adapt_eth(Ethernet_frame ð, - Ipv4_address const &dst_ip, - Packet_descriptor const &pkt, - Domain &remote_domain); + [[nodiscard]] Packet_result _adapt_eth(Ethernet_frame ð, + Ipv4_address const &dst_ip, + Packet_descriptor const &pkt, + Domain &remote_domain); - void _nat_link_and_pass(Ethernet_frame ð, - Size_guard &size_guard, - Ipv4_packet &ip, - Internet_checksum_diff &ip_icd, - L3_protocol const prot, - void *const prot_base, - Genode::size_t const prot_size, - Link_side_id const &local_id, - Domain &local_domain, - Domain &remote_domain); + [[nodiscard]] Packet_result _nat_link_and_pass(Ethernet_frame ð, + Size_guard &size_guard, + Ipv4_packet &ip, + Internet_checksum_diff &ip_icd, + L3_protocol const prot, + void *const prot_base, + Genode::size_t const prot_size, + Link_side_id const &local_id, + Domain &local_domain, + Domain &remote_domain); void _broadcast_arp_request(Ipv4_address const &src_ip, Ipv4_address const &dst_ip); @@ -301,19 +304,20 @@ class Net::Interface : private Interface_list::Element void _handle_pkt(); - void _continue_handle_eth(Domain const &domain, - Packet_descriptor const &pkt); + void _continue_handle_eth(Packet_descriptor const &pkt); Ipv4_address const &_router_ip() const; - void _handle_eth(void *const eth_base, - Size_guard &size_guard, - Packet_descriptor const &pkt); + void _drop_packet(Packet_descriptor const &pkt, char const *reason); - void _handle_eth(Ethernet_frame ð, - Size_guard &size_guard, - Packet_descriptor const &pkt, - Domain &local_domain); + [[nodiscard]] Packet_result _handle_eth(void *const eth_base, + Size_guard &size_guard, + Packet_descriptor const &pkt); + + [[nodiscard]] Packet_result _handle_eth(Ethernet_frame ð, + Size_guard &size_guard, + Packet_descriptor const &pkt, + Domain &local_domain); void _ack_packet(Packet_descriptor const &pkt); @@ -369,20 +373,6 @@ class Net::Interface : private Interface_list::Element public: - struct Free_resources_and_retry_handle_eth : Genode::Exception { L3_protocol prot; Free_resources_and_retry_handle_eth(L3_protocol prot = (L3_protocol)0) : prot(prot) { } }; - struct Bad_send_dhcp_args : Genode::Exception { }; - struct Bad_transport_protocol : Genode::Exception { }; - struct Bad_network_protocol : Genode::Exception { }; - struct Packet_postponed : Genode::Exception { }; - struct Alloc_dhcp_msg_buffer_failed : Genode::Exception { }; - - struct Drop_packet : Genode::Exception - { - char const *reason; - - Drop_packet(char const *reason) : reason(reason) { } - }; - Interface(Genode::Entrypoint &ep, Cached_timer &timer, Mac_address const router_mac, @@ -454,19 +444,30 @@ class Net::Interface : private Interface_list::Element void handle_interface_link_state(); - void report(Genode::Xml_generator &xml); + bool report_empty(Report const &report_cfg) const; + + void report(Genode::Xml_generator &xml, Report const &report_cfg) const; void handle_domain_ready_state(bool state); void destroy_link(Link &link); + void with_domain(auto const &domain_fn, auto const &no_domain_fn) + { + if (_domain_ptr) + domain_fn(*_domain_ptr); + else + no_domain_fn(); + } + + void with_domain(auto const &fn) { with_domain(fn, []{}); } + /*************** ** Accessors ** ***************/ Configuration const &config() const { return _config(); } - Domain &domain() { return _domain(); } Mac_address const &router_mac() const { return _router_mac; } Mac_address const &mac() const { return _mac; } Arp_waiter_list &own_arp_waiters() { return _own_arp_waiters; } diff --git a/repos/os/src/server/nic_router/ip_rule.cc b/repos/os/src/server/nic_router/ip_rule.cc deleted file mode 100644 index b808afee32..0000000000 --- a/repos/os/src/server/nic_router/ip_rule.cc +++ /dev/null @@ -1,26 +0,0 @@ -/* - * \brief IP routing entry - * \author Martin Stein - * \date 2016-08-19 - */ - -/* - * Copyright (C) 2016-2017 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU Affero General Public License version 3. - */ - -/* local includes */ -#include -#include - -using namespace Net; -using namespace Genode; - - -Ip_rule::Ip_rule(Domain_dict &domains, Xml_node const node) -: - Direct_rule { node }, - _domain { domains.deprecated_find_by_domain_attr(node) } -{ } diff --git a/repos/os/src/server/nic_router/ip_rule.h b/repos/os/src/server/nic_router/ip_rule.h index 8340231155..443c0e8485 100644 --- a/repos/os/src/server/nic_router/ip_rule.h +++ b/repos/os/src/server/nic_router/ip_rule.h @@ -35,7 +35,7 @@ class Net::Ip_rule : public Direct_rule public: - Ip_rule(Domain_dict &domains, Genode::Xml_node const node); + Ip_rule(Ipv4_address_prefix const &dst, Domain &domain) : Direct_rule(dst), _domain(domain) { } /*************** diff --git a/repos/os/src/server/nic_router/ipv4_config.cc b/repos/os/src/server/nic_router/ipv4_config.cc index 00a131e807..6dd6e3cbdf 100644 --- a/repos/os/src/server/nic_router/ipv4_config.cc +++ b/repos/os/src/server/nic_router/ipv4_config.cc @@ -90,10 +90,7 @@ Ipv4_config::Ipv4_config(Dhcp_packet &dhcp_ack, dhcp_ipv4_option(dhcp_ack) }, _gateway { dhcp_ipv4_option(dhcp_ack) } { - try { - Dhcp_packet::Dns_server const &dns_server { - dhcp_ack.option() }; - + dhcp_ack.with_option([&] (Dhcp_packet::Dns_server const &dns_server) { dns_server.for_each_address([&] (Ipv4_address const &addr) { Dns_server::construct( alloc, addr, @@ -104,10 +101,9 @@ Ipv4_config::Ipv4_config(Dhcp_packet &dhcp_ack, [&] /* handle_failure */ () { } ); }); - } - catch (Dhcp_packet::Option_not_found) { } - try { - _dns_domain_name.set_to(dhcp_ack.option()); + }); + dhcp_ack.with_option([&] (Dhcp_packet::Domain_name const &domain_name) { + _dns_domain_name.set_to(domain_name); if (domain.config().verbose() && !_dns_domain_name.valid()) { @@ -115,8 +111,7 @@ Ipv4_config::Ipv4_config(Dhcp_packet &dhcp_ack, log("[", domain, "] rejecting oversized DNS " "domain name from DHCP reply"); } - } - catch (Dhcp_packet::Option_not_found) { } + }); } diff --git a/repos/os/src/server/nic_router/l3_protocol.cc b/repos/os/src/server/nic_router/l3_protocol.cc index 4abc94f881..f2a37fd9e9 100644 --- a/repos/os/src/server/nic_router/l3_protocol.cc +++ b/repos/os/src/server/nic_router/l3_protocol.cc @@ -35,5 +35,5 @@ char const *Net::l3_protocol_name(L3_protocol protocol) case L3_protocol::TCP: return tcp_name(); case L3_protocol::UDP: return udp_name(); case L3_protocol::ICMP: return icmp_name(); - default: throw Interface::Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } } diff --git a/repos/os/src/server/nic_router/link.cc b/repos/os/src/server/nic_router/link.cc index 0d287ceb7e..68930ea87f 100644 --- a/repos/os/src/server/nic_router/link.cc +++ b/repos/os/src/server/nic_router/link.cc @@ -86,8 +86,9 @@ void Link::print(Output &output) const Link::Link(Interface &cln_interface, + Domain &cln_domain, Link_side_id const &cln_id, - Pointer srv_port_alloc, + Port_allocator_guard *srv_port_alloc_ptr, Domain &srv_domain, Link_side_id const &srv_id, Cached_timer &timer, @@ -98,12 +99,12 @@ Link::Link(Interface &cln_interface, : _config(config), _client_interface(cln_interface), - _server_port_alloc(srv_port_alloc), + _server_port_alloc_ptr(srv_port_alloc_ptr), _dissolve_timeout(timer, *this, &Link::_handle_dissolve_timeout, Microseconds { 100 * 1000 }), _dissolve_timeout_us(dissolve_timeout), _protocol(protocol), - _client(cln_interface.domain(), cln_id, *this), + _client(cln_domain, cln_id, *this), _server(srv_domain, srv_id, *this), _stats(stats), _stats_curr(stats.opening) @@ -146,30 +147,29 @@ void Link::dissolve(bool timeout) if (_config().verbose()) { log("Dissolve ", l3_protocol_name(_protocol), " link: ", *this); } - try { + if (_server_port_alloc_ptr) { if (_config().verbose()) { log("Free ", l3_protocol_name(_protocol), " port ", _server.dst_port(), " at ", _server.domain(), " that was used by ", _client.domain()); } - _server_port_alloc().free(_server.dst_port()); + _server_port_alloc_ptr->free(_server.dst_port()); } - catch (Pointer::Invalid) { } } -void Link::handle_config(Domain &cln_domain, - Domain &srv_domain, - Pointer srv_port_alloc, - Configuration &config) +void Link::handle_config(Domain &cln_domain, + Domain &srv_domain, + Port_allocator_guard *srv_port_alloc_ptr, + 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.udp_idle_timeout(); break; case L3_protocol::ICMP: dissolve_timeout_us = config.icmp_idle_timeout(); break; - default: throw Interface::Bad_transport_protocol(); + default: ASSERT_NEVER_REACHED; } _dissolve_timeout_us = dissolve_timeout_us; _dissolve_timeout.schedule(_dissolve_timeout_us); @@ -177,10 +177,10 @@ void Link::handle_config(Domain &cln_domain, _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; + _config = config; + _client._domain = cln_domain; + _server._domain = srv_domain; + _server_port_alloc_ptr = srv_port_alloc_ptr; cln_domain.links(_protocol).insert(&_client); srv_domain.links(_protocol).insert(&_server); @@ -196,17 +196,18 @@ void Link::handle_config(Domain &cln_domain, ** Tcp_link ** **************/ -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, - Cached_timer &timer, - Configuration &config, - L3_protocol const protocol, - Interface_link_stats &stats) +Tcp_link::Tcp_link(Interface &cln_interface, + Domain &cln_domain, + Link_side_id const &cln_id, + Port_allocator_guard *srv_port_alloc_ptr, + Domain &srv_domain, + Link_side_id const &srv_id, + Cached_timer &timer, + Configuration &config, + L3_protocol const protocol, + Interface_link_stats &stats) : - Link(cln_interface, cln_id, srv_port_alloc, srv_domain, srv_id, timer, + Link(cln_interface, cln_domain, cln_id, srv_port_alloc_ptr, srv_domain, srv_id, timer, config, protocol, config.tcp_idle_timeout(), stats) { } @@ -277,17 +278,18 @@ void Tcp_link::server_packet(Tcp_packet &tcp) ** Udp_link ** **************/ -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, - Cached_timer &timer, - Configuration &config, - L3_protocol const protocol, - Interface_link_stats &stats) +Udp_link::Udp_link(Interface &cln_interface, + Domain &cln_domain, + Link_side_id const &cln_id, + Port_allocator_guard *srv_port_alloc_ptr, + Domain &srv_domain, + Link_side_id const &srv_id, + Cached_timer &timer, + Configuration &config, + L3_protocol const protocol, + Interface_link_stats &stats) : - Link(cln_interface, cln_id, srv_port_alloc, srv_domain, srv_id, timer, + Link(cln_interface, cln_domain, cln_id, srv_port_alloc_ptr, srv_domain, srv_id, timer, config, protocol, config.udp_idle_timeout(), stats) { } @@ -308,17 +310,18 @@ void Udp_link::server_packet() ** Icmp_link ** ***************/ -Icmp_link::Icmp_link(Interface &cln_interface, - Link_side_id const &cln_id, - Pointer srv_port_alloc, - Domain &srv_domain, - Link_side_id const &srv_id, - Cached_timer &timer, - Configuration &config, - L3_protocol const protocol, - Interface_link_stats &stats) +Icmp_link::Icmp_link(Interface &cln_interface, + Domain &cln_domain, + Link_side_id const &cln_id, + Port_allocator_guard *srv_port_alloc_ptr, + Domain &srv_domain, + Link_side_id const &srv_id, + Cached_timer &timer, + Configuration &config, + L3_protocol const protocol, + Interface_link_stats &stats) : - Link(cln_interface, cln_id, srv_port_alloc, srv_domain, srv_id, timer, + Link(cln_interface, cln_domain, cln_id, srv_port_alloc_ptr, srv_domain, srv_id, timer, config, protocol, config.icmp_idle_timeout(), stats) { } diff --git a/repos/os/src/server/nic_router/link.h b/repos/os/src/server/nic_router/link.h index 1a999165f0..45c8681d06 100644 --- a/repos/os/src/server/nic_router/link.h +++ b/repos/os/src/server/nic_router/link.h @@ -40,7 +40,6 @@ /* local includes */ #include #include -#include #include #include @@ -187,7 +186,7 @@ class Net::Link : public Link_list::Element Reference _config; Interface &_client_interface; - Pointer _server_port_alloc; + Port_allocator_guard *_server_port_alloc_ptr; Lazy_one_shot_timeout _dissolve_timeout; Genode::Microseconds _dissolve_timeout_us; L3_protocol const _protocol; @@ -201,29 +200,36 @@ class Net::Link : public Link_list::Element void _packet() { _dissolve_timeout.schedule(_dissolve_timeout_us); } + /* + * Noncopyable + */ + Link(Link const &); + Link &operator = (Link const &); + public: struct No_port_allocator : Genode::Exception { }; - Link(Interface &cln_interface, - Link_side_id const &cln_id, - Pointer srv_port_alloc, - Domain &srv_domain, - Link_side_id const &srv_id, - Cached_timer &timer, - Configuration &config, - L3_protocol const protocol, - Genode::Microseconds const dissolve_timeout, - Interface_link_stats &stats); + Link(Interface &cln_interface, + Domain &cln_domain, + Link_side_id const &cln_id, + Port_allocator_guard *srv_port_alloc_ptr, + Domain &srv_domain, + Link_side_id const &srv_id, + Cached_timer &timer, + Configuration &config, + L3_protocol const protocol, + Genode::Microseconds const dissolve_timeout, + Interface_link_stats &stats); ~Link(); void dissolve(bool timeout); - void handle_config(Domain &cln_domain, - Domain &srv_domain, - Pointer srv_port_alloc, - Configuration &config); + void handle_config(Domain &cln_domain, + Domain &srv_domain, + Port_allocator_guard *srv_port_alloc_ptr, + Configuration &config); /********* ** Log ** @@ -273,8 +279,9 @@ class Net::Tcp_link : public Link public: Tcp_link(Interface &cln_interface, + Domain &cln_domain, Link_side_id const &cln_id, - Pointer srv_port_alloc, + Port_allocator_guard *srv_port_alloc_ptr, Domain &srv_domain, Link_side_id const &srv_id, Cached_timer &timer, @@ -291,8 +298,9 @@ class Net::Tcp_link : public Link struct Net::Udp_link : Link { Udp_link(Interface &cln_interface, + Domain &cln_domain, Link_side_id const &cln_id, - Pointer srv_port_alloc, + Port_allocator_guard *srv_port_alloc_ptr, Domain &srv_domain, Link_side_id const &srv_id, Cached_timer &timer, @@ -308,15 +316,16 @@ struct Net::Udp_link : Link struct Net::Icmp_link : Link { - Icmp_link(Interface &cln_interface, - Link_side_id const &cln_id, - Pointer srv_port_alloc, - Domain &srv_domain, - Link_side_id const &srv_id, - Cached_timer &timer, - Configuration &config, - L3_protocol const protocol, - Interface_link_stats &stats); + Icmp_link(Interface &cln_interface, + Domain &cln_domain, + Link_side_id const &cln_id, + Port_allocator_guard *srv_port_alloc_ptr, + Domain &srv_domain, + Link_side_id const &srv_id, + Cached_timer &timer, + Configuration &config, + L3_protocol const protocol, + Interface_link_stats &stats); void client_packet() { _packet(); } diff --git a/repos/os/src/server/nic_router/mac_allocator.h b/repos/os/src/server/nic_router/mac_allocator.h index 69892b399c..10d26e09e4 100644 --- a/repos/os/src/server/nic_router/mac_allocator.h +++ b/repos/os/src/server/nic_router/mac_allocator.h @@ -17,6 +17,7 @@ /* Genode includes */ #include #include +#include namespace Net { class Mac_allocator; } @@ -30,14 +31,15 @@ class Net::Mac_allocator public: - struct Alloc_failed : Genode::Exception {}; - Mac_allocator(Mac_address base) : _base(base) { Genode::memset(&_free, true, sizeof(_free)); } - Mac_address alloc() + struct Alloc_error { }; + using Alloc_result = Genode::Attempt; + + Alloc_result alloc() { for (unsigned id = 0; id < sizeof(_free) / sizeof(_free[0]); id++) { if (!_free[id]) { @@ -48,7 +50,7 @@ class Net::Mac_allocator mac.addr[5] = id; return mac; } - throw Alloc_failed(); + return Alloc_error(); } void free(Mac_address mac) { _free[mac.addr[5]] = true; } diff --git a/repos/os/src/server/nic_router/main.cc b/repos/os/src/server/nic_router/main.cc index 179fd75567..0d64a9d14e 100644 --- a/repos/os/src/server/nic_router/main.cc +++ b/repos/os/src/server/nic_router/main.cc @@ -70,9 +70,7 @@ class Net::Main void Main::_handle_report() { - try { _config().report().generate(); } - catch (Pointer::Invalid) { } - + _config().with_report([&] (Report &r) { r.generate(); }); } diff --git a/repos/os/src/server/nic_router/nat_rule.cc b/repos/os/src/server/nic_router/nat_rule.cc index b7a3ff445b..e4f791e087 100644 --- a/repos/os/src/server/nic_router/nat_rule.cc +++ b/repos/os/src/server/nic_router/nat_rule.cc @@ -32,14 +32,14 @@ bool Nat_rule::higher(Nat_rule *rule) } -Nat_rule::Nat_rule(Domain_dict &domains, +Nat_rule::Nat_rule(Domain &domain, Port_allocator &tcp_port_alloc, Port_allocator &udp_port_alloc, Port_allocator &icmp_port_alloc, Xml_node const node, bool const verbose) : - _domain { domains.deprecated_find_by_domain_attr(node) }, + _domain { domain }, _tcp_port_alloc { tcp_port_alloc, node.attribute_value("tcp-ports", 0U), verbose }, _udp_port_alloc { udp_port_alloc, node.attribute_value("udp-ports", 0U), verbose }, _icmp_port_alloc { icmp_port_alloc, node.attribute_value("icmp-ids", 0U), verbose } @@ -61,5 +61,5 @@ Port_allocator_guard &Nat_rule::port_alloc(L3_protocol const prot) case L3_protocol::TCP: return _tcp_port_alloc; case L3_protocol::UDP: return _udp_port_alloc; case L3_protocol::ICMP: return _icmp_port_alloc; - default: throw Interface::Bad_transport_protocol(); } + default: ASSERT_NEVER_REACHED; } } diff --git a/repos/os/src/server/nic_router/nat_rule.h b/repos/os/src/server/nic_router/nat_rule.h index 4df5e5d79e..a87069db9c 100644 --- a/repos/os/src/server/nic_router/nat_rule.h +++ b/repos/os/src/server/nic_router/nat_rule.h @@ -44,7 +44,7 @@ class Net::Nat_rule : public Genode::Avl_node struct Invalid : Genode::Exception { }; - Nat_rule(Domain_dict &domains, + Nat_rule(Domain &domain, Port_allocator &tcp_port_alloc, Port_allocator &udp_port_alloc, Port_allocator &icmp_port_alloc, diff --git a/repos/os/src/server/nic_router/nic_client.cc b/repos/os/src/server/nic_router/nic_client.cc index e289318cc1..ba15391fda 100644 --- a/repos/os/src/server/nic_router/nic_client.cc +++ b/repos/os/src/server/nic_router/nic_client.cc @@ -26,72 +26,68 @@ using namespace Genode; ** Nic_client ** ****************/ -void Nic_client::_invalid(char const *reason) const -{ - if (_config.verbose()) { - log("[", domain(), "] invalid NIC client: ", label(), - " (", reason, ")"); - } - throw Invalid(); -} - - Net::Nic_client::Nic_client(Session_label const &label_arg, Domain_name const &domain_arg, Allocator &alloc, - Nic_client_dict &old_nic_clients, Nic_client_dict &new_nic_clients, - Env &env, - Cached_timer &timer, - Interface_list &interfaces, Configuration &config) : Nic_client_dict::Element { new_nic_clients, label_arg }, _alloc { alloc }, _config { config }, _domain { domain_arg } +{ } + + +bool Nic_client::finish_construction(Env &env, Cached_timer &timer, Interface_list &interfaces, + Nic_client_dict &old_nic_clients) { + char const *error = ""; old_nic_clients.with_element( label(), [&] /* handle_match */ (Nic_client &old_nic_client) { /* reuse existing interface */ - Nic_client_interface &interface = old_nic_client._interface(); - old_nic_client._interface = Pointer(); + Nic_client_interface &interface = *old_nic_client._crit->interface_ptr; + old_nic_client._crit->interface_ptr = nullptr; interface.domain_name(domain()); - _interface = interface; + _crit.construct(&interface); }, [&] /* handle_no_match */ () { /* create a new interface */ - if (config.verbose()) { + if (_config.verbose()) { log("[", domain(), "] create NIC client: ", label()); } try { - _interface = *new (_alloc) - Nic_client_interface { - env, timer, alloc, interfaces, config, domain(), - label() }; + _crit.construct(new (_alloc) + Nic_client_interface(env, timer, _alloc, interfaces, _config, domain(), label())); } - catch (Insufficient_ram_quota) { _invalid("NIC session RAM quota"); } - catch (Insufficient_cap_quota) { _invalid("NIC session CAP quota"); } - catch (Service_denied) { _invalid("NIC session denied"); } + catch (Insufficient_ram_quota) { error = "NIC session RAM quota"; } + catch (Insufficient_cap_quota) { error = "NIC session CAP quota"; } + catch (Service_denied) { error = "NIC session denied"; } } ); + if (_crit.constructed()) + return true; + + if (_config.verbose()) + log("[", domain(), "] invalid NIC client: ", label(), " (", error, ")"); + return false; } Net::Nic_client::~Nic_client() { + if (!_crit.constructed()) + return; + /* if the interface was yet not reused by another NIC client, destroy it */ - try { - Nic_client_interface &interface = _interface(); + if (_crit->interface_ptr) { if (_config.verbose()) { log("[", domain(), "] destroy NIC client: ", label()); } - - destroy(_alloc, &interface); + destroy(_alloc, _crit->interface_ptr); } - catch (Pointer::Invalid) { } } diff --git a/repos/os/src/server/nic_router/nic_client.h b/repos/os/src/server/nic_router/nic_client.h index 6e7b650315..78b4879834 100644 --- a/repos/os/src/server/nic_router/nic_client.h +++ b/repos/os/src/server/nic_router/nic_client.h @@ -41,29 +41,31 @@ class Net::Nic_client : private Nic_client_dict::Element private: - Genode::Allocator &_alloc; - Configuration const &_config; - Domain_name const _domain; - Pointer _interface { }; + struct Critical { Nic_client_interface *interface_ptr; }; - void _invalid(char const *reason) const; + Genode::Allocator &_alloc; + Configuration &_config; + Domain_name const _domain; + Genode::Constructible _crit { }; + + /* + * Noncopyable + */ + Nic_client(Nic_client const &); + Nic_client &operator = (Nic_client const &); public: - struct Invalid : Genode::Exception { }; - Nic_client(Genode::Session_label const &label_arg, Domain_name const &domain_arg, Genode::Allocator &alloc, - Nic_client_dict &old_nic_clients, Nic_client_dict &new_nic_clients, - Genode::Env &env, - Cached_timer &timer, - Interface_list &interfaces, Configuration &config); ~Nic_client(); + [[nodiscard]] bool finish_construction(Genode::Env &, Cached_timer &, Interface_list &, Nic_client_dict &); + /************** ** Acessors ** @@ -93,6 +95,8 @@ class Net::Nic_client_interface_base : public Interface_policy Genode::Session_label const &label() const override { return _label; } void handle_domain_ready_state(bool state) override; bool interface_link_state() const override; + bool report_empty() const override { return true; }; + void report(Genode::Xml_generator &) const override { }; public: diff --git a/repos/os/src/server/nic_router/nic_session_root.cc b/repos/os/src/server/nic_router/nic_session_root.cc index e6dfbfca6d..8d0c8a49dd 100644 --- a/repos/os/src/server/nic_router/nic_session_root.cc +++ b/repos/os/src/server/nic_router/nic_session_root.cc @@ -60,21 +60,17 @@ Interface_policy::Interface_policy(Genode::Session_label const &label, Domain_name Net::Nic_session_component::Interface_policy::determine_domain_name() const { - Domain_name domain_name; + Domain_name domain_name { }; try { Session_policy policy(_label, _config().node()); domain_name = policy.attribute_value("domain", Domain_name()); + if (domain_name == Domain_name() && _config().verbose()) + log("[?] no domain attribute in policy for downlink label \"", _label, "\""); } catch (Session_policy::No_policy_defined) { if (_config().verbose()) { log("[?] no policy for downlink label \"", _label, "\""); } } - catch (Xml_node::Nonexistent_attribute) { - if (_config().verbose()) { - log("[?] no domain attribute in policy for downlink label \"", - _label, "\""); - } - } return domain_name; } @@ -169,8 +165,7 @@ Net::Nic_session_component::Interface_policy::interface_link_state() const case UP_DOWN: return false; case UP_DOWN_UP: return true; } - class Never_reached : Exception { }; - throw Never_reached { }; + ASSERT_NEVER_REACHED; } @@ -216,8 +211,7 @@ Net::Nic_session_component::Interface_policy::read_and_ack_session_link_state() _session_link_state_transition(DOWN_UP); return true; } - class Never_reached { }; - throw Never_reached { }; + ASSERT_NEVER_REACHED; } @@ -296,11 +290,14 @@ Net::Nic_session_root::Nic_session_root(Env &env, _env { env }, _timer { timer }, _mac_alloc { MAC_ALLOC_BASE }, - _router_mac { _mac_alloc.alloc() }, _config { config }, _shared_quota { shared_quota }, _interfaces { interfaces } -{ } +{ + _mac_alloc.alloc().with_result( + [&] (Mac_address const &mac){ _router_mac.construct(mac); }, + [] (auto) { ASSERT_NEVER_REACHED; }); +} Nic_session_component *Net::Nic_session_root::_create_session(char const *args) @@ -311,26 +308,30 @@ Nic_session_component *Net::Nic_session_root::_create_session(char const *args) _env, _shared_quota, args, [&] (Session_env &session_env, void *session_at, Ram_dataspace_capability ram_ds) { + Nic_session_component *result { }; Session_label const label { label_from_args(args) }; - Mac_address const mac { _mac_alloc.alloc() }; - try { - return construct_at( - session_at, session_env, - Arg_string::find_arg(args, "tx_buf_size").ulong_value(0), - Arg_string::find_arg(args, "rx_buf_size").ulong_value(0), - _timer, mac, _router_mac, label, _interfaces, - _config(), ram_ds); - } - catch (...) { - _mac_alloc.free(mac); - throw; - } + _mac_alloc.alloc().with_result( + [&] (auto mac) { + try { + result = construct_at( + session_at, session_env, + Arg_string::find_arg(args, "tx_buf_size").ulong_value(0), + Arg_string::find_arg(args, "rx_buf_size").ulong_value(0), + _timer, mac, *_router_mac, label, _interfaces, + _config(), ram_ds); + } + catch (...) { + _mac_alloc.free(mac); + throw; + } + }, + [&] (auto) { + _invalid_downlink("failed to allocate MAC address"); + throw Service_denied(); + }); + return result; }); } - catch (Mac_allocator::Alloc_failed) { - _invalid_downlink("failed to allocate MAC address"); - throw Service_denied(); - } catch (Region_map::Invalid_dataspace) { _invalid_downlink("Failed to attach RAM"); throw Service_denied(); diff --git a/repos/os/src/server/nic_router/nic_session_root.h b/repos/os/src/server/nic_router/nic_session_root.h index 81d45c5b90..de651e20d0 100644 --- a/repos/os/src/server/nic_router/nic_session_root.h +++ b/repos/os/src/server/nic_router/nic_session_root.h @@ -117,6 +117,7 @@ class Net::Nic_session_component : private Nic_session_component_base, Domain_name determine_domain_name() const override; void handle_config(Configuration const &config) override { _config = config; } Genode::Session_label const &label() const override { return _label; } + bool report_empty() const override { return _session_env.report_empty(); }; void report(Genode::Xml_generator &xml) const override { _session_env.report(xml); }; void handle_domain_ready_state(bool state) override; bool interface_link_state() const override; @@ -167,13 +168,13 @@ class Net::Nic_session_root enum { MAC_ALLOC_BASE = 0x02 }; - Genode::Env &_env; - Cached_timer &_timer; - Mac_allocator _mac_alloc; - Mac_address const _router_mac; - Reference _config; - Quota &_shared_quota; - Interface_list &_interfaces; + Genode::Env &_env; + Cached_timer &_timer; + Mac_allocator _mac_alloc; + Genode::Constructible _router_mac { }; + Reference _config; + Quota &_shared_quota; + Interface_list &_interfaces; void _invalid_downlink(char const *reason); diff --git a/repos/os/src/server/nic_router/packet_result.h b/repos/os/src/server/nic_router/packet_result.h new file mode 100644 index 0000000000..e98e2f58c7 --- /dev/null +++ b/repos/os/src/server/nic_router/packet_result.h @@ -0,0 +1,34 @@ +/* + * \brief Result of handling a packet + * \author Martin Stein + * \date 2024-05-23 + */ + +/* + * Copyright (C) 2024 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _PACKET_RESULT_H_ +#define _PACKET_RESULT_H_ + +namespace Net { + + struct Packet_result + { + enum Type { INVALID, DROP, POSTPONED, HANDLED } type { INVALID }; + char const *drop_reason { "" }; + + bool valid() const { return type != INVALID; } + }; + + inline Packet_result packet_drop(char const *reason) { return { Packet_result::DROP, reason }; } + + inline Packet_result packet_postponed() { return { Packet_result::POSTPONED, "" }; } + + inline Packet_result packet_handled() { return { Packet_result::HANDLED, "" }; } +} + +#endif /* _PACKET_RESULT_H_ */ diff --git a/repos/os/src/server/nic_router/permit_rule.cc b/repos/os/src/server/nic_router/permit_rule.cc index fe5a73b830..d71ef096a0 100644 --- a/repos/os/src/server/nic_router/permit_rule.cc +++ b/repos/os/src/server/nic_router/permit_rule.cc @@ -32,10 +32,7 @@ void Permit_any_rule::print(Output &output) const } -Permit_any_rule::Permit_any_rule(Domain_dict &domains, Xml_node const node) -: - Permit_rule { domains.deprecated_find_by_domain_attr(node) } -{ } +Permit_any_rule::Permit_any_rule(Domain &domain) : Permit_rule { domain } { } /************************ @@ -55,12 +52,8 @@ void Permit_single_rule::print(Output &output) const } -Permit_single_rule::Permit_single_rule(Domain_dict &domains, - Xml_node const node) +Permit_single_rule::Permit_single_rule(Port port, Domain &domain) : - Permit_rule { domains.deprecated_find_by_domain_attr(node) }, - _port { node.attribute_value("port", Port(0)) } -{ - if (_port == Port(0) || dynamic_port(_port)) { - throw Invalid(); } -} + Permit_rule { domain }, + _port { port } +{ } diff --git a/repos/os/src/server/nic_router/permit_rule.h b/repos/os/src/server/nic_router/permit_rule.h index db2cbb49a0..7eddde7766 100644 --- a/repos/os/src/server/nic_router/permit_rule.h +++ b/repos/os/src/server/nic_router/permit_rule.h @@ -16,10 +16,10 @@ /* local includes */ #include +#include /* Genode includes */ #include -#include namespace Genode { @@ -72,9 +72,7 @@ struct Net::Permit_any_rule : Permit_rule { public: - struct Invalid : Genode::Exception { }; - - Permit_any_rule(Domain_dict &domains, Genode::Xml_node const node); + Permit_any_rule(Domain &domain); /********* @@ -99,10 +97,7 @@ class Net::Permit_single_rule : public Permit_rule, public: - struct Invalid : Genode::Exception { }; - - Permit_single_rule(Domain_dict &domains, - Genode::Xml_node const node); + Permit_single_rule(Port port, Domain &domain); template diff --git a/repos/os/src/server/nic_router/pointer.h b/repos/os/src/server/nic_router/pointer.h deleted file mode 100644 index abfd744760..0000000000 --- a/repos/os/src/server/nic_router/pointer.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * \brief Pointer that can be dereferenced only when valid - * \author Martin Stein - * \date 2016-08-24 - */ - -/* - * Copyright (C) 2016-2017 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU Affero General Public License version 3. - */ - -#ifndef _POINTER_H_ -#define _POINTER_H_ - -/* Genode includes */ -#include - -namespace Net { - - template class Pointer; - template class Const_pointer; -} - - -template -class Net::Pointer -{ - private: - - T *_obj; - - public: - - struct Invalid : Genode::Exception { }; - - Pointer() : _obj(nullptr) { } - - Pointer(T &obj) : _obj(&obj) { } - - T &operator () () const - { - if (_obj == nullptr) - throw Invalid(); - - return *_obj; - } - - bool valid() const { return _obj != nullptr; } -}; - - -template -class Net::Const_pointer -{ - private: - - T const *_obj; - - public: - - struct Invalid : Genode::Exception { }; - - Const_pointer() : _obj(nullptr) { } - - Const_pointer(T const &obj) : _obj(&obj) { } - - T const &operator () () const - { - if (_obj == nullptr) - throw Invalid(); - - return *_obj; - } - - bool valid() const { return _obj != nullptr; } -}; - -#endif /* _POINTER_H_ */ diff --git a/repos/os/src/server/nic_router/port_allocator.cc b/repos/os/src/server/nic_router/port_allocator.cc index d43b18216f..cc8e70af90 100644 --- a/repos/os/src/server/nic_router/port_allocator.cc +++ b/repos/os/src/server/nic_router/port_allocator.cc @@ -31,7 +31,7 @@ bool Net::dynamic_port(Port const port) ** Port_allocator ** ********************/ -Port Net::Port_allocator::alloc() +Net::Port_allocator::Alloc_result Net::Port_allocator::alloc() { for (unsigned nr_of_trials { 0 }; nr_of_trials < NR_OF_PORTS; @@ -45,19 +45,18 @@ Port Net::Port_allocator::alloc() } catch (Bit_allocator::Range_conflict) { } } - throw Out_of_indices(); + return Alloc_error(); } -void Net::Port_allocator::alloc(Port const port) +bool Net::Port_allocator::alloc(Port const port) { try { _bit_allocator.alloc_addr(port.value - FIRST_PORT); + return true; } - catch (Bit_allocator::Range_conflict) { - - throw Allocation_conflict(); - } + catch (Bit_allocator::Range_conflict) { } + return false; } @@ -71,29 +70,30 @@ void Port_allocator::free(Port const port) ** Port_allocator_guard ** **************************/ -Port Port_allocator_guard::alloc() +Port_allocator_guard::Alloc_result Port_allocator_guard::alloc() { if (_used_nr_of_ports == _max_nr_of_ports) { - throw Out_of_indices(); - } - try { - Port const port = _port_alloc.alloc(); - _used_nr_of_ports++; - return port; - } - catch (Port_allocator::Out_of_indices) { - throw Out_of_indices(); + return Alloc_error(); } + Alloc_result const result = _port_alloc.alloc(); + if (result.failed()) + return result; + + _used_nr_of_ports++; + return result; } -void Port_allocator_guard::alloc(Port const port) +bool Port_allocator_guard::alloc(Port const port) { - if (_used_nr_of_ports == _max_nr_of_ports) { - throw Out_of_indices(); - } - _port_alloc.alloc(port); + if (_used_nr_of_ports == _max_nr_of_ports) + return false; + + if (!_port_alloc.alloc(port)) + return false; + _used_nr_of_ports++; + return true; } diff --git a/repos/os/src/server/nic_router/port_allocator.h b/repos/os/src/server/nic_router/port_allocator.h index a2b7b65cd0..b85f783cef 100644 --- a/repos/os/src/server/nic_router/port_allocator.h +++ b/repos/os/src/server/nic_router/port_allocator.h @@ -17,6 +17,7 @@ /* Genode includes */ #include +#include /* local includes */ #include @@ -43,12 +44,12 @@ class Net::Port_allocator public: - struct Allocation_conflict : Genode::Exception { }; - struct Out_of_indices : Genode::Exception { }; + struct Alloc_error { }; + using Alloc_result = Genode::Attempt; - Port alloc(); + [[nodiscard]] Alloc_result alloc(); - void alloc(Port const port); + [[nodiscard]] bool alloc(Port const port); void free(Port const port); }; @@ -64,11 +65,12 @@ class Net::Port_allocator_guard public: - class Out_of_indices : Genode::Exception {}; + using Alloc_error = Port_allocator::Alloc_error; + using Alloc_result = Port_allocator::Alloc_result; - Port alloc(); + [[nodiscard]] Alloc_result alloc(); - void alloc(Port const port); + [[nodiscard]] bool alloc(Port const port); void free(Port const port); diff --git a/repos/os/src/server/nic_router/report.cc b/repos/os/src/server/nic_router/report.cc index e5d93a8835..31073811f3 100644 --- a/repos/os/src/server/nic_router/report.cc +++ b/repos/os/src/server/nic_router/report.cc @@ -50,26 +50,25 @@ Net::Report::Report(bool const &verbose, } -void Net::Report::generate() +void Net::Report::generate() const { try { - Reporter::Xml_generator xml(_reporter, [&] () { + Reporter::Xml_generator xml(_reporter, [&] { if (_quota) { - xml.node("ram", [&] () { + xml.node("ram", [&] { xml.attribute("quota", _pd.ram_quota().value); xml.attribute("used", _pd.used_ram().value); xml.attribute("shared", _shared_quota.ram); }); - xml.node("cap", [&] () { + xml.node("cap", [&] { xml.attribute("quota", _pd.cap_quota().value); xml.attribute("used", _pd.used_caps().value); xml.attribute("shared", _shared_quota.cap); }); } - _domains.for_each([&] (Domain &domain) { - try { domain.report(xml); } - catch (Empty) { } - }); + _domains.for_each([&] (Domain const &domain) { + if (!domain.report_empty(*this)) + xml.node("domain", [&] { domain.report(xml, *this); }); }); }); } catch (Xml_generator::Buffer_exceeded) { if (_verbose) { diff --git a/repos/os/src/server/nic_router/report.h b/repos/os/src/server/nic_router/report.h index 407f511356..8981c4bcb3 100644 --- a/repos/os/src/server/nic_router/report.h +++ b/repos/os/src/server/nic_router/report.h @@ -65,8 +65,6 @@ class Net::Report public: - struct Empty : Genode::Exception { }; - Report(bool const &verbose, Genode::Xml_node const node, Cached_timer &timer, @@ -80,7 +78,7 @@ class Net::Report void handle_interface_link_state(); - void generate(); + void generate() const; /*************** diff --git a/repos/os/src/server/nic_router/session_env.h b/repos/os/src/server/nic_router/session_env.h index 8b5fa4061b..6a7d853dac 100644 --- a/repos/os/src/server/nic_router/session_env.h +++ b/repos/os/src/server/nic_router/session_env.h @@ -169,6 +169,8 @@ class Genode::Session_env : public Ram_allocator, return ptr; }; + bool report_empty() const { return false; } + void report(Genode::Xml_generator &xml) const { xml.node("ram-quota", [&] () { diff --git a/repos/os/src/server/nic_router/target.mk b/repos/os/src/server/nic_router/target.mk index 2267411d94..3c41f52089 100644 --- a/repos/os/src/server/nic_router/target.mk +++ b/repos/os/src/server/nic_router/target.mk @@ -4,7 +4,6 @@ LIBS += base net SRC_CC += \ arp_waiter.cc \ - ip_rule.cc \ ipv4_address_prefix.cc \ nic_session_root.cc \ port_allocator.cc \ @@ -18,7 +17,6 @@ SRC_CC += \ configuration.cc \ domain.cc \ l3_protocol.cc \ - direct_rule.cc \ link.cc \ transport_rule.cc \ permit_rule.cc \ diff --git a/repos/os/src/server/nic_router/transport_rule.cc b/repos/os/src/server/nic_router/transport_rule.cc index 70721cf01a..2992a4e8a8 100644 --- a/repos/os/src/server/nic_router/transport_rule.cc +++ b/repos/os/src/server/nic_router/transport_rule.cc @@ -24,63 +24,63 @@ using namespace Net; using namespace Genode; -Pointer -Transport_rule::_read_permit_any_rule(Domain_dict &domains, - Xml_node const node, - Allocator &alloc) -{ - try { - Xml_node sub_node = node.sub_node("permit-any"); - return Pointer(*new (alloc) - Permit_any_rule(domains, sub_node)); - } - catch (Xml_node::Nonexistent_sub_node) { } - return Pointer(); -} - - -Transport_rule::Transport_rule(Domain_dict &domains, - Xml_node const node, - Allocator &alloc, - Cstring const &protocol, - Configuration &config, - Domain const &domain) +Transport_rule::Transport_rule(Ipv4_address_prefix const &dst, + Allocator &alloc) : - Direct_rule(node), - _alloc(alloc), - _permit_any_rule(_read_permit_any_rule(domains, node, alloc)) + Direct_rule(dst), + _alloc(alloc) +{ } + + +bool Transport_rule::finish_construction(Domain_dict &domains, + Xml_node const node, + Cstring const &protocol, + Configuration &config, + Domain const &local_domain) { - /* skip specific permit rules if all ports are permitted anyway */ - try { - Permit_any_rule &permit_any_rule = _permit_any_rule(); - if (config.verbose()) { - log("[", domain, "] ", protocol, " permit-any rule: ", permit_any_rule); - log("[", domain, "] ", protocol, " rule: dst ", _dst); - } - return; - } catch (Pointer::Invalid) { } - - /* read specific permit rules */ - node.for_each_sub_node("permit", [&] (Xml_node const node) { - Permit_single_rule &rule = *new (alloc) - Permit_single_rule(domains, node); - - _permit_single_rules.insert(&rule); - if (config.verbose()) { - log("[", domain, "] ", protocol, " permit rule: ", rule); } + /* try to find a permit-any rule first */ + bool error = false; + node.with_optional_sub_node("permit-any", [&] (Xml_node const &permit_any_node) { + domains.find_by_domain_attr(permit_any_node, + [&] (Domain &remote_domain) { _permit_any_rule_ptr = new (_alloc) Permit_any_rule(remote_domain); }, + [&] { error = true; }); }); - /* drop the transport rule if it has no permitted ports */ - if (!_permit_single_rules.first()) { - throw Invalid(); } + if (error) + return false; - if (config.verbose()) { - log("[", domain, "] ", protocol, " rule: dst ", _dst); } + /* skip specific permit rules if all ports are permitted anyway */ + if (_permit_any_rule_ptr) { + if (config.verbose()) { + log("[", local_domain, "] ", protocol, " permit-any rule: ", *_permit_any_rule_ptr); + log("[", local_domain, "] ", protocol, " rule: dst ", _dst); + } + return true; + } + /* read specific permit rules */ + node.for_each_sub_node("permit", [&] (Xml_node const permit_node) { + if (error) + return; + + Port port = permit_node.attribute_value("port", Port(0)); + if (port == Port(0) || dynamic_port(port)) { + error = true; + return; + } + domains.find_by_domain_attr(permit_node, + [&] (Domain &remote_domain) { + Permit_single_rule &rule = *new (_alloc) Permit_single_rule(port, remote_domain); + _permit_single_rules.insert(&rule); + if (config.verbose()) + log("[", local_domain, "] ", protocol, " permit rule: ", rule); }, + [&] { error = true; }); + }); + return !error && (_permit_any_rule_ptr || _permit_single_rules.first()); } Transport_rule::~Transport_rule() { _permit_single_rules.destroy_each(_alloc); - try { destroy(_alloc, &_permit_any_rule()); } - catch (Pointer::Invalid) { } + if (_permit_any_rule_ptr) + destroy(_alloc, _permit_any_rule_ptr); } diff --git a/repos/os/src/server/nic_router/transport_rule.h b/repos/os/src/server/nic_router/transport_rule.h index abc34b8ac2..edae6eeb78 100644 --- a/repos/os/src/server/nic_router/transport_rule.h +++ b/repos/os/src/server/nic_router/transport_rule.h @@ -17,7 +17,6 @@ /* local includes */ #include #include -#include namespace Genode { class Allocator; } @@ -33,23 +32,25 @@ class Net::Transport_rule : public Direct_rule { private: - Genode::Allocator &_alloc; - Pointer const _permit_any_rule; - Permit_single_rule_tree _permit_single_rules { }; + Genode::Allocator &_alloc; + Permit_any_rule *_permit_any_rule_ptr { }; + Permit_single_rule_tree _permit_single_rules { }; - static Pointer + static Permit_any_rule * _read_permit_any_rule(Domain_dict &domains, Genode::Xml_node const node, Genode::Allocator &alloc); + /* + * Noncopyable + */ + Transport_rule(Transport_rule const &); + Transport_rule &operator = (Transport_rule const &); + public: - Transport_rule(Domain_dict &domains, - Genode::Xml_node const node, - Genode::Allocator &alloc, - Genode::Cstring const &protocol, - Configuration &config, - Domain const &domain); + Transport_rule(Ipv4_address_prefix const &dst, + Genode::Allocator &alloc); ~Transport_rule(); @@ -60,9 +61,9 @@ class Net::Transport_rule : public Direct_rule HANDLE_MATCH_FN && handle_match, HANDLE_NO_MATCH_FN && handle_no_match) const { - if (_permit_any_rule.valid()) { + if (_permit_any_rule_ptr) { - handle_match(_permit_any_rule()); + handle_match(*_permit_any_rule_ptr); } else { @@ -70,6 +71,12 @@ class Net::Transport_rule : public Direct_rule port, handle_match, handle_no_match); } } + + [[nodiscard]] bool finish_construction(Domain_dict &domains, + Genode::Xml_node const node, + Genode::Cstring const &protocol, + Configuration &config, + Domain const &domain); }; diff --git a/repos/os/src/server/nic_router/uplink_session_root.cc b/repos/os/src/server/nic_router/uplink_session_root.cc index fda142b129..f93edd071d 100644 --- a/repos/os/src/server/nic_router/uplink_session_root.cc +++ b/repos/os/src/server/nic_router/uplink_session_root.cc @@ -62,17 +62,13 @@ Net::Uplink_session_component::Interface_policy::determine_domain_name() const try { Session_policy policy(_label, _config().node()); domain_name = policy.attribute_value("domain", Domain_name()); + if (domain_name == Domain_name() && _config().verbose()) + log("[?] no domain attribute in policy for downlink label \"", _label, "\""); } catch (Session_policy::No_policy_defined) { if (_config().verbose()) { log("[?] no policy for downlink label \"", _label, "\""); } } - catch (Xml_node::Nonexistent_attribute) { - if (_config().verbose()) { - log("[?] no domain attribute in policy for downlink label \"", - _label, "\""); - } - } return domain_name; } diff --git a/repos/os/src/server/nic_router/uplink_session_root.h b/repos/os/src/server/nic_router/uplink_session_root.h index a43142496b..360872ebe9 100644 --- a/repos/os/src/server/nic_router/uplink_session_root.h +++ b/repos/os/src/server/nic_router/uplink_session_root.h @@ -81,6 +81,7 @@ class Net::Uplink_session_component : private Uplink_session_component_base, Domain_name determine_domain_name() const override; void handle_config(Configuration const &config) override { _config = config; } Genode::Session_label const &label() const override { return _label; } + bool report_empty() const override { return _session_env.report_empty(); }; void report(Genode::Xml_generator &xml) const override { _session_env.report(xml); }; void handle_domain_ready_state(bool /* state */) override { } bool interface_link_state() const override { return true; }