nic_router: optional icmp type 3 on fragm. ipv4

Via a new configuration attribute, the user can decide whether the router
should answer dropped fragmented IPv4 with an ICMP "destination unreachable"
packet and, if so, which value the ICMP code field of this packet should have.
The default is that the router doesn't send such responses (silently dropping
fragmented IPv4). The behavior is tested by the 'nic_router_ipv4_fragm' test.

Fixes #4236
This commit is contained in:
Martin Stein 2021-08-03 13:19:39 +02:00 committed by Christian Helmuth
parent 06a4608f4a
commit e648e7255a
6 changed files with 153 additions and 63 deletions

View File

@ -99,7 +99,8 @@ append config {
</provides>
<config dhcp_discover_timeout_sec="1"
verbose_packets="yes"
verbose_packet_drop="yes">
verbose_packet_drop="yes"
icmp_type_3_code_on_fragm_ipv4="4">
<policy label_prefix="drivers" domain="uplink"/>
<policy label_prefix="test-lwip-udp-server" domain="downlink"/>
@ -162,12 +163,18 @@ run_genode_until $pattern_string 30 $spawn_id
# ping server with ipv4 fragmentation (should fail)
spawn nping -c 1 --privileged --udp --data-length 1600 --mtu 800 -p 8000 10.0.2.55
set pattern_string ""
expect eof
append pattern_string {.*RCVD .* ICMP .*10\.0\.2\.55 > 10\.0\.2\.1 Fragmentation required.*\n}
append pattern_string {.*RCVD .* ICMP .*10\.0\.2\.55 > 10\.0\.2\.1 Fragmentation required.*\n}
append pattern_string {.*RCVD .* ICMP .*10\.0\.2\.55 > 10\.0\.2\.1 Fragmentation required.*\n}
run_genode_until $pattern_string 30 $spawn_id
# check that the nic router dropped the ipv4 fragments of the second ping
set pattern_string ""
append pattern_string {.*snd .*IPV4.* 10\.0\.2\.55 > 10\.0\.2\.1 .*ICMP.* 3 4.*\n}
append pattern_string {.*drop packet .fragmented IPv4 not supported.*\n}
append pattern_string {.*snd .*IPV4.* 10\.0\.2\.55 > 10\.0\.2\.1 .*ICMP.* 3 4.*\n}
append pattern_string {.*drop packet .fragmented IPv4 not supported.*\n}
append pattern_string {.*snd .*IPV4.* 10\.0\.2\.55 > 10\.0\.2\.1 .*ICMP.* 3 4.*\n}
append pattern_string {.*drop packet .fragmented IPv4 not supported.*\n}
append pattern_string {.*<dropped-fragm-ipv4 value="3"\/>.*\n}
run_genode_until $pattern_string 30 $genode_id

View File

@ -758,6 +758,24 @@ interfaces assigned, current IPv4 config).
Other configuration attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ICMP response to dropped fragmented IPv4
----------------------------------------
The NIC router can notify the sender of a fragmented IPv4 that was dropped by
the router (because fragmented IPv4 isn't supported) via an ICMP type-3
(destination unreachable) packet. This is the attribute that controls this
functionality:
! <config icmp_type_3_code_on_fragm_ipv4="4">
If not set, the value of the attribute defaults to "no" which results in not
sending the above mentioned packets on dropped fragmented IPv4 (i.e.,
fragmented IPv4 gets dropped silently). If the value is set to a number in the
range from 0 to 15, the above mentioned ICMP responses are sent and the value
indicates which value the ICMP code field should have in the responses. If the
value is set to something else than "no" or a number in the range from 0 to 15,
the router prints a warning to the log and assumes value "no".
Maximum number of packets handled per signal
--------------------------------------------

View File

@ -12,6 +12,28 @@
</xs:restriction>
</xs:simpleType><!-- Domain_name -->
<xs:simpleType name="Icmp_type_3_code_attribute">
<xs:restriction base="xs:string">
<xs:enumeration value="no" />
<xs:enumeration value="0" />
<xs:enumeration value="1" />
<xs:enumeration value="2" />
<xs:enumeration value="3" />
<xs:enumeration value="4" />
<xs:enumeration value="5" />
<xs:enumeration value="6" />
<xs:enumeration value="7" />
<xs:enumeration value="8" />
<xs:enumeration value="9" />
<xs:enumeration value="10" />
<xs:enumeration value="11" />
<xs:enumeration value="12" />
<xs:enumeration value="13" />
<xs:enumeration value="14" />
<xs:enumeration value="15" />
</xs:restriction>
</xs:simpleType><!-- Icmp_type_3_code_attribute -->
<xs:simpleType name="Nr_of_ports">
<xs:restriction base="xs:integer">
<xs:minInclusive value="1"/>
@ -144,20 +166,21 @@
</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" />
<xs:attribute name="verbose_domain_state" type="Boolean" />
<xs:attribute name="dhcp_discover_timeout_sec" type="Seconds" />
<xs:attribute name="dhcp_request_timeout_sec" type="Seconds" />
<xs:attribute name="dhcp_offer_timeout_sec" type="Seconds" />
<xs:attribute name="udp_idle_timeout_sec" type="Seconds" />
<xs:attribute name="tcp_idle_timeout_sec" type="Seconds" />
<xs:attribute name="icmp_idle_timeout_sec" type="Seconds" />
<xs:attribute name="tcp_max_segm_lifetime_sec" type="Seconds" />
<xs:attribute name="icmp_echo_server" type="Boolean" />
<xs:attribute name="ld_verbose" type="Boolean" />
<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" />
<xs:attribute name="verbose_domain_state" type="Boolean" />
<xs:attribute name="dhcp_discover_timeout_sec" type="Seconds" />
<xs:attribute name="dhcp_request_timeout_sec" type="Seconds" />
<xs:attribute name="dhcp_offer_timeout_sec" type="Seconds" />
<xs:attribute name="udp_idle_timeout_sec" type="Seconds" />
<xs:attribute name="tcp_idle_timeout_sec" type="Seconds" />
<xs:attribute name="icmp_idle_timeout_sec" type="Seconds" />
<xs:attribute name="tcp_max_segm_lifetime_sec" type="Seconds" />
<xs:attribute name="icmp_echo_server" type="Boolean" />
<xs:attribute name="icmp_type_3_code_on_fragm_ipv4" type="Icmp_type_3_code_attribute" />
<xs:attribute name="ld_verbose" type="Boolean" />
</xs:complexType>
</xs:element><!-- config -->

View File

@ -30,21 +30,22 @@ using namespace Genode;
Configuration::Configuration(Xml_node const node,
Allocator &alloc)
:
_alloc { alloc },
_max_packets_per_signal { 0 },
_verbose { false },
_verbose_packets { false },
_verbose_packet_drop { false },
_verbose_domain_state { false },
_icmp_echo_server { false },
_dhcp_discover_timeout { 0 },
_dhcp_request_timeout { 0 },
_dhcp_offer_timeout { 0 },
_icmp_idle_timeout { 0 },
_udp_idle_timeout { 0 },
_tcp_idle_timeout { 0 },
_tcp_max_segm_lifetime { 0 },
_node { node }
_alloc { alloc },
_max_packets_per_signal { 0 },
_verbose { false },
_verbose_packets { false },
_verbose_packet_drop { false },
_verbose_domain_state { false },
_icmp_echo_server { false },
_icmp_type_3_code_on_fragm_ipv4 { 0 },
_dhcp_discover_timeout { 0 },
_dhcp_request_timeout { 0 },
_dhcp_offer_timeout { 0 },
_icmp_idle_timeout { 0 },
_udp_idle_timeout { 0 },
_tcp_idle_timeout { 0 },
_tcp_max_segm_lifetime { 0 },
_node { node }
{ }
@ -70,6 +71,37 @@ 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<uint8_t>(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;
}
return result;
}
catch (Xml_node::Nonexistent_attribute) {
return Icmp_packet::Code::INVALID;
}
}
Configuration::Configuration(Env &env,
Xml_node const node,
Allocator &alloc,
@ -78,21 +110,22 @@ Configuration::Configuration(Env &env,
Quota const &shared_quota,
Interface_list &interfaces)
:
_alloc { alloc },
_max_packets_per_signal { node.attribute_value("max_packets_per_signal", (unsigned long)32) },
_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", 10) },
_dhcp_request_timeout { read_sec_attr(node, "dhcp_request_timeout_sec", 10) },
_dhcp_offer_timeout { read_sec_attr(node, "dhcp_offer_timeout_sec", 10) },
_icmp_idle_timeout { read_sec_attr(node, "icmp_idle_timeout_sec", 10) },
_udp_idle_timeout { read_sec_attr(node, "udp_idle_timeout_sec", 30) },
_tcp_idle_timeout { read_sec_attr(node, "tcp_idle_timeout_sec", 600) },
_tcp_max_segm_lifetime { read_sec_attr(node, "tcp_max_segm_lifetime_sec", 30) },
_node { node }
_alloc { alloc },
_max_packets_per_signal { node.attribute_value("max_packets_per_signal", (unsigned long)32) },
_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) },
_icmp_type_3_code_on_fragm_ipv4 { _init_icmp_type_3_code_on_fragm_ipv4(node) },
_dhcp_discover_timeout { read_sec_attr(node, "dhcp_discover_timeout_sec", 10) },
_dhcp_request_timeout { read_sec_attr(node, "dhcp_request_timeout_sec", 10) },
_dhcp_offer_timeout { read_sec_attr(node, "dhcp_offer_timeout_sec", 10) },
_icmp_idle_timeout { read_sec_attr(node, "icmp_idle_timeout_sec", 10) },
_udp_idle_timeout { read_sec_attr(node, "udp_idle_timeout_sec", 30) },
_tcp_idle_timeout { read_sec_attr(node, "tcp_idle_timeout_sec", 600) },
_tcp_max_segm_lifetime { read_sec_attr(node, "tcp_max_segm_lifetime_sec", 30) },
_node { node }
{
/* do parts of domain initialization that do not lookup other domains */
node.for_each_sub_node("domain", [&] (Xml_node const node) {

View File

@ -40,6 +40,7 @@ class Net::Configuration
bool const _verbose_packet_drop;
bool const _verbose_domain_state;
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;
@ -53,6 +54,9 @@ class Net::Configuration
Nic_client_tree _nic_clients { };
Genode::Xml_node const _node;
Icmp_packet::Code
_init_icmp_type_3_code_on_fragm_ipv4(Genode::Xml_node const &node) const;
void _invalid_nic_client(Nic_client &nic_client,
char const *reason);
@ -83,22 +87,23 @@ class Net::Configuration
** Accessors **
***************/
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; }
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; }
Icmp_packet::Code icmp_type_3_code_on_fragm_ipv4() const { return _icmp_type_3_code_on_fragm_ipv4; }
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

@ -1119,14 +1119,18 @@ void Interface::_handle_ip(Ethernet_frame &eth,
{
/* drop fragmented IPv4 as it isn't supported */
Ipv4_packet &ip = eth.data<Ipv4_packet>(size_guard);
Ipv4_address_prefix const &local_intf = local_domain.ip_config().interface;
if (ip.more_fragments() ||
ip.fragment_offset() != 0) {
_dropped_fragm_ipv4++;
if (_config().icmp_type_3_code_on_fragm_ipv4() != Icmp_packet::Code::INVALID) {
_send_icmp_dst_unreachable(
local_intf, eth, ip, _config().icmp_type_3_code_on_fragm_ipv4());
}
throw Drop_packet("fragmented IPv4 not supported");
}
/* try handling subnet-local IP packets */
Ipv4_address_prefix const &local_intf = local_domain.ip_config().interface;
if (local_intf.prefix_matches(ip.dst()) &&
ip.dst() != local_intf.address)
{