mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-03 17:23:05 +00:00
ping: dynamic IP configuration
Use DHCP to obtain and maintain an IP configuration if no static configuration is given. Issue #2775
This commit is contained in:
parent
ce57319e4b
commit
d93fda594a
@ -57,8 +57,7 @@ append config {
|
|||||||
</start>
|
</start>
|
||||||
<start name="ping">
|
<start name="ping">
|
||||||
<resource name="RAM" quantum="8M"/>
|
<resource name="RAM" quantum="8M"/>
|
||||||
<config src_ip="10.0.2.55"
|
<config dst_ip="10.0.2.2"
|
||||||
dst_ip="10.0.2.2"
|
|
||||||
period_sec="1"
|
period_sec="1"
|
||||||
verbose="no"
|
verbose="no"
|
||||||
count="3"/>
|
count="3"/>
|
||||||
@ -96,4 +95,4 @@ append_if [have_spec lan9118] qemu_args " -net nic,model=lan9118 "
|
|||||||
|
|
||||||
append qemu_args " -net user -nographic "
|
append qemu_args " -net user -nographic "
|
||||||
|
|
||||||
run_genode_until "\"ping\" exited with exit value 0.*\n" 25
|
run_genode_until ".*\"ping\" exited with exit value 0.*\n" 25
|
||||||
|
@ -119,7 +119,8 @@ append config {
|
|||||||
<start name="ping_11">
|
<start name="ping_11">
|
||||||
<binary name="ping"/>
|
<binary name="ping"/>
|
||||||
<resource name="RAM" quantum="8M"/>
|
<resource name="RAM" quantum="8M"/>
|
||||||
<config src_ip="10.0.3.2"
|
<config interface="10.0.3.2/24"
|
||||||
|
gateway="10.0.3.1"
|
||||||
dst_ip="10.0.2.2"
|
dst_ip="10.0.2.2"
|
||||||
period_sec="1"
|
period_sec="1"
|
||||||
verbose="no"/>
|
verbose="no"/>
|
||||||
@ -132,7 +133,8 @@ append config {
|
|||||||
<start name="ping_12">
|
<start name="ping_12">
|
||||||
<binary name="ping"/>
|
<binary name="ping"/>
|
||||||
<resource name="RAM" quantum="8M"/>
|
<resource name="RAM" quantum="8M"/>
|
||||||
<config src_ip="10.0.3.3"
|
<config interface="10.0.3.3/24"
|
||||||
|
gateway="10.0.3.1"
|
||||||
dst_ip="10.0.2.2"
|
dst_ip="10.0.2.2"
|
||||||
period_sec="1"
|
period_sec="1"
|
||||||
verbose="no"/>
|
verbose="no"/>
|
||||||
@ -145,7 +147,8 @@ append config {
|
|||||||
<start name="ping_21">
|
<start name="ping_21">
|
||||||
<binary name="ping"/>
|
<binary name="ping"/>
|
||||||
<resource name="RAM" quantum="8M"/>
|
<resource name="RAM" quantum="8M"/>
|
||||||
<config src_ip="10.0.5.2"
|
<config interface="10.0.5.2/24"
|
||||||
|
gateway="10.0.5.1"
|
||||||
dst_ip="10.0.2.2"
|
dst_ip="10.0.2.2"
|
||||||
period_sec="1"
|
period_sec="1"
|
||||||
verbose="no"/>
|
verbose="no"/>
|
||||||
@ -158,7 +161,8 @@ append config {
|
|||||||
<start name="ping_22">
|
<start name="ping_22">
|
||||||
<binary name="ping"/>
|
<binary name="ping"/>
|
||||||
<resource name="RAM" quantum="8M"/>
|
<resource name="RAM" quantum="8M"/>
|
||||||
<config src_ip="10.0.5.3"
|
<config interface="10.0.5.3/24"
|
||||||
|
gateway="10.0.5.1"
|
||||||
dst_ip="10.0.1.2"
|
dst_ip="10.0.1.2"
|
||||||
period_sec="1"
|
period_sec="1"
|
||||||
verbose="no"/>
|
verbose="no"/>
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
The 'ping' component continuously sends ICMP Echo requests to a given IP host
|
The 'ping' component continuously sends ICMP Echo requests to a given IP host
|
||||||
and waits for the corresponding ICMP Echo replies. For each successfull ICMP
|
and waits for the corresponding ICMP Echo replies. For each successfull ICMP
|
||||||
Echo handshake it prints a short statistic. By now, it can be used only with a
|
Echo handshake it prints a short statistic. The ICMP data field gets filled
|
||||||
static IP configuration. The ICMP data field gets filled with the letters of
|
with the letters of the alphabet ('a' to 'z') repeatedly.
|
||||||
the alphabet ('a' to 'z') repeatedly.
|
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
This is an example configuration of the component which shows the default
|
This is an example configuration of the component which shows the default
|
||||||
value for each attribute except 'config.dst_ip' and 'config.src_ip':
|
value for each attribute except 'config.dst_ip' and 'config.interface':
|
||||||
|
|
||||||
! <config src_ip="10.0.0.72"
|
! <config interface="10.0.0.72/24"
|
||||||
! dst_ip="10.0.0.24"
|
! dst_ip="10.0.0.24"
|
||||||
! period_sec="5"
|
! period_sec="5"
|
||||||
! verbose="no"
|
! verbose="no"
|
||||||
@ -19,8 +18,12 @@ value for each attribute except 'config.dst_ip' and 'config.src_ip':
|
|||||||
|
|
||||||
This is a short description of the tags and attributes:
|
This is a short description of the tags and attributes:
|
||||||
|
|
||||||
:config.src_ip:
|
:config.interface:
|
||||||
Mandatory. IP address of the component.
|
Optional. IP address and subnet of the component. If not set, the component
|
||||||
|
requests and maintains the IP configuration via DHCP.
|
||||||
|
|
||||||
|
:config.gateway:
|
||||||
|
Optional. IP address of the gateway of the IP subnet.
|
||||||
|
|
||||||
:config.dst_ip:
|
:config.dst_ip:
|
||||||
Mandatory. IP address of the target host.
|
Mandatory. IP address of the target host.
|
||||||
|
@ -25,11 +25,18 @@
|
|||||||
</xs:restriction>
|
</xs:restriction>
|
||||||
</xs:simpleType><!-- Ipv4_address -->
|
</xs:simpleType><!-- Ipv4_address -->
|
||||||
|
|
||||||
|
<xs:simpleType name="Ipv4_address_prefix">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:pattern value="[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2}"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType><!-- Ipv4_address_prefix -->
|
||||||
|
|
||||||
<xs:element name="config">
|
<xs:element name="config">
|
||||||
<xs:complexType>
|
<xs:complexType>
|
||||||
<xs:attribute name="verbose" type="Boolean" />
|
<xs:attribute name="verbose" type="Boolean" />
|
||||||
<xs:attribute name="dst_ip" type="Ipv4_address" />
|
<xs:attribute name="dst_ip" type="Ipv4_address" />
|
||||||
<xs:attribute name="src_ip" type="Ipv4_address" />
|
<xs:attribute name="interface" type="Ipv4_address_prefix" />
|
||||||
|
<xs:attribute name="gateway" type="Ipv4_address" />
|
||||||
<xs:attribute name="period_sec" type="Seconds" />
|
<xs:attribute name="period_sec" type="Seconds" />
|
||||||
<xs:attribute name="count" type="xs:positiveInteger" />
|
<xs:attribute name="count" type="xs:positiveInteger" />
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
267
repos/os/src/app/ping/dhcp_client.cc
Normal file
267
repos/os/src/app/ping/dhcp_client.cc
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
* \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 <nic.h>
|
||||||
|
#include <dhcp_client.h>
|
||||||
|
#include <ipv4_config.h>
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/xml_node.h>
|
||||||
|
|
||||||
|
enum { PKT_SIZE = 1024 };
|
||||||
|
|
||||||
|
struct Send_buffer_too_small : Genode::Exception { };
|
||||||
|
struct Bad_send_dhcp_args : Genode::Exception { };
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
using namespace Net;
|
||||||
|
using Message_type = Dhcp_packet::Message_type;
|
||||||
|
using Dhcp_options = Dhcp_packet::Options_aggregator<Size_guard>;
|
||||||
|
|
||||||
|
|
||||||
|
/***************
|
||||||
|
** Utilities **
|
||||||
|
***************/
|
||||||
|
|
||||||
|
void append_param_req_list(Dhcp_options &dhcp_opts)
|
||||||
|
{
|
||||||
|
dhcp_opts.append_param_req_list([&] (Dhcp_options::Parameter_request_list_data &data) {
|
||||||
|
data.append_param_req<Dhcp_packet::Message_type_option>();
|
||||||
|
data.append_param_req<Dhcp_packet::Server_ipv4>();
|
||||||
|
data.append_param_req<Dhcp_packet::Ip_lease_time>();
|
||||||
|
data.append_param_req<Dhcp_packet::Dns_server_ipv4>();
|
||||||
|
data.append_param_req<Dhcp_packet::Subnet_mask>();
|
||||||
|
data.append_param_req<Dhcp_packet::Router_ipv4>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*****************
|
||||||
|
** Dhcp_client **
|
||||||
|
*****************/
|
||||||
|
|
||||||
|
Dhcp_client::Dhcp_client(Genode::Allocator &alloc,
|
||||||
|
Timer::Connection &timer,
|
||||||
|
Nic &nic,
|
||||||
|
Dhcp_client_handler &handler)
|
||||||
|
:
|
||||||
|
_alloc (alloc),
|
||||||
|
_timeout (timer, *this, &Dhcp_client::_handle_timeout),
|
||||||
|
_nic (nic),
|
||||||
|
_handler (handler)
|
||||||
|
{
|
||||||
|
_discover();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Dhcp_client::_discover()
|
||||||
|
{
|
||||||
|
_set_state(State::SELECT, _discover_timeout);
|
||||||
|
_send(Message_type::DISCOVER, Ipv4_address(), Ipv4_address(),
|
||||||
|
Ipv4_address());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Dhcp_client::_rerequest(State next_state)
|
||||||
|
{
|
||||||
|
_set_state(next_state, _rerequest_timeout(2));
|
||||||
|
Ipv4_address const client_ip = _handler.ip_config().interface.address;
|
||||||
|
_send(Message_type::REQUEST, client_ip, Ipv4_address(), client_ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
default: _discover();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Dhcp_client::handle_eth(Ethernet_frame ð, Size_guard &size_guard)
|
||||||
|
{
|
||||||
|
if (eth.dst() != _nic.mac() &&
|
||||||
|
eth.dst() != Mac_address(0xff))
|
||||||
|
{
|
||||||
|
throw Drop_packet_inform("DHCP client expects Ethernet targeting the router");
|
||||||
|
}
|
||||||
|
Ipv4_packet &ip = eth.data<Ipv4_packet>(size_guard);
|
||||||
|
if (ip.protocol() != Ipv4_packet::Protocol::UDP) {
|
||||||
|
throw Drop_packet_inform("DHCP client expects UDP packet"); }
|
||||||
|
|
||||||
|
Udp_packet &udp = ip.data<Udp_packet>(size_guard);
|
||||||
|
if (!Dhcp_packet::is_dhcp(&udp)) {
|
||||||
|
throw Drop_packet_inform("DHCP client expects DHCP packet"); }
|
||||||
|
|
||||||
|
Dhcp_packet &dhcp = udp.data<Dhcp_packet>(size_guard);
|
||||||
|
if (dhcp.op() != Dhcp_packet::REPLY) {
|
||||||
|
throw Drop_packet_inform("DHCP client expects DHCP reply"); }
|
||||||
|
|
||||||
|
if (dhcp.client_mac() != _nic.mac()) {
|
||||||
|
throw Drop_packet_inform("DHCP client expects DHCP targeting the router"); }
|
||||||
|
|
||||||
|
try { _handle_dhcp_reply(dhcp); }
|
||||||
|
catch (Dhcp_packet::Option_not_found) {
|
||||||
|
throw Drop_packet_inform("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 Drop_packet_inform("DHCP client expects an offer");
|
||||||
|
}
|
||||||
|
_set_state(State::REQUEST, _request_timeout);
|
||||||
|
_send(Message_type::REQUEST, Ipv4_address(),
|
||||||
|
dhcp.option<Dhcp_packet::Server_ipv4>().value(),
|
||||||
|
dhcp.yiaddr());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case State::REQUEST:
|
||||||
|
{
|
||||||
|
if (msg_type != Message_type::ACK) {
|
||||||
|
throw Drop_packet_inform("DHCP client expects an acknowledgement");
|
||||||
|
}
|
||||||
|
_lease_time_sec = dhcp.option<Dhcp_packet::Ip_lease_time>().value();
|
||||||
|
_set_state(State::BOUND, _rerequest_timeout(1));
|
||||||
|
Ipv4_address dns_server;
|
||||||
|
try { dns_server = dhcp.option<Dhcp_packet::Dns_server_ipv4>().value(); }
|
||||||
|
catch (Dhcp_packet::Option_not_found) { }
|
||||||
|
|
||||||
|
Ipv4_config ip_config(
|
||||||
|
Ipv4_address_prefix(
|
||||||
|
dhcp.yiaddr(),
|
||||||
|
dhcp.option<Dhcp_packet::Subnet_mask>().value()),
|
||||||
|
dhcp.option<Dhcp_packet::Router_ipv4>().value(),
|
||||||
|
dns_server);
|
||||||
|
|
||||||
|
_handler.ip_config(ip_config);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case State::RENEW:
|
||||||
|
case State::REBIND:
|
||||||
|
|
||||||
|
if (msg_type != Message_type::ACK) {
|
||||||
|
throw Drop_packet_inform("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 Drop_packet_inform("DHCP client doesn't expect a packet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Dhcp_client::_send(Message_type msg_type,
|
||||||
|
Ipv4_address client_ip,
|
||||||
|
Ipv4_address server_ip,
|
||||||
|
Ipv4_address requested_ip)
|
||||||
|
{
|
||||||
|
_nic.send(PKT_SIZE, [&] (void *pkt_base, Size_guard &size_guard) {
|
||||||
|
|
||||||
|
/* create ETH header of the request */
|
||||||
|
Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
|
||||||
|
eth.dst(Mac_address(0xff));
|
||||||
|
eth.src(_nic.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_guard.head_size();
|
||||||
|
Ipv4_packet &ip = eth.construct_at_data<Ipv4_packet>(size_guard);
|
||||||
|
ip.header_length(sizeof(Ipv4_packet) / 4);
|
||||||
|
ip.version(4);
|
||||||
|
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_guard.head_size();
|
||||||
|
Udp_packet &udp = ip.construct_at_data<Udp_packet>(size_guard);
|
||||||
|
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_guard.head_size();
|
||||||
|
Dhcp_packet &dhcp = udp.construct_at_data<Dhcp_packet>(size_guard);
|
||||||
|
dhcp.op(Dhcp_packet::REQUEST);
|
||||||
|
dhcp.htype(Dhcp_packet::Htype::ETH);
|
||||||
|
dhcp.hlen(sizeof(Mac_address));
|
||||||
|
dhcp.ciaddr(client_ip);
|
||||||
|
dhcp.client_mac(_nic.mac());
|
||||||
|
dhcp.default_magic_cookie();
|
||||||
|
|
||||||
|
/* append DHCP option fields to the request */
|
||||||
|
Dhcp_options dhcp_opts(dhcp, size_guard);
|
||||||
|
dhcp_opts.append_option<Dhcp_packet::Message_type_option>(msg_type);
|
||||||
|
switch (msg_type) {
|
||||||
|
case Message_type::DISCOVER:
|
||||||
|
append_param_req_list(dhcp_opts);
|
||||||
|
dhcp_opts.append_option<Dhcp_packet::Client_id>(_nic.mac());
|
||||||
|
dhcp_opts.append_option<Dhcp_packet::Max_msg_size>(PKT_SIZE - dhcp_off);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Message_type::REQUEST:
|
||||||
|
append_param_req_list(dhcp_opts);
|
||||||
|
dhcp_opts.append_option<Dhcp_packet::Client_id>(_nic.mac());
|
||||||
|
dhcp_opts.append_option<Dhcp_packet::Max_msg_size>(PKT_SIZE - dhcp_off);
|
||||||
|
if (_state == State::REQUEST) {
|
||||||
|
dhcp_opts.append_option<Dhcp_packet::Requested_addr>(requested_ip);
|
||||||
|
dhcp_opts.append_option<Dhcp_packet::Server_ipv4>(server_ip);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw 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_guard.head_size() - udp_off);
|
||||||
|
udp.update_checksum(ip.src(), ip.dst());
|
||||||
|
ip.total_length(size_guard.head_size() - ip_off);
|
||||||
|
ip.update_checksum();
|
||||||
|
});
|
||||||
|
}
|
107
repos/os/src/app/ping/dhcp_client.h
Normal file
107
repos/os/src/app/ping/dhcp_client.h
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* \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 <net/dhcp.h>
|
||||||
|
#include <timer_session/connection.h>
|
||||||
|
|
||||||
|
namespace Net {
|
||||||
|
|
||||||
|
/* external definition */
|
||||||
|
class Nic_peer;
|
||||||
|
class Ipv4_config;
|
||||||
|
class Ethernet_frame;
|
||||||
|
|
||||||
|
/* local definition */
|
||||||
|
class Dhcp_client;
|
||||||
|
class Dhcp_client_handler;
|
||||||
|
class Drop_packet_inform;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Net::Drop_packet_inform : Genode::Exception
|
||||||
|
{
|
||||||
|
char const *msg;
|
||||||
|
|
||||||
|
Drop_packet_inform(char const *msg) : msg(msg) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Net::Dhcp_client_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual void ip_config(Ipv4_config const &ip_config) = 0;
|
||||||
|
|
||||||
|
virtual Ipv4_config const &ip_config() const = 0;
|
||||||
|
|
||||||
|
virtual ~Dhcp_client_handler() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Net::Dhcp_client
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
enum class State
|
||||||
|
{
|
||||||
|
INIT = 0, SELECT = 1, REQUEST = 2, BOUND = 3, RENEW = 4, REBIND = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
enum { DISCOVER_TIMEOUT_SEC = 10 };
|
||||||
|
enum { REQUEST_TIMEOUT_SEC = 10 };
|
||||||
|
enum { OFFER_TIMEOUT_SEC = 10 };
|
||||||
|
|
||||||
|
Genode::Allocator &_alloc;
|
||||||
|
State _state { State::INIT };
|
||||||
|
Timer::One_shot_timeout<Dhcp_client> _timeout;
|
||||||
|
unsigned long _lease_time_sec = 0;
|
||||||
|
Genode::Microseconds const _discover_timeout { DISCOVER_TIMEOUT_SEC * 1000 * 1000 };
|
||||||
|
Genode::Microseconds const _request_timeout { REQUEST_TIMEOUT_SEC * 1000 * 1000 };
|
||||||
|
Genode::Microseconds const _offer_timeout { OFFER_TIMEOUT_SEC * 1000 * 1000 };
|
||||||
|
Nic &_nic;
|
||||||
|
Dhcp_client_handler &_handler;
|
||||||
|
|
||||||
|
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,
|
||||||
|
Ipv4_address requested_ip);
|
||||||
|
|
||||||
|
void _discover();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Dhcp_client(Genode::Allocator &alloc,
|
||||||
|
Timer::Connection &timer,
|
||||||
|
Nic &nic,
|
||||||
|
Dhcp_client_handler &handler);
|
||||||
|
|
||||||
|
void handle_eth(Ethernet_frame ð,
|
||||||
|
Size_guard &size_guard);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _DHCP_CLIENT_H_ */
|
103
repos/os/src/app/ping/ipv4_address_prefix.cc
Normal file
103
repos/os/src/app/ping/ipv4_address_prefix.cc
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* \brief Ipv4 address combined with a subnet prefix length
|
||||||
|
* \author Martin Stein
|
||||||
|
* \date 2017-10-12
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 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 <ipv4_address_prefix.h>
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
using namespace Net;
|
||||||
|
|
||||||
|
|
||||||
|
Ipv4_address Ipv4_address_prefix::subnet_mask() const
|
||||||
|
{
|
||||||
|
Ipv4_address result;
|
||||||
|
if (prefix >= 8) {
|
||||||
|
|
||||||
|
result.addr[0] = 0xff;
|
||||||
|
|
||||||
|
if (prefix >= 16) {
|
||||||
|
|
||||||
|
result.addr[1] = 0xff;
|
||||||
|
|
||||||
|
if (prefix >= 24) {
|
||||||
|
|
||||||
|
result.addr[2] = 0xff;
|
||||||
|
result.addr[3] = 0xff << (32 - prefix);
|
||||||
|
} else {
|
||||||
|
result.addr[2] = 0xff << (24 - prefix);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.addr[1] = 0xff << (16 - prefix);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.addr[0] = 0xff << (8 - prefix);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Ipv4_address_prefix::print(Genode::Output &output) const
|
||||||
|
{
|
||||||
|
Genode::print(output, address, "/", prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Ipv4_address_prefix::prefix_matches(Ipv4_address const &ip) const
|
||||||
|
{
|
||||||
|
uint8_t prefix_left = prefix;
|
||||||
|
uint8_t byte = 0;
|
||||||
|
for (; prefix_left >= 8; prefix_left -= 8, byte++) {
|
||||||
|
if (ip.addr[byte] != address.addr[byte]) {
|
||||||
|
return false; }
|
||||||
|
}
|
||||||
|
if (prefix_left == 0) {
|
||||||
|
return true; }
|
||||||
|
|
||||||
|
uint8_t const mask = ~(0xff >> prefix_left);
|
||||||
|
return !((ip.addr[byte] ^ address.addr[byte]) & mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Ipv4_address Ipv4_address_prefix::broadcast_address() const
|
||||||
|
{
|
||||||
|
Ipv4_address result = address;
|
||||||
|
Ipv4_address const mask = subnet_mask();
|
||||||
|
for (unsigned i = 0; i < 4; i++) {
|
||||||
|
result.addr[i] |= ~mask.addr[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Ipv4_address_prefix::Ipv4_address_prefix(Ipv4_address address,
|
||||||
|
Ipv4_address subnet_mask)
|
||||||
|
:
|
||||||
|
address(address), prefix(0)
|
||||||
|
{
|
||||||
|
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++;
|
||||||
|
}
|
82
repos/os/src/app/ping/ipv4_address_prefix.h
Normal file
82
repos/os/src/app/ping/ipv4_address_prefix.h
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* \brief Ipv4 address combined with a subnet prefix length
|
||||||
|
* \author Martin Stein
|
||||||
|
* \date 2017-10-12
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 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 _IPV4_ADDRESS_PREFIX_H_
|
||||||
|
#define _IPV4_ADDRESS_PREFIX_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <net/ipv4.h>
|
||||||
|
|
||||||
|
namespace Net { class Ipv4_address_prefix; }
|
||||||
|
|
||||||
|
|
||||||
|
struct Net::Ipv4_address_prefix
|
||||||
|
{
|
||||||
|
Ipv4_address address { };
|
||||||
|
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; }
|
||||||
|
|
||||||
|
void print(Genode::Output &output) const;
|
||||||
|
|
||||||
|
bool prefix_matches(Ipv4_address const &ip) const;
|
||||||
|
|
||||||
|
Ipv4_address subnet_mask() const;
|
||||||
|
|
||||||
|
Ipv4_address broadcast_address() const;
|
||||||
|
|
||||||
|
bool operator != (Ipv4_address_prefix const &other) const
|
||||||
|
{
|
||||||
|
return prefix != other.prefix ||
|
||||||
|
address != other.address;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace Genode {
|
||||||
|
|
||||||
|
inline size_t ascii_to(char const *s, Net::Ipv4_address_prefix &result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Genode::size_t Genode::ascii_to(char const *s, Net::Ipv4_address_prefix &result)
|
||||||
|
{
|
||||||
|
/* read the leading IPv4 address, fail if there's no address */
|
||||||
|
Net::Ipv4_address_prefix buf;
|
||||||
|
size_t read_len = ascii_to(s, buf.address);
|
||||||
|
if (!read_len) {
|
||||||
|
return 0; }
|
||||||
|
|
||||||
|
/* check for the following slash */
|
||||||
|
s += read_len;
|
||||||
|
if (*s != '/') {
|
||||||
|
return 0; }
|
||||||
|
read_len++;
|
||||||
|
s++;
|
||||||
|
|
||||||
|
/* read the prefix, fail if there's no prefix */
|
||||||
|
size_t prefix_len = ascii_to_unsigned(s, buf.prefix, 10);
|
||||||
|
if (!prefix_len) {
|
||||||
|
return 0; }
|
||||||
|
|
||||||
|
/* fill result and return read length */
|
||||||
|
result = buf;
|
||||||
|
return read_len + prefix_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _IPV4_ADDRESS_PREFIX_H_ */
|
43
repos/os/src/app/ping/ipv4_config.cc
Normal file
43
repos/os/src/app/ping/ipv4_config.cc
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* \brief IPv4 peer configuration
|
||||||
|
* \author Martin Stein
|
||||||
|
* \date 2016-08-19
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <base/log.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <ipv4_config.h>
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
using namespace Net;
|
||||||
|
|
||||||
|
Ipv4_config::Ipv4_config(Ipv4_address_prefix interface,
|
||||||
|
Ipv4_address gateway,
|
||||||
|
Ipv4_address dns_server)
|
||||||
|
:
|
||||||
|
interface(interface), gateway(gateway), dns_server(dns_server)
|
||||||
|
{
|
||||||
|
if (!valid && (interface_valid || gateway_valid)) {
|
||||||
|
error("Bad IP configuration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Ipv4_config::print(Genode::Output &output) const
|
||||||
|
{
|
||||||
|
Genode::print(output, "interface ", interface);
|
||||||
|
if (gateway.valid()) {
|
||||||
|
Genode::print(output, ", gateway ", gateway); }
|
||||||
|
|
||||||
|
if (dns_server.valid()) {
|
||||||
|
Genode::print(output, ", DNS server ", dns_server); }
|
||||||
|
}
|
49
repos/os/src/app/ping/ipv4_config.h
Normal file
49
repos/os/src/app/ping/ipv4_config.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* \brief IPv4 peer configuration
|
||||||
|
* \author Martin Stein
|
||||||
|
* \date 2016-08-19
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _IPV4_CONFIG_H_
|
||||||
|
#define _IPV4_CONFIG_H_
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <ipv4_address_prefix.h>
|
||||||
|
|
||||||
|
namespace Net { class Ipv4_config; }
|
||||||
|
|
||||||
|
struct Net::Ipv4_config
|
||||||
|
{
|
||||||
|
Ipv4_address_prefix const interface { };
|
||||||
|
bool const interface_valid { interface.valid() };
|
||||||
|
Ipv4_address const gateway { };
|
||||||
|
bool const gateway_valid { gateway.valid() };
|
||||||
|
Ipv4_address const dns_server { };
|
||||||
|
bool const valid { interface_valid &&
|
||||||
|
(!gateway_valid ||
|
||||||
|
interface.prefix_matches(gateway)) };
|
||||||
|
|
||||||
|
Ipv4_config(Ipv4_address_prefix interface,
|
||||||
|
Ipv4_address gateway,
|
||||||
|
Ipv4_address dns_server);
|
||||||
|
|
||||||
|
Ipv4_config() { }
|
||||||
|
|
||||||
|
bool operator != (Ipv4_config const &other) const
|
||||||
|
{
|
||||||
|
return interface != other.interface ||
|
||||||
|
gateway != other.gateway ||
|
||||||
|
dns_server != other.dns_server;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(Genode::Output &output) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _IPV4_CONFIG_H_ */
|
@ -11,6 +11,11 @@
|
|||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <nic.h>
|
||||||
|
#include <ipv4_config.h>
|
||||||
|
#include <dhcp_client.h>
|
||||||
|
|
||||||
/* Genode includes */
|
/* Genode includes */
|
||||||
#include <net/ipv4.h>
|
#include <net/ipv4.h>
|
||||||
#include <net/ethernet.h>
|
#include <net/ethernet.h>
|
||||||
@ -20,18 +25,10 @@
|
|||||||
#include <base/heap.h>
|
#include <base/heap.h>
|
||||||
#include <base/attached_rom_dataspace.h>
|
#include <base/attached_rom_dataspace.h>
|
||||||
#include <timer_session/connection.h>
|
#include <timer_session/connection.h>
|
||||||
#include <nic_session/connection.h>
|
|
||||||
#include <nic/packet_allocator.h>
|
|
||||||
|
|
||||||
using namespace Net;
|
using namespace Net;
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
|
||||||
namespace Net {
|
|
||||||
|
|
||||||
using Packet_descriptor = ::Nic::Packet_descriptor;
|
|
||||||
using Packet_stream_sink = ::Nic::Packet_stream_sink< ::Nic::Session::Policy>;
|
|
||||||
using Packet_stream_source = ::Nic::Packet_stream_source< ::Nic::Session::Policy>;
|
|
||||||
}
|
|
||||||
|
|
||||||
Microseconds read_sec_attr(Xml_node const node,
|
Microseconds read_sec_attr(Xml_node const node,
|
||||||
char const *name,
|
char const *name,
|
||||||
@ -45,7 +42,8 @@ Microseconds read_sec_attr(Xml_node const node,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Main
|
class Main : public Nic_handler,
|
||||||
|
public Dhcp_client_handler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -57,34 +55,26 @@ class Main
|
|||||||
enum { ICMP_DATA_SIZE = 56 };
|
enum { ICMP_DATA_SIZE = 56 };
|
||||||
enum { DEFAULT_COUNT = 5 };
|
enum { DEFAULT_COUNT = 5 };
|
||||||
enum { DEFAULT_PERIOD_SEC = 5 };
|
enum { DEFAULT_PERIOD_SEC = 5 };
|
||||||
enum { PKT_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE };
|
|
||||||
enum { BUF_SIZE = Nic::Session::QUEUE_SIZE * PKT_SIZE };
|
|
||||||
|
|
||||||
Env &_env;
|
Env &_env;
|
||||||
Attached_rom_dataspace _config_rom { _env, "config" };
|
Attached_rom_dataspace _config_rom { _env, "config" };
|
||||||
Xml_node _config { _config_rom.xml() };
|
Xml_node _config { _config_rom.xml() };
|
||||||
Timer::Connection _timer { _env };
|
Timer::Connection _timer { _env };
|
||||||
Microseconds _send_time { 0 };
|
Microseconds _send_time { 0 };
|
||||||
Periodic_timeout _period { _timer, *this, &Main::_send_ping,
|
Microseconds _period_us { read_sec_attr(_config, "period_sec", DEFAULT_PERIOD_SEC) };
|
||||||
read_sec_attr(_config, "period_sec", DEFAULT_PERIOD_SEC) };
|
Constructible<Periodic_timeout> _period { };
|
||||||
Heap _heap { &_env.ram(), &_env.rm() };
|
Heap _heap { &_env.ram(), &_env.rm() };
|
||||||
Nic::Packet_allocator _pkt_alloc { &_heap };
|
bool const _verbose { _config.attribute_value("verbose", false) };
|
||||||
Nic::Connection _nic { _env, &_pkt_alloc, BUF_SIZE, BUF_SIZE };
|
Net::Nic _nic { _env, _heap, *this, _verbose };
|
||||||
Signal_handler _sink_ack { _env.ep(), *this, &Main::_ack_avail };
|
Ipv4_address const _dst_ip { _config.attribute_value("dst_ip", Ipv4_address()) };
|
||||||
Signal_handler _sink_submit { _env.ep(), *this, &Main::_ready_to_submit };
|
Mac_address _dst_mac { };
|
||||||
Signal_handler _source_ack { _env.ep(), *this, &Main::_ready_to_ack };
|
uint16_t _ip_id { 1 };
|
||||||
Signal_handler _source_submit { _env.ep(), *this, &Main::_packet_avail };
|
uint16_t _icmp_seq { 1 };
|
||||||
bool const _verbose { _config.attribute_value("verbose", false) };
|
unsigned long _count { _config.attribute_value("count", (unsigned long)DEFAULT_COUNT) };
|
||||||
Ipv4_address const _src_ip { _config.attribute_value("src_ip", Ipv4_address()) };
|
Constructible<Dhcp_client> _dhcp_client { };
|
||||||
Ipv4_address const _dst_ip { _config.attribute_value("dst_ip", Ipv4_address()) };
|
Reconstructible<Ipv4_config> _ip_config { _config.attribute_value("interface", Ipv4_address_prefix()),
|
||||||
Mac_address const _src_mac { _nic.mac_address() };
|
_config.attribute_value("gateway", Ipv4_address()),
|
||||||
Mac_address _dst_mac { };
|
Ipv4_address() };
|
||||||
uint16_t _ip_id { 1 };
|
|
||||||
uint16_t _icmp_seq { 1 };
|
|
||||||
unsigned long _count { _config.attribute_value("count", (unsigned long)DEFAULT_COUNT) };
|
|
||||||
|
|
||||||
void _handle_eth(void *const eth_base,
|
|
||||||
Size_guard &size_guard);
|
|
||||||
|
|
||||||
void _handle_ip(Ethernet_frame ð,
|
void _handle_ip(Ethernet_frame ð,
|
||||||
Size_guard &size_guard);
|
Size_guard &size_guard);
|
||||||
@ -103,94 +93,93 @@ class Main
|
|||||||
void _handle_arp(Ethernet_frame ð,
|
void _handle_arp(Ethernet_frame ð,
|
||||||
Size_guard &size_guard);
|
Size_guard &size_guard);
|
||||||
|
|
||||||
void _broadcast_arp_request();
|
void _broadcast_arp_request(Ipv4_address const &ip);
|
||||||
|
|
||||||
void _send_arp_reply(Ethernet_frame &req_eth,
|
void _send_arp_reply(Ethernet_frame &req_eth,
|
||||||
Arp_packet &req_arp);
|
Arp_packet &req_arp);
|
||||||
|
|
||||||
template <typename FUNC>
|
|
||||||
void _send(size_t pkt_size,
|
|
||||||
FUNC && write_to_pkt)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
Packet_descriptor pkt = _source().alloc_packet(pkt_size);
|
|
||||||
void *pkt_base = _source().packet_content(pkt);
|
|
||||||
Size_guard size_guard(pkt_size);
|
|
||||||
write_to_pkt(pkt_base, size_guard);
|
|
||||||
_source().submit_packet(pkt);
|
|
||||||
if (_verbose) {
|
|
||||||
try {
|
|
||||||
Size_guard size_guard(pkt_size);
|
|
||||||
log("snd ", Ethernet_frame::cast_from(pkt_base, size_guard));
|
|
||||||
}
|
|
||||||
catch (Size_guard::Exceeded) { log("snd ?"); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Net::Packet_stream_source::Packet_alloc_failed) {
|
|
||||||
warning("failed to allocate packet"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
void _send_ping(Duration not_used = Duration(Microseconds(0)));
|
void _send_ping(Duration not_used = Duration(Microseconds(0)));
|
||||||
|
|
||||||
Net::Packet_stream_sink &_sink() { return *_nic.rx(); }
|
|
||||||
Net::Packet_stream_source &_source() { return *_nic.tx(); }
|
|
||||||
|
|
||||||
|
|
||||||
/***********************************
|
|
||||||
** Packet-stream signal handlers **
|
|
||||||
***********************************/
|
|
||||||
|
|
||||||
void _ready_to_submit();
|
|
||||||
void _ack_avail() { }
|
|
||||||
void _ready_to_ack();
|
|
||||||
void _packet_avail() { }
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
struct Invalid_arguments : Exception { };
|
struct Invalid_arguments : Exception { };
|
||||||
|
|
||||||
Main(Env &env);
|
Main(Env &env);
|
||||||
|
|
||||||
|
|
||||||
|
/*****************
|
||||||
|
** Nic_handler **
|
||||||
|
*****************/
|
||||||
|
|
||||||
|
void handle_eth(Ethernet_frame ð,
|
||||||
|
Size_guard &size_guard) override;
|
||||||
|
|
||||||
|
|
||||||
|
/*************************
|
||||||
|
** Dhcp_client_handler **
|
||||||
|
*************************/
|
||||||
|
|
||||||
|
void ip_config(Ipv4_config const &ip_config) override;
|
||||||
|
|
||||||
|
Ipv4_config const &ip_config() const override { return *_ip_config; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void Main::ip_config(Ipv4_config const &ip_config)
|
||||||
|
{
|
||||||
|
if (_verbose) {
|
||||||
|
log("IP config: ", ip_config); }
|
||||||
|
|
||||||
|
_ip_config.construct(ip_config);
|
||||||
|
_period.construct(_timer, *this, &Main::_send_ping, _period_us);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Main::Main(Env &env) : _env(env)
|
Main::Main(Env &env) : _env(env)
|
||||||
{
|
{
|
||||||
/* exit unsuccessful if parameters are invalid */
|
/* exit unsuccessful if parameters are invalid */
|
||||||
if (_src_ip == Ipv4_address() ||
|
if (_dst_ip == Ipv4_address() || _count == 0) {
|
||||||
_dst_ip == Ipv4_address() ||
|
throw Invalid_arguments(); }
|
||||||
_count == 0)
|
|
||||||
{
|
/* if there is a static IP config, start sending pings periodically */
|
||||||
throw Invalid_arguments();
|
if (ip_config().valid) {
|
||||||
}
|
_period.construct(_timer, *this, &Main::_send_ping, _period_us); }
|
||||||
/* install packet stream signals */
|
|
||||||
_nic.rx_channel()->sigh_ready_to_ack(_sink_ack);
|
/* else, start the DHCP client for requesting an IP config */
|
||||||
_nic.rx_channel()->sigh_packet_avail(_sink_submit);
|
else {
|
||||||
_nic.tx_channel()->sigh_ack_avail(_source_ack);
|
_dhcp_client.construct(_heap, _timer, _nic, *this); }
|
||||||
_nic.tx_channel()->sigh_ready_to_submit(_source_submit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Main::_handle_eth(void *const eth_base,
|
void Main::handle_eth(Ethernet_frame ð,
|
||||||
Size_guard &size_guard)
|
Size_guard &size_guard)
|
||||||
{
|
{
|
||||||
/* print receipt message */
|
try {
|
||||||
Ethernet_frame ð = Ethernet_frame::cast_from(eth_base, size_guard);
|
/* print receipt message */
|
||||||
if (_verbose) {
|
|
||||||
log("rcv ", eth); }
|
|
||||||
|
|
||||||
/* drop packet if ETH does not target us */
|
|
||||||
if (eth.dst() != _src_mac &&
|
|
||||||
eth.dst() != Ethernet_frame::broadcast())
|
|
||||||
{
|
|
||||||
if (_verbose) {
|
if (_verbose) {
|
||||||
log("bad ETH destination"); }
|
log("rcv ", eth); }
|
||||||
return;
|
|
||||||
|
if (!ip_config().valid) {
|
||||||
|
_dhcp_client->handle_eth(eth, size_guard); }
|
||||||
|
|
||||||
|
/* drop packet if ETH does not target us */
|
||||||
|
if (eth.dst() != _nic.mac() &&
|
||||||
|
eth.dst() != Ethernet_frame::broadcast())
|
||||||
|
{
|
||||||
|
if (_verbose) {
|
||||||
|
log("bad ETH destination"); }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* select ETH sub-protocol */
|
||||||
|
switch (eth.type()) {
|
||||||
|
case Ethernet_frame::Type::ARP: _handle_arp(eth, size_guard); break;
|
||||||
|
case Ethernet_frame::Type::IPV4: _handle_ip(eth, size_guard); break;
|
||||||
|
default: ; }
|
||||||
|
}
|
||||||
|
catch (Drop_packet_inform exception) {
|
||||||
|
if (_verbose) {
|
||||||
|
log("drop packet: ", exception.msg); }
|
||||||
}
|
}
|
||||||
/* select ETH sub-protocol */
|
|
||||||
switch (eth.type()) {
|
|
||||||
case Ethernet_frame::Type::ARP: _handle_arp(eth, size_guard); break;
|
|
||||||
case Ethernet_frame::Type::IPV4: _handle_ip(eth, size_guard); break;
|
|
||||||
default: ; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -199,7 +188,7 @@ void Main::_handle_ip(Ethernet_frame ð,
|
|||||||
{
|
{
|
||||||
/* drop packet if IP does not target us */
|
/* drop packet if IP does not target us */
|
||||||
Ipv4_packet &ip = eth.data<Ipv4_packet>(size_guard);
|
Ipv4_packet &ip = eth.data<Ipv4_packet>(size_guard);
|
||||||
if (ip.dst() != _src_ip &&
|
if (ip.dst() != ip_config().interface.address &&
|
||||||
ip.dst() != Ipv4_packet::broadcast())
|
ip.dst() != Ipv4_packet::broadcast())
|
||||||
{
|
{
|
||||||
if (_verbose) {
|
if (_verbose) {
|
||||||
@ -355,9 +344,16 @@ void Main::_handle_arp(Ethernet_frame ð,
|
|||||||
case Arp_packet::REPLY:
|
case Arp_packet::REPLY:
|
||||||
|
|
||||||
/* check whether we waited for this ARP reply */
|
/* check whether we waited for this ARP reply */
|
||||||
if (_dst_mac != Mac_address() || arp.src_ip() != _dst_ip) {
|
if (_dst_mac != Mac_address()) {
|
||||||
return; }
|
return; }
|
||||||
|
|
||||||
|
if (ip_config().interface.prefix_matches(_dst_ip)) {
|
||||||
|
if (arp.src_ip() != _dst_ip) {
|
||||||
|
return; }
|
||||||
|
} else {
|
||||||
|
if (arp.src_ip() != ip_config().gateway) {
|
||||||
|
return; }
|
||||||
|
}
|
||||||
/* set destination MAC address and retry to ping */
|
/* set destination MAC address and retry to ping */
|
||||||
_dst_mac = arp.src_mac();
|
_dst_mac = arp.src_mac();
|
||||||
_send_ping();
|
_send_ping();
|
||||||
@ -366,7 +362,7 @@ void Main::_handle_arp(Ethernet_frame ð,
|
|||||||
case Arp_packet::REQUEST:
|
case Arp_packet::REQUEST:
|
||||||
|
|
||||||
/* check whether the ARP request targets us */
|
/* check whether the ARP request targets us */
|
||||||
if (arp.dst_ip() != _src_ip) {
|
if (arp.dst_ip() != ip_config().interface.address) {
|
||||||
return; }
|
return; }
|
||||||
|
|
||||||
_send_arp_reply(eth, arp);
|
_send_arp_reply(eth, arp);
|
||||||
@ -375,40 +371,16 @@ void Main::_handle_arp(Ethernet_frame ð,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Main::_ready_to_submit()
|
|
||||||
{
|
|
||||||
while (_sink().packet_avail()) {
|
|
||||||
|
|
||||||
Packet_descriptor const pkt = _sink().get_packet();
|
|
||||||
Size_guard size_guard(pkt.size());
|
|
||||||
_handle_eth(_sink().packet_content(pkt), size_guard);
|
|
||||||
|
|
||||||
if (!_sink().ready_to_ack()) {
|
|
||||||
error("ack state FULL");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_sink().acknowledge_packet(pkt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Main::_ready_to_ack()
|
|
||||||
{
|
|
||||||
while (_source().ack_avail()) {
|
|
||||||
_source().release_packet(_source().get_acked_packet()); }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Main::_send_arp_reply(Ethernet_frame &req_eth,
|
void Main::_send_arp_reply(Ethernet_frame &req_eth,
|
||||||
Arp_packet &req_arp)
|
Arp_packet &req_arp)
|
||||||
{
|
{
|
||||||
_send(sizeof(Ethernet_frame) + sizeof(Arp_packet),
|
_nic.send(sizeof(Ethernet_frame) + sizeof(Arp_packet),
|
||||||
[&] (void *pkt_base, Size_guard &size_guard)
|
[&] (void *pkt_base, Size_guard &size_guard)
|
||||||
{
|
{
|
||||||
/* write Ethernet header */
|
/* write Ethernet header */
|
||||||
Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
|
Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
|
||||||
eth.dst(req_eth.src());
|
eth.dst(req_eth.src());
|
||||||
eth.src(_src_mac);
|
eth.src(_nic.mac());
|
||||||
eth.type(Ethernet_frame::Type::ARP);
|
eth.type(Ethernet_frame::Type::ARP);
|
||||||
|
|
||||||
/* write ARP header */
|
/* write ARP header */
|
||||||
@ -418,23 +390,23 @@ void Main::_send_arp_reply(Ethernet_frame &req_eth,
|
|||||||
arp.hardware_address_size(sizeof(Mac_address));
|
arp.hardware_address_size(sizeof(Mac_address));
|
||||||
arp.protocol_address_size(sizeof(Ipv4_address));
|
arp.protocol_address_size(sizeof(Ipv4_address));
|
||||||
arp.opcode(Arp_packet::REPLY);
|
arp.opcode(Arp_packet::REPLY);
|
||||||
arp.src_mac(_src_mac);
|
arp.src_mac(_nic.mac());
|
||||||
arp.src_ip(_src_ip);
|
arp.src_ip(ip_config().interface.address);
|
||||||
arp.dst_mac(req_eth.src());
|
arp.dst_mac(req_eth.src());
|
||||||
arp.dst_ip(req_arp.src_ip());
|
arp.dst_ip(req_arp.src_ip());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Main::_broadcast_arp_request()
|
void Main::_broadcast_arp_request(Ipv4_address const &dst_ip)
|
||||||
{
|
{
|
||||||
_send(sizeof(Ethernet_frame) + sizeof(Arp_packet),
|
_nic.send(sizeof(Ethernet_frame) + sizeof(Arp_packet),
|
||||||
[&] (void *pkt_base, Size_guard &size_guard)
|
[&] (void *pkt_base, Size_guard &size_guard)
|
||||||
{
|
{
|
||||||
/* write Ethernet header */
|
/* write Ethernet header */
|
||||||
Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
|
Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
|
||||||
eth.dst(Mac_address(0xff));
|
eth.dst(Mac_address(0xff));
|
||||||
eth.src(_src_mac);
|
eth.src(_nic.mac());
|
||||||
eth.type(Ethernet_frame::Type::ARP);
|
eth.type(Ethernet_frame::Type::ARP);
|
||||||
|
|
||||||
/* write ARP header */
|
/* write ARP header */
|
||||||
@ -444,28 +416,32 @@ void Main::_broadcast_arp_request()
|
|||||||
arp.hardware_address_size(sizeof(Mac_address));
|
arp.hardware_address_size(sizeof(Mac_address));
|
||||||
arp.protocol_address_size(sizeof(Ipv4_address));
|
arp.protocol_address_size(sizeof(Ipv4_address));
|
||||||
arp.opcode(Arp_packet::REQUEST);
|
arp.opcode(Arp_packet::REQUEST);
|
||||||
arp.src_mac(_src_mac);
|
arp.src_mac(_nic.mac());
|
||||||
arp.src_ip(_src_ip);
|
arp.src_ip(ip_config().interface.address);
|
||||||
arp.dst_mac(Mac_address(0xff));
|
arp.dst_mac(Mac_address(0xff));
|
||||||
arp.dst_ip(_dst_ip);
|
arp.dst_ip(dst_ip);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Main::_send_ping(Duration)
|
void Main::_send_ping(Duration)
|
||||||
{
|
{
|
||||||
|
/* if we do not yet know the Ethernet destination, request it via ARP */
|
||||||
if (_dst_mac == Mac_address()) {
|
if (_dst_mac == Mac_address()) {
|
||||||
_broadcast_arp_request();
|
if (ip_config().interface.prefix_matches(_dst_ip)) {
|
||||||
|
_broadcast_arp_request(_dst_ip); }
|
||||||
|
else {
|
||||||
|
_broadcast_arp_request(ip_config().gateway); }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_send(sizeof(Ethernet_frame) + sizeof(Ipv4_packet) +
|
_nic.send(sizeof(Ethernet_frame) + sizeof(Ipv4_packet) +
|
||||||
sizeof(Icmp_packet) + ICMP_DATA_SIZE,
|
sizeof(Icmp_packet) + ICMP_DATA_SIZE,
|
||||||
[&] (void *pkt_base, Size_guard &size_guard)
|
[&] (void *pkt_base, Size_guard &size_guard)
|
||||||
{
|
{
|
||||||
/* create ETH header */
|
/* create ETH header */
|
||||||
Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
|
Ethernet_frame ð = Ethernet_frame::construct_at(pkt_base, size_guard);
|
||||||
eth.dst(_dst_mac);
|
eth.dst(_dst_mac);
|
||||||
eth.src(_src_mac);
|
eth.src(_nic.mac());
|
||||||
eth.type(Ethernet_frame::Type::IPV4);
|
eth.type(Ethernet_frame::Type::IPV4);
|
||||||
|
|
||||||
/* create IP header */
|
/* create IP header */
|
||||||
@ -475,7 +451,7 @@ void Main::_send_ping(Duration)
|
|||||||
ip.version(4);
|
ip.version(4);
|
||||||
ip.time_to_live(IPV4_TIME_TO_LIVE);
|
ip.time_to_live(IPV4_TIME_TO_LIVE);
|
||||||
ip.protocol(Ipv4_packet::Protocol::ICMP);
|
ip.protocol(Ipv4_packet::Protocol::ICMP);
|
||||||
ip.src(_src_ip);
|
ip.src(ip_config().interface.address);
|
||||||
ip.dst(_dst_ip);
|
ip.dst(_dst_ip);
|
||||||
|
|
||||||
/* create ICMP header */
|
/* create ICMP header */
|
||||||
|
46
repos/os/src/app/ping/nic.cc
Normal file
46
repos/os/src/app/ping/nic.cc
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* \brief NIC connection wrapper for a more convenient interface
|
||||||
|
* \author Martin Stein
|
||||||
|
* \date 2018-04-16
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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 <nic.h>
|
||||||
|
|
||||||
|
using namespace Net;
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
|
||||||
|
void Net::Nic::_ready_to_ack()
|
||||||
|
{
|
||||||
|
while (_source().ack_avail()) {
|
||||||
|
_source().release_packet(_source().get_acked_packet()); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Net::Nic::_ready_to_submit()
|
||||||
|
{
|
||||||
|
while (_sink().packet_avail()) {
|
||||||
|
|
||||||
|
Packet_descriptor const pkt = _sink().get_packet();
|
||||||
|
if (!pkt.size()) {
|
||||||
|
continue; }
|
||||||
|
|
||||||
|
Size_guard size_guard(pkt.size());
|
||||||
|
_handler.handle_eth(Ethernet_frame::cast_from(_sink().packet_content(pkt), size_guard),
|
||||||
|
size_guard);
|
||||||
|
|
||||||
|
if (!_sink().ready_to_ack()) {
|
||||||
|
error("ack state FULL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_sink().acknowledge_packet(pkt);
|
||||||
|
}
|
||||||
|
}
|
129
repos/os/src/app/ping/nic.h
Normal file
129
repos/os/src/app/ping/nic.h
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* \brief NIC connection wrapper for a more convenient interface
|
||||||
|
* \author Martin Stein
|
||||||
|
* \date 2018-04-16
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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 _NIC_H_
|
||||||
|
#define _NIC_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <nic_session/connection.h>
|
||||||
|
#include <nic/packet_allocator.h>
|
||||||
|
#include <net/ethernet.h>
|
||||||
|
|
||||||
|
namespace Genode {
|
||||||
|
|
||||||
|
class Env;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Net {
|
||||||
|
|
||||||
|
struct Nic_handler;
|
||||||
|
class Nic;
|
||||||
|
|
||||||
|
using Packet_descriptor = ::Nic::Packet_descriptor;
|
||||||
|
using Packet_stream_sink = ::Nic::Packet_stream_sink< ::Nic::Session::Policy>;
|
||||||
|
using Packet_stream_source = ::Nic::Packet_stream_source< ::Nic::Session::Policy>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Net::Nic_handler
|
||||||
|
{
|
||||||
|
virtual void handle_eth(Ethernet_frame ð,
|
||||||
|
Size_guard &size_guard) = 0;
|
||||||
|
|
||||||
|
virtual ~Nic_handler() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Net::Nic
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
using Signal_handler = Genode::Signal_handler<Nic>;
|
||||||
|
|
||||||
|
enum { PKT_SIZE = ::Nic::Packet_allocator::DEFAULT_PACKET_SIZE };
|
||||||
|
enum { BUF_SIZE = ::Nic::Session::QUEUE_SIZE * PKT_SIZE };
|
||||||
|
|
||||||
|
Genode::Env &_env;
|
||||||
|
Genode::Allocator &_alloc;
|
||||||
|
Nic_handler &_handler;
|
||||||
|
bool const &_verbose;
|
||||||
|
::Nic::Packet_allocator _pkt_alloc { &_alloc };
|
||||||
|
::Nic::Connection _nic { _env, &_pkt_alloc, BUF_SIZE, BUF_SIZE };
|
||||||
|
Signal_handler _sink_ack { _env.ep(), *this, &Nic::_ack_avail };
|
||||||
|
Signal_handler _sink_submit { _env.ep(), *this, &Nic::_ready_to_submit };
|
||||||
|
Signal_handler _source_ack { _env.ep(), *this, &Nic::_ready_to_ack };
|
||||||
|
Signal_handler _source_submit { _env.ep(), *this, &Nic::_packet_avail };
|
||||||
|
Mac_address const _mac { _nic.mac_address() };
|
||||||
|
|
||||||
|
Net::Packet_stream_sink &_sink() { return *_nic.rx(); }
|
||||||
|
Net::Packet_stream_source &_source() { return *_nic.tx(); }
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************
|
||||||
|
** Packet-stream signal handlers **
|
||||||
|
***********************************/
|
||||||
|
|
||||||
|
void _ready_to_submit();
|
||||||
|
void _ack_avail() { }
|
||||||
|
void _ready_to_ack();
|
||||||
|
void _packet_avail() { }
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Nic(Genode::Env &env,
|
||||||
|
Genode::Allocator &alloc,
|
||||||
|
Nic_handler &handler,
|
||||||
|
bool const &verbose)
|
||||||
|
:
|
||||||
|
_env (env),
|
||||||
|
_alloc (alloc),
|
||||||
|
_handler (handler),
|
||||||
|
_verbose (verbose)
|
||||||
|
{
|
||||||
|
/* install packet stream signals */
|
||||||
|
_nic.rx_channel()->sigh_ready_to_ack(_sink_ack);
|
||||||
|
_nic.rx_channel()->sigh_packet_avail(_sink_submit);
|
||||||
|
_nic.tx_channel()->sigh_ack_avail(_source_ack);
|
||||||
|
_nic.tx_channel()->sigh_ready_to_submit(_source_submit);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
void send(Genode::size_t pkt_size,
|
||||||
|
FUNC && write_to_pkt)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Packet_descriptor pkt = _source().alloc_packet(pkt_size);
|
||||||
|
void *pkt_base = _source().packet_content(pkt);
|
||||||
|
Size_guard size_guard(pkt_size);
|
||||||
|
write_to_pkt(pkt_base, size_guard);
|
||||||
|
_source().submit_packet(pkt);
|
||||||
|
if (_verbose) {
|
||||||
|
Size_guard size_guard(pkt_size);
|
||||||
|
try { Genode::log("snd ", Ethernet_frame::cast_from(pkt_base, size_guard)); }
|
||||||
|
catch (Size_guard::Exceeded) { Genode::log("snd ?"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Net::Packet_stream_source::Packet_alloc_failed) {
|
||||||
|
Genode::warning("failed to allocate packet"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***************
|
||||||
|
** Accessors **
|
||||||
|
***************/
|
||||||
|
|
||||||
|
Mac_address const &mac() const { return _mac; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _NIC_H_ */
|
@ -2,7 +2,8 @@ TARGET = ping
|
|||||||
|
|
||||||
LIBS += base net
|
LIBS += base net
|
||||||
|
|
||||||
SRC_CC += main.cc
|
SRC_CC += main.cc dhcp_client.cc xml_node.cc ipv4_address_prefix.cc
|
||||||
|
SRC_CC += nic.cc ipv4_config.cc
|
||||||
|
|
||||||
INC_DIR += $(PRG_DIR)
|
INC_DIR += $(PRG_DIR)
|
||||||
|
|
||||||
|
29
repos/os/src/app/ping/xml_node.cc
Normal file
29
repos/os/src/app/ping/xml_node.cc
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* \brief Genode XML nodes plus local utilities
|
||||||
|
* \author Martin Stein
|
||||||
|
* \date 2016-08-19
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 <xml_node.h>
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
|
||||||
|
Microseconds Genode::read_sec_attr(Xml_node const node,
|
||||||
|
char const *name,
|
||||||
|
unsigned long const default_sec)
|
||||||
|
{
|
||||||
|
unsigned long sec = node.attribute_value(name, 0UL);
|
||||||
|
if (!sec) {
|
||||||
|
sec = default_sec;
|
||||||
|
}
|
||||||
|
return Microseconds(sec * 1000 * 1000);
|
||||||
|
}
|
29
repos/os/src/app/ping/xml_node.h
Normal file
29
repos/os/src/app/ping/xml_node.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* \brief Genode XML nodes plus local utilities
|
||||||
|
* \author Martin Stein
|
||||||
|
* \date 2016-08-19
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _XML_NODE_H_
|
||||||
|
#define _XML_NODE_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/xml_node.h>
|
||||||
|
#include <os/duration.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Genode {
|
||||||
|
|
||||||
|
Microseconds read_sec_attr(Xml_node const node,
|
||||||
|
char const *name,
|
||||||
|
unsigned long const default_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _XML_NODE_H_ */
|
Loading…
x
Reference in New Issue
Block a user