nic_router: destroy links on insufficient resource

If the NIC router has insufficient CAP or RAM quota for the creation of
a state object for an interface, it tries to destroy a certain amount of
existing state objects of this interface to free resources. Afterwards,
it retries handling the current packet once. If it does fail again, the
router drops the packet.

Issue #2953
This commit is contained in:
Martin Stein 2018-07-13 01:40:45 +02:00 committed by Christian Helmuth
parent 4442c79526
commit d6c6549354
3 changed files with 65 additions and 39 deletions

View File

@ -196,10 +196,4 @@ append_if [have_spec lan9118] qemu_args " -net nic,model=lan9118 "
append qemu_args " -net user -nographic " append qemu_args " -net user -nographic "
set done_string "" run_genode_until {.*ping\] 64 bytes from 10\.0\.2\.2: icmp_seq=30 .*\n} 120
for {set i 0} {$i < 2} {incr i 1} {
append done_string {.*nic_router\] \[flood_links\] drop packet \(RAM quota exhausted.*\n}
append done_string {.*ping\] 64 bytes from 10\.0\.2\.2.*\n}
}
run_genode_until $done_string 120

View File

@ -362,9 +362,8 @@ Interface::_new_link(L3_protocol const protocol,
remote, _timer, _config(), protocol }; remote, _timer, _config(), protocol };
} }
catch (Quota_guard<Ram_quota>::Limit_exceeded) { catch (Quota_guard<Ram_quota>::Limit_exceeded) {
throw Drop_packet( throw Free_resources_and_retry_handle_eth(); }
"RAM quota exhausted during allocation of TCP link");
}
break; break;
case L3_protocol::UDP: case L3_protocol::UDP:
try { try {
@ -373,9 +372,8 @@ Interface::_new_link(L3_protocol const protocol,
remote, _timer, _config(), protocol }; remote, _timer, _config(), protocol };
} }
catch (Quota_guard<Ram_quota>::Limit_exceeded) { catch (Quota_guard<Ram_quota>::Limit_exceeded) {
throw Drop_packet( throw Free_resources_and_retry_handle_eth(); }
"RAM quota exhausted during allocation of UDP link");
}
break; break;
case L3_protocol::ICMP: case L3_protocol::ICMP:
try { try {
@ -384,9 +382,8 @@ Interface::_new_link(L3_protocol const protocol,
remote, _timer, _config(), protocol }; remote, _timer, _config(), protocol };
} }
catch (Quota_guard<Ram_quota>::Limit_exceeded) { catch (Quota_guard<Ram_quota>::Limit_exceeded) {
throw Drop_packet( throw Free_resources_and_retry_handle_eth(); }
"RAM quota exhausted during allocation of ICMP link");
}
break; break;
default: throw Bad_transport_protocol(); } default: throw Bad_transport_protocol(); }
} }
@ -437,9 +434,8 @@ void Interface::_adapt_eth(Ethernet_frame &eth,
}); });
try { 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) { catch (Quota_guard<Ram_quota>::Limit_exceeded) {
throw Drop_packet( throw Free_resources_and_retry_handle_eth(); }
"RAM quota exhausted during allocation of ARP waiter");
}
throw Packet_postponed(); throw Packet_postponed();
} }
} }
@ -577,9 +573,7 @@ void Interface::_new_dhcp_allocation(Ethernet_frame &eth,
local_domain.ip_config().interface); local_domain.ip_config().interface);
} }
catch (Quota_guard<Ram_quota>::Limit_exceeded) { catch (Quota_guard<Ram_quota>::Limit_exceeded) {
throw Drop_packet( throw Free_resources_and_retry_handle_eth(); }
"RAM quota exhausted during allocation of DHCP allocation");
}
} }
@ -1289,10 +1283,12 @@ void Interface::_ready_to_submit()
while (_sink.packet_avail()) { while (_sink.packet_avail()) {
Packet_descriptor const pkt = _sink.get_packet(); Packet_descriptor const pkt = _sink.get_packet();
Size_guard size_guard(pkt.size()); Size_guard size_guard(pkt.size());
try { _handle_eth(_sink.packet_content(pkt), size_guard, pkt); } try {
catch (Packet_postponed) { continue; } _handle_eth(_sink.packet_content(pkt), size_guard, pkt);
_ack_packet(pkt); _ack_packet(pkt);
} }
catch (Packet_postponed) { }
}
} }
@ -1334,6 +1330,27 @@ void Interface::_destroy_released_dhcp_allocations(Domain &local_domain)
} }
void Interface::_handle_eth(Ethernet_frame &eth,
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(); }
} else {
switch (eth.type()) {
case Ethernet_frame::Type::IPV4: _dhcp_client.handle_ip(eth, size_guard); break;
default: throw Bad_network_protocol(); }
}
}
void Interface::_handle_eth(void *const eth_base, void Interface::_handle_eth(void *const eth_base,
Size_guard &size_guard, Size_guard &size_guard,
Packet_descriptor const &pkt) Packet_descriptor const &pkt)
@ -1350,21 +1367,30 @@ void Interface::_handle_eth(void *const eth_base,
_destroy_dissolved_links<Tcp_link>(_dissolved_tcp_links, _alloc); _destroy_dissolved_links<Tcp_link>(_dissolved_tcp_links, _alloc);
_destroy_released_dhcp_allocations(local_domain); _destroy_released_dhcp_allocations(local_domain);
/* log received packet if desired */
if (local_domain.verbose_packets()) { if (local_domain.verbose_packets()) {
log("[", local_domain, "] rcv ", eth); } log("[", local_domain, "] rcv ", eth); }
if (local_domain.ip_config().valid) { /* 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"); }
switch (eth.type()) { /* resources do not suffice, destroy all links */
case Ethernet_frame::Type::ARP: _handle_arp(eth, size_guard, local_domain); break; _destroy_links<Tcp_link> (_tcp_links, _dissolved_tcp_links, _alloc);
case Ethernet_frame::Type::IPV4: _handle_ip(eth, size_guard, pkt, local_domain); break; _destroy_links<Udp_link> (_udp_links, _dissolved_udp_links, _alloc);
default: throw Bad_network_protocol(); } _destroy_links<Icmp_link>(_icmp_links, _dissolved_icmp_links, _alloc);
} else { /* retry to handle ethernet frame */
_handle_eth(eth, size_guard, pkt, local_domain);
}
catch (Free_resources_and_retry_handle_eth) {
switch (eth.type()) { /* give up if the resources still not suffice */
case Ethernet_frame::Type::IPV4: _dhcp_client.handle_ip(eth, size_guard); break; throw Drop_packet("insufficient resources");
default: throw Bad_network_protocol(); } }
} }
} }
catch (Dhcp_server::Alloc_ip_failed) { catch (Dhcp_server::Alloc_ip_failed) {

View File

@ -248,6 +248,11 @@ class Net::Interface : private Interface_list::Element
Size_guard &size_guard, Size_guard &size_guard,
Packet_descriptor const &pkt); Packet_descriptor const &pkt);
void _handle_eth(Ethernet_frame &eth,
Size_guard &size_guard,
Packet_descriptor const &pkt,
Domain &local_domain);
void _ack_packet(Packet_descriptor const &pkt); void _ack_packet(Packet_descriptor const &pkt);
void _send_alloc_pkt(Genode::Packet_descriptor &pkt, void _send_alloc_pkt(Genode::Packet_descriptor &pkt,
@ -307,6 +312,7 @@ class Net::Interface : private Interface_list::Element
public: public:
struct Free_resources_and_retry_handle_eth : Genode::Exception { };
struct Bad_send_dhcp_args : Genode::Exception { }; struct Bad_send_dhcp_args : Genode::Exception { };
struct Bad_transport_protocol : Genode::Exception { }; struct Bad_transport_protocol : Genode::Exception { };
struct Bad_network_protocol : Genode::Exception { }; struct Bad_network_protocol : Genode::Exception { };