nic_router: use increasing src port for new nat

The NAT feature of the NIC router used to prefer re-using source ports that
have been freed recently. From an external server's perspective, if a client
dies and restarts, chances are high that the new connect arrives with the same
source-IP/source-port as the old connection. The server has to forcefully reset
the connection. If that happens a lot, the server may even start to ignore
further connections from this IP/port combination for a while as a mitigation.

This patch adds a continuous counter feature that makes sure that every new
port allocation will increment and result in a port that hasn't been used for a
long time.

The NAT feature of the nic_router is now more in line with RFC 6056 chapter 4.

Ref #4086
This commit is contained in:
Sid Hussmann 2021-04-26 10:42:42 +02:00 committed by Norman Feske
parent ce75b25fd4
commit 4e822436fc
5 changed files with 54 additions and 13 deletions

View File

@ -313,7 +313,7 @@ void Domain::init(Domain_tree &domains)
try {
Nat_rule &rule = *new (_alloc)
Nat_rule(domains, _tcp_port_alloc, _udp_port_alloc,
_icmp_port_alloc, node);
_icmp_port_alloc, node, _config.verbose());
_nat_rules.insert(&rule);
if (_config.verbose()) {
log("[", *this, "] NAT rule: ", rule); }

View File

@ -47,12 +47,13 @@ Nat_rule::Nat_rule(Domain_tree &domains,
Port_allocator &tcp_port_alloc,
Port_allocator &udp_port_alloc,
Port_allocator &icmp_port_alloc,
Xml_node const node)
Xml_node const node,
bool const verbose)
:
_domain(_find_domain(domains, node)),
_tcp_port_alloc (tcp_port_alloc, node.attribute_value("tcp-ports", 0UL)),
_udp_port_alloc (udp_port_alloc, node.attribute_value("udp-ports", 0UL)),
_icmp_port_alloc(icmp_port_alloc, node.attribute_value("icmp-ids", 0UL))
_tcp_port_alloc (tcp_port_alloc, node.attribute_value("tcp-ports", 0UL), verbose),
_udp_port_alloc (udp_port_alloc, node.attribute_value("udp-ports", 0UL), verbose),
_icmp_port_alloc(icmp_port_alloc, node.attribute_value("icmp-ids", 0UL), verbose)
{ }

View File

@ -54,7 +54,8 @@ class Net::Nat_rule : public Genode::Avl_node<Nat_rule>
Port_allocator &tcp_port_alloc,
Port_allocator &udp_port_alloc,
Port_allocator &icmp_port_alloc,
Genode::Xml_node const node);
Genode::Xml_node const node,
bool const verbose);
Nat_rule &find_by_domain(Domain &domain);

View File

@ -11,6 +11,9 @@
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/log.h>
/* local includes */
#include <port_allocator.h>
@ -26,6 +29,24 @@ bool Net::dynamic_port(Port const port) {
** Port_allocator **
********************/
Port Net::Port_allocator::alloc()
{
for (unsigned nr_of_trials { 0 };
nr_of_trials < COUNT;
nr_of_trials++) {
uint16_t const port_offset = _next_port_offset;
_next_port_offset = (_next_port_offset + 1) % COUNT;
try {
_alloc.alloc_addr(port_offset);
return Port { (uint16_t)(port_offset + FIRST) };
}
catch (Bit_allocator<COUNT>::Range_conflict) { }
}
throw Out_of_indices();
}
void Net::Port_allocator::alloc(Port const port)
{
try { _alloc.alloc_addr(port.value - FIRST); }
@ -34,6 +55,12 @@ void Net::Port_allocator::alloc(Port const port)
}
void Port_allocator::free(Port const port)
{
_alloc.free(port.value - FIRST);
}
/**************************
** Port_allocator_guard **
**************************/
@ -67,7 +94,15 @@ void Port_allocator_guard::free(Port const port)
Port_allocator_guard::Port_allocator_guard(Port_allocator &port_alloc,
unsigned const max)
unsigned const max,
bool const verbose)
:
_port_alloc(port_alloc), _max(max)
{ }
_port_alloc(port_alloc),
_max(min(max, static_cast<uint16_t>(Port_allocator::COUNT)))
{
if (verbose && max > (Port_allocator::COUNT)) {
warning("number of ports was truncated to capacity of allocator");
}
}

View File

@ -38,17 +38,19 @@ class Net::Port_allocator
private:
Genode::Bit_allocator<COUNT> _alloc { };
Genode::Bit_allocator<COUNT> _alloc { };
Genode::uint16_t _next_port_offset { 0 };
public:
struct Allocation_conflict : Genode::Exception { };
struct Out_of_indices : Genode::Exception { };
Port alloc() { return Port(_alloc.alloc() + FIRST); }
Port alloc();
void alloc(Port const port);
void free(Port const port) { _alloc.free(port.value - FIRST); }
void free(Port const port);
};
@ -70,7 +72,9 @@ class Net::Port_allocator_guard
void free(Port const port);
Port_allocator_guard(Port_allocator & port_alloc, unsigned const max);
Port_allocator_guard(Port_allocator &port_alloc,
unsigned const max,
bool const verbose);
unsigned max() const { return _max; }
};