nic_router: find nat rules w/o exceptions

Replaces the former implementation of the 'find_by_domain' method at the data
structure for NAT rules. This method used to return a reference to the found
object and threw an exception if no matching object was found.

The new implementation doesn't return anything and doesn't throw exceptions. It
takes two lambda arguments instead. One for handling the case that a match was
found with a reference to the matching object as argument and another for
handling the case that no object matches.

This way, expensive exception handling can be avoided and object references
stay in a local scope.

Ref #4536
This commit is contained in:
Martin Stein 2022-06-27 21:19:17 +02:00 committed by Christian Helmuth
parent 65955601f0
commit 6294167eff
3 changed files with 77 additions and 42 deletions

View File

@ -589,16 +589,19 @@ void Interface::_nat_link_and_pass(Ethernet_frame &eth,
{ {
try { try {
Pointer<Port_allocator_guard> remote_port_alloc; Pointer<Port_allocator_guard> remote_port_alloc;
try { remote_domain.nat_rules().find_by_domain(
Nat_rule &nat = remote_domain.nat_rules().find_by_domain(local_domain); local_domain,
if(_config().verbose()) { [&] /* handle_match */ (Nat_rule &nat)
log("[", local_domain, "] using NAT rule: ", nat); } {
if(_config().verbose()) {
log("[", local_domain, "] using NAT rule: ", nat); }
_src_port(prot, prot_base, nat.port_alloc(prot).alloc()); _src_port(prot, prot_base, nat.port_alloc(prot).alloc());
ip.src(remote_domain.ip_config().interface().address); ip.src(remote_domain.ip_config().interface().address);
remote_port_alloc = nat.port_alloc(prot); remote_port_alloc = nat.port_alloc(prot);
} },
catch (Nat_rule_tree::No_match) { } [&] /* no_match */ () { }
);
Link_side_id const remote_id = { ip.dst(), _dst_port(prot, prot_base), Link_side_id const remote_id = { ip.dst(), _dst_port(prot, prot_base),
ip.src(), _src_port(prot, prot_base) }; ip.src(), _src_port(prot, prot_base) };
_new_link(prot, local_id, remote_port_alloc, remote_domain, remote_id); _new_link(prot, local_id, remote_port_alloc, remote_domain, remote_id);
@ -1869,14 +1872,28 @@ void Interface::_update_link_check_nat(Link &link,
_dismiss_link_log(link, "NAT IP"); _dismiss_link_log(link, "NAT IP");
throw Dismiss_link(); throw Dismiss_link();
} }
Nat_rule &nat = new_srv_dom.nat_rules().find_by_domain(cln_dom); bool done { false };
Port_allocator_guard &remote_port_alloc = nat.port_alloc(prot); new_srv_dom.nat_rules().find_by_domain(
remote_port_alloc.alloc(link.server().dst_port()); cln_dom,
remote_port_alloc_ptr = remote_port_alloc; [&] /* handle_match */ (Nat_rule &nat)
link.handle_config(cln_dom, new_srv_dom, remote_port_alloc_ptr, _config()); {
return; Port_allocator_guard &remote_port_alloc = nat.port_alloc(prot);
remote_port_alloc.alloc(link.server().dst_port());
remote_port_alloc_ptr = remote_port_alloc;
link.handle_config(
cln_dom, new_srv_dom, remote_port_alloc_ptr, _config());
done = true;
},
[&] /* handle_no_match */ ()
{
_dismiss_link_log(link, "no NAT rule");
}
);
if (done) {
return;
}
} }
catch (Nat_rule_tree::No_match) { _dismiss_link_log(link, "no NAT rule"); }
catch (Port_allocator::Allocation_conflict) { _dismiss_link_log(link, "no NAT-port"); } catch (Port_allocator::Allocation_conflict) { _dismiss_link_log(link, "no NAT-port"); }
catch (Port_allocator_guard::Out_of_indices) { _dismiss_link_log(link, "no NAT-port quota"); } catch (Port_allocator_guard::Out_of_indices) { _dismiss_link_log(link, "no NAT-port quota"); }
throw Dismiss_link(); throw Dismiss_link();

View File

@ -57,30 +57,6 @@ Nat_rule::Nat_rule(Domain_tree &domains,
{ } { }
Nat_rule &Nat_rule::find_by_domain(Domain &domain)
{
if (&domain == &_domain) {
return *this; }
bool const side = (addr_t)&domain > (addr_t)&_domain;
Nat_rule *const rule = Avl_node<Nat_rule>::child(side);
if (!rule) {
throw Nat_rule_tree::No_match(); }
return rule->find_by_domain(domain);
}
Nat_rule &Nat_rule_tree::find_by_domain(Domain &domain)
{
Nat_rule *const rule = first();
if (!rule) {
throw No_match(); }
return rule->find_by_domain(domain);
}
void Nat_rule::print(Output &output) const void Nat_rule::print(Output &output) const
{ {
Genode::print(output, "domain ", _domain, Genode::print(output, "domain ", _domain,

View File

@ -59,6 +59,35 @@ class Net::Nat_rule : public Genode::Avl_node<Nat_rule>
Nat_rule &find_by_domain(Domain &domain); Nat_rule &find_by_domain(Domain &domain);
template <typename HANDLE_MATCH_FN,
typename HANDLE_NO_MATCH_FN>
void find_by_domain(Domain &domain,
HANDLE_MATCH_FN && handle_match,
HANDLE_NO_MATCH_FN && handle_no_match)
{
if (&domain != &_domain) {
Nat_rule *const rule_ptr {
Avl_node<Nat_rule>::child(
(Genode::addr_t)&domain > (Genode::addr_t)&_domain) };
if (rule_ptr != nullptr) {
rule_ptr->find_by_domain(
domain, handle_match, handle_no_match);
} else {
handle_no_match();
}
} else {
handle_match(*this);
}
}
Port_allocator_guard &port_alloc(L3_protocol const prot); Port_allocator_guard &port_alloc(L3_protocol const prot);
@ -89,9 +118,22 @@ class Net::Nat_rule : public Genode::Avl_node<Nat_rule>
struct Net::Nat_rule_tree : Avl_tree<Nat_rule> struct Net::Nat_rule_tree : Avl_tree<Nat_rule>
{ {
struct No_match : Genode::Exception { }; template <typename HANDLE_MATCH_FN,
typename HANDLE_NO_MATCH_FN>
Nat_rule &find_by_domain(Domain &domain); void find_by_domain(Domain &domain,
HANDLE_MATCH_FN && handle_match,
HANDLE_NO_MATCH_FN && handle_no_match)
{
if (first() != nullptr) {
first()->find_by_domain(domain, handle_match, handle_no_match);
} else {
handle_no_match();
}
}
}; };
#endif /* _NAT_RULE_H_ */ #endif /* _NAT_RULE_H_ */