nic_router: limit packets handled per signal

Make it configurable how many packets get handled at a max per signal to
prevent DoS attacks by clients.

Issue #2953
This commit is contained in:
Martin Stein 2018-09-30 17:27:44 +02:00 committed by Christian Helmuth
parent b48c917984
commit 3db7181104
6 changed files with 73 additions and 34 deletions

View File

@ -494,6 +494,21 @@ Whether to log most important changes in the state of a domain (number of NIC
sessions connected, current IPv4 config).
Other configuration attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If possible, the NIC router normally handles multiple packets from a NIC
session per signal. However, if one NIC session has a high packet rate and a
big buffer, this can lead to starvation of the other NIC sessions. Thus, the
maximum number of packets handled per signal is limited by default. This limit
can be configured as follows (default value shown):
! <config max_packets_per_signal="32">
When set to zero, the limit is deactivated, meaning that the router always
handles all available packets of a NIC session.
Examples
~~~~~~~~

View File

@ -129,6 +129,7 @@
</xs:element><!-- domain -->
</xs:choice>
<xs:attribute name="max_packets_per_signal" type="xs:nonNegativeInteger" />
<xs:attribute name="verbose" type="Boolean" />
<xs:attribute name="verbose_packets" type="Boolean" />
<xs:attribute name="verbose_packet_drop" type="Boolean" />

View File

