From 0a7798777878db7712ea60636eabc3c4a81fe353 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Mon, 13 Nov 2017 12:45:13 +0100 Subject: [PATCH] nic_router: support domain-local ARP Improve ARP handling code in general: Make the several cases and their handling more clear by using a more readable if/else statement structure. Drop gratuitous ARP requests. Domain-local ARP: Handle ARP packets that target local IPs other than the routers IP (forward them to all other interfaces of the domain). Issue #2609 --- repos/os/src/server/nic_router/interface.cc | 92 +++++++++++++++------ repos/os/src/server/nic_router/interface.h | 10 ++- 2 files changed, 76 insertions(+), 26 deletions(-) diff --git a/repos/os/src/server/nic_router/interface.cc b/repos/os/src/server/nic_router/interface.cc index 06bbf6d79a..6541e1a56d 100644 --- a/repos/os/src/server/nic_router/interface.cc +++ b/repos/os/src/server/nic_router/interface.cc @@ -548,6 +548,12 @@ void Interface::_handle_dhcp_request(Ethernet_frame ð, } +void Interface::_domain_broadcast(Ethernet_frame ð, size_t eth_size) +{ + throw Drop_packet_warn("domain broadcasts not yet supported"); +} + + void Interface::_handle_ip(Ethernet_frame ð, Genode::size_t const eth_size, Packet_descriptor const &pkt) @@ -693,20 +699,23 @@ void Interface::_broadcast_arp_request(Ipv4_address const &ip) } -void Interface::_handle_arp_reply(Arp_packet &arp) +void Interface::_handle_arp_reply(Ethernet_frame ð, + size_t const eth_size, + Arp_packet &arp) { - /* do nothing if ARP info already exists */ try { + /* check wether a matching ARP cache entry already exists */ _arp_cache.find_by_ip(arp.src_ip()); if (_config().verbose()) { log("ARP entry already exists"); } - - return; } - /* create cache entry and continue handling of matching packets */ catch (Arp_cache::No_match) { + + /* by now, no matching ARP cache entry exists, so create one */ Ipv4_address const ip = arp.src_ip(); _arp_cache.new_entry(ip, arp.src_mac()); + + /* continue handling of packets that waited for the entry */ for (Arp_waiter_list_element *waiter_le = _foreign_arp_waiters.first(); waiter_le; ) { @@ -717,6 +726,15 @@ void Interface::_handle_arp_reply(Arp_packet &arp) destroy(waiter.src()._alloc, &waiter); } } + if (_ip_config().interface.prefix_matches(arp.dst_ip()) && + arp.dst_ip() != _router_ip()) + { + /* + * Packet targets IP local to the domain's subnet and doesn't target + * the router. Thus, forward it to all other interfaces of the domain. + */ + _domain_broadcast(eth, eth_size); + } } @@ -726,26 +744,10 @@ Ipv4_address const &Interface::_router_ip() const } -void Interface::_handle_arp_request(Ethernet_frame ð, - size_t const eth_size, - Arp_packet &arp) +void Interface::_send_arp_reply(Ethernet_frame ð, + size_t const eth_size, + Arp_packet &arp) { - /* - * We handle ARP only if it asks for the routers IP or if the router - * shall forward an ARP to a foreign address as gateway. The second - * is the case if no gateway attribute is specified (so we're the - * gateway) and the address is not of the same subnet like the interface - * attribute. - */ - if (arp.dst_ip() != _router_ip() && - (_ip_config().gateway_valid || - _ip_config().interface.prefix_matches(arp.dst_ip()))) - { - if (_config().verbose()) { - log("Ignore ARP request"); } - - return; - } /* interchange source and destination MAC and IP addresses */ Ipv4_address dst_ip = arp.dst_ip(); arp.dst_ip(arp.src_ip()); @@ -761,6 +763,46 @@ void Interface::_handle_arp_request(Ethernet_frame ð, } +void Interface::_handle_arp_request(Ethernet_frame ð, + size_t const eth_size, + Arp_packet &arp) +{ + if (_ip_config().interface.prefix_matches(arp.dst_ip())) { + + /* ARP request for an IP local to the domain's subnet */ + if (arp.src_ip() == arp.dst_ip()) { + + /* gratuitous ARP requests are not really necessary */ + throw Drop_packet_inform("gratuitous ARP request"); + + } else if (arp.dst_ip() == _router_ip()) { + + /* ARP request for the routers IP at this domain */ + _send_arp_reply(eth, eth_size, arp); + + } else { + + /* forward request to all other interfaces of the domain */ + _domain_broadcast(eth, eth_size); + } + + } else { + + /* ARP request for an IP foreign to the domain's subnet */ + if (_ip_config().gateway_valid) { + + /* leave request up to the gateway of the domain */ + throw Drop_packet_inform("leave ARP request up to gateway"); + + } else { + + /* try to act as gateway for the domain as none is configured */ + _send_arp_reply(eth, eth_size, arp); + } + } +} + + void Interface::_handle_arp(Ethernet_frame ð, size_t const eth_size) { /* ignore ARP regarding protocols other than IPv4 via ethernet */ @@ -770,7 +812,7 @@ void Interface::_handle_arp(Ethernet_frame ð, size_t const eth_size) error("ARP for unknown protocol"); } switch (arp.opcode()) { - case Arp_packet::REPLY: _handle_arp_reply(arp); break; + case Arp_packet::REPLY: _handle_arp_reply(eth, eth_size, arp); break; case Arp_packet::REQUEST: _handle_arp_request(eth, eth_size, arp); break; default: error("unknown ARP operation"); } } diff --git a/repos/os/src/server/nic_router/interface.h b/repos/os/src/server/nic_router/interface.h index 26402158a9..1a0f6d621f 100644 --- a/repos/os/src/server/nic_router/interface.h +++ b/repos/os/src/server/nic_router/interface.h @@ -102,12 +102,18 @@ class Net::Interface void _handle_arp(Ethernet_frame ð, Genode::size_t const eth_size); - void _handle_arp_reply(Arp_packet &arp); + void _handle_arp_reply(Ethernet_frame ð, + Genode::size_t const eth_size, + Arp_packet &arp); void _handle_arp_request(Ethernet_frame ð, Genode::size_t const eth_size, Arp_packet &arp); + void _send_arp_reply(Ethernet_frame ð, + Genode::size_t const eth_size, + Arp_packet &arp); + void _handle_dhcp_request(Ethernet_frame ð, Genode::size_t eth_size, Dhcp_packet &dhcp); @@ -133,6 +139,8 @@ class Net::Interface void _broadcast_arp_request(Ipv4_address const &ip); + void _domain_broadcast(Ethernet_frame ð, Genode::size_t eth_size); + void _pass_prot(Ethernet_frame ð, Genode::size_t const eth_size, Ipv4_packet &ip,