mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-21 02:01:38 +00:00
nic_router: re-use ARP waiters for same IP address
For each packet that got stuck with an ARP-cache miss, the router used to send one ARP request and create one ARP waiter. However, in situations where many packets target the same IP at one destination domain and during a short period of time, this causes unnecessary session-quota consumption and network traffic. This issue becomes especially pressing when taking malicious source peers, absent destination peers, and packet batching into account. Therefore, with this commit, the router can accumulate multiple source packets with the same destination IP at one ARP waiter. This means, that only the first packet with an ARP-cache for a certain IP sends an ARP request and creates an ARP waiter. For situations where the ARP request is not answered, this essentially rate-limits ARP requests for one IP at one destination domain according to the lifetime of ARP waiters (default: 10s) Ref #4534
This commit is contained in:
parent
31a438edf6
commit
7d576b4f15
@ -868,8 +868,10 @@ following attribute (default value shown):
|
||||
! <config arp_request_timeout_sec="10">
|
||||
|
||||
When the router does not receive a reply for a sent ARP request within this
|
||||
timeout, the packet that caused the ARP request and the ARP request state are
|
||||
dropped in order to re-use the resources.
|
||||
timeout, the packets that were waiting for the reply and the ARP request state
|
||||
are dropped in order to re-use the resources. Note that this is also the
|
||||
rate-limit for the router sending ARP requests for one IP address at one
|
||||
domain.
|
||||
|
||||
Behavior regarding the NIC-session link state
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -20,20 +20,21 @@ using namespace Net;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
Arp_waiter::Arp_waiter(Interface &src,
|
||||
Domain &dst,
|
||||
Ipv4_address const &ip,
|
||||
Packet_descriptor const &packet,
|
||||
Microseconds timeout,
|
||||
Cached_timer &timer)
|
||||
Arp_waiter::Arp_waiter(Interface &src,
|
||||
Domain &dst,
|
||||
Ipv4_address const &ip,
|
||||
Packet_list_element &packet_le,
|
||||
Microseconds timeout,
|
||||
Cached_timer &timer)
|
||||
:
|
||||
_src_le(this), _src(src), _dst_le(this), _dst_ptr(&dst), _ip(ip),
|
||||
_packet(packet), _timeout(timer, *this, &Arp_waiter::_handle_timeout, timeout)
|
||||
_timeout(timer, *this, &Arp_waiter::_handle_timeout, timeout)
|
||||
{
|
||||
_src.arp_stats().alive++;
|
||||
_src.own_arp_waiters().insert(&_src_le);
|
||||
_dst_ptr->foreign_arp_waiters().insert(&_dst_le);
|
||||
_timeout.schedule(timeout);
|
||||
add_packet(packet_le);
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
/* local includes */
|
||||
#include <list.h>
|
||||
#include <lazy_one_shot_timeout.h>
|
||||
#include <assertion.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/ipv4.h>
|
||||
@ -31,10 +32,20 @@ namespace Net {
|
||||
class Configuration;
|
||||
class Arp_waiter;
|
||||
using Arp_waiter_list_element = Genode::List_element<Arp_waiter>;
|
||||
using Arp_waiter_list = List<Arp_waiter_list_element>;
|
||||
struct Packet_list_element;
|
||||
using Packet_list = Genode::List<Packet_list_element>;
|
||||
struct Arp_waiter_list;
|
||||
}
|
||||
|
||||
|
||||
struct Net::Packet_list_element : Genode::List<Packet_list_element>::Element
|
||||
{
|
||||
Packet_descriptor const packet;
|
||||
|
||||
Packet_list_element(Packet_descriptor const &packet) : packet(packet) { }
|
||||
};
|
||||
|
||||
|
||||
class Net::Arp_waiter
|
||||
{
|
||||
private:
|
||||
@ -44,7 +55,7 @@ class Net::Arp_waiter
|
||||
Arp_waiter_list_element _dst_le;
|
||||
Domain *_dst_ptr;
|
||||
Ipv4_address const _ip;
|
||||
Packet_descriptor const _packet;
|
||||
Packet_list _packets { };
|
||||
Lazy_one_shot_timeout<Arp_waiter> _timeout;
|
||||
|
||||
/*
|
||||
@ -59,17 +70,28 @@ class Net::Arp_waiter
|
||||
|
||||
public:
|
||||
|
||||
Arp_waiter(Interface &src,
|
||||
Domain &dst,
|
||||
Ipv4_address const &ip,
|
||||
Packet_descriptor const &packet,
|
||||
Genode::Microseconds dissolve_timeout,
|
||||
Cached_timer &timer);
|
||||
Arp_waiter(Interface &src,
|
||||
Domain &dst,
|
||||
Ipv4_address const &ip,
|
||||
Packet_list_element &packet_le,
|
||||
Genode::Microseconds dissolve_timeout,
|
||||
Cached_timer &timer);
|
||||
|
||||
~Arp_waiter();
|
||||
|
||||
void handle_config(Domain &dst);
|
||||
|
||||
void add_packet(Packet_list_element &le) { _packets.insert(&le); }
|
||||
|
||||
void flush_packets(Genode::Deallocator &dealloc, auto const &fn)
|
||||
{
|
||||
while (Packet_list_element *le = _packets.first()) {
|
||||
_packets.remove(le);
|
||||
fn(le->packet);
|
||||
destroy(dealloc, le);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*********
|
||||
** Log **
|
||||
@ -82,10 +104,31 @@ class Net::Arp_waiter
|
||||
** Accessors **
|
||||
***************/
|
||||
|
||||
Interface &src() const { return _src; }
|
||||
Ipv4_address const &ip() const { return _ip; }
|
||||
Packet_descriptor const &packet() const { return _packet; }
|
||||
Domain &dst() { return *_dst_ptr; }
|
||||
Interface &src() const { return _src; }
|
||||
Ipv4_address const &ip() const { return _ip; }
|
||||
Domain &dst() { return *_dst_ptr; }
|
||||
};
|
||||
|
||||
|
||||
struct Net::Arp_waiter_list : List<Arp_waiter_list_element>
|
||||
{
|
||||
void find_by_ip(Ipv4_address const &ip, auto const &found_fn, auto const ¬_found_fn)
|
||||
{
|
||||
bool found = false;
|
||||
for_each([&] (Arp_waiter_list_element &elem) {
|
||||
if (found)
|
||||
return;
|
||||
|
||||
Arp_waiter &waiter = *elem.object();
|
||||
if (ip != waiter.ip())
|
||||
return;
|
||||
|
||||
found_fn(waiter);
|
||||
found = true;
|
||||
});
|
||||
if (!found)
|
||||
not_found_fn();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _ARP_WAITER_H_ */
|
||||
|
@ -596,7 +596,30 @@ Packet_result Interface::_adapt_eth(Ethernet_frame ð,
|
||||
if (!remote_domain.use_arp())
|
||||
return result;
|
||||
|
||||
auto with_next_hop = [&] (Ipv4_address const &hop_ip) {
|
||||
auto with_next_hop = [&] (Ipv4_address const &hop_ip)
|
||||
{
|
||||
auto send_arp_fn = [&]
|
||||
{
|
||||
Packet_list_element &packet_le = *new (_alloc) Packet_list_element(pkt);
|
||||
auto create_new_arp_waiter = [&]
|
||||
{
|
||||
new (_alloc) Arp_waiter(
|
||||
*this, remote_domain, hop_ip, packet_le, _config_ptr->arp_request_timeout(), _timer);
|
||||
|
||||
remote_domain.interfaces().for_each([&] (Interface &interface) {
|
||||
interface._broadcast_arp_request(remote_ip_cfg.interface().address, hop_ip); });
|
||||
|
||||
result = packet_postponed();
|
||||
};
|
||||
remote_domain.foreign_arp_waiters().find_by_ip(hop_ip,
|
||||
[&] (Arp_waiter &waiter) { waiter.add_packet(packet_le); },
|
||||
[&] {
|
||||
retry_once<Out_of_ram, Out_of_caps>(
|
||||
create_new_arp_waiter,
|
||||
[&] { _try_emergency_free_quota(); },
|
||||
[&] { result = packet_drop("out of quota while creating ARP waiter"); });
|
||||
});
|
||||
};
|
||||
remote_domain.arp_cache().find_by_ip(
|
||||
hop_ip,
|
||||
[&] /* handle_match */ (Arp_cache_entry const &entry)
|
||||
@ -606,17 +629,9 @@ Packet_result Interface::_adapt_eth(Ethernet_frame ð,
|
||||
[&] /* handle_no_match */ ()
|
||||
{
|
||||
retry_once<Out_of_ram, Out_of_caps>(
|
||||
[&] {
|
||||
new (_alloc) Arp_waiter { *this, remote_domain, hop_ip, pkt, _config_ptr->arp_request_timeout(), _timer };
|
||||
remote_domain.interfaces().for_each([&] (Interface &interface)
|
||||
{
|
||||
interface._broadcast_arp_request(
|
||||
remote_ip_cfg.interface().address, hop_ip);
|
||||
});
|
||||
result = packet_postponed();
|
||||
},
|
||||
send_arp_fn,
|
||||
[&] { _try_emergency_free_quota(); },
|
||||
[&] { result = packet_drop("out of quota while creating ARP waiter"); });
|
||||
[&] { result = packet_drop("out of quota while creating ARP packet list element"); });
|
||||
}
|
||||
);
|
||||
};
|
||||
@ -1491,7 +1506,8 @@ void Interface::_handle_arp_reply(Ethernet_frame ð,
|
||||
Arp_waiter &waiter = *waiter_le->object();
|
||||
waiter_le = waiter_le->next();
|
||||
if (ip != waiter.ip()) { continue; }
|
||||
waiter.src()._continue_handle_eth(waiter.packet());
|
||||
waiter.flush_packets(waiter.src()._alloc, [&] (Packet_descriptor const &packet) {
|
||||
waiter.src()._continue_handle_eth(packet); });
|
||||
destroy(waiter.src()._alloc, &waiter);
|
||||
}
|
||||
}
|
||||
@ -1737,7 +1753,8 @@ void Interface::_destroy_timed_out_arp_waiters()
|
||||
{
|
||||
while (Arp_waiter_list_element *le = _timed_out_arp_waiters.first()) {
|
||||
Arp_waiter &waiter = *le->object();
|
||||
_drop_packet(waiter.packet(), "ARP request timed out");
|
||||
waiter.flush_packets(_alloc, [&] (Packet_descriptor const &packet) {
|
||||
_drop_packet(packet, "ARP request timed out"); });
|
||||
_timed_out_arp_waiters.remove(le);
|
||||
destroy(_alloc, &waiter);
|
||||
}
|
||||
@ -2264,7 +2281,9 @@ void Interface::_ack_packet(Packet_descriptor const &pkt)
|
||||
|
||||
void Interface::cancel_arp_waiting(Arp_waiter &waiter)
|
||||
{
|
||||
_drop_packet(waiter.packet(), "ARP got cancelled");
|
||||
|
||||
waiter.flush_packets(_alloc, [&] (Packet_descriptor const &packet) {
|
||||
_drop_packet(packet, "ARP got cancelled"); });
|
||||
destroy(_alloc, &waiter);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user