mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-21 02:01:38 +00:00
nic_router: DHCP client functionality
If the attribute 'interface' is not set in a 'domain' tag, the router tries to dynamically receive and maintain an IP configuration for that domain by using DHCP in the client role at all interfaces that connect to the domain. In the DHCP discover phase, the router simply chooses the first DHCP offer that arrives. So, no comparison of different DHCP offers is done. In the DHCP request phase, the server is expected to provide an IP address, a gateway, a subnet mask, and an IP lease time to the router. If anything substantial goes wrong during a DHCP exchange, the router discards the outcome of the exchange and goes back to the DHCP discover phase. At any time where there is no valid IP configuration present at a domain, the domain does only act as DHCP client and all other router functionality is disabled for the domain. A domain cannot act as DHCP client and DHCP server at once. So, a 'domain' tag must either have an 'interface' attribute or must not contain a 'dhcp-server' tag. Ref #2534
This commit is contained in:
parent
3560555acc
commit
db9d4d3a3c
@ -11,7 +11,7 @@ The 'nic_router' component can be used to individually route IPv4 packets
|
||||
between multiple NIC sessions. Thereby, it can translate between different
|
||||
subnets. The component supports IP routing, TCP and UDP routing, the
|
||||
partitioning of the TCP and UDP port spaces, port forwarding, NAT, and can
|
||||
also act as DHCP server.
|
||||
also act as DHCP server and as DHCP client.
|
||||
|
||||
|
||||
Basics
|
||||
@ -41,7 +41,8 @@ named "uplink". For each domain there must be a domain tag:
|
||||
The 'interface' attribute defines two things at once. First, it tells the
|
||||
router which subnet can be found behind this domain, and second, which IP
|
||||
identity the router shall use in case it has to communicate as itself with
|
||||
the subnet.
|
||||
the subnet. If the 'interface' attribute is not set in a 'domain' tag, the
|
||||
router acts as DHCP client (Section [Configuring DHCP client functionality]).
|
||||
|
||||
Additionaly, the optional 'gateway' attribute can be set for a domain:
|
||||
|
||||
@ -50,7 +51,9 @@ Additionaly, the optional 'gateway' attribute can be set for a domain:
|
||||
It defines the standard gateway of the subnet behind this domain. If a packet
|
||||
shall be routed to this domain and its final IP destination does not match
|
||||
the subnet, its Ethernet destination is set to the MAC address of the gateway.
|
||||
If a gateway isn't given for a domain, such packets get dropped.
|
||||
If a gateway isn't given for a domain, such packets get dropped. If a gateway
|
||||
is given for a domain without an 'interface' attribute, this gateway
|
||||
configuration is not getting effective.
|
||||
|
||||
For each domain, the routing of packets from this domain can be configured
|
||||
individually by adding subtags to the corresponding domain tag. There are
|
||||
@ -299,14 +302,32 @@ the router while ip_lease_time_sec is applied only when the offer is
|
||||
acknowledged by the client in time.
|
||||
|
||||
|
||||
Configuring DHCP client functionality
|
||||
#####################################
|
||||
|
||||
If the attribute 'interface' is not set in a 'domain' tag, the router tries to
|
||||
dynamically receive and maintain an IP configuration for that domain by using
|
||||
DHCP in the client role at all interfaces that connect to the domain. In the
|
||||
DHCP discover phase, the router simply chooses the first DHCP offer that
|
||||
arrives. So, no comparison of different DHCP offers is done. In the DHCP
|
||||
request phase, the server is expected to provide an IP address, a gateway, a
|
||||
subnet mask, and an IP lease time to the router. If anything substantial goes
|
||||
wrong during a DHCP exchange, the router discards the outcome of the exchange
|
||||
and goes back to the DHCP discover phase. At any time where there is no valid
|
||||
IP configuration present at a domain, the domain does only act as DHCP client
|
||||
and all other router functionality is disabled for the domain. A domain cannot
|
||||
act as DHCP client and DHCP server at once. So, a 'domain' tag must either
|
||||
have an 'interface' attribute or must not contain a 'dhcp-server' tag.
|
||||
|
||||
|
||||
Examples
|
||||
########
|
||||
|
||||
This section will list and explain some interesting configuration snippets. A
|
||||
comprehensive example of how to use the router (except DHCP server
|
||||
functionality) can be found in the test script 'libports/run/nic_router.run'.
|
||||
For an example of how to use the DHCP server functionality see the
|
||||
'ports/run/virtualbox_nic_router.run' script.
|
||||
For an example of how to use the DHCP server and the DHCP client functionality
|
||||
see the 'ports/run/virtualbox_nic_router.run' script.
|
||||
|
||||
The environment for the examples shall be as
|
||||
follows. There are two virtual subnets 192.168.1.0/24 and 192.168.2.0/24 that
|
||||
|
@ -74,6 +74,7 @@ Net::Session_component::Session_component(Allocator &alloc,
|
||||
_tx.sigh_packet_avail(_sink_submit);
|
||||
_rx.sigh_ack_avail(_source_ack);
|
||||
_rx.sigh_ready_to_submit(_source_submit);
|
||||
Interface::_init();
|
||||
}
|
||||
|
||||
|
||||
|
259
repos/os/src/server/nic_router/dhcp_client.cc
Normal file
259
repos/os/src/server/nic_router/dhcp_client.cc
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* \brief DHCP client state model
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-24
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <dhcp_client.h>
|
||||
#include <interface.h>
|
||||
#include <domain.h>
|
||||
#include <configuration.h>
|
||||
#include <size_guard.h>
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Net;
|
||||
using Message_type = Dhcp_packet::Message_type;
|
||||
using Packet_ignored = Interface::Packet_ignored;
|
||||
|
||||
|
||||
Configuration &Dhcp_client::_config() { return _domain().config(); };
|
||||
|
||||
|
||||
Domain &Dhcp_client::_domain() { return _interface.domain(); }
|
||||
|
||||
|
||||
Dhcp_client::Dhcp_client(Genode::Allocator &alloc,
|
||||
Timer::Connection &timer,
|
||||
Interface &interface)
|
||||
:
|
||||
_alloc(alloc), _interface(interface),
|
||||
_timeout(timer, *this, &Dhcp_client::_handle_timeout)
|
||||
{ }
|
||||
|
||||
|
||||
void Dhcp_client::discover()
|
||||
{
|
||||
_set_state(State::SELECT, _config().rtt());
|
||||
_send(Message_type::DISCOVER, Ipv4_address(), Ipv4_address());
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_client::_rerequest(State next_state)
|
||||
{
|
||||
_set_state(next_state, _rerequest_timeout(2));
|
||||
_send(Message_type::REQUEST, _domain().ip_config().interface.address,
|
||||
Ipv4_address());
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_client::_set_state(State state, Microseconds timeout)
|
||||
{
|
||||
_state = state;
|
||||
_timeout.schedule(timeout);
|
||||
}
|
||||
|
||||
|
||||
Microseconds Dhcp_client::_rerequest_timeout(unsigned lease_time_div_log2)
|
||||
{
|
||||
/* FIXME limit the time because of shortcomings in timeout framework */
|
||||
enum { MAX_TIMEOUT_SEC = 3600 };
|
||||
unsigned long timeout_sec = _lease_time_sec >> lease_time_div_log2;
|
||||
|
||||
if (timeout_sec > MAX_TIMEOUT_SEC) {
|
||||
timeout_sec = MAX_TIMEOUT_SEC;
|
||||
warning("Had to prune the state timeout of DHCP client");
|
||||
}
|
||||
return Microseconds(timeout_sec * 1000UL * 1000UL);
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_client::_handle_timeout(Duration)
|
||||
{
|
||||
switch (_state) {
|
||||
case State::BOUND: _rerequest(State::RENEW); break;
|
||||
case State::RENEW: _rerequest(State::REBIND); break;
|
||||
case State::REBIND: _domain().discard_ip_config();
|
||||
default: discover();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_client::handle_ip(Ethernet_frame ð, size_t eth_size)
|
||||
{
|
||||
if (eth.dst() != _interface.router_mac()) {
|
||||
throw Packet_ignored("DHCP client expects Ethernet targeting the router");
|
||||
}
|
||||
Ipv4_packet &ip = *new (eth.data<void>())
|
||||
Ipv4_packet(eth_size - sizeof(Ethernet_frame));
|
||||
|
||||
if (ip.protocol() != Ipv4_packet::Protocol::UDP) {
|
||||
throw Packet_ignored("DHCP client expects UDP packet");
|
||||
}
|
||||
Udp_packet &udp = *new (ip.data<void>())
|
||||
Udp_packet(eth_size - sizeof(Ipv4_packet));
|
||||
|
||||
if (!Dhcp_packet::is_dhcp(&udp)) {
|
||||
throw Packet_ignored("DHCP client expects DHCP packet");
|
||||
}
|
||||
Dhcp_packet &dhcp = *new (udp.data<void>())
|
||||
Dhcp_packet(eth_size - sizeof(Ipv4_packet) - sizeof(Udp_packet));
|
||||
|
||||
if (dhcp.op() != Dhcp_packet::REPLY) {
|
||||
throw Packet_ignored("DHCP client expects DHCP reply");
|
||||
}
|
||||
try { _handle_dhcp_reply(dhcp); }
|
||||
catch (Dhcp_packet::Option_not_found) {
|
||||
throw Packet_ignored("DHCP client misses DHCP option");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_client::_handle_dhcp_reply(Dhcp_packet &dhcp)
|
||||
{
|
||||
Message_type const msg_type =
|
||||
dhcp.option<Dhcp_packet::Message_type_option>().value();
|
||||
|
||||
switch (_state) {
|
||||
case State::SELECT:
|
||||
|
||||
if (msg_type != Message_type::OFFER) {
|
||||
throw Packet_ignored("DHCP client expects an offer");
|
||||
}
|
||||
_set_state(State::REQUEST, _config().rtt());
|
||||
_send(Message_type::REQUEST, dhcp.yiaddr(),
|
||||
dhcp.option<Dhcp_packet::Server_ipv4>().value());
|
||||
break;
|
||||
|
||||
case State::REQUEST:
|
||||
|
||||
if (msg_type != Message_type::ACK) {
|
||||
throw Packet_ignored("DHCP client expects an acknowledgement");
|
||||
}
|
||||
_lease_time_sec = dhcp.option<Dhcp_packet::Ip_lease_time>().value();
|
||||
_set_state(State::BOUND, _rerequest_timeout(1));
|
||||
_domain().ip_config(dhcp.yiaddr(),
|
||||
dhcp.option<Dhcp_packet::Subnet_mask>().value(),
|
||||
dhcp.option<Dhcp_packet::Router_ipv4>().value());
|
||||
break;
|
||||
|
||||
case State::RENEW:
|
||||
case State::REBIND:
|
||||
|
||||
if (msg_type != Message_type::ACK) {
|
||||
throw Packet_ignored("DHCP client expects an acknowledgement");
|
||||
}
|
||||
_set_state(State::BOUND, _rerequest_timeout(1));
|
||||
_lease_time_sec = dhcp.option<Dhcp_packet::Ip_lease_time>().value();
|
||||
break;
|
||||
|
||||
default: throw Packet_ignored("DHCP client doesn't expect a packet");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Dhcp_client::_send(Message_type msg_type,
|
||||
Ipv4_address client_ip,
|
||||
Ipv4_address server_ip)
|
||||
{
|
||||
/* allocate buffer for the request */
|
||||
enum { BUF_SIZE = 1024 };
|
||||
using Size_guard = Size_guard_tpl<BUF_SIZE, Interface::Dhcp_msg_buffer_too_small>;
|
||||
void *buf;
|
||||
try { _alloc.alloc(BUF_SIZE, &buf); }
|
||||
catch (...) { throw Interface::Alloc_dhcp_msg_buffer_failed(); }
|
||||
Mac_address client_mac = _interface.router_mac();
|
||||
|
||||
/* create ETH header of the request */
|
||||
Size_guard size;
|
||||
size.add(sizeof(Ethernet_frame));
|
||||
Ethernet_frame ð = *reinterpret_cast<Ethernet_frame *>(buf);
|
||||
eth.dst(Mac_address(0xff));
|
||||
eth.src(client_mac);
|
||||
eth.type(Ethernet_frame::Type::IPV4);
|
||||
|
||||
/* create IP header of the request */
|
||||
enum { IPV4_TIME_TO_LIVE = 64 };
|
||||
size_t const ip_off = size.curr();
|
||||
size.add(sizeof(Ipv4_packet));
|
||||
Ipv4_packet &ip = *eth.data<Ipv4_packet>();
|
||||
ip.header_length(sizeof(Ipv4_packet) / 4);
|
||||
ip.version(4);
|
||||
ip.diff_service(0);
|
||||
ip.identification(0);
|
||||
ip.flags(0);
|
||||
ip.fragment_offset(0);
|
||||
ip.time_to_live(IPV4_TIME_TO_LIVE);
|
||||
ip.protocol(Ipv4_packet::Protocol::UDP);
|
||||
ip.src(client_ip);
|
||||
ip.dst(Ipv4_address(0xff));
|
||||
|
||||
/* create UDP header of the request */
|
||||
size_t const udp_off = size.curr();
|
||||
size.add(sizeof(Udp_packet));
|
||||
Udp_packet &udp = *ip.data<Udp_packet>();
|
||||
udp.src_port(Port(Dhcp_packet::BOOTPC));
|
||||
udp.dst_port(Port(Dhcp_packet::BOOTPS));
|
||||
|
||||
/* create mandatory DHCP fields of the request */
|
||||
size_t const dhcp_off = size.curr();
|
||||
size.add(sizeof(Dhcp_packet));
|
||||
Dhcp_packet &dhcp = *udp.data<Dhcp_packet>();
|
||||
dhcp.op(Dhcp_packet::REQUEST);
|
||||
dhcp.htype(Dhcp_packet::Htype::ETH);
|
||||
dhcp.hlen(sizeof(Mac_address));
|
||||
dhcp.hops(0);
|
||||
dhcp.xid(0x12345678);
|
||||
dhcp.secs(0);
|
||||
dhcp.flags(0);
|
||||
dhcp.ciaddr(client_ip);
|
||||
dhcp.yiaddr(Ipv4_address());
|
||||
dhcp.siaddr(Ipv4_address());
|
||||
dhcp.giaddr(Ipv4_address());
|
||||
dhcp.client_mac(client_mac);
|
||||
dhcp.zero_fill_sname();
|
||||
dhcp.zero_fill_file();
|
||||
dhcp.default_magic_cookie();
|
||||
|
||||
/* append DHCP option fields to the request */
|
||||
Dhcp_packet::Options_aggregator<Size_guard>
|
||||
dhcp_opts(dhcp, size);
|
||||
dhcp_opts.append_option<Dhcp_packet::Message_type_option>(msg_type);
|
||||
|
||||
switch (msg_type) {
|
||||
case Message_type::DISCOVER:
|
||||
dhcp_opts.append_option<Dhcp_packet::Client_id>(client_mac);
|
||||
dhcp_opts.append_option<Dhcp_packet::Max_msg_size>(BUF_SIZE - dhcp_off);
|
||||
break;
|
||||
|
||||
case Message_type::REQUEST:
|
||||
dhcp_opts.append_option<Dhcp_packet::Client_id>(client_mac);
|
||||
dhcp_opts.append_option<Dhcp_packet::Max_msg_size>(BUF_SIZE - dhcp_off);
|
||||
if (_state == State::REQUEST) {
|
||||
dhcp_opts.append_option<Dhcp_packet::Requested_addr>(client_ip);
|
||||
dhcp_opts.append_option<Dhcp_packet::Server_ipv4>(server_ip);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Interface::Bad_send_dhcp_args();
|
||||
}
|
||||
dhcp_opts.append_option<Dhcp_packet::Options_end>();
|
||||
|
||||
/* fill in header values that need the packet to be complete already */
|
||||
udp.length(size.curr() - udp_off);
|
||||
udp.update_checksum(ip.src(), ip.dst());
|
||||
ip.total_length(size.curr() - ip_off);
|
||||
ip.checksum(Ipv4_packet::calculate_checksum(ip));
|
||||
|
||||
/* send request to sender of request and free request buffer */
|
||||
_interface.send(eth, size.curr());
|
||||
_alloc.free(buf, BUF_SIZE);
|
||||
}
|
74
repos/os/src/server/nic_router/dhcp_client.h
Normal file
74
repos/os/src/server/nic_router/dhcp_client.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* \brief DHCP client state model
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-24
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _DHCP_CLIENT_H_
|
||||
#define _DHCP_CLIENT_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <timer_session/connection.h>
|
||||
#include <net/dhcp.h>
|
||||
|
||||
namespace Net {
|
||||
|
||||
class Domain;
|
||||
class Configuration;
|
||||
class Interface;
|
||||
class Ethernet_frame;
|
||||
class Dhcp_client;
|
||||
}
|
||||
|
||||
class Net::Dhcp_client
|
||||
{
|
||||
private:
|
||||
|
||||
enum class State
|
||||
{
|
||||
INIT = 0, SELECT = 1, REQUEST = 2, BOUND = 3, RENEW = 4, REBIND = 5
|
||||
};
|
||||
|
||||
Genode::Allocator &_alloc;
|
||||
Interface &_interface;
|
||||
State _state { State::INIT };
|
||||
Timer::One_shot_timeout<Dhcp_client> _timeout;
|
||||
unsigned long _lease_time_sec;
|
||||
|
||||
void _handle_dhcp_reply(Dhcp_packet &dhcp);
|
||||
|
||||
void _handle_timeout(Genode::Duration);
|
||||
|
||||
void _rerequest(State next_state);
|
||||
|
||||
Genode::Microseconds _rerequest_timeout(unsigned lease_time_div_log2);
|
||||
|
||||
void _set_state(State state, Genode::Microseconds timeout);
|
||||
|
||||
void _send(Dhcp_packet::Message_type msg_type,
|
||||
Ipv4_address client_ip,
|
||||
Ipv4_address server_ip);
|
||||
|
||||
Configuration &_config();
|
||||
|
||||
Domain &_domain();
|
||||
|
||||
public:
|
||||
|
||||
Dhcp_client(Genode::Allocator &alloc,
|
||||
Timer::Connection &timer,
|
||||
Interface &interface);
|
||||
|
||||
void handle_ip(Ethernet_frame ð, Genode::size_t eth_size);
|
||||
|
||||
void discover();
|
||||
};
|
||||
|
||||
#endif /* _DHCP_CLIENT_H_ */
|
@ -116,6 +116,22 @@ Domain_base::Domain_base(Xml_node const node)
|
||||
** Domain **
|
||||
************/
|
||||
|
||||
void Domain::ip_config(Ipv4_address ip,
|
||||
Ipv4_address subnet_mask,
|
||||
Ipv4_address gateway)
|
||||
{
|
||||
_ip_config.construct(Ipv4_address_prefix(ip, subnet_mask), gateway);
|
||||
_ip_config_changed();
|
||||
}
|
||||
|
||||
|
||||
void Domain::discard_ip_config()
|
||||
{
|
||||
_ip_config.construct();
|
||||
_ip_config_changed();
|
||||
}
|
||||
|
||||
|
||||
void Domain::_read_forward_rules(Cstring const &protocol,
|
||||
Domain_tree &domains,
|
||||
Xml_node const node,
|
||||
@ -163,17 +179,36 @@ Domain::Domain(Configuration &config, Xml_node const node, Allocator &alloc)
|
||||
_ip_config(_node.attribute_value("interface", Ipv4_address_prefix()),
|
||||
_node.attribute_value("gateway", Ipv4_address()))
|
||||
{
|
||||
if (_name == Domain_name() || !_ip_config.interface_valid) {
|
||||
if (_name == Domain_name()) {
|
||||
error("Missing name attribute in domain node");
|
||||
throw Invalid();
|
||||
}
|
||||
if (!ip_config().valid && _node.has_sub_node("dhcp-server")) {
|
||||
error("Domain cannot act as DHCP client and server at once");
|
||||
throw Invalid();
|
||||
}
|
||||
_ip_config_changed();
|
||||
}
|
||||
|
||||
|
||||
void Domain::_ip_config_changed()
|
||||
{
|
||||
if (!ip_config().valid) {
|
||||
return;
|
||||
}
|
||||
if (_config.verbose()) {
|
||||
log("New IP config at domain \"", *this, "\":"
|
||||
" interface ", ip_config().interface,
|
||||
" gateway ", ip_config().gateway);
|
||||
}
|
||||
/* try to find configuration for DHCP server role */
|
||||
try {
|
||||
_dhcp_server.set(*new (alloc)
|
||||
Dhcp_server(node.sub_node("dhcp-server"), alloc,
|
||||
_ip_config.interface));
|
||||
_dhcp_server.set(*new (_alloc)
|
||||
Dhcp_server(_node.sub_node("dhcp-server"), _alloc,
|
||||
ip_config().interface));
|
||||
|
||||
if (_config.verbose()) {
|
||||
log(" DHCP server: ", _dhcp_server.deref()); }
|
||||
log("DHCP server at domain \"", *this, "\": ", _dhcp_server.deref()); }
|
||||
}
|
||||
catch (Xml_node::Nonexistent_sub_node) { }
|
||||
catch (Dhcp_server::Invalid) {
|
||||
@ -219,8 +254,8 @@ void Domain::create_rules(Domain_tree &domains)
|
||||
|
||||
Ipv4_address const &Domain::next_hop(Ipv4_address const &ip) const
|
||||
{
|
||||
if (_ip_config.interface.prefix_matches(ip)) { return ip; }
|
||||
if (_ip_config.gateway_valid) { return _ip_config.gateway; }
|
||||
if (ip_config().interface.prefix_matches(ip)) { return ip; }
|
||||
if (ip_config().gateway_valid) { return ip_config().gateway; }
|
||||
throw No_next_hop();
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <util/xml_node.h>
|
||||
#include <util/noncopyable.h>
|
||||
#include <os/duration.h>
|
||||
#include <util/reconstructible.h>
|
||||
|
||||
namespace Genode { class Allocator; }
|
||||
|
||||
@ -125,21 +126,21 @@ class Net::Domain : public Domain_base
|
||||
{
|
||||
private:
|
||||
|
||||
Domain_avl_member _avl_member;
|
||||
Configuration &_config;
|
||||
Genode::Xml_node _node;
|
||||
Genode::Allocator &_alloc;
|
||||
Ip_rule_list _ip_rules;
|
||||
Forward_rule_tree _tcp_forward_rules;
|
||||
Forward_rule_tree _udp_forward_rules;
|
||||
Transport_rule_list _tcp_rules;
|
||||
Transport_rule_list _udp_rules;
|
||||
Port_allocator _tcp_port_alloc;
|
||||
Port_allocator _udp_port_alloc;
|
||||
Nat_rule_tree _nat_rules;
|
||||
Pointer<Interface> _interface;
|
||||
Pointer<Dhcp_server> _dhcp_server;
|
||||
Ipv4_config _ip_config;
|
||||
Domain_avl_member _avl_member;
|
||||
Configuration &_config;
|
||||
Genode::Xml_node _node;
|
||||
Genode::Allocator &_alloc;
|
||||
Ip_rule_list _ip_rules;
|
||||
Forward_rule_tree _tcp_forward_rules;
|
||||
Forward_rule_tree _udp_forward_rules;
|
||||
Transport_rule_list _tcp_rules;
|
||||
Transport_rule_list _udp_rules;
|
||||
Port_allocator _tcp_port_alloc;
|
||||
Port_allocator _udp_port_alloc;
|
||||
Nat_rule_tree _nat_rules;
|
||||
Pointer<Interface> _interface;
|
||||
Pointer<Dhcp_server> _dhcp_server;
|
||||
Genode::Reconstructible<Ipv4_config> _ip_config;
|
||||
|
||||
void _read_forward_rules(Genode::Cstring const &protocol,
|
||||
Domain_tree &domains,
|
||||
@ -153,6 +154,8 @@ class Net::Domain : public Domain_base
|
||||
char const *type,
|
||||
Transport_rule_list &rules);
|
||||
|
||||
void _ip_config_changed();
|
||||
|
||||
public:
|
||||
|
||||
struct Invalid : Genode::Exception { };
|
||||
@ -168,6 +171,12 @@ class Net::Domain : public Domain_base
|
||||
|
||||
Ipv4_address const &next_hop(Ipv4_address const &ip) const;
|
||||
|
||||
void ip_config(Ipv4_address ip,
|
||||
Ipv4_address subnet_mask,
|
||||
Ipv4_address gateway);
|
||||
|
||||
void discard_ip_config();
|
||||
|
||||
|
||||
/*********
|
||||
** log **
|
||||
@ -180,7 +189,7 @@ class Net::Domain : public Domain_base
|
||||
** Accessors **
|
||||
***************/
|
||||
|
||||
Ipv4_config const &ip_config() const { return _ip_config; }
|
||||
Ipv4_config const &ip_config() const { return *_ip_config; }
|
||||
Domain_name const &name() { return _name; }
|
||||
Ip_rule_list &ip_rules() { return _ip_rules; }
|
||||
Forward_rule_tree &tcp_forward_rules() { return _tcp_forward_rules; }
|
||||
|
@ -20,32 +20,11 @@
|
||||
#include <interface.h>
|
||||
#include <configuration.h>
|
||||
#include <l3_protocol.h>
|
||||
#include <size_guard.h>
|
||||
|
||||
using namespace Net;
|
||||
using namespace Genode;
|
||||
|
||||
/**
|
||||
* Utility to ensure that a size value doesn't exceed a limit
|
||||
*/
|
||||
template <size_t MAX, typename EXCEPTION>
|
||||
class Size_guard
|
||||
{
|
||||
private:
|
||||
|
||||
size_t _curr { 0 };
|
||||
|
||||
public:
|
||||
|
||||
void add(size_t size)
|
||||
{
|
||||
size_t const new_size = _curr + size;
|
||||
if (new_size > MAX) { throw EXCEPTION(); }
|
||||
_curr = new_size;
|
||||
}
|
||||
|
||||
size_t curr() const { return _curr; }
|
||||
};
|
||||
|
||||
|
||||
/***************
|
||||
** Utilities **
|
||||
@ -186,7 +165,7 @@ void Interface::_pass_ip(Ethernet_frame ð,
|
||||
Ipv4_packet &ip)
|
||||
{
|
||||
ip.checksum(Ipv4_packet::calculate_checksum(ip));
|
||||
_send(eth, eth_size);
|
||||
send(eth, eth_size);
|
||||
}
|
||||
|
||||
|
||||
@ -339,12 +318,13 @@ void Interface::_send_dhcp_reply(Dhcp_server const &dhcp_srv,
|
||||
{
|
||||
/* allocate buffer for the reply */
|
||||
enum { BUF_SIZE = 512 };
|
||||
using Size_guard = Size_guard_tpl<BUF_SIZE, Dhcp_msg_buffer_too_small>;
|
||||
void *buf;
|
||||
try { _alloc.alloc(BUF_SIZE, &buf); }
|
||||
catch (...) { throw Alloc_dhcp_reply_buffer_failed(); }
|
||||
catch (...) { throw Alloc_dhcp_msg_buffer_failed(); }
|
||||
|
||||
/* create ETH header of the reply */
|
||||
Size_guard<BUF_SIZE, Dhcp_reply_buffer_too_small> reply_size;
|
||||
Size_guard reply_size;
|
||||
reply_size.add(sizeof(Ethernet_frame));
|
||||
Ethernet_frame &reply_eth = *reinterpret_cast<Ethernet_frame *>(buf);
|
||||
reply_eth.dst(client_mac);
|
||||
@ -394,7 +374,7 @@ void Interface::_send_dhcp_reply(Dhcp_server const &dhcp_srv,
|
||||
reply_dhcp.default_magic_cookie();
|
||||
|
||||
/* append DHCP option fields to the reply */
|
||||
Dhcp_packet::Options_aggregator<Size_guard<BUF_SIZE, Dhcp_reply_buffer_too_small> >
|
||||
Dhcp_packet::Options_aggregator<Size_guard>
|
||||
reply_dhcp_opts(reply_dhcp, reply_size);
|
||||
reply_dhcp_opts.append_option<Dhcp_packet::Message_type_option>(msg_type);
|
||||
reply_dhcp_opts.append_option<Dhcp_packet::Server_ipv4>(_router_ip());
|
||||
@ -413,7 +393,7 @@ void Interface::_send_dhcp_reply(Dhcp_server const &dhcp_srv,
|
||||
reply_ip.checksum(Ipv4_packet::calculate_checksum(reply_ip));
|
||||
|
||||
/* send reply to sender of request and free reply buffer */
|
||||
_send(reply_eth, reply_size.curr());
|
||||
send(reply_eth, reply_size.curr());
|
||||
_alloc.free(buf, BUF_SIZE);
|
||||
}
|
||||
|
||||
@ -584,6 +564,9 @@ void Interface::_handle_ip(Ethernet_frame ð,
|
||||
return;
|
||||
}
|
||||
catch (Pointer<Dhcp_server>::Invalid) { }
|
||||
} else {
|
||||
_dhcp_client.handle_ip(eth, eth_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -690,7 +673,7 @@ void Interface::_broadcast_arp_request(Ipv4_address const &ip)
|
||||
arp.src_ip(_router_ip());
|
||||
arp.dst_mac(Mac_address(0xff));
|
||||
arp.dst_ip(ip);
|
||||
_send(eth_arp, sizeof(eth_arp));
|
||||
send(eth_arp, sizeof(eth_arp));
|
||||
}
|
||||
|
||||
|
||||
@ -758,7 +741,7 @@ void Interface::_handle_arp_request(Ethernet_frame ð,
|
||||
|
||||
/* mark packet as reply and send it back to its sender */
|
||||
arp.opcode(Arp_packet::REPLY);
|
||||
_send(eth, eth_size);
|
||||
send(eth, eth_size);
|
||||
}
|
||||
|
||||
|
||||
@ -838,10 +821,19 @@ void Interface::_handle_eth(void *const eth_base,
|
||||
if (_config().verbose()) {
|
||||
log("\033[33m(router <- ", _domain, ")\033[0m ", *eth); }
|
||||
|
||||
switch (eth->type()) {
|
||||
case Ethernet_frame::Type::ARP: _handle_arp(*eth, eth_size); break;
|
||||
case Ethernet_frame::Type::IPV4: _handle_ip(*eth, eth_size, pkt); break;
|
||||
default: throw Bad_network_protocol(); }
|
||||
if (_domain.ip_config().valid) {
|
||||
|
||||
switch (eth->type()) {
|
||||
case Ethernet_frame::Type::ARP: _handle_arp(*eth, eth_size); break;
|
||||
case Ethernet_frame::Type::IPV4: _handle_ip(*eth, eth_size, pkt); break;
|
||||
default: throw Bad_network_protocol(); }
|
||||
|
||||
} else {
|
||||
|
||||
switch (eth->type()) {
|
||||
case Ethernet_frame::Type::IPV4: _dhcp_client.handle_ip(*eth, eth_size); break;
|
||||
default: throw Bad_network_protocol(); }
|
||||
}
|
||||
}
|
||||
catch (Ethernet_frame::No_ethernet_frame) {
|
||||
error("invalid ethernet frame"); }
|
||||
@ -866,10 +858,10 @@ void Interface::_handle_eth(void *const eth_base,
|
||||
catch (Bad_dhcp_request) {
|
||||
error("bad DHCP request"); }
|
||||
|
||||
catch (Alloc_dhcp_reply_buffer_failed) {
|
||||
catch (Alloc_dhcp_msg_buffer_failed) {
|
||||
error("failed to allocate buffer for DHCP reply"); }
|
||||
|
||||
catch (Dhcp_reply_buffer_too_small) {
|
||||
catch (Dhcp_msg_buffer_too_small) {
|
||||
error("DHCP reply buffer too small"); }
|
||||
|
||||
catch (Dhcp_server::Alloc_ip_failed) {
|
||||
@ -877,7 +869,7 @@ void Interface::_handle_eth(void *const eth_base,
|
||||
}
|
||||
|
||||
|
||||
void Interface::_send(Ethernet_frame ð, Genode::size_t const size)
|
||||
void Interface::send(Ethernet_frame ð, Genode::size_t const size)
|
||||
{
|
||||
if (_config().verbose()) {
|
||||
log("\033[33m(", _domain, " <- router)\033[0m ", eth); }
|
||||
@ -919,6 +911,14 @@ Interface::Interface(Entrypoint &ep,
|
||||
}
|
||||
|
||||
|
||||
void Interface::_init()
|
||||
{
|
||||
if (!_domain.ip_config().valid) {
|
||||
_dhcp_client.discover();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Interface::_ack_packet(Packet_descriptor const &pkt)
|
||||
{
|
||||
if (!_sink().ready_to_ack()) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <arp_cache.h>
|
||||
#include <arp_waiter.h>
|
||||
#include <l3_protocol.h>
|
||||
#include <dhcp_client.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <nic_session/nic_session.h>
|
||||
@ -120,6 +121,8 @@ class Net::Interface
|
||||
Mac_address const _router_mac;
|
||||
Mac_address const _mac;
|
||||
|
||||
void _init();
|
||||
|
||||
private:
|
||||
|
||||
Timer::Connection &_timer;
|
||||
@ -134,6 +137,7 @@ class Net::Interface
|
||||
Link_list _closed_udp_links;
|
||||
Ip_allocation_tree _ip_allocations;
|
||||
Ip_allocation_list _released_ip_allocations;
|
||||
Dhcp_client _dhcp_client { _alloc, _timer, *this };
|
||||
|
||||
void _new_link(L3_protocol const protocol,
|
||||
Link_side_id const &local_id,
|
||||
@ -190,8 +194,6 @@ class Net::Interface
|
||||
|
||||
void _broadcast_arp_request(Ipv4_address const &ip);
|
||||
|
||||
void _send(Ethernet_frame ð, Genode::size_t const eth_size);
|
||||
|
||||
void _pass_prot(Ethernet_frame ð,
|
||||
Genode::size_t const eth_size,
|
||||
Ipv4_packet &ip,
|
||||
@ -239,12 +241,20 @@ class Net::Interface
|
||||
|
||||
public:
|
||||
|
||||
struct Bad_transport_protocol : Genode::Exception { };
|
||||
struct Bad_network_protocol : Genode::Exception { };
|
||||
struct Packet_postponed : Genode::Exception { };
|
||||
struct Bad_dhcp_request : Genode::Exception { };
|
||||
struct Alloc_dhcp_reply_buffer_failed : Genode::Exception { };
|
||||
struct Dhcp_reply_buffer_too_small : Genode::Exception { };
|
||||
struct Bad_send_dhcp_args : Genode::Exception { };
|
||||
struct Bad_transport_protocol : Genode::Exception { };
|
||||
struct Bad_network_protocol : Genode::Exception { };
|
||||
struct Packet_postponed : Genode::Exception { };
|
||||
struct Bad_dhcp_request : Genode::Exception { };
|
||||
struct Alloc_dhcp_msg_buffer_failed : Genode::Exception { };
|
||||
struct Dhcp_msg_buffer_too_small : Genode::Exception { };
|
||||
|
||||
struct Packet_ignored : Genode::Exception
|
||||
{
|
||||
char const *reason;
|
||||
|
||||
Packet_ignored(char const *reason) : reason(reason) { }
|
||||
};
|
||||
|
||||
Interface(Genode::Entrypoint &ep,
|
||||
Timer::Connection &timer,
|
||||
@ -261,6 +271,8 @@ class Net::Interface
|
||||
|
||||
void dissolve_link(Link_side &link_side, L3_protocol const prot);
|
||||
|
||||
void send(Ethernet_frame ð, Genode::size_t const eth_size);
|
||||
|
||||
|
||||
/*********
|
||||
** log **
|
||||
@ -273,6 +285,8 @@ class Net::Interface
|
||||
** Accessors **
|
||||
***************/
|
||||
|
||||
Domain &domain() { return _domain; }
|
||||
Mac_address router_mac() const { return _router_mac; }
|
||||
Arp_waiter_list &own_arp_waiters() { return _own_arp_waiters; }
|
||||
Arp_waiter_list &foreign_arp_waiters() { return _foreign_arp_waiters; }
|
||||
};
|
||||
|
@ -77,3 +77,27 @@ Ipv4_address Ipv4_address_prefix::broadcast_address() const
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Ipv4_address_prefix::Ipv4_address_prefix(Ipv4_address address,
|
||||
Ipv4_address subnet_mask)
|
||||
:
|
||||
address(address)
|
||||
{
|
||||
Genode::uint8_t rest;
|
||||
if (subnet_mask.addr[0] != 0xff) {
|
||||
rest = subnet_mask.addr[0];
|
||||
prefix = 0;
|
||||
} else if (subnet_mask.addr[1] != 0xff) {
|
||||
rest = subnet_mask.addr[1];
|
||||
prefix = 8;
|
||||
} else if (subnet_mask.addr[2] != 0xff) {
|
||||
rest = subnet_mask.addr[2];
|
||||
prefix = 16;
|
||||
} else {
|
||||
rest = subnet_mask.addr[3];
|
||||
prefix = 24;
|
||||
}
|
||||
for (Genode::uint8_t mask = 1 << 7; rest & mask; mask >>= 1)
|
||||
prefix++;
|
||||
}
|
||||
|
@ -23,7 +23,12 @@ namespace Net { class Ipv4_address_prefix; }
|
||||
struct Net::Ipv4_address_prefix
|
||||
{
|
||||
Ipv4_address address;
|
||||
Genode::uint8_t prefix { 32 };
|
||||
Genode::uint8_t prefix;
|
||||
|
||||
Ipv4_address_prefix(Ipv4_address address,
|
||||
Ipv4_address subnet_mask);
|
||||
|
||||
Ipv4_address_prefix() : prefix(32) { }
|
||||
|
||||
bool valid() const { return address.valid() || prefix == 0; }
|
||||
|
||||
|
45
repos/os/src/server/nic_router/size_guard.h
Normal file
45
repos/os/src/server/nic_router/size_guard.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* \brief Utility to ensure that a size value doesn't exceed a limit
|
||||
* \author Martin Stein
|
||||
* \date 2016-08-24
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SIZE_GUARD_H_
|
||||
#define _SIZE_GUARD_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/stdint.h>
|
||||
|
||||
namespace Genode {
|
||||
|
||||
template <size_t MAX, typename EXCEPTION>
|
||||
class Size_guard_tpl;
|
||||
}
|
||||
|
||||
template <Genode::size_t MAX, typename EXCEPTION>
|
||||
class Genode::Size_guard_tpl
|
||||
{
|
||||
private:
|
||||
|
||||
size_t _curr { 0 };
|
||||
|
||||
public:
|
||||
|
||||
void add(size_t size)
|
||||
{
|
||||
size_t const new_size = _curr + size;
|
||||
if (new_size > MAX) { throw EXCEPTION(); }
|
||||
_curr = new_size;
|
||||
}
|
||||
|
||||
size_t curr() const { return _curr; }
|
||||
};
|
||||
|
||||
#endif /* _SIZE_GUARD_H_ */
|
@ -8,5 +8,6 @@ SRC_CC += nat_rule.cc mac_allocator.cc main.cc ipv4_config.cc
|
||||
SRC_CC += uplink.cc interface.cc arp_cache.cc configuration.cc
|
||||
SRC_CC += domain.cc l3_protocol.cc direct_rule.cc link.cc
|
||||
SRC_CC += transport_rule.cc leaf_rule.cc permit_rule.cc
|
||||
SRC_CC += dhcp_client.cc
|
||||
|
||||
INC_DIR += $(PRG_DIR)
|
||||
|
@ -37,4 +37,5 @@ Net::Uplink::Uplink(Env &env,
|
||||
rx_channel()->sigh_packet_avail(_sink_submit);
|
||||
tx_channel()->sigh_ack_avail(_source_ack);
|
||||
tx_channel()->sigh_ready_to_submit(_source_submit);
|
||||
Interface::_init();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user