nic_router: fix uncaught interface RAM exhaustion

* Catch Quota_guard exceptions wherever we do 'new (_alloc)' in Interface
  and in case drop the packet that caused the exception
* Modify Interface::handle_config_2 to not use the guarded allocator of the
  NIC session quota as we cannot handle a RAM exhaustion well at this point
  in time. Instead use a Constructible member in Interface so that a needed
  RAM adds up to the calculation in the create_session implementation where
  an exhaustion is handled via a service denial.

Issue #2857
This commit is contained in:
Martin Stein 2018-06-08 17:32:17 +02:00 committed by Christian Helmuth
parent 97ea513122
commit bf055e2cb4
2 changed files with 92 additions and 55 deletions

View File

@ -16,6 +16,7 @@
#include <net/udp.h>
#include <net/icmp.h>
#include <net/arp.h>
#include <base/quota_guard.h>
/* local includes */
#include <interface.h>
@ -30,6 +31,9 @@ using Genode::log;
using Genode::error;
using Genode::warning;
using Genode::construct_at;
using Genode::Quota_guard;
using Genode::Ram_quota;
using Genode::Constructible;
using Genode::Signal_context_capability;
using Genode::Signal_transmitter;
@ -345,16 +349,37 @@ Interface::_new_link(L3_protocol const protocol,
{
switch (protocol) {
case L3_protocol::TCP:
new (_alloc) Tcp_link(*this, local, remote_port_alloc, remote_domain,
remote, _timer, _config(), protocol);
try {
new (_alloc)
Tcp_link { *this, local, remote_port_alloc, remote_domain,
remote, _timer, _config(), protocol };
}
catch (Quota_guard<Ram_quota>::Limit_exceeded) {
throw Drop_packet_inform(
"RAM quota exhausted during allocation of TCP link");
}
break;
case L3_protocol::UDP:
new (_alloc) Udp_link(*this, local, remote_port_alloc, remote_domain,
remote, _timer, _config(), protocol);
try {
new (_alloc)
Udp_link { *this, local, remote_port_alloc, remote_domain,
remote, _timer, _config(), protocol };
}
catch (Quota_guard<Ram_quota>::Limit_exceeded) {
throw Drop_packet_inform(
"RAM quota exhausted during allocation of UDP link");
}
break;
case L3_protocol::ICMP:
new (_alloc) Icmp_link(*this, local, remote_port_alloc, remote_domain,
remote, _timer, _config(), protocol);
try {
new (_alloc)
Icmp_link { *this, local, remote_port_alloc, remote_domain,
remote, _timer, _config(), protocol };
}
catch (Quota_guard<Ram_quota>::Limit_exceeded) {
throw Drop_packet_inform(
"RAM quota exhausted during allocation of ICMP link");
}
break;
default: throw Bad_transport_protocol(); }
}
@ -403,7 +428,11 @@ void Interface::_adapt_eth(Ethernet_frame &eth,
interface._broadcast_arp_request(remote_ip_cfg.interface.address,
hop_ip);
});
new (_alloc) Arp_waiter(*this, remote_domain, hop_ip, pkt);
try { new (_alloc) Arp_waiter { *this, remote_domain, hop_ip, pkt }; }
catch (Quota_guard<Ram_quota>::Limit_exceeded) {
throw Drop_packet_inform(
"RAM quota exhausted during allocation of ARP waiter");
}
throw Packet_postponed();
}
}
@ -526,22 +555,26 @@ void Interface::_new_dhcp_allocation(Ethernet_frame &eth,
Dhcp_server &dhcp_srv,
Domain &local_domain)
{
Dhcp_allocation &allocation = *new (_alloc)
Dhcp_allocation(*this, dhcp_srv.alloc_ip(),
dhcp.client_mac(), _timer,
_config().dhcp_offer_timeout());
try {
Dhcp_allocation &allocation = *new (_alloc)
Dhcp_allocation { *this, dhcp_srv.alloc_ip(), dhcp.client_mac(),
_timer, _config().dhcp_offer_timeout() };
_dhcp_allocations.insert(allocation);
if (_config().verbose()) {
log("Offer DHCP allocation: ", allocation,
" at ", local_domain);
_dhcp_allocations.insert(allocation);
if (_config().verbose()) {
log("Offer DHCP allocation: ", allocation,
" at ", local_domain);
}
_send_dhcp_reply(dhcp_srv, eth.src(), dhcp.client_mac(),
allocation.ip(),
Dhcp_packet::Message_type::OFFER,
dhcp.xid(),
local_domain.ip_config().interface);
}
catch (Quota_guard<Ram_quota>::Limit_exceeded) {
throw Drop_packet_inform(
"RAM quota exhausted during allocation of DHCP allocation");
}
_send_dhcp_reply(dhcp_srv, eth.src(), dhcp.client_mac(),
allocation.ip(),
Dhcp_packet::Message_type::OFFER,
dhcp.xid(),
local_domain.ip_config().interface);
return;
}
@ -1600,9 +1633,7 @@ void Interface::handle_config_2()
_attach_to_domain_raw(new_domain);
/* remember that the interface stays attached to the same domain */
_update_domain = *new (_alloc)
Update_domain { old_domain, new_domain };
_update_domain.construct(old_domain, new_domain);
return;
}
catch (Domain_tree::No_match) {
@ -1628,11 +1659,10 @@ void Interface::handle_config_3()
* interface already got detached from its old domain and there is
* nothing to update.
*/
Update_domain &update_domain = _update_domain();
Update_domain &update_domain = *_update_domain;
Domain &old_domain = update_domain.old_domain;
Domain &new_domain = update_domain.new_domain;
destroy(_alloc, &update_domain);
_update_domain = Pointer<Update_domain>();
_update_domain.destruct();
/* if the IP configs differ, detach completely from the IP config */
if (old_domain.ip_config() != new_domain.ip_config()) {
@ -1650,7 +1680,7 @@ void Interface::handle_config_3()
_update_dhcp_allocations(old_domain, new_domain);
_update_own_arp_waiters(new_domain);
}
catch (Pointer<Update_domain>::Invalid) {
catch (Constructible<Update_domain>::Deref_unconstructed_object) {
/* if the interface moved to another domain, finish the operation */
try { attach_to_domain_finish(); }

View File

@ -76,35 +76,42 @@ class Net::Interface : private Interface_list::Element
{
Domain &old_domain;
Domain &new_domain;
Update_domain(Domain &old_domain,
Domain &new_domain)
:
old_domain(old_domain),
new_domain(new_domain)
{ }
};
Packet_stream_sink &_sink;
Packet_stream_source &_source;
bool &_session_link_state;
Signal_context_capability _session_link_state_sigh { };
Signal_handler _sink_ack;
Signal_handler _sink_submit;
Signal_handler _source_ack;
Signal_handler _source_submit;
Mac_address const _router_mac;
Mac_address const _mac;
Reference<Configuration> _config;
Interface_policy &_policy;
Timer::Connection &_timer;
Genode::Allocator &_alloc;
Pointer<Domain> _domain { };
Arp_waiter_list _own_arp_waiters { };
Link_list _tcp_links { };
Link_list _udp_links { };
Link_list _icmp_links { };
Link_list _dissolved_tcp_links { };
Link_list _dissolved_udp_links { };
Link_list _dissolved_icmp_links { };
Dhcp_allocation_tree _dhcp_allocations { };
Dhcp_allocation_list _released_dhcp_allocations { };
Dhcp_client _dhcp_client { _alloc, _timer, *this };
Interface_list &_interfaces;
Pointer<Update_domain> _update_domain { };
Packet_stream_sink &_sink;
Packet_stream_source &_source;
bool &_session_link_state;
Signal_context_capability _session_link_state_sigh { };
Signal_handler _sink_ack;
Signal_handler _sink_submit;
Signal_handler _source_ack;
Signal_handler _source_submit;
Mac_address const _router_mac;
Mac_address const _mac;
Reference<Configuration> _config;
Interface_policy &_policy;
Timer::Connection &_timer;
Genode::Allocator &_alloc;
Pointer<Domain> _domain { };
Arp_waiter_list _own_arp_waiters { };
Link_list _tcp_links { };
Link_list _udp_links { };
Link_list _icmp_links { };
Link_list _dissolved_tcp_links { };
Link_list _dissolved_udp_links { };
Link_list _dissolved_icmp_links { };
Dhcp_allocation_tree _dhcp_allocations { };
Dhcp_allocation_list _released_dhcp_allocations { };
Dhcp_client _dhcp_client { _alloc, _timer, *this };
Interface_list &_interfaces;
Genode::Constructible<Update_domain> _update_domain { };
void _new_link(L3_protocol const protocol,
Link_side_id const &local_id,