@ -65,18 +65,19 @@ Configuration::Configuration(Env &env,
Interface_list &interfaces)
:
_alloc(alloc),
_verbose (node.attribute_value("verbose", false)),
_verbose_packets (node.attribute_value("verbose_packets", false)),
_verbose_packet_drop (node.attribute_value("verbose_packet_drop", false)),
_verbose_domain_state (node.attribute_value("verbose_domain_state", false)),
_icmp_echo_server (node.attribute_value("icmp_echo_server", true)),
_dhcp_discover_timeout(read_sec_attr(node, "dhcp_discover_timeout_sec", DEFAULT_DHCP_DISCOVER_TIMEOUT_SEC)),
_dhcp_request_timeout (read_sec_attr(node, "dhcp_request_timeout_sec", DEFAULT_DHCP_REQUEST_TIMEOUT_SEC )),
_dhcp_offer_timeout (read_sec_attr(node, "dhcp_offer_timeout_sec", DEFAULT_DHCP_OFFER_TIMEOUT_SEC )),
_icmp_idle_timeout (read_sec_attr(node, "icmp_idle_timeout_sec", DEFAULT_ICMP_IDLE_TIMEOUT_SEC )),
_udp_idle_timeout (read_sec_attr(node, "udp_idle_timeout_sec", DEFAULT_UDP_IDLE_TIMEOUT_SEC )),
_tcp_idle_timeout (read_sec_attr(node, "tcp_idle_timeout_sec", DEFAULT_TCP_IDLE_TIMEOUT_SEC )),
_tcp_max_segm_lifetime(read_sec_attr(node, "tcp_max_segm_lifetime_sec", DEFAULT_TCP_MAX_SEGM_LIFETIME_SEC)),
_max_packets_per_signal(node.attribute_value("max_packets_per_signal", (unsigned long)DEFAULT_MAX_PACKETS_PER_SIGNAL)),
_verbose (node.attribute_value("verbose", false)),
_verbose_packets (node.attribute_value("verbose_packets", false)),
_verbose_packet_drop (node.attribute_value("verbose_packet_drop", false)),
_verbose_domain_state (node.attribute_value("verbose_domain_state", false)),
_icmp_echo_server (node.attribute_value("icmp_echo_server", true)),
_dhcp_discover_timeout (read_sec_attr(node, "dhcp_discover_timeout_sec", DEFAULT_DHCP_DISCOVER_TIMEOUT_SEC)),
_dhcp_request_timeout (read_sec_attr(node, "dhcp_request_timeout_sec", DEFAULT_DHCP_REQUEST_TIMEOUT_SEC )),
_dhcp_offer_timeout (read_sec_attr(node, "dhcp_offer_timeout_sec", DEFAULT_DHCP_OFFER_TIMEOUT_SEC )),
_icmp_idle_timeout (read_sec_attr(node, "icmp_idle_timeout_sec", DEFAULT_ICMP_IDLE_TIMEOUT_SEC )),
_udp_idle_timeout (read_sec_attr(node, "udp_idle_timeout_sec", DEFAULT_UDP_IDLE_TIMEOUT_SEC )),
_tcp_idle_timeout (read_sec_attr(node, "tcp_idle_timeout_sec", DEFAULT_TCP_IDLE_TIMEOUT_SEC )),
_tcp_max_segm_lifetime (read_sec_attr(node, "tcp_max_segm_lifetime_sec", DEFAULT_TCP_MAX_SEGM_LIFETIME_SEC)),
_node(node)
{
/* do parts of domain initialization that do not lookup other domains */

View File

@ -34,6 +34,7 @@ class Net::Configuration
using Mac_string = Genode::String<17>;
Genode::Allocator &_alloc;
unsigned long const _max_packets_per_signal { 0 };
bool const _verbose { false };
bool const _verbose_packets { false };
bool const _verbose_packet_drop { false };
@ -68,6 +69,7 @@ class Net::Configuration
enum { DEFAULT_UDP_IDLE_TIMEOUT_SEC = 30 };
enum { DEFAULT_TCP_IDLE_TIMEOUT_SEC = 600 };
enum { DEFAULT_TCP_MAX_SEGM_LIFETIME_SEC = 30 };
enum { DEFAULT_MAX_PACKETS_PER_SIGNAL = 32 };
Configuration(Genode::Xml_node const node,
Genode::Allocator &alloc);
@ -86,21 +88,22 @@ class Net::Configuration
** Accessors **
***************/
bool verbose() const { return _verbose; }
bool verbose_packets() const { return _verbose_packets; }
bool verbose_packet_drop() const { return _verbose_packet_drop; }
bool verbose_domain_state() const { return _verbose_domain_state; }
bool icmp_echo_server() const { return _icmp_echo_server; }
Genode::Microseconds dhcp_discover_timeout() const { return _dhcp_discover_timeout; }
Genode::Microseconds dhcp_request_timeout() const { return _dhcp_request_timeout; }
Genode::Microseconds dhcp_offer_timeout() const { return _dhcp_offer_timeout; }
Genode::Microseconds icmp_idle_timeout() const { return _icmp_idle_timeout; }
Genode::Microseconds udp_idle_timeout() const { return _udp_idle_timeout; }
Genode::Microseconds tcp_idle_timeout() const { return _tcp_idle_timeout; }
Genode::Microseconds tcp_max_segm_lifetime() const { return _tcp_max_segm_lifetime; }
Domain_tree &domains() { return _domains; }
Report &report() { return _report(); }
Genode::Xml_node node() const { return _node; }
unsigned long max_packets_per_signal() const { return _max_packets_per_signal; }
bool verbose() const { return _verbose; }
bool verbose_packets() const { return _verbose_packets; }
bool verbose_packet_drop() const { return _verbose_packet_drop; }
bool verbose_domain_state() const { return _verbose_domain_state; }
bool icmp_echo_server() const { return _icmp_echo_server; }
Genode::Microseconds dhcp_discover_timeout() const { return _dhcp_discover_timeout; }
Genode::Microseconds dhcp_request_timeout() const { return _dhcp_request_timeout; }
Genode::Microseconds dhcp_offer_timeout() const { return _dhcp_offer_timeout; }
Genode::Microseconds icmp_idle_timeout() const { return _icmp_idle_timeout; }
Genode::Microseconds udp_idle_timeout() const { return _udp_idle_timeout; }
Genode::Microseconds tcp_idle_timeout() const { return _tcp_idle_timeout; }
Genode::Microseconds tcp_max_segm_lifetime() const { return _tcp_max_segm_lifetime; }
Domain_tree &domains() { return _domains; }
Report &report() { return _report(); }
Genode::Xml_node node() const { return _node; }
};
#endif /* _CONFIGURATION_H_ */

View File

@ -1278,16 +1278,33 @@ void Interface::_handle_arp(Ethernet_frame &eth,
}
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);
}
catch (Packet_postponed) { }
}
void Interface::_ready_to_submit()
{
while (_sink.packet_avail()) {
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);
unsigned long const max_pkts = _config().max_packets_per_signal();
if (max_pkts) {
for (unsigned long i = 0; _sink.packet_avail(); i++) {
if (i >= max_pkts) {
Signal_transmitter(_sink_submit).submit();
break;
}
_handle_pkt();
}
catch (Packet_postponed) { }
} else {
while (_sink.packet_avail()) {
_handle_pkt(); }
}
}

View File

@ -239,6 +239,8 @@ class Net::Interface : private Interface_list::Element
Size_guard &size_guard,
Ipv4_packet &ip);
void _handle_pkt();
void _continue_handle_eth(Domain const &domain,
Packet_descriptor const &pkt);